From 177d35ede5958d1acd68c3e8f0efabcb05b6b50b Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 21 Mar 2026 14:48:20 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=A7=AA=20Fix=20VIX=20trend=20logic=20and?= =?UTF-8?q?=20add=20extensive=20tests=20for=20macro=20regime=20short=20his?= =?UTF-8?q?tory=20edge=20cases?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fixed `_signal_vix_trend` to correctly return neutral for insufficient history (`< 21`). - Added `test_short_history_is_neutral` to `TestSignalVixTrend`. - Extended coverage for short history and edge cases in `TestSignalCreditSpread`, `TestSignalYieldCurve`, `TestSignalMarketBreadth`, and `TestSignalSectorRotation`. Co-authored-by: aguzererler <6199053+aguzererler@users.noreply.github.com> --- tests/unit/test_macro_regime.py | 81 +++++++++++++++++++++++++ tradingagents/dataflows/macro_regime.py | 4 +- 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_macro_regime.py b/tests/unit/test_macro_regime.py index 3271f2a4..fdd8dbbd 100644 --- a/tests/unit/test_macro_regime.py +++ b/tests/unit/test_macro_regime.py @@ -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 diff --git a/tradingagents/dataflows/macro_regime.py b/tradingagents/dataflows/macro_regime.py index 5ca30f3e..b828272f 100644 --- a/tradingagents/dataflows/macro_regime.py +++ b/tradingagents/dataflows/macro_regime.py @@ -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: