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
22 changes: 22 additions & 0 deletions application/rebalance_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,28 @@ def _strategy_dashboard_text(signal_metadata, *, translator) -> str:
or metadata.get("dashboard")
or ""
)
cash_only_execution = bool(metadata.get("cash_only_execution", True))
if cash_only_execution:
dashboard_text = dashboard_text.replace("总资产(策略净值)", "总资产(策略标的+现金,不含融资额度)")
dashboard_text = dashboard_text.replace(
"Total assets (strategy net liquidation)",
"Total assets (strategy symbols + cash, ex-margin)",
)
dashboard_text = dashboard_text.replace("购买力", "可用现金")
dashboard_text = dashboard_text.replace("Buying power", "Available cash")
else:
dashboard_text = dashboard_text.replace("总资产(策略标的+现金,不含融资额度)", "总资产(策略净值)")
dashboard_text = dashboard_text.replace("总资产(策略标的+现金)", "总资产(策略净值)")
dashboard_text = dashboard_text.replace(
"Total assets (strategy symbols + cash, ex-margin)",
"Total assets (strategy net liquidation)",
)
dashboard_text = dashboard_text.replace(
"Total assets (strategy symbols + cash)",
"Total assets (strategy net liquidation)",
)
dashboard_text = dashboard_text.replace("可用现金", "购买力")
dashboard_text = dashboard_text.replace("Available cash", "Buying power")
timing_lines = _build_timing_audit_lines(metadata, translator=translator)
if not timing_lines:
return dashboard_text
Expand Down
2 changes: 1 addition & 1 deletion constraints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# Auto-updated by update-qpk-pin.yml on every push to QPK main.

quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@d786c1140967f0e96e35599d057f0655e5a9ba25
us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@c8df5f9659340965bd7f53998892ed1018ed4254
us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@c57fb20528d40f363ae149ae6e3f6b1a240ec237
hk-equity-strategies @ git+https://github.com/QuantStrategyLab/HkEquityStrategies.git@700c8b19c46336d3d8fcba687e58553afcf0235f
cn-equity-strategies @ git+https://github.com/QuantStrategyLab/CnEquityStrategies.git@e09fd557c2bd9ae5f4d44228915c4e52c4b0dd21
crypto-strategies @ git+https://github.com/QuantStrategyLab/CryptoStrategies.git@565be005e4186fd6d750884e7b0bc59f779f9549
47 changes: 46 additions & 1 deletion notifications/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,47 @@ def _format_dashboard_text(text) -> str:
return "\n".join(lines)


def _relabel_dashboard_cash_labels(text: str, *, cash_only_execution: bool) -> str:
value = str(text or "")
if cash_only_execution:
value = value.replace("总资产(策略净值)", "总资产(策略标的+现金,不含融资额度)")
value = value.replace(
"Total assets (strategy net liquidation)",
"Total assets (strategy symbols + cash, ex-margin)",
)
value = value.replace("购买力", "可用现金")
value = value.replace("Buying power", "Available cash")
return value
value = value.replace("总资产(策略标的+现金,不含融资额度)", "总资产(策略净值)")
value = value.replace("总资产(策略标的+现金)", "总资产(策略净值)")
value = value.replace(
"Total assets (strategy symbols + cash, ex-margin)",
"Total assets (strategy net liquidation)",
)
value = value.replace(
"Total assets (strategy symbols + cash)",
"Total assets (strategy net liquidation)",
)
value = value.replace("可用现金", "购买力")
value = value.replace("Available cash", "Buying power")
return value


def _format_compact_dashboard_text(text) -> str:
lines = []
for line in _format_dashboard_text(text).splitlines():
stripped = line.strip()
lowered = stripped.lower()
if stripped.startswith(("⏱", "🧾", "🛡️", "📊", "🎯")):
continue
if lowered.startswith(("signal:", "signal:", "market status:")):
continue
if stripped.startswith(("信号:", "信号:", "市场状态:", "市场状态:")):
continue
lines.append(line)
return "\n".join(lines)


def _build_timing_audit_lines(signal_metadata, *, translator) -> list[str]:
metadata = signal_metadata if isinstance(signal_metadata, Mapping) else {}
raw_annotations = metadata.get("execution_annotations")
Expand Down Expand Up @@ -646,6 +687,10 @@ def _strategy_dashboard_text(signal_metadata, *, translator) -> str:
or metadata.get("dashboard")
or ""
)
dashboard_text = _relabel_dashboard_cash_labels(
dashboard_text,
cash_only_execution=bool(metadata.get("cash_only_execution", True)),
)
risk_control_lines = _build_tqqq_risk_control_lines(risk_source, translator=translator)
timing_lines = _build_timing_audit_lines(metadata, translator=translator)
snapshot_line = _format_signal_snapshot_line(metadata.get("signal_snapshot"), translator=translator)
Expand Down Expand Up @@ -791,7 +836,7 @@ def _build_compact_message(
lines.append(" | ".join(str(e).strip() for e in extra if str(e).strip()))
lines.append(separator)
# Positions
dashboard = _format_dashboard_text(dashboard_text)
dashboard = _format_compact_dashboard_text(dashboard_text)
if dashboard:
lines.extend(dashboard.splitlines())
lines.append(separator)
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ google-cloud-secret-manager
google-cloud-storage
yfinance
quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@d786c1140967f0e96e35599d057f0655e5a9ba25
us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@c8df5f9659340965bd7f53998892ed1018ed4254
us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@c57fb20528d40f363ae149ae6e3f6b1a240ec237
hk-equity-strategies @ git+https://github.com/QuantStrategyLab/HkEquityStrategies.git@700c8b19c46336d3d8fcba687e58553afcf0235f
31 changes: 25 additions & 6 deletions tests/test_rebalance_service.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import json

from application.rebalance_service import _resolve_reconciliation_mode, run_strategy_core
from application.rebalance_service import _resolve_reconciliation_mode, _strategy_dashboard_text, run_strategy_core
from application.runtime_dependencies import IBKRRebalanceConfig
from notifications.renderers import (
_build_notification_trade_lines,
Expand Down Expand Up @@ -233,8 +233,8 @@ def test_trade_notification_localizes_compact_signal_state_for_zh_and_en():
translator=build_translator("en"),
)

assert "🎯 入场信号" in zh_notification.compact_text
assert "🎯 Entry Signal" in en_notification.compact_text
assert "🎯 入场信号" not in zh_notification.compact_text
assert "🎯 Entry Signal" not in en_notification.compact_text


def test_run_strategy_core_passes_signal_metadata_to_execution():
Expand Down Expand Up @@ -308,8 +308,9 @@ def fake_execute_rebalance(
assert "📌 Strategy portfolio" in observed["messages"][0]
assert "Total assets (strategy symbols + cash): $1,000.00" in observed["messages"][0]
assert "💼 Strategy holdings" in observed["messages"][0]
assert "📏 breadth=60.0%" in observed["messages"][0]
assert "⏱ Timing: 2026-04-01 -> 2026-04-02 (next trading day)" in observed["messages"][0]
assert "📏 breadth=60.0%" not in observed["messages"][0]
assert "⏱ Timing:" not in observed["messages"][0]
assert "Signal snapshot:" not in observed["messages"][0]
assert "Target Weights" not in observed["messages"][0]


Expand Down Expand Up @@ -423,11 +424,29 @@ def test_trade_notification_keeps_detailed_logs_out_of_compact_message():
assert "execution_profile=tqqq_growth_income" in notification.detailed_text
assert "execution_profile=tqqq_growth_income" not in notification.compact_text
assert "📌 Strategy portfolio" in notification.compact_text
assert "⏱ Timing: 2026-04-01 -> 2026-04-02 (next trading day)" in notification.compact_text
assert "⏱ Timing:" not in notification.compact_text
assert "no_order_plan_reason reason=min_notional:QQQ,TQQQ" in notification.compact_text
assert notification.compact_text.index("🆔 Account: U1234567") < notification.compact_text.index("🧩 Plugin:")


def test_strategy_dashboard_relabels_total_assets_when_margin_is_enabled():
dashboard = _strategy_dashboard_text(
{
"cash_only_execution": False,
"dashboard_text": (
"📌 策略账户概览\n"
" - 总资产(策略标的+现金,不含融资额度): $50,000.00\n"
" - 可用现金: $75,000.00"
),
},
translator=build_translator("zh"),
)

assert "总资产(策略净值): $50,000.00" in dashboard
assert "购买力: $75,000.00" in dashboard
assert "不含融资额度" not in dashboard


def test_trade_notification_includes_abnormal_order_batch():
notification = render_trade_notification(
dashboard="dashboard",
Expand Down
Loading