mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-28 04:52:07 +00:00
* refactor quantitative for v5 * add factors endpoint and add a few charts --------- Co-authored-by: deeleeramone <>
126 lines
4.6 KiB
Python
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"]
|