From c6bd90024071cdb114b862a4b1a84c6b3d9a41d3 Mon Sep 17 00:00:00 2001 From: Pigbibi <20649888+Pigbibi@users.noreply.github.com> Date: Tue, 23 Jun 2026 04:54:12 +0800 Subject: [PATCH] fix SOXL strategy plugin auto mounts --- scripts/build_runtime_switch.py | 6 +++-- tests/test_runtime_settings.py | 45 ++++++++++++++++++++++++++------- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/scripts/build_runtime_switch.py b/scripts/build_runtime_switch.py index 71d9eb8..b6e62c7 100644 --- a/scripts/build_runtime_switch.py +++ b/scripts/build_runtime_switch.py @@ -24,11 +24,13 @@ DEFAULT_ARTIFACT_BUCKET_URI = "gs://qsl-runtime-logs-shared" +# Keep this list limited to strategy-scope artifacts that the publisher +# currently produces. QPK may parse broader explicit mounts for forward +# compatibility, but auto mode must not generate missing latest_signal paths. MARKET_REGIME_CONTROL_PROFILES = frozenset( { "tqqq_growth_income", - "global_etf_rotation", - "russell_top50_leader_rotation", + "soxl_soxx_trend_income", } ) IBIT_ZSCORE_EXIT_STRATEGY_PROFILE = "ibit_smart_dca" diff --git a/tests/test_runtime_settings.py b/tests/test_runtime_settings.py index 9565342..39cdca0 100644 --- a/tests/test_runtime_settings.py +++ b/tests/test_runtime_settings.py @@ -88,6 +88,21 @@ def test_plugin_mount_schema_version_is_rendered_for_platform_parser(self): assignments["SCHWAB_STRATEGY_PLUGIN_MOUNTS_JSON"], ) + def test_auto_market_regime_control_profiles_cover_published_strategy_artifacts(self): + strategy_profiles = { + item["profile"] + for item in json.loads( + (ROOT / "web/strategy-switch-console/strategy-profiles.example.json").read_text(encoding="utf-8") + ) + } + published_strategy_artifact_profiles = { + "tqqq_growth_income", + "soxl_soxx_trend_income", + } + + self.assertLessEqual(published_strategy_artifact_profiles, strategy_profiles) + self.assertEqual(published_strategy_artifact_profiles, build_runtime_switch.MARKET_REGIME_CONTROL_PROFILES) + def test_assignment_payload_can_redact_values(self): _, target = self.load_target("examples/targets/longbridge/sg.example.json") assignment = next( @@ -300,8 +315,6 @@ def test_build_switch_target_defaults_schwab_repository_scope(self): "live", "--strategy-profile", "soxl_soxx_trend_income", - "--plugin-mode", - "none", ] ) @@ -313,12 +326,15 @@ def test_build_switch_target_defaults_schwab_repository_scope(self): self.assertNotIn("environment", target["github"]) self.assertEqual(target["runtime_target"]["service_name"], "charles-schwab-quant-service") self.assertEqual(assignments["SCHWAB_DRY_RUN_ONLY"], "false") + plugin_payload = json.loads(assignments["SCHWAB_STRATEGY_PLUGIN_MOUNTS_JSON"]) + self.assertEqual(plugin_payload["strategy_plugins"][0]["plugin"], "market_regime_control") self.assertEqual( - json.loads(assignments["SCHWAB_STRATEGY_PLUGIN_MOUNTS_JSON"]), - {"strategy_plugins": []}, + plugin_payload["strategy_plugins"][0]["signal_path"], + "gs://qsl-runtime-logs-shared/strategy-artifacts/us_equity/" + "soxl_soxx_trend_income/plugins/market_regime_control/latest_signal.json", ) - def test_build_switch_target_clears_plugin_mounts_for_unmounted_strategy(self): + def test_build_switch_target_auto_mounts_market_regime_control_for_soxl(self): parser = build_runtime_switch.build_parser() args = parser.parse_args( [ @@ -335,9 +351,12 @@ def test_build_switch_target_clears_plugin_mounts_for_unmounted_strategy(self): assignments = {item.name: item.value for item in runtime_settings.build_assignments(target)} self.assertEqual(assignments["STRATEGY_PROFILE"], "soxl_soxx_trend_income") + plugin_payload = json.loads(assignments["LONGBRIDGE_STRATEGY_PLUGIN_MOUNTS_JSON"]) + self.assertEqual(plugin_payload["strategy_plugins"][0]["plugin"], "market_regime_control") self.assertEqual( - json.loads(assignments["LONGBRIDGE_STRATEGY_PLUGIN_MOUNTS_JSON"]), - {"strategy_plugins": []}, + plugin_payload["strategy_plugins"][0]["signal_path"], + "gs://qsl-runtime-logs-shared/strategy-artifacts/us_equity/" + "soxl_soxx_trend_income/plugins/market_regime_control/latest_signal.json", ) def test_build_switch_target_defaults_firstrade_repository_scope(self): @@ -828,7 +847,7 @@ def test_build_switch_target_can_clear_preserved_ibkr_reserved_cash_fields(self) self.assertEqual(selected["IBKR_MIN_RESERVED_CASH_USD"], "") self.assertEqual(selected["IBKR_RESERVED_CASH_RATIO"], "") - def test_build_switch_target_patches_ibkr_service_targets_with_empty_plugin_mounts(self): + def test_build_switch_target_patches_ibkr_service_targets_with_soxl_plugin_mounts(self): existing = { "targets": [ { @@ -885,7 +904,15 @@ def test_build_switch_target_patches_ibkr_service_targets_with_empty_plugin_moun selected = patched["targets"][0] self.assertEqual(selected["runtime_target"]["strategy_profile"], "soxl_soxx_trend_income") - self.assertEqual(selected["IBKR_STRATEGY_PLUGIN_MOUNTS_JSON"], {"strategy_plugins": []}) + self.assertEqual( + selected["IBKR_STRATEGY_PLUGIN_MOUNTS_JSON"]["strategy_plugins"][0]["plugin"], + "market_regime_control", + ) + self.assertEqual( + selected["IBKR_STRATEGY_PLUGIN_MOUNTS_JSON"]["strategy_plugins"][0]["signal_path"], + "gs://qsl-runtime-logs-shared/strategy-artifacts/us_equity/" + "soxl_soxx_trend_income/plugins/market_regime_control/latest_signal.json", + ) if __name__ == "__main__":