Merge pull request #65 from aguzererler/testing-macro-regime-edge-cases-7674628334263228754

🧪 Fix VIX trend logic and add extensive tests for macro regime short history edge cases
This commit is contained in:
ahmet guzererler 2026-03-21 17:23:38 +01:00 committed by GitHub
commit 319feac087
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 83 additions and 2 deletions

View File

@ -86,6 +86,12 @@ class TestSignalVixTrend:
score, desc = self.fn(vix)
assert score == 0
def test_short_history_is_neutral(self):
vix = _make_series([20.0] * 20)
score, desc = self.fn(vix)
assert score == 0
assert "insufficient history" in desc
def test_none_series_is_neutral(self):
score, desc = self.fn(None)
assert score == 0
@ -110,6 +116,42 @@ class TestSignalCreditSpread:
score, desc = self.fn(hyg, lqd)
assert score == -1
def test_short_history_is_neutral(self):
hyg = _trending_series(80, 85, 21)
lqd = _flat_series(100, 21)
score, desc = self.fn(hyg, lqd)
assert score == 0
assert "insufficient history" in desc
def test_none_data_is_neutral(self):
score, _ = self.fn(None, None)
assert score == 0
class TestSignalYieldCurve:
def setup_method(self):
from tradingagents.dataflows.macro_regime import _signal_yield_curve
self.fn = _signal_yield_curve
def test_flight_to_safety_is_risk_off(self):
tlt = _trending_series(100, 110, 30)
shy = _flat_series(100, 30)
score, desc = self.fn(tlt, shy)
assert score == -1
def test_risk_appetite_is_risk_on(self):
tlt = _trending_series(110, 100, 30)
shy = _flat_series(100, 30)
score, desc = self.fn(tlt, shy)
assert score == 1
def test_short_history_is_neutral(self):
tlt = _trending_series(100, 110, 21)
shy = _flat_series(100, 21)
score, desc = self.fn(tlt, shy)
assert score == 0
assert "insufficient history" in desc
def test_none_data_is_neutral(self):
score, _ = self.fn(None, None)
assert score == 0
@ -140,6 +182,45 @@ class TestSignalMarketBreadth:
score, _ = self.fn(spx)
assert score == 0 # < 200 points for SMA200
def test_short_history_is_neutral(self):
spx = _make_series([5000.0] * 199)
score, desc = self.fn(spx)
assert score == 0
assert "insufficient history" in desc
def test_none_data_is_neutral(self):
score, _ = self.fn(None)
assert score == 0
class TestSignalSectorRotation:
def setup_method(self):
from tradingagents.dataflows.macro_regime import _signal_sector_rotation
self.fn = _signal_sector_rotation
def test_defensives_leading_is_risk_off(self):
defensives = {"XLU": _trending_series(100, 110, 30)}
cyclicals = {"XLY": _flat_series(100, 30)}
score, desc = self.fn(defensives, cyclicals)
assert score == -1
def test_cyclicals_leading_is_risk_on(self):
defensives = {"XLU": _flat_series(100, 30)}
cyclicals = {"XLY": _trending_series(100, 110, 30)}
score, desc = self.fn(defensives, cyclicals)
assert score == 1
def test_short_history_is_neutral(self):
defensives = {"XLU": _trending_series(100, 110, 21)}
cyclicals = {"XLY": _flat_series(100, 21)}
score, desc = self.fn(defensives, cyclicals)
assert score == 0
assert "unavailable" in desc
def test_empty_dicts_is_neutral(self):
score, desc = self.fn({}, {})
assert score == 0
# ---------------------------------------------------------------------------
# Classify macro regime

View File

@ -92,8 +92,8 @@ def _signal_vix_level(vix_price: Optional[float]) -> tuple[int, str]:
def _signal_vix_trend(vix_series: Optional[pd.Series]) -> tuple[int, str]:
"""VIX 5-day SMA vs 20-day SMA: rising VIX = risk-off."""
if vix_series is None:
return 0, "VIX trend: unavailable (neutral)"
if vix_series is None or len(vix_series) < 21:
return 0, "VIX trend: insufficient history (neutral)"
sma5 = _sma(vix_series, 5)
sma20 = _sma(vix_series, 20)
if sma5 is None or sma20 is None: