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
41 changes: 38 additions & 3 deletions application/rebalance_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from __future__ import annotations

import json
import re

from application.reconciliation_service import (
build_reconciliation_record,
Expand Down Expand Up @@ -88,6 +89,7 @@
("fail_reason=", "失败原因="),
("decision=", "决策="),
)
_DETAIL_FIELD_SPLIT_RE = re.compile(r"\s+(?=[^\s=::]+[=::])")


def _format_text(value, *, fallback: str) -> str:
Expand Down Expand Up @@ -121,6 +123,34 @@ def _localize_notification_text(text: str, *, translator) -> str:
return localized


def _split_detail_segment(text: str) -> list[str]:
value = str(text or "").strip()
if not value:
return []
if "=" not in value and ":" not in value and ":" not in value:
return [value]
return [part.strip() for part in _DETAIL_FIELD_SPLIT_RE.split(value) if part.strip()]


def _split_labeled_text(text: str) -> list[str]:
segments = [segment.strip() for segment in str(text or "").split(" | ") if segment.strip()]
if not segments:
return []
lines = [segments[0]]
for segment in segments[1:]:
lines.extend(_split_detail_segment(segment))
return lines


def _format_prefixed_text(prefix: str, text: str) -> list[str]:
parts = _split_labeled_text(text)
if not parts:
return []
lines = [f"{prefix} {parts[0]}".strip()]
lines.extend(f" - {part}" for part in parts[1:])
return lines


def _summarize_target_changes(target_vs_current, *, limit: int = 5) -> str | None:
rows = []
for row in target_vs_current or ():
Expand Down Expand Up @@ -240,7 +270,7 @@ def _build_notification_trade_lines(
if "same_day_execution_locked" in text or "当日执行锁已存在" in text:
continue
if text not in lines:
lines.append(text)
lines.extend(_split_labeled_text(text))

return lines

Expand Down Expand Up @@ -327,6 +357,10 @@ def build_dashboard(
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)
status_lines = _format_prefixed_text(status_icon, localized_status_desc)
signal_lines = _format_prefixed_text("🎯", localized_signal_desc)
status_text = "\n".join(status_lines)
signal_text = "\n".join(signal_lines)
return (
f"{translator('account_summary_title')}\n"
f" - {translator('equity')}: ${equity:,.2f}\n"
Expand All @@ -338,8 +372,8 @@ def build_dashboard(
f"{translator('execution_summary_title')}\n"
f"{diagnostics_text}\n"
f"{separator}\n"
f"{status_icon} {localized_status_desc}\n"
f"🎯 {localized_signal_desc}\n"
f"{status_text}\n"
f"{signal_text}\n"
f"{separator}\n"
f"{translator('target_weights_title')}:\n{target_text}"
)
Expand Down Expand Up @@ -397,6 +431,7 @@ def run_strategy_core(
no_op_text = f"{no_op_text} | {_localize_notification_text(f'reason={no_op_reason}', translator=translator)}"
if fail_reason:
no_op_text = f"{no_op_text} | {_localize_notification_text(f'fail_reason={fail_reason}', translator=translator)}"
no_op_text = "\n".join(_split_labeled_text(no_op_text))
record = build_reconciliation_record(
strategy_profile=signal_metadata.get("strategy_profile"),
mode="dry_run" if signal_metadata.get("dry_run_only") else "paper",
Expand Down
17 changes: 11 additions & 6 deletions tests/test_rebalance_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ def test_build_dashboard_localizes_snapshot_guard_text_for_zh():
status_icon="🛑",
)

assert "🛑 关闭执行 | 原因=缺少特征快照路径" in dashboard
assert "🛑 关闭执行\n - 原因=缺少特征快照路径" in dashboard
assert "🎯 特征快照校验阻止执行" in dashboard


Expand All @@ -138,7 +138,9 @@ def test_build_dashboard_localizes_qqq_tech_diagnostics_for_zh():
strategy_display_name="科技通信回调增强",
)

assert "市场阶段=软防御 | 市场宽度=41.2% | 目标股票仓位=60.0% | 实际股票仓位=60.0%" in dashboard
assert "🐤 市场阶段=软防御\n - 市场宽度=41.2%" in dashboard
assert " - 目标股票仓位=60.0%" in dashboard
assert " - 实际股票仓位=60.0%" in dashboard
assert "基准趋势=向下" in dashboard
assert "入选标的数=8 前排标的=CIEN(0.92)" in dashboard

Expand All @@ -156,10 +158,13 @@ def test_notification_trade_lines_localize_runtime_diagnostic_tail_for_zh():
)

assert lines == [
(
"执行配置=SOXL/SOXX 半导体趋势收益 | 市场阶段=无 | 宽度=0.0% | "
"目标股票仓位=0.0% | 实际股票仓位=0.0% | 快照日期=无 | 交易日=无"
)
"执行配置=SOXL/SOXX 半导体趋势收益",
"市场阶段=无",
"宽度=0.0%",
"目标股票仓位=0.0%",
"实际股票仓位=0.0%",
"快照日期=无",
"交易日=无",
]


Expand Down