mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-11-08 02:11:44 +00:00
* refactor openbb-technical for v5 * forward refs * linting * no python 3.15 in test matrix * reame * ty * move all params to body --------- Co-authored-by: deeleeramone <>
120 lines
4.0 KiB
Python
120 lines
4.0 KiB
Python
"""Tests for openbb_technical.signals.regime."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import numpy as np
|
|
import pandas as pd
|
|
import pytest
|
|
from openbb_core.app.utils import df_to_basemodel
|
|
|
|
from openbb_technical.signals.regime import (
|
|
RegimeData,
|
|
RegimeQueryParams,
|
|
_classify,
|
|
regime,
|
|
)
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def trending_records():
|
|
"""A clean monotone uptrend — ADX should rise, choppiness should fall."""
|
|
n = 120
|
|
base = np.arange(1, n + 1, dtype="float64")
|
|
df = pd.DataFrame(
|
|
{
|
|
"open": base,
|
|
"high": base + 0.5,
|
|
"low": base - 0.5,
|
|
"close": base,
|
|
"volume": base * 100,
|
|
},
|
|
index=pd.date_range("2022-01-01", periods=n, freq="D", name="date"),
|
|
)
|
|
return df_to_basemodel(df.reset_index())
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def choppy_records():
|
|
"""Sideways sawtooth so choppiness sits near the upper rail."""
|
|
n = 120
|
|
sawtooth = np.tile([100.0, 101.0, 100.0, 99.0], n // 4 + 1)[:n]
|
|
df = pd.DataFrame(
|
|
{
|
|
"open": sawtooth,
|
|
"high": sawtooth + 0.2,
|
|
"low": sawtooth - 0.2,
|
|
"close": sawtooth,
|
|
"volume": np.full(n, 1000.0),
|
|
},
|
|
index=pd.date_range("2022-01-01", periods=n, freq="D", name="date"),
|
|
)
|
|
return df_to_basemodel(df.reset_index())
|
|
|
|
|
|
class TestRegimeEndpoint:
|
|
def test_returns_one_row_per_bar(self, trending_records):
|
|
result = regime(RegimeQueryParams(data=trending_records))
|
|
assert len(result.results) == 120
|
|
assert all(isinstance(r, RegimeData) for r in result.results)
|
|
|
|
def test_trending_data_reaches_trend_label(self, trending_records):
|
|
result = regime(RegimeQueryParams(data=trending_records))
|
|
labels = {r.regime for r in result.results}
|
|
assert "strong_trend" in labels or "weak_trend" in labels
|
|
|
|
def test_first_bars_are_warm_up_transition(self, trending_records):
|
|
result = regime(RegimeQueryParams(data=trending_records))
|
|
assert result.results[0].regime == "transition"
|
|
assert result.results[0].regime_changed is False
|
|
assert result.results[0].adx is None
|
|
assert result.results[0].choppiness is None
|
|
|
|
def test_choppy_data_hits_ranging(self, choppy_records):
|
|
result = regime(RegimeQueryParams(data=choppy_records))
|
|
labels = {r.regime for r in result.results}
|
|
assert "ranging" in labels
|
|
|
|
def test_regime_changed_flag(self, trending_records):
|
|
result = regime(RegimeQueryParams(data=trending_records))
|
|
assert any(r.regime_changed for r in result.results)
|
|
|
|
def test_custom_thresholds_alter_labels(self, trending_records):
|
|
tight = regime(
|
|
RegimeQueryParams(data=trending_records, adx_trend_threshold=200.0)
|
|
)
|
|
assert "strong_trend" not in {r.regime for r in tight.results}
|
|
|
|
|
|
class TestClassifyDirect:
|
|
"""Drive ``_classify`` directly to guarantee every branch executes."""
|
|
|
|
def test_none_adx_short_circuits(self):
|
|
assert _classify(None, 10.0, 25.0, 61.8) == "transition"
|
|
|
|
def test_none_choppiness_short_circuits(self):
|
|
assert _classify(50.0, None, 25.0, 61.8) == "transition"
|
|
|
|
def test_strong_trend(self):
|
|
assert _classify(40.0, 30.0, 25.0, 61.8) == "strong_trend"
|
|
|
|
def test_weak_trend(self):
|
|
assert _classify(20.0, 70.0, 25.0, 61.8) == "weak_trend"
|
|
|
|
def test_weak_trend_high_chop_overrides_strong(self):
|
|
assert _classify(30.0, 70.0, 25.0, 61.8) == "weak_trend"
|
|
|
|
def test_ranging(self):
|
|
assert _classify(5.0, 70.0, 25.0, 61.8) == "ranging"
|
|
|
|
def test_transition_fallback(self):
|
|
assert _classify(5.0, 30.0, 25.0, 61.8) == "transition"
|
|
|
|
|
|
class TestQueryParamModel:
|
|
def test_defaults(self, trending_records):
|
|
params = RegimeQueryParams(data=trending_records)
|
|
assert params.adx_length == 14
|
|
assert params.choppiness_length == 14
|
|
assert params.adx_trend_threshold == 25.0
|
|
assert params.choppiness_range_threshold == 61.8
|