Skip to content

Commit bb922a9

Browse files
committed
fix(review-feedback-1280): address latest review comments
1 parent 6f31d6d commit bb922a9

2 files changed

Lines changed: 151 additions & 35 deletions

File tree

src/market_analyzer.py

Lines changed: 108 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1387,6 +1387,7 @@ def _build_review_prompt(self, overview: MarketOverview, news: List) -> str:
13871387
def _generate_template_review(self, overview: MarketOverview, news: List) -> str:
13881388
"""使用模板生成复盘报告(无大模型时的备选方案)"""
13891389
template_language = self._get_template_review_language()
1390+
is_a_share_review = self.region == "cn"
13901391
mood_code = self.profile.mood_index_code
13911392
# 根据 mood_index_code 查找对应指数
13921393
# cn: mood_code="000001",idx.code 可能为 "sh000001"(以 mood_code 结尾)
@@ -1422,7 +1423,7 @@ def _generate_template_review(self, overview: MarketOverview, news: List) -> str
14221423
top_text = separator.join([s['name'] for s in overview.top_sectors[:3]])
14231424
bottom_text = separator.join([s['name'] for s in overview.bottom_sectors[:3]])
14241425
top_concept_text = separator.join([s['name'] for s in overview.top_concepts[:3]])
1425-
hot_stock_block = self._build_hot_stock_block(overview)
1426+
hot_stock_block = self._build_hot_stock_block(overview) if is_a_share_review else ""
14261427

14271428
if template_language == "en":
14281429
stats_section = ""
@@ -1495,19 +1496,22 @@ def _generate_template_review(self, overview: MarketOverview, news: List) -> str
14951496
if index_laggard
14961497
else "暂无拖累指数"
14971498
)
1498-
hot_names = separator.join(
1499-
[
1500-
s.get("name", "-")
1501-
for s in overview.hot_stocks[:5]
1502-
if s.get("name")
1503-
]
1504-
)
1505-
limit_chain = self._build_limit_chain_summary(overview.limit_up_stocks)
1506-
limit_chain_text = (
1507-
limit_chain.replace("> **连板结构**:", "")
1508-
if limit_chain
1509-
else "暂无连板梯队数据"
1510-
)
1499+
hot_names = ""
1500+
limit_chain_text = ""
1501+
if is_a_share_review:
1502+
hot_names = separator.join(
1503+
[
1504+
s.get("name", "-")
1505+
for s in overview.hot_stocks[:5]
1506+
if s.get("name")
1507+
]
1508+
)
1509+
limit_chain = self._build_limit_chain_summary(overview.limit_up_stocks)
1510+
limit_chain_text = (
1511+
limit_chain.replace("> **连板结构**:", "")
1512+
if limit_chain
1513+
else "暂无连板梯队数据"
1514+
)
15111515
news_block = self._build_news_block(news)
15121516
if news:
15131517
news_titles = separator.join(
@@ -1520,12 +1524,90 @@ def _generate_template_review(self, overview: MarketOverview, news: List) -> str
15201524
else:
15211525
news_titles = "暂无可用新闻,需降低题材持续性的确定性判断"
15221526
summary_mood = scorecard["temperature_label"] if scorecard else market_mood
1527+
1528+
if is_a_share_review:
1529+
overview_commentary = (
1530+
f"今日盘面核心不是单一指数涨跌,而是宽度、量能和涨跌停结构是否形成共振。"
1531+
f"上涨占比为 {up_ratio_text},涨跌停差为 {limit_spread:+d},"
1532+
f"成交状态为“{turnover_desc}”,说明短线风险偏好需要结合主线承接来判断。"
1533+
"若次日量能保持且高标不明显退潮,行情更容易沿强势方向延续;"
1534+
"若量能回落而跌停扩散,则需从进攻转为观察。"
1535+
)
1536+
sector_heading = "### 三、板块主线"
1537+
sector_commentary = (
1538+
f"行业涨跌榜只能说明资金流向的表层,真正主线还要看概念题材、人气股和涨停梯队是否相互验证。"
1539+
f"当前行业强项集中在 {top_text or '暂无行业领涨数据'},"
1540+
f"概念侧关注 {top_concept_text or '暂无概念领涨数据'};若两者方向一致,主线持续性更强,"
1541+
"若明显背离,则应优先相信人气股和涨停池反馈。"
1542+
f"弱势方向集中在 {bottom_text or '暂无明显领跌方向'},短线不宜把被动反弹当成主线切换。"
1543+
)
1544+
sector_fallback = "- 暂无板块涨跌榜数据。"
1545+
hot_stock_section = f"""### 四、热门股票与连板
1546+
人气股可以观察资金审美,涨停连板则反映短线情绪高度。当前人气前排包括 {hot_names or "暂无人气榜数据"},连板结构为 {limit_chain_text}。如果人气中军、题材弹性股和连板高标指向同一方向,说明主线可信度更高;如果高标独强但中军不跟,追高风险会明显增加。
1547+
1548+
{hot_stock_block or "- 暂无人气股与涨停池数据。"}
1549+
1550+
"""
1551+
sentiment_heading = "### 五、资金与情绪"
1552+
sentiment_observation = (
1553+
f"- **情绪观察**:涨跌停差 {limit_spread:+d},"
1554+
"若涨停数量保持但跌停同步增加,意味着高位分歧正在放大。"
1555+
)
1556+
news_heading = "### 六、消息催化"
1557+
plan_heading = "### 七、明日交易计划"
1558+
plan_anchor = "- **观察锚点**:领涨指数是否继续强于大盘、人气股是否维持前排、连板高度是否继续打开。"
1559+
plan_invalidation = "- **失效条件**:若成交额明显萎缩、跌停数量扩散,或人气前排集体冲高回落,应从进攻转为防守。"
1560+
risk_heading = "### 八、风险提示"
1561+
risk_lines = """- **量能透支风险**:放量大涨后若无法继续承接,容易出现冲高回落。
1562+
- **主线误判风险**:行业涨幅第一不等于真实交易主线,需要持续用概念、人气股和涨停池校验。
1563+
- **高位股分歧风险**:连板高度提升时,若中位股掉队,短线亏钱效应可能快速扩散。
1564+
- **消息扰动风险**:外部市场、政策和产业消息变化可能影响风险偏好。
1565+
- 建议仅供参考,不构成投资建议。"""
1566+
else:
1567+
stats_context = (
1568+
f"上涨占比为 {up_ratio_text}"
1569+
if self.profile.has_market_stats
1570+
else "当前未提供涨跌家数统计"
1571+
)
1572+
overview_commentary = (
1573+
"今日盘面核心不是单一指数涨跌,而是主要指数承接、成交活跃度和消息催化是否形成共振。"
1574+
f"{stats_context},成交状态为“{turnover_desc}”,"
1575+
"说明风险偏好需要结合指数结构和新闻线索判断。"
1576+
"若次日量能保持且领涨指数不明显回吐,行情更容易沿强势方向延续;"
1577+
"若量能回落且关键指数跌破短期支撑,则需从进攻转为观察。"
1578+
)
1579+
sector_heading = "### 三、板块与主题线索"
1580+
sector_focus = top_text or top_concept_text or "暂无板块/主题数据"
1581+
sector_weak = bottom_text or "暂无明显走弱方向"
1582+
sector_commentary = (
1583+
"可得板块和主题线索需要结合指数分化与新闻催化判断。"
1584+
f"当前强项集中在 {sector_focus};若板块信号和新闻方向一致,主线持续性更强,"
1585+
"若数据不足,则优先使用指数结构和催化兑现情况校验方向。"
1586+
f"弱势方向集中在 {sector_weak},不宜把被动反弹当成主线切换。"
1587+
)
1588+
sector_fallback = "- 暂无可用板块/主题数据。"
1589+
hot_stock_section = ""
1590+
sentiment_heading = "### 四、资金与情绪"
1591+
sentiment_observation = (
1592+
"- **情绪观察**:若领涨指数扩散、回调时承接稳定,风险偏好更健康;"
1593+
"若只有单一指数拉动,仍需防范分化。"
1594+
)
1595+
news_heading = "### 五、消息催化"
1596+
plan_heading = "### 六、明日交易计划"
1597+
plan_anchor = "- **观察锚点**:领涨指数是否继续强于大盘、成交是否维持、新闻催化是否兑现。"
1598+
plan_invalidation = "- **失效条件**:若成交额明显萎缩、关键指数跌破短期支撑,或主要催化被证伪,应从进攻转为防守。"
1599+
risk_heading = "### 七、风险提示"
1600+
risk_lines = """- **量能回落风险**:放量反弹后若无法继续承接,容易出现冲高回落。
1601+
- **主线误判风险**:指数或板块强弱不等于可交易主线,需要结合新闻催化、成交变化和承接情况持续验证。
1602+
- **消息扰动风险**:外部市场、政策和产业消息变化可能影响风险偏好。
1603+
- 建议仅供参考,不构成投资建议。"""
1604+
15231605
return f"""## {overview.date} 大盘复盘
15241606
15251607
> 今日{market_label}市场整体呈现**{market_mood}**态势,盘面状态偏向**{summary_mood}**,优先观察指数承接、成交额变化和板块持续性。
15261608
15271609
### 一、盘面总览
1528-
今日盘面核心不是单一指数涨跌,而是宽度、量能和涨跌停结构是否形成共振。上涨占比为 {up_ratio_text},涨跌停差为 {limit_spread:+d},成交状态为“{turnover_desc}”,说明短线风险偏好需要结合主线承接来判断。若次日量能保持且高标不明显退潮,行情更容易沿强势方向延续;若量能回落而跌停扩散,则需从进攻转为观察。
1610+
{overview_commentary}
15291611
15301612
{dashboard_block or "暂无市场宽度数据。"}
15311613
@@ -1534,42 +1616,33 @@ def _generate_template_review(self, overview: MarketOverview, news: List) -> str
15341616
15351617
{indices_block or indices_text or "暂无指数数据。"}
15361618
1537-
### 三、板块主线
1538-
行业涨跌榜只能说明资金流向的表层,真正主线还要看概念题材、人气股和涨停梯队是否相互验证。当前行业强项集中在 {top_text or "暂无行业领涨数据"},概念侧关注 {top_concept_text or "暂无概念领涨数据"};若两者方向一致,主线持续性更强,若明显背离,则应优先相信人气股和涨停池反馈。弱势方向集中在 {bottom_text or "暂无明显领跌方向"},短线不宜把被动反弹当成主线切换。
1619+
{sector_heading}
1620+
{sector_commentary}
15391621
1540-
{sector_block or "- 暂无板块涨跌榜数据。"}
1622+
{sector_block or sector_fallback}
15411623
1542-
### 四、热门股票与连板
1543-
人气股可以观察资金审美,涨停连板则反映短线情绪高度。当前人气前排包括 {hot_names or "暂无人气榜数据"},连板结构为 {limit_chain_text}。如果人气中军、题材弹性股和连板高标指向同一方向,说明主线可信度更高;如果高标独强但中军不跟,追高风险会明显增加。
1544-
1545-
{hot_stock_block or "- 暂无人气股与涨停池数据。"}
1546-
1547-
### 五、资金与情绪
1624+
{hot_stock_section}{sentiment_heading}
15481625
- **量能观察**:成交额处于“{turnover_desc}”状态,若继续放量且指数不冲高回落,资金承接仍可看作积极。
15491626
- **宽度观察**:上涨占比 {up_ratio_text},说明赚钱效应的扩散程度需要和主线强度一起验证,不能只看指数涨跌。
1550-
- **情绪观察**:涨跌停差 {limit_spread:+d},若涨停数量保持但跌停同步增加,意味着高位分歧正在放大。
1627+
{sentiment_observation}
15511628
1552-
### 六、消息催化
1629+
{news_heading}
15531630
{news_block or "- 暂无可用新闻时,应降低对题材持续性的确定性判断。"}
15541631
15551632
- **利好线索**:重点观察与 {top_concept_text or top_text or "强势主线"} 相关的政策、产业订单、业绩和海外映射是否继续发酵。
15561633
- **扰动线索**:若外围市场、汇率、商品价格或监管消息出现反向变化,可能削弱风险偏好。
15571634
- **待验证线索**:{news_titles}
15581635
1559-
### 七、明日交易计划
1636+
{plan_heading}
15601637
- **结论**:{scorecard['label'] if scorecard else '均衡观察'},优先等指数、成交额和主线方向形成共振。
15611638
- **仓位**:控制在中性到积极区间,强势日不盲目满仓,分歧日保留机动仓位。
15621639
- **关注方向**:{top_concept_text or top_text or "强于指数的主线板块"}
15631640
- **回避方向**:{bottom_text or "连续走弱且缺少修复信号的方向"}
1564-
- **观察锚点**:领涨指数是否继续强于大盘、人气股是否维持前排、连板高度是否继续打开。
1565-
- **失效条件**:若成交额明显萎缩、跌停数量扩散,或人气前排集体冲高回落,应从进攻转为防守。
1641+
{plan_anchor}
1642+
{plan_invalidation}
15661643
1567-
### 八、风险提示
1568-
- **量能透支风险**:放量大涨后若无法继续承接,容易出现冲高回落。
1569-
- **主线误判风险**:行业涨幅第一不等于真实交易主线,需要持续用概念、人气股和涨停池校验。
1570-
- **高位股分歧风险**:连板高度提升时,若中位股掉队,短线亏钱效应可能快速扩散。
1571-
- **消息扰动风险**:外部市场、政策和产业消息变化可能影响风险偏好。
1572-
- 建议仅供参考,不构成投资建议。
1644+
{risk_heading}
1645+
{risk_lines}
15731646
15741647
---
15751648
*复盘时间: {datetime.now().strftime('%H:%M')}*

tests/test_market_analyzer_generate_text.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,49 @@ def test_generate_template_review_keeps_chinese_shell_for_us_when_report_languag
731731
assert "今日美股市场整体呈现**小幅下跌**态势" in result
732732
assert "### 1. Market Summary" not in result
733733
assert "US Market Recap" not in result
734+
assert "### 四、热门股票与连板" not in result
735+
assert "人气股" not in result
736+
assert "涨停" not in result
737+
assert "连板" not in result
738+
assert "涨停池" not in result
739+
assert "高位股分歧风险" not in result
740+
assert "### 四、资金与情绪" in result
741+
742+
def test_generate_template_review_omits_a_share_hot_stock_sections_for_hk(self):
743+
from src.core.market_profile import HK_PROFILE
744+
from src.core.market_strategy import get_market_strategy_blueprint
745+
from src.market_analyzer import MarketOverview, MarketIndex
746+
747+
ma = self._make_market_analyzer_with_mock_generate_text(return_value=None)
748+
ma.region = "hk"
749+
ma.profile = HK_PROFILE
750+
ma.strategy = get_market_strategy_blueprint("hk")
751+
overview = MarketOverview(
752+
date="2026-03-05",
753+
indices=[
754+
MarketIndex(
755+
code="HSI",
756+
name="恒生指数",
757+
current=18600.0,
758+
change=120.0,
759+
change_pct=0.65,
760+
)
761+
],
762+
hot_stocks=[{"rank": 1, "code": "00700", "name": "腾讯控股"}],
763+
limit_up_stocks=[{"code": "00700", "name": "腾讯控股"}],
764+
)
765+
766+
result = ma.generate_market_review(overview, [])
767+
768+
assert "今日港股市场整体呈现**小幅上涨**态势" in result
769+
assert "### 四、热门股票与连板" not in result
770+
assert "人气股" not in result
771+
assert "涨停" not in result
772+
assert "连板" not in result
773+
assert "涨停池" not in result
774+
assert "高位股分歧风险" not in result
775+
assert "### 四、资金与情绪" in result
776+
assert "### 五、消息催化" in result
734777

735778
def test_inject_data_into_review_matches_english_headings(self):
736779
from src.market_analyzer import MarketOverview, MarketIndex

0 commit comments

Comments
 (0)