@@ -173,6 +173,85 @@ def render_final_card(pick: dict[str, Any], *, rank: int, horizon: str) -> str:
173173 """
174174
175175
176+ def join_zh_items (items : list [str ], * , limit : int = 5 , empty : str = "暂无" ) -> str :
177+ cleaned = []
178+ seen = set ()
179+ for item in items :
180+ text = str (item or "" ).strip ()
181+ if not text or text in seen :
182+ continue
183+ cleaned .append (text )
184+ seen .add (text )
185+ if not cleaned :
186+ return empty
187+ visible = cleaned [:limit ]
188+ suffix = "等" if len (cleaned ) > limit else ""
189+ return "、" .join (visible ) + suffix
190+
191+
192+ def displayed_horizon_symbols (report : dict [str , Any ], horizon : str ) -> list [str ]:
193+ decisions = report .get ("final_decisions" , {})
194+ if not isinstance (decisions , dict ):
195+ return []
196+ final_picks = [pick for pick in decisions .get ("recommendations" , []) if isinstance (pick , dict )]
197+ return [
198+ str (pick .get ("symbol" , "" )).strip ()
199+ for pick in ranked_horizon_picks (final_picks , horizon )
200+ if str (pick .get ("symbol" , "" )).strip ()
201+ ]
202+
203+
204+ def format_horizon_conclusion (report : dict [str , Any ]) -> str :
205+ parts = []
206+ for horizon , label , _window in HORIZON_COLUMNS :
207+ symbols = displayed_horizon_symbols (report , horizon )
208+ value = join_zh_items (symbols , empty = "暂无稳定结论" )
209+ parts .append (f"{ label } :{ value } " )
210+ return ";" .join (parts )
211+
212+
213+ def format_momentum_factor_summary (report : dict [str , Any ]) -> str :
214+ theme_momentum = report .get ("theme_momentum" , {})
215+ if not isinstance (theme_momentum , dict ) or not theme_momentum .get ("available" ):
216+ return "动量因子暂无可用主题排序。"
217+
218+ themes = [theme for theme in theme_momentum .get ("top_themes" , []) if isinstance (theme , dict )]
219+ theme_names = [theme_label (theme .get ("theme_id" ), theme .get ("theme_name" )) for theme in themes [:4 ]]
220+ sectors = [sector_label (theme .get ("sector" )) for theme in themes [:4 ]]
221+ sector_text = join_zh_items (sectors , limit = 3 , empty = "暂无明确板块" )
222+ theme_text = join_zh_items (theme_names , limit = 4 , empty = "暂无明确主题" )
223+ if sector_text == "暂无明确板块" :
224+ return f"动量因子领先主题包括 { theme_text } 。"
225+ return f"动量因子主要集中在{ sector_text } 板块,领先主题包括 { theme_text } 。"
226+
227+
228+ def format_report_takeaway (report : dict [str , Any ]) -> str :
229+ has_long = bool (displayed_horizon_symbols (report , "long" ))
230+ has_medium = bool (displayed_horizon_symbols (report , "medium" ))
231+ has_short = bool (displayed_horizon_symbols (report , "short" ))
232+ if has_long and has_medium and has_short :
233+ return "整体看,当前信号存在跨周期共振,后续重点观察动量延续和风险事件变化。"
234+ if has_long and has_medium :
235+ return "整体看,当前信号更偏中长线,短线暂不强调,后续重点观察动量延续和基本面兑现。"
236+ if has_medium :
237+ return "整体看,当前信号更偏中线,短线暂不强调,后续重点观察动量延续和基本面兑现。"
238+ if has_long :
239+ return "整体看,当前信号更偏长线观察,短线和中线暂未形成稳定排序。"
240+ if has_short :
241+ return "整体看,当前只出现短线机会,持续性仍需要后续数据确认。"
242+ return "整体看,当前暂未形成稳定系统结论,继续等待更清晰的主题和价格确认。"
243+
244+
245+ def render_report_lead (report : dict [str , Any ]) -> str :
246+ return " " .join (
247+ [
248+ f"本期结论:{ format_horizon_conclusion (report )} 。" ,
249+ format_momentum_factor_summary (report ),
250+ format_report_takeaway (report ),
251+ ]
252+ )
253+
254+
176255def render_final_decisions_html (report : dict [str , Any ]) -> str :
177256 decisions = report .get ("final_decisions" , {})
178257 if not decisions :
@@ -491,7 +570,7 @@ def render_report_html(report: dict[str, Any]) -> str:
491570 { render_site_mark ()}
492571 <p class="eyebrow">Advisory briefing</p>
493572 <h1>{ html .escape (display_title )} </h1>
494- <p class="report-lead">按长线、中线、短线分栏展示系统结论;每张卡片保留股票背景、推荐理由、多源依据和主要风险。 </p>
573+ <p class="report-lead">{ html . escape ( render_report_lead ( report )) } </p>
495574 </div>
496575 <aside class="date-card" aria-label="报告日期">
497576 <p class="date-label">Report date</p>
0 commit comments