Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions notifications/telegram.py
Original file line number Diff line number Diff line change
Expand Up @@ -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": "无法获取报价",
Expand Down Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
]
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion strategy_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
6 changes: 3 additions & 3 deletions tests/test_decision_mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
24 changes: 12 additions & 12 deletions tests/test_rebalance_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
8 changes: 4 additions & 4 deletions tests/test_request_handling.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -213,15 +213,15 @@ 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):
monkeypatch.setenv("FIRSTRADE_RUN_STRATEGY_ON_HTTP", "true")
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):
Expand All @@ -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


Expand Down
8 changes: 4 additions & 4 deletions tests/test_runtime_config_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 (
Expand All @@ -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
)

Expand Down Expand Up @@ -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")
14 changes: 7 additions & 7 deletions tests/test_session_check_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
{
Expand All @@ -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,
)

Expand All @@ -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 == [
Expand All @@ -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",
Expand Down Expand Up @@ -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"}})

Expand All @@ -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",
}
),
Expand Down
4 changes: 2 additions & 2 deletions tests/test_strategy_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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}

Expand Down