diff --git a/contracts/signals.schema.json b/contracts/signals.schema.json index 1956a55..d321abe 100644 --- a/contracts/signals.schema.json +++ b/contracts/signals.schema.json @@ -20,8 +20,8 @@ }, "market_regime": { "type": "string", - "enum": ["bull", "neutral", "caution", "bear"], - "description": "Current market regime assessment" + "enum": ["bull", "neutral", "bear"], + "description": "Current market regime assessment. 3-class Ang-Bekaert taxonomy (v0.42.0 / 2026-05-28). Legacy 4-class 'caution' retired per caution-regime-retirement-260528.md — its driving signals (VIX, HY OAS, SPY 30d return) are weighted continuously into regime_intensity_z (predictor META_FEATURE 13) rather than discretized into a 4th category. Portfolio-protective hysteresis is a separate axis on the predictor drawdown leg (risk_on/caution/risk_off). Historical signals.json artifacts with 'caution' in market_regime are grandfathered — consumers tolerant of legacy enum on read." }, "sector_ratings": { "type": "object", diff --git a/tests/integration/test_data_contract_validation.py b/tests/integration/test_data_contract_validation.py index d41c944..c99cf61 100644 --- a/tests/integration/test_data_contract_validation.py +++ b/tests/integration/test_data_contract_validation.py @@ -69,6 +69,41 @@ def test_missing_required_field_fails(self): with pytest.raises(ValidationError): validate(instance=bad_signals, schema=schema) + def test_market_regime_enum_is_3class(self): + # 3-class Ang-Bekaert taxonomy (v0.42.0 / 2026-05-28). + # Legacy 4-class "caution" retired per + # caution-regime-retirement-260528.md. + schema = _load_schema("signals.schema.json") + regime_field = schema["properties"]["market_regime"] + assert regime_field["enum"] == ["bull", "neutral", "bear"] + + def test_market_regime_caution_rejected(self): + schema = _load_schema("signals.schema.json") + bad_signals = { + "date": "2026-04-03", + "run_time": "00:30:00", + "market_regime": "caution", + "sector_ratings": {}, + "universe": [], + "buy_candidates": [], + } + with pytest.raises(ValidationError): + validate(instance=bad_signals, schema=schema) + + def test_each_3class_regime_accepted(self): + schema = _load_schema("signals.schema.json") + for regime in ("bull", "neutral", "bear"): + signals = { + "date": "2026-04-03", + "run_time": "00:30:00", + "market_regime": regime, + "sector_ratings": {}, + "universe": [], + "buy_candidates": [], + } + # Should not raise + validate(instance=signals, schema=schema) + class TestPredictionsContract: def test_valid_predictions_pass(self):