Skip to content

Commit 1fb89e5

Browse files
committed
Refine advisory site public copy
1 parent 2daa46b commit 1fb89e5

2 files changed

Lines changed: 26 additions & 30 deletions

File tree

src/quant_advisor_research/publisher.py

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -154,25 +154,18 @@ def horizon_display_meta(pick: dict[str, Any], horizon: str) -> tuple[str, str,
154154

155155

156156
def render_final_card(pick: dict[str, Any], *, rank: int, horizon: str) -> str:
157-
reasons = "".join(f"<li>{html.escape(str(reason))}</li>" for reason in pick.get("why_selected", []))
158-
horizon_label, horizon_window, score = horizon_display_meta(pick, horizon)
157+
horizon_label, horizon_window, _score = horizon_display_meta(pick, horizon)
159158
return f"""
160159
<article class="final-card">
161160
<header>
162-
<h3><span class="rank">#{rank}</span>{html.escape(str(pick.get('symbol', '')))}</h3>
161+
<h3><span class="rank">推荐 #{rank}</span>{html.escape(str(pick.get('symbol', '')))}</h3>
163162
<p>{html.escape(str(pick.get('name', '')))}</p>
164163
</header>
165164
<dl>
166165
<div><dt>周期</dt><dd>{html.escape(horizon_label)} / {html.escape(horizon_window)}</dd></div>
167-
<div><dt>综合分</dt><dd>{html.escape(display_number(score))}</dd></div>
168-
<div><dt>政策/新闻</dt><dd>{html.escape(display_number(pick.get('source_score')))}</dd></div>
169-
<div><dt>动量</dt><dd>{html.escape(display_number(pick.get('momentum_score')))}</dd></div>
170-
<div><dt>主题/背景</dt><dd>{html.escape(display_number(pick.get('medium_context_score', pick.get('ai_signal_score'))))}</dd></div>
171166
</dl>
172167
<p><strong>股票背景:</strong>{html.escape(str(pick.get('business_summary', '')))}</p>
173168
<p><strong>推荐理由:</strong>{html.escape(str(pick.get('prospect_summary', '')))}</p>
174-
<p><strong>多源依据:</strong></p>
175-
<ul>{reasons}</ul>
176169
<p><strong>主要风险:</strong>{html.escape(str(pick.get('risk_summary', '')))}</p>
177170
</article>
178171
"""
@@ -218,28 +211,28 @@ def format_horizon_conclusion(report: dict[str, Any]) -> str:
218211
def format_momentum_factor_summary(report: dict[str, Any]) -> str:
219212
theme_momentum = report.get("theme_momentum", {})
220213
if not isinstance(theme_momentum, dict) or not theme_momentum.get("available"):
221-
return "动量因子暂无可用主题排序。"
214+
return "本期暂无足够稳定的主题排序。"
222215

223216
themes = [theme for theme in theme_momentum.get("top_themes", []) if isinstance(theme, dict)]
224217
theme_names = [theme_label(theme.get("theme_id"), theme.get("theme_name")) for theme in themes[:4]]
225218
sectors = [sector_label(theme.get("sector")) for theme in themes[:4]]
226219
sector_text = join_zh_items(sectors, limit=3, empty="暂无明确板块")
227220
theme_text = join_zh_items(theme_names, limit=4, empty="暂无明确主题")
228221
if sector_text == "暂无明确板块":
229-
return f"动量因子领先主题包括 {theme_text}。"
230-
return f"动量因子主要集中在{sector_text}板块,领先主题包括 {theme_text}。"
222+
return f"本期较突出的方向包括 {theme_text}。"
223+
return f"本期较突出的方向主要在{sector_text}板块,代表主题包括 {theme_text}。"
231224

232225

233226
def format_report_takeaway(report: dict[str, Any]) -> str:
234227
has_long = bool(displayed_horizon_symbols(report, "long"))
235228
has_medium = bool(displayed_horizon_symbols(report, "medium"))
236229
has_short = bool(displayed_horizon_symbols(report, "short"))
237230
if has_long and has_medium and has_short:
238-
return "整体看,当前信号存在跨周期共振,后续重点观察动量延续和风险事件变化。"
231+
return "整体看,当前结论存在跨周期共振,后续重点观察趋势能否延续和风险事件变化。"
239232
if has_long and has_medium:
240-
return "整体看,当前信号更偏中长线,短线暂不强调,后续重点观察动量延续和基本面兑现。"
233+
return "整体看,当前结论更偏中长线,短线暂不强调,后续重点观察趋势能否延续和基本面兑现。"
241234
if has_medium:
242-
return "整体看,当前信号更偏中线,短线暂不强调,后续重点观察动量延续和基本面兑现。"
235+
return "整体看,当前结论更偏中线,短线暂不强调,后续重点观察趋势能否延续和基本面兑现。"
243236
if has_long:
244237
return "整体看,当前信号更偏长线观察,短线和中线暂未形成稳定排序。"
245238
if has_short:
@@ -740,10 +733,8 @@ def render_index_html(reports: list[dict[str, Any]]) -> str:
740733
.eyebrow {{ margin: 0 0 10px; color: var(--blue); font-size: .72rem; font-weight: 850; letter-spacing: .18em; text-transform: uppercase; }}
741734
h1 {{ margin: 0; max-width: 780px; font-family: var(--font-display); font-size: clamp(2.15rem, 5vw, 4.05rem); font-weight: 700; line-height: 1.06; letter-spacing: -.045em; }}
742735
.hero p {{ margin: 16px 0 0; max-width: 680px; color: var(--muted); font-size: 1rem; line-height: 1.7; }}
743-
.rss-card {{ justify-self: end; min-width: 170px; padding: 14px 16px; border: 1px solid var(--line); border-radius: 18px; background: rgba(255,255,255,.66); box-shadow: 0 18px 45px rgba(31,45,61,.08); }}
744-
.rss-card a {{ display: block; color: var(--ink); text-decoration: none; font-weight: 800; }}
745-
.rss-card a + a {{ margin-top: 8px; }}
746-
.rss-card span {{ display: block; color: var(--muted); margin-top: 5px; font-size: .88rem; }}
736+
.rss-card {{ justify-self: end; display: flex; flex-wrap: wrap; gap: 10px; justify-content: flex-end; align-items: center; }}
737+
.rss-card a {{ min-height: 38px; display: inline-flex; align-items: center; padding: 0 14px; border: 1px solid var(--line); border-radius: 999px; background: rgba(255,255,255,.72); color: var(--ink); text-decoration: none; font-size: .92rem; font-weight: 750; box-shadow: 0 10px 24px rgba(31,45,61,.06); }}
747738
.latest-panel {{ position: relative; overflow: hidden; display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(360px, .95fr); gap: 22px; border: 1px solid rgba(30,77,216,.16); border-radius: 30px; padding: 26px; background: linear-gradient(135deg, rgba(255,255,255,.9), rgba(255,250,240,.76)); box-shadow: 0 26px 70px rgba(31,45,61,.13); }}
748739
.latest-panel::after {{ content: ""; position: absolute; right: -80px; top: -110px; width: 260px; height: 260px; border-radius: 999px; background: rgba(0,166,200,.16); filter: blur(2px); }}
749740
.latest-copy {{ position: relative; z-index: 1; }}
@@ -797,12 +788,11 @@ def render_index_html(reports: list[dict[str, Any]]) -> str:
797788
<h1>智慧投顾研究系统</h1>
798789
</div>
799790
</div>
800-
<p>把主题动量、市场确认和政策/新闻证据整理成普通投资者能读懂的研究结论。页面只展示推荐、周期、背景、理由和风险。</p>
791+
<p>把主题动量、市场确认和政策/新闻证据整理成普通投资者能读懂的研究结论。页面只展示推荐、周期、背景、理由和风险。投资有风险,不构成投资建议。</p>
801792
</div>
802793
<aside class="rss-card">
803794
<a href="archive.html">历史归档</a>
804795
<a href="feed.xml">RSS 订阅</a>
805-
<span>周度更新 · 静态页面</span>
806796
</aside>
807797
</section>
808798
{latest_block}

tests/test_publisher.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@ def test_render_report_html_is_direct_public_recommendation_page() -> None:
5555
assert "长线:暂无稳定结论" in html
5656
assert "中线:MU、NVDA" in html
5757
assert "短线:暂无稳定结论" in html
58-
assert "动量因子主要集中在科技板块" in html
58+
assert "本期较突出的方向主要在科技板块" in html
59+
assert "综合分" not in html
60+
assert "多源依据" not in html
5961
assert "<span class=\"pill\">模式:" not in html
6062
assert "受众:" not in html
6163
assert "AI 状态:" not in html
@@ -109,6 +111,8 @@ def test_publish_reports_writes_site_files(tmp_path: Path) -> None:
109111
assert "#172033" in favicon
110112
assert "Latest advisory" in index_html
111113
assert "历史归档" in index_html
114+
assert "投资有风险,不构成投资建议。" in index_html
115+
assert "周度更新 · 静态页面" not in index_html
112116
assert "近期历史报告" in index_html
113117
assert "archive.html" in index_html
114118
assert "历史归档" in archive_html
@@ -230,9 +234,9 @@ def pick(symbol: str, horizon: str, score: float) -> dict:
230234
assert "horizon-medium" in html
231235
assert "horizon-short" in html
232236
assert html.index("<h2>长线</h2>") < html.index("<h2>中线</h2>") < html.index("<h2>短线</h2>")
233-
assert html.index("#1</span>MID1") < html.index("#2</span>MID2")
234-
assert "#1</span>LONG1" in html
235-
assert "#1</span>SHORT1" in html
237+
assert html.index("推荐 #1</span>MID1") < html.index("推荐 #2</span>MID2")
238+
assert "推荐 #1</span>LONG1" in html
239+
assert "推荐 #1</span>SHORT1" in html
236240
assert "最终推荐" not in html
237241

238242

@@ -293,8 +297,8 @@ def test_short_and_medium_columns_use_primary_horizon_only() -> None:
293297
assert "<h2>短线</h2>" in html
294298
short_start = html.index("<h2>短线</h2>")
295299
short_block = html[short_start:]
296-
assert "#1</span>DELL" in short_block
297-
assert "#1</span>MU" not in short_block
300+
assert "推荐 #1</span>DELL" in short_block
301+
assert "推荐 #1</span>MU" not in short_block
298302

299303
def test_render_report_html_renders_long_context_as_full_card_when_primary_bucket_is_medium() -> None:
300304
report = build_sample_report()
@@ -330,7 +334,7 @@ def test_render_report_html_renders_long_context_as_full_card_when_primary_bucke
330334
html = render_report_html(report)
331335

332336
assert "长线观察" not in html
333-
assert "#1</span>MU" in html
337+
assert "推荐 #1</span>MU" in html
334338
assert "长线 / 1-3年" in html
335339
assert "MU background" in html
336340
assert "MU reason" in html
@@ -395,7 +399,9 @@ def test_render_report_html_includes_theme_momentum_context() -> None:
395399
assert "<span class=\"pill\">模式:" not in html
396400
assert "受众:" not in html
397401
assert "AI 状态:" not in html
398-
assert "主题/背景" in html
402+
assert "主题/背景" not in html
403+
assert "综合分" not in html
404+
assert "多源依据" not in html
399405
assert "AI信号仓库" not in html
400406
assert "ResearchSignalContextPipelines" not in html
401407
assert "股票背景" in html
@@ -406,7 +412,7 @@ def test_render_report_html_includes_theme_momentum_context() -> None:
406412
assert "长线:暂无稳定结论" in html
407413
assert "中线:MU、NVDA" in html
408414
assert "短线:暂无稳定结论" in html
409-
assert "动量因子主要集中在科技板块" in html
415+
assert "本期较突出的方向主要在科技板块" in html
410416
assert "存储周期" in html
411417
assert "需复核估值、财报、回撤和流动性" not in html
412418
assert "做什么" not in html

0 commit comments

Comments
 (0)