From bc235cc3c806d6b4a587bea2b457c9ff959c0854 Mon Sep 17 00:00:00 2001 From: Pigbibi <20649888+Pigbibi@users.noreply.github.com> Date: Sat, 18 Apr 2026 02:07:30 +0800 Subject: [PATCH] Beautify notification layout --- application/rebalance_service.py | 25 +++++++++++++------------ notifications/telegram.py | 4 ++++ tests/test_notifications.py | 2 ++ tests/test_rebalance_service.py | 8 +++++++- 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/application/rebalance_service.py b/application/rebalance_service.py index 960f15f..ec382bb 100644 --- a/application/rebalance_service.py +++ b/application/rebalance_service.py @@ -136,6 +136,14 @@ def _build_benchmark_line(execution): ) +def _format_holdings_lines(portfolio_rows, market_values, *, translator) -> list[str]: + lines = [translator("holdings_title")] + for row in portfolio_rows: + for symbol in row: + lines.append(f" - {symbol}: ${market_values[symbol]:,.2f}") + return lines + + def _append_status_lines(lines, *, execution, translator, signal_key): status_display = _localize_notification_text(execution.get("status_display"), translator=translator) if status_display: @@ -501,7 +509,7 @@ def record_dry_run(symbol, side, quantity, price, *, order_type): available=f"{available_cash:.2f}", investable=f"{investable_cash:.2f}", ) - formatted_logs = "\n".join(f" {log}" for log in [*logs, *skip_logs, *note_logs]) + formatted_logs = "\n".join(f" - {log}" for log in [*logs, *skip_logs, *note_logs]) tg_lines = [translator("rebalance_title")] _append_strategy_line( tg_lines, @@ -517,7 +525,7 @@ def record_dry_run(symbol, side, quantity, price, *, order_type): translator=translator, signal_key="signal", ) - tg_lines.extend([separator, formatted_logs]) + tg_lines.extend([separator, translator("order_logs_title"), formatted_logs]) tg_message = "\n".join(tg_lines) print(with_prefix(tg_message), flush=True) send_tg_message(tg_message) @@ -528,14 +536,7 @@ def record_dry_run(symbol, side, quantity, price, *, order_type): available=f"{available_cash:.2f}", investable=f"{investable_cash:.2f}", ) - holdings_lines = [] - for row in portfolio_rows: - holdings_lines.append( - " ".join( - f"{symbol}: ${market_values[symbol]:,.2f}" - for symbol in row - ) - ) + holdings_lines = _format_holdings_lines(portfolio_rows, market_values, translator=translator) no_trade_lines = [ translator("heartbeat_title"), ] @@ -572,13 +573,13 @@ def record_dry_run(symbol, side, quantity, price, *, order_type): no_trade_message += ( f"\n{separator}\n" f"{translator('skipped_actions')}\n" - + "\n".join(f" {log}" for log in skip_logs) + + "\n".join(f" - {log}" for log in skip_logs) ) if note_logs: no_trade_message += ( f"\n{separator}\n" f"{translator('notes_title')}\n" - + "\n".join(f" {log}" for log in note_logs) + + "\n".join(f" - {log}" for log in note_logs) ) print(with_prefix(no_trade_message), flush=True) send_tg_message(no_trade_message) diff --git a/notifications/telegram.py b/notifications/telegram.py index d5025a3..8e52584 100644 --- a/notifications/telegram.py +++ b/notifications/telegram.py @@ -28,6 +28,8 @@ "equity": "💰 净值: ${value}", "cash_summary": "💵 账户现金: ${available} | 可投资现金: ${investable}", "cash_label": "现金", + "holdings_title": "💼 持仓", + "order_logs_title": "🧾 执行明细", "heartbeat_signal": "🎯 信号: {msg}", "no_trades": "✅ 无需调仓", "no_executable_orders": "⚠️ 本轮没有可执行订单", @@ -95,6 +97,8 @@ "equity": "💰 Equity: ${value}", "cash_summary": "💵 Cash: ${available} | Investable cash: ${investable}", "cash_label": "Cash", + "holdings_title": "💼 Holdings", + "order_logs_title": "🧾 Execution details", "heartbeat_signal": "🎯 Signal: {msg}", "no_trades": "✅ No trades needed", "no_executable_orders": "⚠️ No executable orders this cycle", diff --git a/tests/test_notifications.py b/tests/test_notifications.py index e9fede5..c51917e 100644 --- a/tests/test_notifications.py +++ b/tests/test_notifications.py @@ -30,6 +30,8 @@ class NotificationTests(unittest.TestCase): def test_build_translator_supports_chinese(self): translate = build_translator("zh") self.assertEqual(translate("equity", value="123.45"), "💰 净值: $123.45") + self.assertEqual(translate("holdings_title"), "💼 持仓") + self.assertEqual(translate("order_logs_title"), "🧾 执行明细") self.assertEqual(translate("market_status_blend_gate_risk_on", asset="SOXX+SOXL"), "🚀 风险开启(SOXX+SOXL)") self.assertEqual( translate( diff --git a/tests/test_rebalance_service.py b/tests/test_rebalance_service.py index 04afed9..c685aac 100644 --- a/tests/test_rebalance_service.py +++ b/tests/test_rebalance_service.py @@ -609,7 +609,8 @@ def test_heartbeat_accepts_normalized_portfolio_and_execution_sections(self): self.assertEqual(len(sent_messages), 1) self.assertIn("💓 【心跳检测】", sent_messages[0]) self.assertIn("可投资现金", sent_messages[0]) - self.assertIn("SOXX", sent_messages[0]) + self.assertIn("💼 持仓", sent_messages[0]) + self.assertIn(" - SOXX:", sent_messages[0]) def test_hybrid_heartbeat_hides_empty_semiconductor_fields_and_shows_benchmark_line(self): plan = _build_plan( @@ -650,8 +651,13 @@ def test_hybrid_heartbeat_hides_empty_semiconductor_fields_and_shows_benchmark_l self.assertIn("💓 【心跳检测】", sent_messages[0]) self.assertIn("🧭 策略: TQQQ 增长收益", sent_messages[0]) self.assertIn("🧪 模拟运行模式", sent_messages[0]) + self.assertIn("💼 持仓", sent_messages[0]) + self.assertIn(" - TQQQ: $0.00", sent_messages[0]) + self.assertIn(" - BOXX: $0.00", sent_messages[0]) + self.assertIn(" - QQQI: $0.00", sent_messages[0]) self.assertIn("QQQ: 588.50 | MA200: 595.25 | Exit: 573.00", sent_messages[0]) self.assertIn("🎯 信号: 💤 等待信号", sent_messages[0]) + self.assertNotIn("TQQQ: $0.00 BOXX", sent_messages[0]) self.assertNotIn("📊 市场状态: ", sent_messages[0]) self.assertNotIn("💼 交易层风险仓位: ", sent_messages[0]) self.assertNotIn("🏦 收入层锁定占比: ", sent_messages[0])