diff --git a/notifications/telegram.py b/notifications/telegram.py index 5993115..7f5eaeb 100644 --- a/notifications/telegram.py +++ b/notifications/telegram.py @@ -260,7 +260,7 @@ def format_small_account_cash_substitution_notes( "strategy_name_global_etf_confidence_vol_gate": "全球 ETF 置信波动门控", "strategy_name_tech_communication_pullback_enhancement": "科技通信回调增强", "strategy_name_qqq_tech_enhancement": "科技通信回调增强", - "strategy_name_russell_top50_leader_rotation_aggressive": "罗素 Top50 领涨轮动(激进)", + "strategy_name_russell_top50_leader_rotation": "罗素 Top50 领涨轮动", "strategy_name_ibit_smart_dca": "IBIT 比特币 ETF 智能定投", "skip_reason_below_trade_threshold": "低于调仓阈值", "skip_reason_quote_unavailable": "无法获取报价", @@ -410,7 +410,7 @@ def format_small_account_cash_substitution_notes( "strategy_name_global_etf_confidence_vol_gate": "Global ETF Confidence Vol Gate", "strategy_name_tech_communication_pullback_enhancement": "Tech Communication Pullback Enhancement", "strategy_name_qqq_tech_enhancement": "Tech Communication Pullback Enhancement", - "strategy_name_russell_top50_leader_rotation_aggressive": "Russell Top50 Leader Rotation Aggressive", + "strategy_name_russell_top50_leader_rotation": "Russell Top50 Leader Rotation", "strategy_name_ibit_smart_dca": "IBIT Smart DCA", "skip_reason_below_trade_threshold": "below trade threshold", "skip_reason_quote_unavailable": "quote unavailable", diff --git a/pyproject.toml b/pyproject.toml index 79c21c8..7b89dfa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,7 @@ authors = [ dependencies = [ "firstrade==0.0.39", "quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@b846c9d777a450e95d23c264853997d671f47dd9", - "us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@d789c5cacea6b82cccc0202561bd53482294b14a", + "us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@031a5bbd5a5ec64a57225a2b24f6569359dc9b11", "google-cloud-storage", "requests", ] diff --git a/requirements.txt b/requirements.txt index c29a82a..a160db0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ flask gunicorn firstrade==0.0.39 quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@b846c9d777a450e95d23c264853997d671f47dd9 -us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@d789c5cacea6b82cccc0202561bd53482294b14a +us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@031a5bbd5a5ec64a57225a2b24f6569359dc9b11 google-cloud-storage google-auth requests diff --git a/strategy_runtime.py b/strategy_runtime.py index af65233..7d8ea7e 100644 --- a/strategy_runtime.py +++ b/strategy_runtime.py @@ -173,7 +173,7 @@ def _build_runtime_overrides(profile: str, runtime_settings: PlatformRuntimeSett if runtime_settings.qqqi_income_ratio is not None: overrides["qqqi_income_ratio"] = runtime_settings.qqqi_income_ratio if profile in { - "russell_top50_leader_rotation_aggressive", + "russell_top50_leader_rotation", "tech_communication_pullback_enhancement", }: if runtime_settings.runtime_execution_window_trading_days is not None: diff --git a/tests/test_decision_mapper.py b/tests/test_decision_mapper.py index 89b6057..156a43c 100644 --- a/tests/test_decision_mapper.py +++ b/tests/test_decision_mapper.py @@ -26,7 +26,7 @@ def test_applies_platform_reserved_cash_policy_to_weight_decision(): plan = map_strategy_decision_to_plan( decision, snapshot=snapshot, - strategy_profile="russell_top50_leader_rotation_aggressive", + strategy_profile="russell_top50_leader_rotation", runtime_metadata={ "firstrade_execution_policy": { "reserved_cash_floor_usd": 1500.0, @@ -157,7 +157,7 @@ def test_value_decision_without_threshold_uses_platform_default(): plan = map_strategy_decision_to_plan( decision, snapshot=snapshot, - strategy_profile="russell_top50_leader_rotation_aggressive", + strategy_profile="russell_top50_leader_rotation", ) assert plan["execution"]["trade_threshold_value"] == 200.0 @@ -181,7 +181,7 @@ def test_no_execute_decision_without_threshold_holds_current_positions(): plan = map_strategy_decision_to_plan( decision, snapshot=snapshot, - strategy_profile="russell_top50_leader_rotation_aggressive", + strategy_profile="russell_top50_leader_rotation", ) assert plan["execution"]["trade_threshold_value"] == 100.0 diff --git a/tests/test_rebalance_service.py b/tests/test_rebalance_service.py index cb63480..32eb2c5 100644 --- a/tests/test_rebalance_service.py +++ b/tests/test_rebalance_service.py @@ -254,8 +254,8 @@ def get_balances(self, _account): return {"cash_balance": "$1000.00", "buying_power": "$1000.00"} class WeightTargetRuntime(FakeStrategyRuntime): - profile = "russell_top50_leader_rotation_aggressive" - display_name = "Russell Top50 Leader Rotation Aggressive" + profile = "russell_top50_leader_rotation" + display_name = "Russell Top50 Leader Rotation" def evaluate(self, **inputs): assert "portfolio_snapshot" in inputs @@ -276,8 +276,8 @@ def evaluate(self, **inputs): result = run_strategy_cycle( runtime_settings=_runtime_settings_with_persistence( - strategy_profile="russell_top50_leader_rotation_aggressive", - strategy_display_name="Russell Top50 Leader Rotation Aggressive", + strategy_profile="russell_top50_leader_rotation", + strategy_display_name="Russell Top50 Leader Rotation", ), credentials=FirstradeCredentials(username="user", password="pass"), client_factory=CashOnlyClient, @@ -296,8 +296,8 @@ def get_balances(self, _account): return {"total_value": "$0.00", "cash_balance": "$0.00", "buying_power": "$0.00"} class WeightTargetRuntime(FakeStrategyRuntime): - profile = "russell_top50_leader_rotation_aggressive" - display_name = "Russell Top50 Leader Rotation Aggressive" + profile = "russell_top50_leader_rotation" + display_name = "Russell Top50 Leader Rotation" def evaluate(self, **inputs): assert "portfolio_snapshot" in inputs @@ -318,8 +318,8 @@ def evaluate(self, **inputs): result = run_strategy_cycle( runtime_settings=_runtime_settings_with_persistence( - strategy_profile="russell_top50_leader_rotation_aggressive", - strategy_display_name="Russell Top50 Leader Rotation Aggressive", + strategy_profile="russell_top50_leader_rotation", + strategy_display_name="Russell Top50 Leader Rotation", ), credentials=FirstradeCredentials(username="user", password="pass"), client_factory=ZeroEquityClient, @@ -938,8 +938,8 @@ def test_render_cycle_summary_shows_funding_blocked_banner(): message = render_cycle_summary( { "account": "****1234", - "strategy_profile": "russell_top50_leader_rotation_aggressive", - "strategy_display_name": "Russell Top50 Leader Rotation Aggressive", + "strategy_profile": "russell_top50_leader_rotation", + "strategy_display_name": "Russell Top50 Leader Rotation", "dry_run_only": False, "execution_blocked": True, "execution_block_retryable": False, @@ -971,8 +971,8 @@ def test_render_cycle_summary_shows_retryable_execution_blocked_banner(): message = render_cycle_summary( { "account": "****1234", - "strategy_profile": "russell_top50_leader_rotation_aggressive", - "strategy_display_name": "Russell Top50 Leader Rotation Aggressive", + "strategy_profile": "russell_top50_leader_rotation", + "strategy_display_name": "Russell Top50 Leader Rotation", "dry_run_only": False, "execution_blocked": True, "execution_block_retryable": True, diff --git a/tests/test_request_handling.py b/tests/test_request_handling.py index 76feb7a..a2dcb9b 100644 --- a/tests/test_request_handling.py +++ b/tests/test_request_handling.py @@ -192,7 +192,7 @@ def send(message): monkeypatch.setenv("GLOBAL_TELEGRAM_CHAT_ID", "chat-1") monkeypatch.setenv("STRATEGY_PLUGIN_ALERT_TELEGRAM_BOT_TOKEN", "plugin-token") monkeypatch.setenv("STRATEGY_PLUGIN_ALERT_TELEGRAM_CHAT_IDS", "plugin-chat") - monkeypatch.setenv("STRATEGY_PROFILE", "russell_top50_leader_rotation_aggressive") + monkeypatch.setenv("STRATEGY_PROFILE", "russell_top50_leader_rotation") monkeypatch.setattr(main, "build_sender", fake_build_sender) monkeypatch.setattr( main, @@ -213,7 +213,7 @@ def send(message): assert sent_messages[0][1] == "chat-1" assert "Firstrade strategy run failed" in sent_messages[0][2] assert "ValueError: snapshot denied" in sent_messages[0][2] - assert "strategy: russell_top50_leader_rotation_aggressive" in sent_messages[0][2] + assert "strategy: russell_top50_leader_rotation" in sent_messages[0][2] def test_run_endpoint_error_notification_uses_chinese_copy(monkeypatch): @@ -221,7 +221,7 @@ def test_run_endpoint_error_notification_uses_chinese_copy(monkeypatch): monkeypatch.setenv("TELEGRAM_TOKEN", "token-1") monkeypatch.setenv("GLOBAL_TELEGRAM_CHAT_ID", "chat-1") monkeypatch.setenv("NOTIFY_LANG", "zh") - monkeypatch.setenv("STRATEGY_PROFILE", "russell_top50_leader_rotation_aggressive") + monkeypatch.setenv("STRATEGY_PROFILE", "russell_top50_leader_rotation") sent_messages = [] def fake_build_sender(token, chat_id): @@ -243,7 +243,7 @@ def send(message): assert response.status_code == 500 text = sent_messages[0][2] assert "Firstrade 策略运行失败" in text - assert "策略: russell_top50_leader_rotation_aggressive" in text + assert "策略: russell_top50_leader_rotation" in text assert "错误: ValueError: snapshot denied" in text diff --git a/tests/test_runtime_config_support.py b/tests/test_runtime_config_support.py index 7264bf8..db582ea 100644 --- a/tests/test_runtime_config_support.py +++ b/tests/test_runtime_config_support.py @@ -10,7 +10,7 @@ ) -def _target_json(profile="russell_top50_leader_rotation_aggressive") -> str: +def _target_json(profile="russell_top50_leader_rotation") -> str: return ( '{"platform_id":"firstrade","strategy_profile":"' + profile @@ -23,7 +23,7 @@ def test_runtime_execution_window_uses_generic_env(monkeypatch): monkeypatch.setenv("FIRSTRADE_TECH_RUNTIME_EXECUTION_WINDOW_TRADING_DAYS", "3") assert ( - _runtime_execution_window_trading_days_env("russell_top50_leader_rotation_aggressive") + _runtime_execution_window_trading_days_env("russell_top50_leader_rotation") == 7 ) assert ( @@ -41,7 +41,7 @@ def test_runtime_execution_window_keeps_legacy_tech_env(monkeypatch): == 5 ) assert ( - _runtime_execution_window_trading_days_env("russell_top50_leader_rotation_aggressive") + _runtime_execution_window_trading_days_env("russell_top50_leader_rotation") is None ) @@ -255,4 +255,4 @@ def test_runtime_execution_window_rejects_invalid_generic_env(monkeypatch, raw_v ValueError, match="FIRSTRADE_RUNTIME_EXECUTION_WINDOW_TRADING_DAYS", ): - _runtime_execution_window_trading_days_env("russell_top50_leader_rotation_aggressive") + _runtime_execution_window_trading_days_env("russell_top50_leader_rotation") diff --git a/tests/test_session_check_service.py b/tests/test_session_check_service.py index 3df119f..165dcb5 100644 --- a/tests/test_session_check_service.py +++ b/tests/test_session_check_service.py @@ -112,7 +112,7 @@ def test_run_session_check_persists_funds_snapshot_when_enabled(): def test_monthly_session_check_skips_when_current_period_is_already_maintained(): now = datetime(2026, 6, 3, 1, 2, 3, tzinfo=timezone.utc) state_key = ( - "session-checks/auto/russell_top50_leader_rotation_aggressive/2026_06/latest.json" + "session-checks/auto/russell_top50_leader_rotation/2026_06/latest.json" ) store = FakeStateStore( { @@ -126,7 +126,7 @@ def test_monthly_session_check_skips_when_current_period_is_already_maintained() result = run_session_check( client_factory=ExplodingClient, state_store=store, - env_reader=_env({"STRATEGY_PROFILE": "russell_top50_leader_rotation_aggressive"}), + env_reader=_env({"STRATEGY_PROFILE": "russell_top50_leader_rotation"}), now=now, ) @@ -147,14 +147,14 @@ def test_monthly_session_check_runs_and_persists_maintenance_state_when_due(): credentials=FirstradeCredentials(username="user", password="pass"), client_factory=FakeClient, state_store=store, - env_reader=_env({"STRATEGY_PROFILE": "russell_top50_leader_rotation_aggressive"}), + env_reader=_env({"STRATEGY_PROFILE": "russell_top50_leader_rotation"}), now=now, ) assert result["ok"] is True assert result["session_check_maintenance_state_persisted"] is True state_key = ( - "session-checks/auto/russell_top50_leader_rotation_aggressive/2026_06/latest.json" + "session-checks/auto/russell_top50_leader_rotation/2026_06/latest.json" ) assert store.reads == [state_key] assert store.writes == [ @@ -164,7 +164,7 @@ def test_monthly_session_check_runs_and_persists_maintenance_state_when_due(): "checked_at": "2026-06-03T01:02:03+00:00", "account": "****5678", "session_reused": True, - "strategy_profile": "russell_top50_leader_rotation_aggressive", + "strategy_profile": "russell_top50_leader_rotation", "strategy_cadence": "monthly", "strategy_required_inputs": ["feature_snapshot"], "period": "2026-06", @@ -196,7 +196,7 @@ def test_daily_session_check_runs_every_time_without_maintenance_state_lookup(): def test_session_check_policy_always_overrides_monthly_throttle(): now = datetime(2026, 6, 3, 1, 2, 3, tzinfo=timezone.utc) state_key = ( - "session-checks/auto/russell_top50_leader_rotation_aggressive/2026_06/latest.json" + "session-checks/auto/russell_top50_leader_rotation/2026_06/latest.json" ) store = FakeStateStore({state_key: {"checked_at": "2026-06-01T01:02:03+00:00"}}) @@ -206,7 +206,7 @@ def test_session_check_policy_always_overrides_monthly_throttle(): state_store=store, env_reader=_env( { - "STRATEGY_PROFILE": "russell_top50_leader_rotation_aggressive", + "STRATEGY_PROFILE": "russell_top50_leader_rotation", "FIRSTRADE_SESSION_CHECK_POLICY": "always", } ), diff --git a/tests/test_strategy_runtime.py b/tests/test_strategy_runtime.py index 6ed528f..ccff68a 100644 --- a/tests/test_strategy_runtime.py +++ b/tests/test_strategy_runtime.py @@ -9,7 +9,7 @@ def _runtime_settings(**overrides) -> PlatformRuntimeSettings: "project_id": None, "account_prefix": "FIRSTRADE", "account_region": "US", - "strategy_profile": "russell_top50_leader_rotation_aggressive", + "strategy_profile": "russell_top50_leader_rotation", "strategy_display_name": "Mega Cap Leader Rotation Top 50 Balanced", "strategy_domain": "us_equity", "notify_lang": "en", @@ -29,7 +29,7 @@ def test_runtime_execution_window_override_applies_to_mega_strategy(): settings = _runtime_settings(runtime_execution_window_trading_days=7) assert _build_runtime_overrides( - "russell_top50_leader_rotation_aggressive", + "russell_top50_leader_rotation", settings, ) == {"runtime_execution_window_trading_days": 7}