3232)
3333TACO_REBOUND_SHADOW_SUPPORTED_STRATEGIES = frozenset ({"tqqq_growth_income" })
3434MACRO_RISK_GOVERNOR_SUPPORTED_STRATEGIES = frozenset ({"tqqq_growth_income" })
35- MARKET_REGIME_CONTROL_SUPPORTED_STRATEGIES = frozenset ({"tqqq_growth_income" })
35+ MARKET_REGIME_CONTROL_SUPPORTED_STRATEGIES = frozenset ({"tqqq_growth_income" , "soxl_soxx_trend_income" })
36+ STRATEGY_PLUGIN_SCHEMA_VERSIONS : Mapping [str , frozenset [str ]] = {
37+ PLUGIN_CRISIS_RESPONSE_SHADOW : frozenset ({"crisis_response_shadow.v1" }),
38+ PLUGIN_MARKET_REGIME_CONTROL : frozenset ({"market_regime_control.v1" }),
39+ PLUGIN_MACRO_RISK_GOVERNOR : frozenset ({"macro_risk_governor.v1" }),
40+ PLUGIN_TACO_REBOUND_SHADOW : frozenset ({"taco_rebound_shadow.v2" }),
41+ }
3642_DEFAULT_STRATEGY_PLUGIN_ALERT_GUIDANCE : Mapping [tuple [str , str , str ], str ] = {
3743 (
3844 PLUGIN_CRISIS_RESPONSE_SHADOW ,
@@ -117,6 +123,10 @@ class StrategyPluginDefinition:
117123 plugin : str
118124 supported_strategies : frozenset [str ] | None = None
119125 supported_modes : frozenset [str ] = field (default_factory = lambda : SUPPORTED_STRATEGY_PLUGIN_MODES )
126+ supported_schema_versions : frozenset [str ] = field (default_factory = frozenset )
127+ default_schema_version : str | None = None
128+ deprecated : bool = False
129+ successor_plugin : str | None = None
120130 alert_channels : tuple [str , ...] = ()
121131
122132 def normalized (self ) -> "StrategyPluginDefinition" :
@@ -132,6 +142,16 @@ def normalized(self) -> "StrategyPluginDefinition":
132142 )
133143 if not supported_modes :
134144 raise ValueError (f"strategy plugin definition for { plugin } must include at least one supported mode" )
145+ supported_schema_versions = frozenset (
146+ _required_string (version , field_name = "supported_schema_version" )
147+ for version in self .supported_schema_versions
148+ )
149+ default_schema_version = _optional_string (self .default_schema_version )
150+ if default_schema_version is not None and supported_schema_versions and default_schema_version not in supported_schema_versions :
151+ raise ValueError (
152+ f"strategy plugin definition for { plugin } default_schema_version must be listed in supported_schema_versions"
153+ )
154+ successor_plugin = _optional_string (self .successor_plugin )
135155 alert_channels = tuple (
136156 _required_string (channel , field_name = "alert_channel" )
137157 for channel in self .alert_channels
@@ -140,6 +160,10 @@ def normalized(self) -> "StrategyPluginDefinition":
140160 plugin = plugin ,
141161 supported_strategies = supported_strategies ,
142162 supported_modes = supported_modes ,
163+ supported_schema_versions = supported_schema_versions ,
164+ default_schema_version = default_schema_version ,
165+ deprecated = bool (self .deprecated ),
166+ successor_plugin = successor_plugin ,
143167 alert_channels = alert_channels ,
144168 )
145169
@@ -154,6 +178,10 @@ def supports_strategy(self, strategy: str) -> bool:
154178 plugin = PLUGIN_CRISIS_RESPONSE_SHADOW ,
155179 supported_strategies = CRISIS_RESPONSE_SHADOW_SUPPORTED_STRATEGIES ,
156180 supported_modes = SUPPORTED_STRATEGY_PLUGIN_MODES ,
181+ supported_schema_versions = STRATEGY_PLUGIN_SCHEMA_VERSIONS [PLUGIN_CRISIS_RESPONSE_SHADOW ],
182+ default_schema_version = "crisis_response_shadow.v1" ,
183+ deprecated = True ,
184+ successor_plugin = PLUGIN_MARKET_REGIME_CONTROL ,
157185 alert_channels = (
158186 STRATEGY_PLUGIN_ALERT_CHANNEL_EMAIL ,
159187 STRATEGY_PLUGIN_ALERT_CHANNEL_SMS ,
@@ -165,6 +193,10 @@ def supports_strategy(self, strategy: str) -> bool:
165193 plugin = PLUGIN_TACO_REBOUND_SHADOW ,
166194 supported_strategies = TACO_REBOUND_SHADOW_SUPPORTED_STRATEGIES ,
167195 supported_modes = SUPPORTED_STRATEGY_PLUGIN_MODES ,
196+ supported_schema_versions = STRATEGY_PLUGIN_SCHEMA_VERSIONS [PLUGIN_TACO_REBOUND_SHADOW ],
197+ default_schema_version = "taco_rebound_shadow.v2" ,
198+ deprecated = True ,
199+ successor_plugin = PLUGIN_MARKET_REGIME_CONTROL ,
168200 alert_channels = (
169201 STRATEGY_PLUGIN_ALERT_CHANNEL_EMAIL ,
170202 STRATEGY_PLUGIN_ALERT_CHANNEL_SMS ,
@@ -176,6 +208,8 @@ def supports_strategy(self, strategy: str) -> bool:
176208 plugin = PLUGIN_MARKET_REGIME_CONTROL ,
177209 supported_strategies = MARKET_REGIME_CONTROL_SUPPORTED_STRATEGIES ,
178210 supported_modes = SUPPORTED_STRATEGY_PLUGIN_MODES ,
211+ supported_schema_versions = STRATEGY_PLUGIN_SCHEMA_VERSIONS [PLUGIN_MARKET_REGIME_CONTROL ],
212+ default_schema_version = "market_regime_control.v1" ,
179213 alert_channels = (
180214 STRATEGY_PLUGIN_ALERT_CHANNEL_EMAIL ,
181215 STRATEGY_PLUGIN_ALERT_CHANNEL_SMS ,
@@ -187,6 +221,10 @@ def supports_strategy(self, strategy: str) -> bool:
187221 plugin = PLUGIN_MACRO_RISK_GOVERNOR ,
188222 supported_strategies = MACRO_RISK_GOVERNOR_SUPPORTED_STRATEGIES ,
189223 supported_modes = SUPPORTED_STRATEGY_PLUGIN_MODES ,
224+ supported_schema_versions = STRATEGY_PLUGIN_SCHEMA_VERSIONS [PLUGIN_MACRO_RISK_GOVERNOR ],
225+ default_schema_version = "macro_risk_governor.v1" ,
226+ deprecated = True ,
227+ successor_plugin = PLUGIN_MARKET_REGIME_CONTROL ,
190228 alert_channels = (
191229 STRATEGY_PLUGIN_ALERT_CHANNEL_EMAIL ,
192230 STRATEGY_PLUGIN_ALERT_CHANNEL_SMS ,
@@ -204,6 +242,7 @@ class StrategyPluginMountConfig:
204242 signal_path : str
205243 enabled : bool = True
206244 expected_mode : str | None = None
245+ expected_schema_version : str | None = None
207246
208247
209248@dataclass (frozen = True )
@@ -222,6 +261,9 @@ class StrategyPluginSignal:
222261 payload : Mapping [str , Any ]
223262 source_uri : str | None = None
224263 local_path : str | None = None
264+ deprecated_plugin : bool = False
265+ successor_plugin : str | None = None
266+ supported_schema_versions : tuple [str , ...] = ()
225267
226268 def report_summary (self ) -> dict [str , Any ]:
227269 return {
@@ -231,6 +273,9 @@ def report_summary(self) -> dict[str, Any]:
231273 "configured_mode" : self .configured_mode ,
232274 "effective_mode" : self .effective_mode ,
233275 "schema_version" : self .schema_version ,
276+ "deprecated_plugin" : self .deprecated_plugin ,
277+ "successor_plugin" : self .successor_plugin ,
278+ "supported_schema_versions" : self .supported_schema_versions ,
234279 "as_of" : self .as_of ,
235280 "canonical_route" : self .canonical_route ,
236281 "suggested_action" : self .suggested_action ,
@@ -309,6 +354,34 @@ def validate_strategy_plugin_compatibility(
309354 )
310355
311356
357+ def validate_strategy_plugin_schema_version (
358+ * ,
359+ plugin : str ,
360+ schema_version : str | None ,
361+ expected_schema_version : str | None = None ,
362+ plugin_definitions : Mapping [str , StrategyPluginDefinition ] | Sequence [StrategyPluginDefinition ] | None = None ,
363+ source : str = "artifact" ,
364+ ) -> None :
365+ plugin_name = _required_string (plugin , field_name = "plugin" )
366+ normalized_schema_version = _optional_string (schema_version )
367+ normalized_expected_schema_version = _optional_string (expected_schema_version )
368+ if normalized_expected_schema_version is not None and normalized_schema_version != normalized_expected_schema_version :
369+ raise ValueError (
370+ "strategy plugin artifact schema_version mismatch: "
371+ f"expected { normalized_expected_schema_version } , got { normalized_schema_version or '<missing>' } "
372+ )
373+ definitions = normalize_strategy_plugin_definitions (plugin_definitions )
374+ definition = definitions .get (plugin_name )
375+ if definition is None or not definition .supported_schema_versions or normalized_schema_version is None :
376+ return
377+ if normalized_schema_version not in definition .supported_schema_versions :
378+ allowed = ", " .join (sorted (definition .supported_schema_versions ))
379+ raise ValueError (
380+ f"strategy plugin { plugin_name } does not support schema_version { normalized_schema_version } "
381+ f"in { source } ; supported schema versions: { allowed } "
382+ )
383+
384+
312385def parse_strategy_plugin_mounts (
313386 raw_config : str | Sequence [Mapping [str , Any ]] | Mapping [str , Any ] | None ,
314387 * ,
@@ -351,6 +424,14 @@ def parse_strategy_plugin_mounts(
351424 if expected_mode is not None
352425 else None
353426 )
427+ expected_schema_version = _optional_string (item .get ("expected_schema_version" ))
428+ validate_strategy_plugin_schema_version (
429+ plugin = plugin ,
430+ schema_version = expected_schema_version ,
431+ expected_schema_version = expected_schema_version ,
432+ plugin_definitions = definitions ,
433+ source = "mount" ,
434+ )
354435 validate_strategy_plugin_compatibility (
355436 strategy = strategy ,
356437 plugin = plugin ,
@@ -365,6 +446,7 @@ def parse_strategy_plugin_mounts(
365446 signal_path = signal_path ,
366447 enabled = _as_bool (item .get ("enabled" ), default = True ),
367448 expected_mode = normalized_expected_mode ,
449+ expected_schema_version = expected_schema_version ,
368450 )
369451 )
370452 return tuple (mounts )
@@ -398,6 +480,7 @@ def load_configured_strategy_plugin_signals(
398480 expected_strategy = mount .strategy ,
399481 expected_plugin = mount .plugin ,
400482 expected_mode = mount .expected_mode ,
483+ expected_schema_version = mount .expected_schema_version ,
401484 client_factory = client_factory ,
402485 plugin_definitions = definitions ,
403486 )
@@ -411,6 +494,7 @@ def load_strategy_plugin_signal(
411494 expected_strategy : str | None = None ,
412495 expected_plugin : str | None = None ,
413496 expected_mode : str | None = None ,
497+ expected_schema_version : str | None = None ,
414498 client_factory : Any = None ,
415499 plugin_definitions : Mapping [str , StrategyPluginDefinition ] | Sequence [StrategyPluginDefinition ] | None = None ,
416500) -> StrategyPluginSignal :
@@ -425,6 +509,7 @@ def load_strategy_plugin_signal(
425509 expected_strategy = expected_strategy ,
426510 expected_plugin = expected_plugin ,
427511 expected_mode = expected_mode ,
512+ expected_schema_version = expected_schema_version ,
428513 source_uri = metadata .get ("source_uri" ),
429514 local_path = str (local_path ),
430515 plugin_definitions = plugin_definitions ,
@@ -437,6 +522,7 @@ def validate_strategy_plugin_signal_payload(
437522 expected_strategy : str | None = None ,
438523 expected_plugin : str | None = None ,
439524 expected_mode : str | None = None ,
525+ expected_schema_version : str | None = None ,
440526 source_uri : str | None = None ,
441527 local_path : str | None = None ,
442528 plugin_definitions : Mapping [str , StrategyPluginDefinition ] | Sequence [StrategyPluginDefinition ] | None = None ,
@@ -473,6 +559,16 @@ def validate_strategy_plugin_signal_payload(
473559 plugin_definitions = plugin_definitions ,
474560 source = "artifact" ,
475561 )
562+ schema_version = _optional_string (payload .get ("schema_version" )) or ""
563+ definitions = normalize_strategy_plugin_definitions (plugin_definitions )
564+ definition = definitions .get (plugin )
565+ validate_strategy_plugin_schema_version (
566+ plugin = plugin ,
567+ schema_version = schema_version ,
568+ expected_schema_version = expected_schema_version ,
569+ plugin_definitions = definitions ,
570+ source = "artifact" ,
571+ )
476572
477573 execution_controls = payload .get ("execution_controls" ) or {}
478574 if not isinstance (execution_controls , Mapping ):
@@ -484,7 +580,7 @@ def validate_strategy_plugin_signal_payload(
484580 mode = mode ,
485581 configured_mode = configured_mode ,
486582 effective_mode = effective_mode ,
487- schema_version = _optional_string ( payload . get ( " schema_version" )) or "" ,
583+ schema_version = schema_version ,
488584 as_of = _optional_string (payload .get ("as_of" )) or "" ,
489585 canonical_route = _optional_string (payload .get ("canonical_route" )),
490586 suggested_action = _optional_string (payload .get ("suggested_action" )),
@@ -493,6 +589,9 @@ def validate_strategy_plugin_signal_payload(
493589 payload = dict (payload ),
494590 source_uri = _optional_string (source_uri ),
495591 local_path = _optional_string (local_path ),
592+ deprecated_plugin = bool (definition .deprecated ) if definition is not None else False ,
593+ successor_plugin = definition .successor_plugin if definition is not None else None ,
594+ supported_schema_versions = tuple (sorted (definition .supported_schema_versions )) if definition is not None else (),
496595 )
497596
498597
0 commit comments