From f0af63160d73b33d86de9ed2a139e64e285d030b Mon Sep 17 00:00:00 2001 From: Pigbibi <20649888+Pigbibi@users.noreply.github.com> Date: Thu, 2 Jul 2026 16:19:24 +0800 Subject: [PATCH] Fix runtime settings defaults Co-Authored-By: Codex --- notifications/telegram.py | 4 ++++ runtime_config_support.py | 16 +++++++++++----- tests/test_runtime_config_support.py | 28 ++++++++++++++++++++-------- 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/notifications/telegram.py b/notifications/telegram.py index 208d960..c341405 100644 --- a/notifications/telegram.py +++ b/notifications/telegram.py @@ -159,6 +159,8 @@ def _break_telegram_market_symbol_auto_links(value) -> str: "strategy_name_russell_top50_leader_rotation": "罗素 Top50 领涨轮动", "strategy_name_ibit_smart_dca": "IBIT 比特币 ETF 智能定投", "strategy_name_nasdaq_sp500_smart_dca": "纳指100 / 标普500 智能定投", + "strategy_name_us_equity_combo": "美股核心组合", + "strategy_name_us_equity_combo_leveraged": "美股Alpha组合", "strategy_name_hk_global_etf_tactical_rotation": "港股全球 ETF 战术轮动", "strategy_name_hk_low_vol_dividend_quality_snapshot": "港股低波股息质量快照", "strategy_name_cn_industry_etf_rotation": "A股行业ETF轮动", @@ -346,6 +348,8 @@ def _break_telegram_market_symbol_auto_links(value) -> str: "strategy_name_russell_top50_leader_rotation": "Russell Top50 Leader Rotation", "strategy_name_ibit_smart_dca": "IBIT Smart DCA", "strategy_name_nasdaq_sp500_smart_dca": "Nasdaq 100 / S&P 500 Smart DCA", + "strategy_name_us_equity_combo": "US Equity Combo", + "strategy_name_us_equity_combo_leveraged": "US Equity Combo Leveraged", "strategy_name_hk_global_etf_tactical_rotation": "HK Global ETF Tactical Rotation", "strategy_name_hk_low_vol_dividend_quality_snapshot": "HK Low-Vol Dividend Quality Snapshot", "strategy_name_cn_industry_etf_rotation": "CN Industry ETF Rotation", diff --git a/runtime_config_support.py b/runtime_config_support.py index 010ed7c..da17bb8 100644 --- a/runtime_config_support.py +++ b/runtime_config_support.py @@ -334,19 +334,19 @@ def load_platform_runtime_settings( debug_position_snapshot=resolve_bool_value(os.getenv("LONGBRIDGE_DEBUG_POSITION_SNAPSHOT")), income_threshold_usd=resolve_optional_float_env(os.environ, "INCOME_THRESHOLD_USD"), qqqi_income_ratio=_qqqi_income_ratio_env(), - income_layer_enabled=resolve_optional_bool_env("INCOME_LAYER_ENABLED"), + income_layer_enabled=_optional_bool_env("INCOME_LAYER_ENABLED"), income_layer_start_usd=_optional_non_negative_float_env("INCOME_LAYER_START_USD"), income_layer_max_ratio=resolve_optional_ratio_env("INCOME_LAYER_MAX_RATIO"), dca_mode=resolve_optional_dca_mode_env("DCA_MODE"), dca_base_investment_usd=resolve_optional_positive_float_env("DCA_BASE_INVESTMENT_USD"), - ibit_zscore_exit_enabled=resolve_optional_bool_env("IBIT_ZSCORE_EXIT_ENABLED"), + ibit_zscore_exit_enabled=_optional_bool_env("IBIT_ZSCORE_EXIT_ENABLED"), ibit_zscore_exit_mode=resolve_optional_ibit_zscore_exit_mode_env("IBIT_ZSCORE_EXIT_MODE"), ibit_zscore_exit_parking_symbol=resolve_optional_symbol_env("IBIT_ZSCORE_EXIT_PARKING_SYMBOL"), ibit_zscore_exit_risk_reduced_exposure=resolve_optional_ratio_env( "IBIT_ZSCORE_EXIT_RISK_REDUCED_EXPOSURE" ), ibit_zscore_exit_risk_off_exposure=resolve_optional_ratio_env("IBIT_ZSCORE_EXIT_RISK_OFF_EXPOSURE"), - ibit_zscore_exit_allow_outside_execution_window=resolve_optional_bool_env( + ibit_zscore_exit_allow_outside_execution_window=_optional_bool_env( "IBIT_ZSCORE_EXIT_ALLOW_OUTSIDE_EXECUTION_WINDOW" ), runtime_execution_window_trading_days=_runtime_execution_window_trading_days_env( @@ -487,8 +487,14 @@ def _qqqi_income_ratio_env() -> float | None: def _runtime_target_enabled_env() -> bool: - value = resolve_optional_bool_env("RUNTIME_TARGET_ENABLED") - return True if value is None else value + return resolve_optional_bool_env("RUNTIME_TARGET_ENABLED", default=True) + + +def _optional_bool_env(name: str) -> bool | None: + raw_value = os.getenv(f"QSL_{name}") or os.getenv(name) + if raw_value is None or str(raw_value).strip() == "": + return None + return resolve_optional_bool_env(name) def _optional_non_negative_float_env(name: str) -> float | None: diff --git a/tests/test_runtime_config_support.py b/tests/test_runtime_config_support.py index 7c870f3..c421a57 100644 --- a/tests/test_runtime_config_support.py +++ b/tests/test_runtime_config_support.py @@ -12,8 +12,11 @@ if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) QPK_SRC = ROOT.parent / "QuantPlatformKit" / "src" -if (QPK_SRC / "quant_platform_kit" / "common" / "runtime_config.py").exists() and str(QPK_SRC) not in sys.path: - sys.path.insert(0, str(QPK_SRC)) +UES_SRC = ROOT.parent / "UsEquityStrategies" / "src" +HES_SRC = ROOT.parent / "HkEquityStrategies" / "src" +for candidate in (QPK_SRC, UES_SRC, HES_SRC): + if candidate.exists() and str(candidate) not in sys.path: + sys.path.insert(0, str(candidate)) SCRIPT_PATH = ROOT / "scripts" / "print_strategy_profile_status.py" SWITCH_PLAN_SCRIPT_PATH = ROOT / "scripts" / "print_strategy_switch_env_plan.py" @@ -54,9 +57,13 @@ BASE_LONGBRIDGE_PROFILES = frozenset( { "global_etf_rotation", + "ibit_smart_dca", + "nasdaq_sp500_smart_dca", "russell_top50_leader_rotation", "tqqq_growth_income", "soxl_soxx_trend_income", + "us_equity_combo", + "us_equity_combo_leveraged", } ) OPTIONAL_LONGBRIDGE_PROFILES = frozenset({"global_etf_confidence_vol_gate"}) @@ -213,7 +220,7 @@ def test_load_platform_runtime_settings_prefers_runtime_target_json(self): def test_load_platform_runtime_settings_requires_strategy_profile(self): with patch.dict(os.environ, {}, clear=True): - with self.assertRaisesRegex(EnvironmentError, "RUNTIME_TARGET_JSON is required"): + with self.assertRaisesRegex(EnvironmentError, "RUNTIME_TARGET_JSON"): load_platform_runtime_settings(project_id_resolver=lambda: "project-1") def test_platform_supported_profiles_are_filtered_by_registry(self): @@ -496,7 +503,7 @@ def test_income_layer_overrides_are_loaded_from_env(self): self.assertEqual(settings.income_layer_start_usd, 250000.0) self.assertEqual(settings.income_layer_max_ratio, 0.25) - def test_ibit_smart_dca_profile_is_rejected_on_longbridge(self): + def test_ibit_smart_dca_profile_is_loaded_on_longbridge(self): with patch.dict( os.environ, { @@ -510,8 +517,11 @@ def test_ibit_smart_dca_profile_is_rejected_on_longbridge(self): }, clear=True, ): - with self.assertRaises(ValueError): - load_platform_runtime_settings(project_id_resolver=lambda: "project-1") + settings = load_platform_runtime_settings(project_id_resolver=lambda: "project-1") + + self.assertEqual(settings.strategy_profile, "ibit_smart_dca") + self.assertTrue(settings.ibit_zscore_exit_enabled) + self.assertEqual(settings.ibit_zscore_exit_mode, "live") def test_tech_runtime_execution_window_override_rejects_research_only_profile(self): with patch.dict( @@ -661,6 +671,7 @@ def test_platform_profile_status_matrix_matches_current_longbridge_rollout(self) { "canonical_profile": "soxl_soxx_trend_income", "display_name": "SOXL/SOXX Semiconductor Trend Income", + "display_name_zh": "半导体趋势收益", "domain": "us_equity", "eligible": True, "enabled": True, @@ -690,6 +701,7 @@ def test_platform_profile_status_matrix_matches_current_longbridge_rollout(self) { "canonical_profile": "hk_global_etf_tactical_rotation", "display_name": "HK Global ETF Tactical Rotation", + "display_name_zh": "港股ETF战术轮动", "domain": "hk_equity", "eligible": True, "enabled": True, @@ -819,7 +831,7 @@ def test_print_strategy_profile_status_json_matches_registry(self): self.assertEqual(by_profile["russell_top50_leader_rotation"]["profile_group"], "snapshot_backed") self.assertEqual( by_profile["russell_top50_leader_rotation"]["display_name_zh"], - "罗素 Top50 领涨轮动", + "罗素Top50领涨", ) self.assertEqual(by_profile["russell_top50_leader_rotation"]["input_mode"], "feature_snapshot") self.assertTrue(by_profile["russell_top50_leader_rotation"]["requires_snapshot_artifacts"]) @@ -865,7 +877,7 @@ def test_print_strategy_profile_status_table_contains_expected_headers(self): self.assertIn("HK Global ETF Tactical Rotation", result.stdout) self.assertNotIn("HK Dividend-Gold Defensive Rotation", result.stdout) self.assertIn("Russell Top50 Leader Rotation", result.stdout) - self.assertIn("罗素 Top50 领涨轮动", result.stdout) + self.assertIn("罗素Top50领涨", result.stdout) self.assertNotIn("Tech/Communication Pullback Enhancement", result.stdout) self.assertNotIn("hk_blue_chip_leader_rotation", result.stdout) self.assertNotIn("hk_index_mean_reversion", result.stdout)