Skip to content

Commit 34c9734

Browse files
committed
Add versioned market regime plugin contract
1 parent ed07ba9 commit 34c9734

3 files changed

Lines changed: 133 additions & 62 deletions

File tree

docs/examples/strategy_plugins.example.toml

Lines changed: 67 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,72 @@ default_mode = "shadow"
33

44
[[strategy_plugins]]
55
strategy = "tqqq_growth_income"
6-
plugin = "crisis_response_shadow"
6+
plugin = "market_regime_control"
7+
enabled = true
8+
# Default runtime contract. This facade arbitrates crisis_response_shadow,
9+
# macro_risk_governor, and taco_rebound_shadow into one deterministic signal.
10+
# Strategy code consumes only notification and position_control from this
11+
# artifact; broker writes and live allocation mutation remain disabled.
12+
13+
[strategy_plugins.inputs]
14+
prices = "data/output/market_regime_control/input/tqqq_price_history.csv"
15+
external_context = "data/output/market_regime_control/input/external_context.csv"
16+
event_set = "geopolitical-deescalation"
17+
benchmark_symbol = "QQQ"
18+
attack_symbol = "TQQQ"
19+
vix_symbols = ["VIX", "^VIX", "VIXCLS"]
20+
credit_pairs = ["HYG:IEF", "LQD:IEF"]
21+
financial_symbols = ["XLF", "KRE"]
22+
rate_symbols = ["IEF", "TLT"]
23+
strategy_policy = "levered_growth_income_v1"
24+
realized_vol_threshold = 0.30
25+
realized_vol_requires_confirmation = true
26+
delever_risk_asset_scalar = 0.0
27+
taco_opportunity_size_scalar = 0.0
28+
crisis_enabled = true
29+
macro_enabled = true
30+
taco_enabled = true
31+
32+
[strategy_plugins.outputs]
33+
output_dir = "data/output/tqqq_growth_income/plugins/market_regime_control"
34+
35+
[[strategy_plugins]]
36+
strategy = "soxl_soxx_trend_income"
37+
plugin = "market_regime_control"
738
enabled = true
8-
# The runner enforces plugin/strategy compatibility. This plugin is scoped to
9-
# TQQQ/SOXL leveraged equity black-swan defense strategies.
10-
# mode is optional when it matches default_mode; only shadow notification mode is supported.
39+
# SOXL uses the same macro/crisis contract. TACO stays disabled because the
40+
# current rebound-event playbook is calibrated for TQQQ/QQQ.
41+
42+
[strategy_plugins.inputs]
43+
prices = "data/output/market_regime_control/input/soxl_price_history.csv"
44+
external_context = "data/output/market_regime_control/input/external_context.csv"
45+
event_set = "full"
46+
benchmark_symbol = "SOXX"
47+
attack_symbol = "SOXL"
48+
vix_symbols = ["VIX", "^VIX", "VIXCLS"]
49+
credit_pairs = ["HYG:IEF", "LQD:IEF"]
50+
financial_symbols = ["XLF", "KRE"]
51+
rate_symbols = ["IEF", "TLT"]
52+
strategy_policy = "levered_growth_income_v1"
53+
realized_vol_threshold = 0.30
54+
realized_vol_requires_confirmation = true
55+
delever_risk_asset_scalar = 0.0
56+
taco_enabled = false
57+
crisis_enabled = true
58+
macro_enabled = true
59+
60+
[strategy_plugins.outputs]
61+
output_dir = "data/output/soxl_soxx_trend_income/plugins/market_regime_control"
62+
63+
# Deprecated compatibility mounts. They remain runnable for historical
64+
# backtests and downstream consumers that have not migrated to
65+
# market_regime_control yet. New strategy integrations should not read them
66+
# directly.
67+
68+
[[strategy_plugins]]
69+
strategy = "tqqq_growth_income"
70+
plugin = "crisis_response_shadow"
71+
enabled = false
1172

1273
[strategy_plugins.inputs]
1374
prices = "data/output/crisis_response_shadow/input/price_history.csv"
@@ -16,30 +77,14 @@ event_set = "full"
1677
financial_symbols = ["XLF", "KRE"]
1778
credit_pairs = ["HYG:IEF", "LQD:IEF"]
1879
rate_symbols = ["IEF", "TLT"]
19-
# Optional shadow-only AI audit. API keys are read from env vars, not TOML:
20-
# QSP_STRATEGY_PLUGIN_AI_AUDIT_API_KEY / QSP_STRATEGY_PLUGIN_AI_AUDIT_FALLBACK_API_KEY
21-
# Legacy QSP_CRISIS_AI_AUDIT_* names are still accepted.
22-
ai_audit_enabled = true
23-
ai_audit_codex_enabled = true
24-
ai_audit_model = "gpt-5.4-mini"
25-
# ai_audit_base_url = "https://api.openai.com/v1"
26-
# ai_audit_fallback_base_url = "https://fallback.example.com/v1"
27-
# ai_audit_fallback_model = "fallback-model"
28-
# Anthropic fallback follows the CryptoCodexAuditBridge provider-fallback style.
29-
# ai_audit_anthropic_model = "claude-sonnet-4-6"
30-
# ai_audit_anthropic_base_url = "https://api.anthropic.com/v1"
31-
# ai_audit_anthropic_version = "2023-06-01"
3280

3381
[strategy_plugins.outputs]
3482
output_dir = "data/output/tqqq_growth_income/plugins/crisis_response_shadow"
3583

3684
[[strategy_plugins]]
3785
strategy = "tqqq_growth_income"
3886
plugin = "macro_risk_governor"
39-
enabled = true
40-
# Deterministic macro de-leveraging governor. VIX/credit/price stress can emit
41-
# leverage_scalar and risk_asset_scalar for strategies that explicitly consume
42-
# mounted plugin metadata. OSINT fields remain watch-only evidence.
87+
enabled = false
4388

4489
[strategy_plugins.inputs]
4590
prices = "data/output/macro_risk_governor/input/price_history.csv"
@@ -55,50 +100,12 @@ output_dir = "data/output/tqqq_growth_income/plugins/macro_risk_governor"
55100
[[strategy_plugins]]
56101
strategy = "tqqq_growth_income"
57102
plugin = "taco_rebound_shadow"
58-
enabled = true
59-
# Notification-only TACO context. The artifact may trigger manual-review alerts,
60-
# but it never recommends position size or mutates allocations.
61-
# Manual-review alerts require post-event price rebound confirmation by default.
103+
enabled = false
62104

63105
[strategy_plugins.inputs]
64106
prices = "data/output/taco_rebound_shadow/input/price_history.csv"
65107
event_set = "geopolitical-deescalation"
66108
start_date = "2026-01-01"
67-
# Optional shadow-only AI audit for event/source quality. It cannot alter the
68-
# manual-review/watch-only route or generate allocation/order instructions.
69-
ai_audit_enabled = true
70-
ai_audit_codex_enabled = true
71-
ai_audit_model = "gpt-5.4-mini"
72-
# ai_audit_anthropic_model = "claude-sonnet-4-6"
73109

74110
[strategy_plugins.outputs]
75111
output_dir = "data/output/tqqq_growth_income/plugins/taco_rebound_shadow"
76-
77-
[[strategy_plugins]]
78-
strategy = "tqqq_growth_income"
79-
plugin = "market_regime_control"
80-
enabled = true
81-
# Unified facade over crisis_response_shadow, macro_risk_governor, and
82-
# taco_rebound_shadow. It emits one deterministic arbiter result with two
83-
# exits: notification and strategy-opt-in position_control. Broker writes and
84-
# live allocation mutation remain disabled in shadow mode.
85-
86-
[strategy_plugins.inputs]
87-
prices = "data/output/market_regime_control/input/price_history.csv"
88-
external_context = "data/output/market_regime_control/input/external_context.csv"
89-
event_set = "geopolitical-deescalation"
90-
benchmark_symbol = "QQQ"
91-
attack_symbol = "TQQQ"
92-
vix_symbols = ["VIX", "^VIX", "VIXCLS"]
93-
credit_pairs = ["HYG:IEF", "LQD:IEF"]
94-
financial_symbols = ["XLF", "KRE"]
95-
rate_symbols = ["IEF", "TLT"]
96-
strategy_policy = "levered_growth_income_v1"
97-
taco_opportunity_size_scalar = 0.0
98-
# Component toggles support staged shadow rollout.
99-
crisis_enabled = true
100-
macro_enabled = true
101-
taco_enabled = true
102-
103-
[strategy_plugins.outputs]
104-
output_dir = "data/output/tqqq_growth_income/plugins/market_regime_control"

src/quant_strategy_plugins/strategy_plugin_runner.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,21 @@
4040
SUPPORTED_PLUGIN_MODES = (SHADOW_MODE,)
4141
PLUGIN_COMPATIBLE_STRATEGIES: dict[str, tuple[str, ...]] = {
4242
PLUGIN_CRISIS_RESPONSE_SHADOW: ("tqqq_growth_income", "soxl_soxx_trend_income"),
43-
PLUGIN_MARKET_REGIME_CONTROL: ("tqqq_growth_income",),
43+
PLUGIN_MARKET_REGIME_CONTROL: ("tqqq_growth_income", "soxl_soxx_trend_income"),
4444
PLUGIN_MACRO_RISK_GOVERNOR: ("tqqq_growth_income",),
4545
PLUGIN_TACO_REBOUND_SHADOW: ("tqqq_growth_income",),
4646
}
47+
PLUGIN_SCHEMA_VERSIONS: dict[str, tuple[str, ...]] = {
48+
PLUGIN_CRISIS_RESPONSE_SHADOW: ("crisis_response_shadow.v1",),
49+
PLUGIN_MARKET_REGIME_CONTROL: ("market_regime_control.v1",),
50+
PLUGIN_MACRO_RISK_GOVERNOR: ("macro_risk_governor.v1",),
51+
PLUGIN_TACO_REBOUND_SHADOW: ("taco_rebound_shadow.v2",),
52+
}
53+
PLUGIN_DEPRECATED_SUCCESSORS: dict[str, str] = {
54+
PLUGIN_CRISIS_RESPONSE_SHADOW: PLUGIN_MARKET_REGIME_CONTROL,
55+
PLUGIN_MACRO_RISK_GOVERNOR: PLUGIN_MARKET_REGIME_CONTROL,
56+
PLUGIN_TACO_REBOUND_SHADOW: PLUGIN_MARKET_REGIME_CONTROL,
57+
}
4758
PLUGIN_RESEARCH_ONLY_REASONS: dict[str, str] = {}
4859

4960

@@ -615,7 +626,9 @@ def main(argv: list[str] | None = None) -> int:
615626
"PLUGIN_MACRO_RISK_GOVERNOR",
616627
"PLUGIN_TACO_REBOUND_SHADOW",
617628
"PLUGIN_COMPATIBLE_STRATEGIES",
629+
"PLUGIN_DEPRECATED_SUCCESSORS",
618630
"PLUGIN_RESEARCH_ONLY_REASONS",
631+
"PLUGIN_SCHEMA_VERSIONS",
619632
"PluginRunResult",
620633
"load_plugin_config",
621634
"main",

tests/test_strategy_plugin_runner.py

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@
88

99
from quant_strategy_plugins.crisis_response_research import ROUTE_TRUE_CRISIS
1010
from quant_strategy_plugins.strategy_plugin_runner import (
11+
PLUGIN_COMPATIBLE_STRATEGIES,
1112
PLUGIN_CRISIS_RESPONSE_SHADOW,
13+
PLUGIN_DEPRECATED_SUCCESSORS,
1214
PLUGIN_MARKET_REGIME_CONTROL,
1315
PLUGIN_MACRO_RISK_GOVERNOR,
16+
PLUGIN_SCHEMA_VERSIONS,
1417
PLUGIN_TACO_REBOUND_SHADOW,
1518
load_plugin_config,
1619
main,
@@ -263,6 +266,53 @@ def test_strategy_plugin_runner_runs_unified_market_regime_control_for_tqqq(tmp_
263266
assert payload["execution_controls"]["live_allocation_mutation_allowed"] is False
264267

265268

269+
def test_strategy_plugin_runner_runs_unified_market_regime_control_for_soxl(tmp_path) -> None:
270+
prices_path = tmp_path / "market_regime_prices.csv"
271+
output_dir = tmp_path / SOXL_STRATEGY_NAME / "plugins" / PLUGIN_MARKET_REGIME_CONTROL
272+
_soxl_quiet_prices().to_csv(prices_path, index=False)
273+
config = {
274+
"output_dir": str(tmp_path / "runner"),
275+
"default_mode": "shadow",
276+
"strategy_plugins": [
277+
{
278+
"strategy": SOXL_STRATEGY_NAME,
279+
"plugin": PLUGIN_MARKET_REGIME_CONTROL,
280+
"enabled": True,
281+
"inputs": {
282+
"prices": str(prices_path),
283+
"as_of": "2025-11-19",
284+
"benchmark_symbol": "SOXX",
285+
"attack_symbol": "SOXL",
286+
"crisis_enabled": False,
287+
"macro_enabled": False,
288+
"taco_enabled": False,
289+
},
290+
"outputs": {"output_dir": str(output_dir)},
291+
}
292+
],
293+
}
294+
295+
summary = run_configured_plugins(config)
296+
297+
result = summary["strategy_plugins"][0]
298+
assert result["strategy"] == SOXL_STRATEGY_NAME
299+
assert result["plugin"] == PLUGIN_MARKET_REGIME_CONTROL
300+
assert result["status"] == "ok"
301+
payload = json.loads((output_dir / "latest_signal.json").read_text(encoding="utf-8"))
302+
assert payload["strategy"] == SOXL_STRATEGY_NAME
303+
assert payload["plugin"] == PLUGIN_MARKET_REGIME_CONTROL
304+
assert payload["schema_version"] in PLUGIN_SCHEMA_VERSIONS[PLUGIN_MARKET_REGIME_CONTROL]
305+
assert payload["canonical_route"] == "no_action"
306+
307+
308+
def test_strategy_plugin_runner_contract_registry_prefers_unified_plugin() -> None:
309+
assert SOXL_STRATEGY_NAME in PLUGIN_COMPATIBLE_STRATEGIES[PLUGIN_MARKET_REGIME_CONTROL]
310+
assert PLUGIN_SCHEMA_VERSIONS[PLUGIN_MARKET_REGIME_CONTROL] == ("market_regime_control.v1",)
311+
assert PLUGIN_DEPRECATED_SUCCESSORS[PLUGIN_CRISIS_RESPONSE_SHADOW] == PLUGIN_MARKET_REGIME_CONTROL
312+
assert PLUGIN_DEPRECATED_SUCCESSORS[PLUGIN_MACRO_RISK_GOVERNOR] == PLUGIN_MARKET_REGIME_CONTROL
313+
assert PLUGIN_DEPRECATED_SUCCESSORS[PLUGIN_TACO_REBOUND_SHADOW] == PLUGIN_MARKET_REGIME_CONTROL
314+
315+
266316
def test_strategy_plugin_runner_rehearses_triggered_shadow_artifact_without_execution_permissions(tmp_path) -> None:
267317
prices = _financial_crisis_prices()
268318
prices_path = tmp_path / "crisis_prices.csv"
@@ -642,6 +692,7 @@ def test_strategy_plugin_runner_example_config_uses_default_mode_without_duplica
642692

643693
assert config["default_mode"] == "shadow"
644694
assert "mode" not in config["strategy_plugins"][0]
695+
assert config["strategy_plugins"][0]["plugin"] == PLUGIN_MARKET_REGIME_CONTROL
645696
assert config["strategy_plugins"][0]["outputs"]["output_dir"].endswith(
646-
"tqqq_growth_income/plugins/crisis_response_shadow"
697+
"tqqq_growth_income/plugins/market_regime_control"
647698
)

0 commit comments

Comments
 (0)