Skip to content

Commit e3c2678

Browse files
authored
fix compact notifications and longport runtime (#281)
1 parent fb77024 commit e3c2678

6 files changed

Lines changed: 85 additions & 40 deletions

File tree

constraints.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# Auto-updated by update-qpk-pin.yml on every push to QPK main.
55

66
quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@d786c1140967f0e96e35599d057f0655e5a9ba25
7-
us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@c8df5f9659340965bd7f53998892ed1018ed4254
7+
us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@c57fb20528d40f363ae149ae6e3f6b1a240ec237
88
hk-equity-strategies @ git+https://github.com/QuantStrategyLab/HkEquityStrategies.git@700c8b19c46336d3d8fcba687e58553afcf0235f
99
cn-equity-strategies @ git+https://github.com/QuantStrategyLab/CnEquityStrategies.git@e09fd557c2bd9ae5f4d44228915c4e52c4b0dd21
1010
crypto-strategies @ git+https://github.com/QuantStrategyLab/CryptoStrategies.git@565be005e4186fd6d750884e7b0bc59f779f9549

notifications/renderers.py

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -346,11 +346,26 @@ def _build_risk_control_lines(execution, *, translator):
346346
def _relabel_dashboard_buying_power(text: str, *, cash_only_execution: bool, translator) -> str:
347347
value = str(text or "")
348348
if cash_only_execution:
349+
value = value.replace("总资产(策略净值)", "总资产(策略标的+现金,不含融资额度)")
350+
value = value.replace(
351+
"Total assets (strategy net liquidation)",
352+
"Total assets (strategy symbols + cash, ex-margin)",
353+
)
349354
target = translator("buying_power")
350355
for source in ("Buying power", "购买力"):
351356
if source != target:
352357
value = value.replace(source, target)
353358
return value
359+
value = value.replace("总资产(策略标的+现金,不含融资额度)", "总资产(策略净值)")
360+
value = value.replace("总资产(策略标的+现金)", "总资产(策略净值)")
361+
value = value.replace(
362+
"Total assets (strategy symbols + cash, ex-margin)",
363+
"Total assets (strategy net liquidation)",
364+
)
365+
value = value.replace(
366+
"Total assets (strategy symbols + cash)",
367+
"Total assets (strategy net liquidation)",
368+
)
354369
target = translator("buying_power_margin")
355370
for source in ("Available cash", "可用现金"):
356371
if source != target:
@@ -377,16 +392,30 @@ def _format_dashboard_text(text, *, translator=None, cash_only_execution: bool =
377392
return result
378393

379394

380-
def _append_dashboard_block(lines, *, execution, separator, translator) -> None:
395+
def _is_compact_dashboard_audit_line(line: str) -> bool:
396+
text = str(line or "").strip()
397+
lowered = text.lower()
398+
return (
399+
text.startswith(("⏱", "🧾", "🧩 输入状态", "📊", "🎯", "🛡️"))
400+
or lowered.startswith(("signal:", "signal:", "market status:"))
401+
or text.startswith(("信号:", "信号:", "市场状态:", "市场状态:"))
402+
)
403+
404+
405+
def _append_dashboard_block(lines, *, execution, separator, translator, compact: bool = False) -> None:
381406
cash_only_execution = bool(execution.get("cash_only_execution", True))
382407
dashboard_text = _format_dashboard_text(
383408
execution.get("dashboard_text"),
384409
translator=translator,
385410
cash_only_execution=cash_only_execution,
386411
)
387-
if dashboard_text:
412+
dashboard_lines = [
413+
line for line in dashboard_text.splitlines()
414+
if not (compact and _is_compact_dashboard_audit_line(line))
415+
]
416+
if dashboard_lines:
388417
lines.append(separator)
389-
lines.extend(dashboard_text.splitlines())
418+
lines.extend(dashboard_lines)
390419

391420

392421
def _append_timing_lines(lines, *, execution, translator) -> None:
@@ -570,8 +599,7 @@ def render_rebalance_notification(
570599
if dry_run_only:
571600
compact_lines.append(translator("dry_run_banner"))
572601
_append_extra_notification_lines(compact_lines, extra_notification_lines)
573-
compact_lines.append(separator)
574-
_append_dashboard_block(compact_lines, execution=execution, separator=separator, translator=translator)
602+
_append_dashboard_block(compact_lines, execution=execution, separator=separator, translator=translator, compact=True)
575603
compact_lines.append(separator)
576604
compact_lines.append(formatted_logs)
577605
return RenderedNotification(
@@ -633,16 +661,7 @@ def render_heartbeat_notification(
633661
if dry_run_only:
634662
compact_lines.append(translator("dry_run_banner"))
635663
_append_extra_notification_lines(compact_lines, extra_notification_lines)
636-
_append_dashboard_block(compact_lines, execution=execution, separator=separator, translator=translator)
637-
_append_timing_lines(compact_lines, execution=execution, translator=translator)
638-
_append_signal_snapshot_line(compact_lines, execution=execution, translator=translator)
639-
_append_source_input_line(compact_lines, execution=execution, translator=translator)
640-
_append_compact_status_lines(
641-
compact_lines,
642-
execution=execution,
643-
translator=translator,
644-
signal_key="heartbeat_signal",
645-
)
664+
_append_dashboard_block(compact_lines, execution=execution, separator=separator, translator=translator, compact=True)
646665
compact_lines.extend(
647666
[
648667
separator,

notifications/telegram.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ def _break_telegram_market_symbol_auto_links(value) -> str:
158158
"strategy_name_qqq_tech_enhancement": "科技通信回调增强",
159159
"strategy_name_russell_top50_leader_rotation": "罗素 Top50 领涨轮动",
160160
"strategy_name_ibit_smart_dca": "IBIT 比特币 ETF 智能定投",
161+
"strategy_name_nasdaq_sp500_smart_dca": "纳指100 / 标普500 智能定投",
161162
"strategy_name_hk_global_etf_tactical_rotation": "港股全球 ETF 战术轮动",
162163
"strategy_name_hk_low_vol_dividend_quality_snapshot": "港股低波股息质量快照",
163164
"strategy_name_cn_industry_etf_rotation": "A股行业ETF轮动",
@@ -344,6 +345,7 @@ def _break_telegram_market_symbol_auto_links(value) -> str:
344345
"strategy_name_qqq_tech_enhancement": "Tech/Communication Pullback Enhancement",
345346
"strategy_name_russell_top50_leader_rotation": "Russell Top50 Leader Rotation",
346347
"strategy_name_ibit_smart_dca": "IBIT Smart DCA",
348+
"strategy_name_nasdaq_sp500_smart_dca": "Nasdaq 100 / S&P 500 Smart DCA",
347349
"strategy_name_hk_global_etf_tactical_rotation": "HK Global ETF Tactical Rotation",
348350
"strategy_name_hk_low_vol_dividend_quality_snapshot": "HK Low-Vol Dividend Quality Snapshot",
349351
"strategy_name_cn_industry_etf_rotation": "CN Industry ETF Rotation",

requirements.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ pandas-market-calendars
77
google-cloud-secret-manager
88
google-cloud-storage
99
google-auth
10-
longport
10+
longport==3.0.23
1111
quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@d786c1140967f0e96e35599d057f0655e5a9ba25
12-
us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@c8df5f9659340965bd7f53998892ed1018ed4254
12+
us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@c57fb20528d40f363ae149ae6e3f6b1a240ec237
1313
hk-equity-strategies @ git+https://github.com/QuantStrategyLab/HkEquityStrategies.git@700c8b19c46336d3d8fcba687e58553afcf0235f

tests/test_dependency_pins.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from pathlib import Path
2+
3+
4+
def test_longport_stays_on_python_312_compatible_major() -> None:
5+
requirements = Path("requirements.txt").read_text(encoding="utf-8")
6+
7+
assert "longport==3.0.23" in requirements

tests/test_notifications.py

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -171,10 +171,11 @@ def test_heartbeat_signal_snapshot_localizes_price_source(self):
171171
dry_run_only=False,
172172
)
173173

174-
self.assertIn("数据源 LongBridge 日线K线", rendered.compact_text)
175-
self.assertIn("🧩 输入状态: 价格 2026-05-28 | 股票池 2026-04-30 | 股票池复用 连续1次", rendered.compact_text)
176-
self.assertNotIn("报价覆盖", rendered.compact_text)
177-
self.assertIn("📊 市场状态: 🚀 风险开启(SOXX+SOXL)", rendered.compact_text)
174+
self.assertIn("数据源 LongBridge 日线K线", rendered.detailed_text)
175+
self.assertIn("🧩 输入状态: 价格 2026-05-28 | 股票池 2026-04-30 | 股票池复用 连续1次", rendered.detailed_text)
176+
self.assertIn("📊 市场状态: 🚀 风险开启(SOXX+SOXL)", rendered.detailed_text)
177+
self.assertNotIn("数据源 LongBridge 日线K线", rendered.compact_text)
178+
self.assertNotIn("📊 市场状态", rendered.compact_text)
178179
self.assertNotIn("longbridge_candlesticks", rendered.compact_text)
179180

180181
def test_dry_run_heartbeat_uses_dry_run_title(self):
@@ -264,12 +265,14 @@ def test_heartbeat_renders_tqqq_volatility_delever_risk_control(self):
264265

265266
self.assertIn(
266267
"🛡️ 风控: QQQ 5 日年化波动率 31.2% 高于实际阈值 30.0%(动态 p90,252日窗口,范围 24.0%-36.0%,样本 252),TQQQ 转向 QQQ(杠杆仓位:TQQQ 保留 25.0%,QQQ 75.0%)",
267-
zh_rendered.compact_text,
268+
zh_rendered.detailed_text,
268269
)
270+
self.assertNotIn("🛡️ 风控:", zh_rendered.compact_text)
269271
self.assertIn(
270272
"🛡️ Risk control: QQQ 5d annualized volatility 31.2% is above effective threshold 30.0% (dynamic p90, 252d lookback, bounded 24.0%-36.0%, samples 252); TQQQ redirects to QQQ (leveraged sleeve: TQQQ retained 25.0%, QQQ 75.0%)",
271-
en_rendered.compact_text,
273+
en_rendered.detailed_text,
272274
)
275+
self.assertNotIn("🛡️ Risk control:", en_rendered.compact_text)
273276

274277
def test_heartbeat_renders_tqqq_volatility_delever_hysteresis_risk_control(self):
275278
zh_rendered = render_heartbeat_notification(
@@ -331,12 +334,14 @@ def test_heartbeat_renders_tqqq_volatility_delever_hysteresis_risk_control(self)
331334

332335
self.assertIn(
333336
"🛡️ 风控: QQQ 5 日年化波动率 26.2% 仍高于退出阈值 24.0%;入场实际阈值 30.0%(动态 p90,252日窗口,范围 24.0%-36.0%,样本 252),维持 TQQQ 转向 QQQM(杠杆仓位:TQQQ 保留 0.0%,QQQM 100.0%)",
334-
zh_rendered.compact_text,
337+
zh_rendered.detailed_text,
335338
)
339+
self.assertNotIn("🛡️ 风控:", zh_rendered.compact_text)
336340
self.assertIn(
337341
"🛡️ Risk control: QQQ 5d annualized volatility 26.2% remains above exit threshold 24.0%; entry effective threshold 30.0% (dynamic p90, 252d lookback, bounded 24.0%-36.0%, samples 252); keep TQQQ redirected to QQQM (leveraged sleeve: TQQQ retained 0.0%, QQQM 100.0%)",
338-
en_rendered.compact_text,
342+
en_rendered.detailed_text,
339343
)
344+
self.assertNotIn("🛡️ Risk control:", en_rendered.compact_text)
340345

341346
def test_heartbeat_localizes_strategy_diagnostics_and_source_input_status(self):
342347
rendered = render_heartbeat_notification(
@@ -369,25 +374,37 @@ def test_heartbeat_localizes_strategy_diagnostics_and_source_input_status(self):
369374
dry_run_only=False,
370375
)
371376

372-
self.assertIn("🧩 输入状态: 价格 2026-06-01 | 股票池 2026-05-14 | 状态 部分行情刷新", rendered.compact_text)
373-
self.assertIn(
374-
"🎯 信号: 市场阶段=进攻 市场宽度=68.0% 基准趋势=向上 "
375-
"目标股票仓位=100.0% 实际股票仓位=100.0% 入选标的数=4 "
376-
"前排标的=MU(4.07), INTC(2.23), AMD(1.96)",
377-
rendered.compact_text,
378-
)
379-
self.assertIn("📊 市场状态: 市场阶段=进攻", rendered.compact_text)
380-
self.assertIn(
381-
"🎯 信号: 市场阶段=进攻 市场宽度=68.0% 基准趋势=向上 "
382-
"目标股票仓位=100.0% 实际股票仓位=100.0% 入选标的数=4 "
383-
"前排标的=MU(4.07), INTC(2.23), AMD(1.96)",
384-
rendered.compact_text,
385-
)
377+
self.assertIn("🧩 输入状态: 价格 2026-06-01 | 股票池 2026-05-14 | 状态 部分行情刷新", rendered.detailed_text)
378+
self.assertNotIn("🎯 信号:", rendered.compact_text)
379+
self.assertIn("📊 市场状态: 市场阶段=进攻", rendered.detailed_text)
380+
self.assertNotIn("🧩 输入状态", rendered.compact_text)
381+
self.assertNotIn("📊 市场状态", rendered.compact_text)
386382
self.assertNotIn("regime=risk_on", rendered.compact_text)
387383
self.assertNotIn("benchmark_trend=up", rendered.compact_text)
388384
self.assertNotIn("target_stock=", rendered.compact_text)
389385
self.assertNotIn("partial_history_refresh", rendered.compact_text)
390386

387+
def test_dashboard_relabels_total_assets_for_margin_execution(self):
388+
rendered = render_rebalance_notification(
389+
execution={
390+
"dashboard_text": " - 总资产(策略标的+现金,不含融资额度): $50,000.00\n - 可用现金: $75,000.00",
391+
"cash_only_execution": False,
392+
"signal_display": "hold",
393+
"status_display": "hold",
394+
},
395+
logs=(),
396+
skip_logs=(),
397+
note_logs=(),
398+
translator=build_translator("zh"),
399+
separator="━━━━━━━━━━━━━━━━━━",
400+
strategy_display_name="TQQQ 增长收益",
401+
dry_run_only=False,
402+
)
403+
404+
self.assertIn("总资产(策略净值): $50,000.00", rendered.compact_text)
405+
self.assertIn("购买力: $75,000.00", rendered.compact_text)
406+
self.assertNotIn("不含融资额度", rendered.compact_text)
407+
391408
def test_dashboard_relabels_buying_power_for_cash_only_execution(self):
392409
rendered = render_rebalance_notification(
393410
execution={

0 commit comments

Comments
 (0)