Skip to content

Commit bffcaeb

Browse files
committed
Localize LongBridge strategy notifications
1 parent 7396874 commit bffcaeb

5 files changed

Lines changed: 74 additions & 10 deletions

File tree

application/rebalance_service.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,13 @@
2525
("regime=risk_on", "市场阶段=进攻"),
2626
("benchmark_trend=down", "基准趋势=向下"),
2727
("benchmark_trend=up", "基准趋势=向上"),
28+
("benchmark=down", "基准趋势=向下"),
29+
("benchmark=up", "基准趋势=向上"),
2830
("breadth=", "市场宽度="),
2931
("target_stock=", "目标股票仓位="),
3032
("realized_stock=", "实际股票仓位="),
33+
("stock_exposure=", "股票目标仓位="),
34+
("safe_haven=", "避险仓位="),
3135
("selected=", "入选标的数="),
3236
("top=", "前排标的="),
3337
("no_selection", "无入选标的"),
@@ -212,9 +216,17 @@ def run_strategy(
212216
current_min_trade = float(execution["current_min_trade"])
213217
portfolio_rows = tuple(portfolio["portfolio_rows"])
214218
def record_dry_run(symbol, side, quantity, price, *, order_type):
215-
price_text = f"${price:.2f}" if price is not None else "market"
216-
suffix = " LIMIT" if order_type == "limit" else ""
217-
message = f"🧪 DRY_RUN {side.upper()} {symbol} {quantity} @ {price_text}{suffix}"
219+
price_text = f"${price:.2f}" if price is not None else translator("order_type_market")
220+
side_key = "side_buy" if str(side).lower() == "buy" else "side_sell"
221+
order_type_key = "order_type_limit" if order_type == "limit" else "order_type_market"
222+
message = translator(
223+
"dry_run_order",
224+
side=translator(side_key),
225+
symbol=symbol,
226+
qty=quantity,
227+
price=price_text,
228+
order_type=translator(order_type_key),
229+
)
218230
logs.append(message)
219231
print(with_prefix(message), flush=True)
220232
return True

notifications/order_alerts.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,17 @@ def monitor_submitted_order_status(
158158
)
159159

160160

161+
162+
def _append_order_id_suffix(log_message, order_id, *, translator):
163+
order_id_text = str(order_id or "").strip()
164+
if not order_id_text:
165+
return log_message
166+
suffix = str(translator("order_id_suffix", order_id=order_id_text)).strip()
167+
if not suffix or suffix == "order_id_suffix":
168+
suffix = f"[order_id={order_id_text}]"
169+
return f"{log_message} {suffix}"
170+
171+
161172
def submit_order_with_alert(
162173
trade_context,
163174
symbol,
@@ -190,7 +201,7 @@ def submit_order_with_alert(
190201
submitted_price=submitted_price,
191202
)
192203
order_id = report.broker_order_id or ""
193-
log_with_order_id = f"{log_message} [order_id={order_id}]" if order_id else log_message
204+
log_with_order_id = _append_order_id_suffix(log_message, order_id, translator=translator)
194205
print_with_prefix(f"OK {log_with_order_id}")
195206
logs.append(log_with_order_id)
196207
monitor_submitted_order_status(

notifications/telegram.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
I18N = {
1818
"zh": {
1919
"rebalance_title": "🔔 【调仓指令】",
20-
"dry_run_banner": "🧪 dry-run 模式,本次不会真实下单",
20+
"dry_run_banner": "🧪 模拟运行模式,本次不会真实下单",
2121
"strategy_label": "🧭 策略: {name}",
2222
"market_status": "📊 市场状态: {status}",
2323
"risk_position": "💼 交易层风险仓位: {ratio}",
@@ -36,6 +36,7 @@
3636
"order_filled": "✅ 订单成交 | {symbol} {side} {qty}股 均价 ${price} (ID: {order_id})",
3737
"order_partial": "⚠️ 订单部分成交 | {symbol} {side} 已成交 {executed}/{qty}股 均价 ${price} (ID: {order_id})",
3838
"order_error": "❌ 订单异常 | {symbol} {side} {qty}股 已{status} (ID: {order_id}) 原因: {reason}",
39+
"order_id_suffix": "(订单号: {order_id})",
3940
"error_title": "🚨 【策略异常】",
4041
"buy_skipped": "⚪️ [买入跳过] {detail}",
4142
"sell_skipped": "⚪️ [卖出跳过] {detail}",
@@ -47,6 +48,9 @@
4748
"market_buy": "📈 [市价买入] {symbol}: {qty}股 @ ${price}",
4849
"limit_sell": "📉 [限价卖出] {symbol}: {qty}股 @ ${price}",
4950
"market_sell": "📉 [市价卖出] {symbol}: {qty}股 @ ${price}",
51+
"dry_run_order": "🧪 模拟{order_type}{side} {symbol}: {qty}股 @ {price}",
52+
"order_type_market": "市价",
53+
"order_type_limit": "限价",
5054
"side_buy": "买入",
5155
"side_sell": "卖出",
5256
"status_rejected": "拒绝",
@@ -87,6 +91,7 @@
8791
"order_filled": "✅ Order Filled | {symbol} {side} {qty} shares avg ${price} (ID: {order_id})",
8892
"order_partial": "⚠️ Partial Fill | {symbol} {side} filled {executed}/{qty} shares avg ${price} (ID: {order_id})",
8993
"order_error": "❌ Order Error | {symbol} {side} {qty} shares {status} (ID: {order_id}) reason: {reason}",
94+
"order_id_suffix": "[order_id={order_id}]",
9095
"error_title": "🚨 【Strategy Error】",
9196
"buy_skipped": "⚪️ [Buy skipped] {detail}",
9297
"sell_skipped": "⚪️ [Sell skipped] {detail}",
@@ -98,6 +103,9 @@
98103
"market_buy": "📈 [Market buy] {symbol}: {qty} shares @ ${price}",
99104
"limit_sell": "📉 [Limit sell] {symbol}: {qty} shares @ ${price}",
100105
"market_sell": "📉 [Market sell] {symbol}: {qty} shares @ ${price}",
106+
"dry_run_order": "🧪 DRY_RUN {order_type} {side} {symbol} {qty} @ {price}",
107+
"order_type_market": "market",
108+
"order_type_limit": "limit",
101109
"side_buy": "Buy",
102110
"side_sell": "Sell",
103111
"status_rejected": "Rejected",

tests/test_order_alerts.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from types import SimpleNamespace
2+
3+
from notifications.order_alerts import submit_order_with_alert
4+
from notifications.telegram import build_translator
5+
6+
7+
def test_submit_order_with_alert_localizes_order_id_suffix_for_zh():
8+
logs = []
9+
printed = []
10+
11+
submitted = submit_order_with_alert(
12+
trade_context=object(),
13+
symbol="BOXX.US",
14+
order_type="market",
15+
side="buy",
16+
quantity=49,
17+
logs=logs,
18+
log_message="📈 [市价买入] BOXX: 49股 @ $116.31",
19+
submit_order=lambda *_args, **_kwargs: SimpleNamespace(broker_order_id="1227343614540054528"),
20+
fetch_order_status=lambda *_args, **_kwargs: None,
21+
translator=build_translator("zh"),
22+
send_tg_message=lambda _message: None,
23+
notify_issue=lambda *_args, **_kwargs: None,
24+
order_poll_interval_sec=0,
25+
order_poll_max_attempts=0,
26+
sleeper=lambda _seconds: None,
27+
print_with_prefix=printed.append,
28+
)
29+
30+
assert submitted is True
31+
assert logs == ["📈 [市价买入] BOXX: 49股 @ $116.31 (订单号: 1227343614540054528)"]
32+
assert "order_id=" not in logs[0]
33+
assert printed == ["OK 📈 [市价买入] BOXX: 49股 @ $116.31 (订单号: 1227343614540054528)"]

tests/test_rebalance_service.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ def fake_submit_order_with_alert(
163163
submitted_price=None,
164164
):
165165
del trade_context, order_type, side, quantity, submitted_price
166-
logs.append(f"{log_message} [order_id=test-order]")
166+
logs.append(f"{log_message} (订单号: test-order")
167167
return True
168168

169169
plan_side_effect = [plan, refreshed_plan or plan]
@@ -444,9 +444,9 @@ def test_dry_run_replaces_real_order_submission_with_summary_lines(self):
444444
)
445445

446446
self.assertEqual(len(sent_messages), 1)
447-
self.assertIn("🧪 dry-run 模式", sent_messages[0])
448-
self.assertIn("🧪 DRY_RUN SELL SOXL.US", sent_messages[0])
449-
self.assertIn("🧪 DRY_RUN BUY SOXX.US", sent_messages[0])
447+
self.assertIn("🧪 模拟运行模式", sent_messages[0])
448+
self.assertIn("🧪 模拟限价卖出 SOXL.US", sent_messages[0])
449+
self.assertIn("🧪 模拟限价买入 SOXX.US", sent_messages[0])
450450

451451
def test_heartbeat_accepts_normalized_portfolio_and_execution_sections(self):
452452
plan = _build_plan(
@@ -517,7 +517,7 @@ def test_hybrid_heartbeat_hides_empty_semiconductor_fields_and_shows_benchmark_l
517517
self.assertEqual(len(sent_messages), 1)
518518
self.assertIn("💓 【心跳检测】", sent_messages[0])
519519
self.assertIn("🧭 策略: TQQQ 增长收益", sent_messages[0])
520-
self.assertIn("🧪 dry-run 模式", sent_messages[0])
520+
self.assertIn("🧪 模拟运行模式", sent_messages[0])
521521
self.assertIn("QQQ: 588.50 | MA200: 595.25 | Exit: 573.00", sent_messages[0])
522522
self.assertIn("🎯 信号: 💤 等待信号", sent_messages[0])
523523
self.assertNotIn("📊 市场状态: ", sent_messages[0])

0 commit comments

Comments
 (0)