diff --git a/src/quant_strategy_plugins/market_regime_control_plugin.py b/src/quant_strategy_plugins/market_regime_control_plugin.py index d77fc94..96d8d5f 100644 --- a/src/quant_strategy_plugins/market_regime_control_plugin.py +++ b/src/quant_strategy_plugins/market_regime_control_plugin.py @@ -432,6 +432,15 @@ def build_market_regime_control_signal( final_route = ROUTE_BLOCKED suggested_action = ACTION_BLOCKED route_source = COMPONENT_BENCHMARK_GUARD + # The generic guard owns a hard fail-closed data contract. Keep this + # output shadow-only, but expose zero scalars and a defense requirement + # so a separately approved strategy consumer cannot accidentally treat + # absent/stale benchmark data as permission to retain risk. + risk_budget_scalar = 0.0 + leverage_scalar = 0.0 + risk_asset_scalar = 0.0 + crisis_defense_required = True + blocked_actions = ("increase_leverage", "increase_risk", "taco_rebound_veto", "panic_reversal_veto") reason_codes.extend( f"benchmark_guard:{code}" for code in _reason_codes(benchmark_guard) or ("blocked",) diff --git a/tests/test_benchmark_drawdown_guard.py b/tests/test_benchmark_drawdown_guard.py index 83af715..91d6885 100644 --- a/tests/test_benchmark_drawdown_guard.py +++ b/tests/test_benchmark_drawdown_guard.py @@ -114,3 +114,30 @@ def test_unified_market_regime_requires_an_explicit_policy_to_mount_the_guard() assert payload["position_control"]["risk_asset_scalar"] == 0.5 with pytest.raises(ValueError, match="explicit frozen policy"): _build_market_regime_control_payload(_prices([100.0] * 30), {"benchmark_drawdown_guard_enabled": True}) + + +def test_unified_market_regime_preserves_fail_closed_scalars_when_guard_data_is_unavailable() -> None: + config = { + "crisis_enabled": False, + "macro_enabled": False, + "taco_enabled": False, + "panic_reversal_enabled": False, + "benchmark_drawdown_guard_enabled": True, + "benchmark_guard_benchmark_symbol": "QQQ", + "benchmark_guard_drawdown_lookback_sessions": 20, + "benchmark_guard_soft_drawdown_threshold": -0.05, + "benchmark_guard_hard_drawdown_threshold": -0.10, + "benchmark_guard_soft_risk_asset_scalar": 0.50, + "benchmark_guard_hard_risk_asset_scalar": 0.0, + "benchmark_guard_max_price_age_days": 3, + "as_of": "2026-02-12", + } + + payload = _build_market_regime_control_payload(pd.DataFrame(), config) + + assert payload["canonical_route"] == ROUTE_BLOCKED + assert payload["position_control"]["risk_budget_scalar"] == 0.0 + assert payload["position_control"]["leverage_scalar"] == 0.0 + assert payload["position_control"]["risk_asset_scalar"] == 0.0 + assert payload["position_control"]["crisis_defense_required"] is True + assert payload["execution_controls"]["broker_order_allowed"] is False