diff --git a/application/rebalance_service.py b/application/rebalance_service.py index 78f679f..64cbb7e 100644 --- a/application/rebalance_service.py +++ b/application/rebalance_service.py @@ -288,13 +288,13 @@ def build_dashboard( qty = positions[symbol]["quantity"] avg = positions[symbol]["avg_cost"] market_value = qty * avg - position_lines.append(f" {symbol}: {qty}股 ${market_value:,.2f}") + position_lines.append(f" - {symbol}: {qty}股 | ${market_value:,.2f}") position_text = "\n".join(position_lines) if position_lines else translator("empty_positions") signal_metadata = signal_metadata or {} allocation = _resolve_weight_allocation(signal_metadata, required=False) target_lines = [] for symbol, weight in sorted(allocation.get("targets", {}).items(), key=lambda item: (-item[1], item[0])): - target_lines.append(f" {symbol}: {weight:.1%}") + target_lines.append(f" - {symbol}: {weight:.1%}") target_text = "\n".join(target_lines) if target_lines else translator("empty_target_weights") regime = signal_metadata.get("regime") breadth_ratio = signal_metadata.get("breadth_ratio") @@ -323,14 +323,19 @@ def build_dashboard( else None, translator("snapshot_as_of_detail", value=_format_text(snapshot_as_of, fallback="")) if snapshot_as_of else None, ] - diagnostics_text = " | ".join(part for part in diagnostics if part) + diagnostics_lines = [f" - {part}" for part in diagnostics if part] + diagnostics_text = "\n".join(diagnostics_lines) localized_status_desc = _localize_notification_text(status_desc, translator=translator) localized_signal_desc = _localize_notification_text(signal_desc, translator=translator) return ( - f"{translator('equity')}: ${equity:,.2f} | {translator('buying_power')}: ${buying_power:,.2f}\n" + f"{translator('account_summary_title')}\n" + f" - {translator('equity')}: ${equity:,.2f}\n" + f" - {translator('buying_power')}: ${buying_power:,.2f}\n" f"{separator}\n" + f"{translator('positions_title')}\n" f"{position_text}\n" f"{separator}\n" + f"{translator('execution_summary_title')}\n" f"{diagnostics_text}\n" f"{separator}\n" f"{status_icon} {localized_status_desc}\n" diff --git a/notifications/telegram.py b/notifications/telegram.py index 871bced..3c33cef 100644 --- a/notifications/telegram.py +++ b/notifications/telegram.py @@ -14,6 +14,9 @@ "buying_power": "购买力", "empty_positions": " (空仓)", "empty_target_weights": " (无目标持仓)", + "account_summary_title": "📊 账户摘要", + "positions_title": "💼 当前持仓", + "execution_summary_title": "🧾 执行摘要", "target_weights_title": "目标持仓", "strategy_profile_detail": "策略={profile}", "execution_profile_detail": "执行配置={profile}", @@ -93,6 +96,9 @@ "buying_power": "Buying Power", "empty_positions": " (No positions)", "empty_target_weights": " (No target positions)", + "account_summary_title": "📊 Account Summary", + "positions_title": "💼 Current Positions", + "execution_summary_title": "🧾 Execution Summary", "target_weights_title": "Target Weights", "strategy_profile_detail": "strategy_profile={profile}", "execution_profile_detail": "profile={profile}", diff --git a/tests/test_notifications.py b/tests/test_notifications.py index 16a6655..39c00cb 100644 --- a/tests/test_notifications.py +++ b/tests/test_notifications.py @@ -5,6 +5,9 @@ def test_build_translator_supports_chinese(): translate = build_translator("zh") assert translate("equity") == "净值" + assert translate("account_summary_title") == "📊 账户摘要" + assert translate("positions_title") == "💼 当前持仓" + assert translate("execution_summary_title") == "🧾 执行摘要" assert translate("target_weights_title") == "目标持仓" assert translate("market_status_risk_on", asset="SOXL") == "🚀 风险开启(SOXL)" assert translate("signal_risk_on", window=150, ratio="40.2%") == "SOXL 站上 150 日均线,持有 SOXL,交易层风险仓位 40.2%" diff --git a/tests/test_rebalance_service.py b/tests/test_rebalance_service.py index fdf47fa..a48c1c4 100644 --- a/tests/test_rebalance_service.py +++ b/tests/test_rebalance_service.py @@ -25,6 +25,9 @@ def _build_test_translator(): "buying_power": "buying_power", "empty_positions": "(empty positions)", "empty_target_weights": "(empty target positions)", + "account_summary_title": "Account Summary", + "positions_title": "Current Positions", + "execution_summary_title": "Execution Summary", "target_weights_title": "Target Weights", "strategy_label": "strategy={name}", "strategy_profile_detail": "strategy_profile={profile}", @@ -85,6 +88,9 @@ def test_build_dashboard_localizes_strategy_details(): ) assert "🧭 策略: 全球 ETF 轮动" in dashboard + assert "📊 账户摘要" in dashboard + assert "💼 当前持仓" in dashboard + assert "🧾 执行摘要" in dashboard assert "目标持仓" in dashboard assert "市场阶段=risk_off" in dashboard assert "快照路径" not in dashboard @@ -209,6 +215,9 @@ def fake_execute_rebalance( assert observed["strategy_symbols"] == ("AAA", "BOXX") assert observed["signal_metadata"]["managed_symbols"] == ("AAA", "BOXX") assert observed["messages"] + assert "Account Summary" in observed["messages"][0] + assert "Current Positions" in observed["messages"][0] + assert "Execution Summary" in observed["messages"][0] assert "📏 breadth=60.0%" in observed["messages"][0] assert "Target Weights" in observed["messages"][0]