33from __future__ import annotations
44
55import json
6+ import re
67
78from application .reconciliation_service import (
89 build_reconciliation_record ,
8889 ("fail_reason=" , "失败原因=" ),
8990 ("decision=" , "决策=" ),
9091)
92+ _DETAIL_FIELD_SPLIT_RE = re .compile (r"\s+(?=[^\s=::]+[=::])" )
9193
9294
9395def _format_text (value , * , fallback : str ) -> str :
@@ -121,6 +123,34 @@ def _localize_notification_text(text: str, *, translator) -> str:
121123 return localized
122124
123125
126+ def _split_detail_segment (text : str ) -> list [str ]:
127+ value = str (text or "" ).strip ()
128+ if not value :
129+ return []
130+ if "=" not in value and ":" not in value and ":" not in value :
131+ return [value ]
132+ return [part .strip () for part in _DETAIL_FIELD_SPLIT_RE .split (value ) if part .strip ()]
133+
134+
135+ def _split_labeled_text (text : str ) -> list [str ]:
136+ segments = [segment .strip () for segment in str (text or "" ).split (" | " ) if segment .strip ()]
137+ if not segments :
138+ return []
139+ lines = [segments [0 ]]
140+ for segment in segments [1 :]:
141+ lines .extend (_split_detail_segment (segment ))
142+ return lines
143+
144+
145+ def _format_prefixed_text (prefix : str , text : str ) -> list [str ]:
146+ parts = _split_labeled_text (text )
147+ if not parts :
148+ return []
149+ lines = [f"{ prefix } { parts [0 ]} " .strip ()]
150+ lines .extend (f" - { part } " for part in parts [1 :])
151+ return lines
152+
153+
124154def _summarize_target_changes (target_vs_current , * , limit : int = 5 ) -> str | None :
125155 rows = []
126156 for row in target_vs_current or ():
@@ -240,7 +270,7 @@ def _build_notification_trade_lines(
240270 if "same_day_execution_locked" in text or "当日执行锁已存在" in text :
241271 continue
242272 if text not in lines :
243- lines .append ( text )
273+ lines .extend ( _split_labeled_text ( text ) )
244274
245275 return lines
246276
@@ -327,6 +357,10 @@ def build_dashboard(
327357 diagnostics_text = "\n " .join (diagnostics_lines )
328358 localized_status_desc = _localize_notification_text (status_desc , translator = translator )
329359 localized_signal_desc = _localize_notification_text (signal_desc , translator = translator )
360+ status_lines = _format_prefixed_text (status_icon , localized_status_desc )
361+ signal_lines = _format_prefixed_text ("🎯" , localized_signal_desc )
362+ status_text = "\n " .join (status_lines )
363+ signal_text = "\n " .join (signal_lines )
330364 return (
331365 f"{ translator ('account_summary_title' )} \n "
332366 f" - { translator ('equity' )} : ${ equity :,.2f} \n "
@@ -338,8 +372,8 @@ def build_dashboard(
338372 f"{ translator ('execution_summary_title' )} \n "
339373 f"{ diagnostics_text } \n "
340374 f"{ separator } \n "
341- f"{ status_icon } { localized_status_desc } \n "
342- f"🎯 { localized_signal_desc } \n "
375+ f"{ status_text } \n "
376+ f"{ signal_text } \n "
343377 f"{ separator } \n "
344378 f"{ translator ('target_weights_title' )} :\n { target_text } "
345379 )
@@ -397,6 +431,7 @@ def run_strategy_core(
397431 no_op_text = f"{ no_op_text } | { _localize_notification_text (f'reason={ no_op_reason } ' , translator = translator )} "
398432 if fail_reason :
399433 no_op_text = f"{ no_op_text } | { _localize_notification_text (f'fail_reason={ fail_reason } ' , translator = translator )} "
434+ no_op_text = "\n " .join (_split_labeled_text (no_op_text ))
400435 record = build_reconciliation_record (
401436 strategy_profile = signal_metadata .get ("strategy_profile" ),
402437 mode = "dry_run" if signal_metadata .get ("dry_run_only" ) else "paper" ,
0 commit comments