Files
OpenBB/openbb_platform/extensions/quantitative/tests/test_metrics.py
Danglewood cd7d602b8f [V5] openbb-quantitative: Refactor for V5 (#7506)
* refactor quantitative for v5

* add factors endpoint and add a few charts

---------

Co-authored-by: deeleeramone <>
2026-05-29 10:46:25 +03:00

126 lines
4.6 KiB
Python

"""Tests for ``openbb_quantitative.metrics`` - quantitative analysis commands."""
import numpy as np
import pandas as pd
import pytest
from openbb_quantitative import metrics
def test_normality(prices_data):
"""Normality returns finite test statistics and p-values for every test."""
out = metrics.normality(
metrics.NormalityQueryParams(data=prices_data, target="close")
)
dumped = out.results.model_dump()
for field in metrics.NormalityQueryParams.__output_columns__:
value = dumped[field]
assert isinstance(value, float)
assert np.isfinite(value)
def test_normality_non_numeric_raises():
"""A non-numeric target column raises a clear ValueError."""
data = [{"close": "a"}, {"close": "b"}, {"close": "c"}]
params = metrics.NormalityQueryParams(data=data, target="close")
with pytest.raises(ValueError, match="must be numeric"):
metrics.normality(params)
def test_capm(prices_data, monkeypatch):
"""CAPM reports finite risk measures from mocked monthly Fama-French factors."""
def fake(start_date, end_date):
rng = np.random.default_rng(0)
index = pd.date_range(start_date, end_date, freq="MS")
return pd.DataFrame(
{
"mkt_rf": rng.normal(0.01, 0.04, len(index)),
"smb": rng.normal(0.0, 0.02, len(index)),
"hml": rng.normal(0.0, 0.02, len(index)),
"rf": rng.normal(0.002, 0.001, len(index)),
},
index=index,
)
monkeypatch.setattr("openbb_quantitative.helpers.get_fama_raw", fake)
out = metrics.capm(metrics.CapmQueryParams(data=prices_data, target="close"))
dumped = out.results.model_dump()
for field in metrics.CapmQueryParams.__output_columns__:
value = dumped[field]
assert isinstance(value, float)
assert np.isfinite(value)
def test_unitroot_test(prices_data):
"""The unit root test reports finite ADF and KPSS statistics."""
out = metrics.unitroot_test(
metrics.UnitRootTestQueryParams(data=prices_data, target="close")
)
params = metrics.UnitRootTestQueryParams(data=prices_data, target="close")
assert params.fuller_reg == "c"
assert params.kpss_reg == "c"
assert params.maxlag is None
assert params.autolag == "AIC"
assert params.nlags == "auto"
dumped = out.results.model_dump()
assert isinstance(dumped["adf_statistic"], float)
assert np.isfinite(dumped["adf_statistic"])
assert isinstance(dumped["adf_p_value"], float)
assert isinstance(dumped["adf_nlags"], int)
assert isinstance(dumped["adf_nobs"], int)
assert isinstance(dumped["adf_icbest"], float)
assert isinstance(dumped["kpss_statistic"], float)
assert isinstance(dumped["kpss_p_value"], float)
assert isinstance(dumped["kpss_nlags"], int)
assert isinstance(dumped["kpss_p_value_interpolated"], bool)
def test_unitroot_test_regression_options(prices_data):
"""The unit root test accepts non-default ADF and KPSS regression terms."""
out = metrics.unitroot_test(
metrics.UnitRootTestQueryParams(
data=prices_data,
target="close",
fuller_reg="ct",
kpss_reg="ct",
)
)
dumped = out.results.model_dump()
assert np.isfinite(dumped["adf_statistic"])
assert np.isfinite(dumped["kpss_statistic"])
def test_unitroot_test_no_autolag(prices_data):
"""With autolag disabled the ADF test uses maxlag directly and has no icbest."""
out = metrics.unitroot_test(
metrics.UnitRootTestQueryParams(
data=prices_data, target="close", maxlag=4, autolag=None
)
)
dumped = out.results.model_dump()
assert dumped["adf_nlags"] == 4
assert dumped["adf_icbest"] is None
def test_unitroot_test_explicit_kpss_nlags(prices_data):
"""The KPSS test accepts an explicit integer lag count."""
out = metrics.unitroot_test(
metrics.UnitRootTestQueryParams(data=prices_data, target="close", nlags=6)
)
assert out.results.kpss_nlags == 6
def test_summary(prices_data):
"""Summary reports finite descriptive statistics for the target column."""
out = metrics.summary(metrics.SummaryQueryParams(data=prices_data, target="close"))
dumped = out.results.model_dump()
assert isinstance(dumped["count"], int)
assert dumped["count"] == 250
for field in ("mean", "std", "var", "min", "p25", "p50", "p75", "max"):
value = dumped[field]
assert isinstance(value, float)
assert np.isfinite(value)
assert dumped["min"] <= dumped["p25"] <= dumped["p50"] <= dumped["p75"]
assert dumped["p75"] <= dumped["max"]