Skip to content

Commit 6f31d6d

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

2 files changed

Lines changed: 146 additions & 48 deletions

File tree

src/market_analyzer.py

Lines changed: 97 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,6 +1076,7 @@ def _build_market_temperature(self, overview: MarketOverview) -> tuple[int, str]
10761076
def _build_review_prompt(self, overview: MarketOverview, news: List) -> str:
10771077
"""构建复盘报告 Prompt"""
10781078
review_language = self._get_review_language()
1079+
is_a_share_review = self.region == "cn"
10791080

10801081
# 指数行情信息(简洁格式,不用emoji)
10811082
indices_text = ""
@@ -1088,22 +1089,25 @@ def _build_review_prompt(self, overview: MarketOverview, news: List) -> str:
10881089
bottom_sectors_text = ", ".join([f"{s['name']}({s['change_pct']:+.2f}%)" for s in overview.bottom_sectors[:3]])
10891090
top_concepts_text = ", ".join([f"{s['name']}({s['change_pct']:+.2f}%)" for s in overview.top_concepts[:5]])
10901091
bottom_concepts_text = ", ".join([f"{s['name']}({s['change_pct']:+.2f}%)" for s in overview.bottom_concepts[:3]])
1091-
hot_stocks_text = "\n".join(
1092-
[
1093-
f"- {s.get('rank', '-')}. {s.get('name', '-')}"
1094-
f"({s.get('code', '-')}) {self._format_signed_pct(s.get('change_pct'))}"
1095-
f" 来源:{s.get('source', '-')}"
1096-
for s in overview.hot_stocks[:8]
1097-
]
1098-
)
1099-
limit_up_text = "\n".join(
1100-
[
1101-
f"- {s.get('name', '-')}({s.get('code', '-')}): "
1102-
f"{s.get('consecutive_boards') or 1}连板, {s.get('industry') or '-'}, "
1103-
f"首封 {self._format_limit_time(s.get('first_limit_time'))}"
1104-
for s in overview.limit_up_stocks[:10]
1105-
]
1106-
)
1092+
hot_stocks_text = ""
1093+
limit_up_text = ""
1094+
if is_a_share_review:
1095+
hot_stocks_text = "\n".join(
1096+
[
1097+
f"- {s.get('rank', '-')}. {s.get('name', '-')}"
1098+
f"({s.get('code', '-')}) {self._format_signed_pct(s.get('change_pct'))}"
1099+
f" 来源:{s.get('source', '-')}"
1100+
for s in overview.hot_stocks[:8]
1101+
]
1102+
)
1103+
limit_up_text = "\n".join(
1104+
[
1105+
f"- {s.get('name', '-')}({s.get('code', '-')}): "
1106+
f"{s.get('consecutive_boards') or 1}连板, {s.get('industry') or '-'}, "
1107+
f"首封 {self._format_limit_time(s.get('first_limit_time'))}"
1108+
for s in overview.limit_up_stocks[:10]
1109+
]
1110+
)
11071111

11081112
# 新闻信息 - 支持 SearchResult 对象或字典
11091113
news_text = ""
@@ -1157,14 +1161,15 @@ def _build_review_prompt(self, overview: MarketOverview, news: List) -> str:
11571161
else:
11581162
sector_block = "## 板块表现\n(该市场暂无板块涨跌数据)"
11591163

1160-
if review_language == "en":
1164+
hot_stock_context = ""
1165+
if is_a_share_review and review_language == "en":
11611166
hot_stock_context = f"""## Hot Stocks and Limit-up Ladder
11621167
Hot stocks:
11631168
{hot_stocks_text if hot_stocks_text else "N/A"}
11641169
11651170
Limit-up ladder:
11661171
{limit_up_text if limit_up_text else "N/A"}"""
1167-
else:
1172+
elif is_a_share_review:
11681173
hot_stock_context = f"""## 热门个股与涨停梯队
11691174
人气股:
11701175
{hot_stocks_text if hot_stocks_text else "暂无数据"}
@@ -1191,6 +1196,35 @@ def _build_review_prompt(self, overview: MarketOverview, news: List) -> str:
11911196

11921197
if review_language == "en":
11931198
report_title = self._get_review_title(overview.date).removeprefix("## ").strip()
1199+
leadership_requirement = (
1200+
"- Separate industry rankings from tradable themes: use concept themes, hot stocks, and limit-up ladder to validate the real market leadership."
1201+
if is_a_share_review
1202+
else "- Separate index action, available sector signals, and news catalysts; do not invent unsupported A-share-only short-term sentiment signals for this market."
1203+
)
1204+
tail_template = (
1205+
"""### 5. Hot Stocks & Limit-up Ladder
1206+
(Summarize hot stocks, limit-up clusters, consecutive-board leaders, and what they confirm or contradict about leadership.)
1207+
1208+
### 6. Outlook
1209+
(Provide the near-term outlook based on price action and news; classify catalysts as tailwinds, disturbances, or unconfirmed signals.)
1210+
1211+
### 7. Risk Alerts
1212+
(List 3-5 concrete risks to monitor.)
1213+
1214+
### 8. Strategy Plan
1215+
(Provide an offensive/balanced/defensive stance, a position-sizing guideline, one invalidation trigger, and end with “For reference only, not investment advice.”)
1216+
"""
1217+
if is_a_share_review
1218+
else """### 5. Outlook
1219+
(Provide the near-term outlook based on price action and news; classify catalysts as tailwinds, disturbances, or unconfirmed signals.)
1220+
1221+
### 6. Risk Alerts
1222+
(List 3-5 concrete risks to monitor.)
1223+
1224+
### 7. Strategy Plan
1225+
(Provide an offensive/balanced/defensive stance, a position-sizing guideline, one invalidation trigger, and end with “For reference only, not investment advice.”)
1226+
"""
1227+
)
11941228
return f"""You are a professional US/A/H market analyst. Please produce a concise market recap report based on the data below.
11951229
11961230
[Requirements]
@@ -1199,7 +1233,7 @@ def _build_review_prompt(self, overview: MarketOverview, news: List) -> str:
11991233
- No code blocks
12001234
- Use emoji sparingly in headings (at most one per heading)
12011235
- The entire fixed shell, headings, guidance, and conclusion must be in English
1202-
- Separate industry rankings from tradable themes: use concept themes, hot stocks, and limit-up ladder to validate the real market leadership.
1236+
{leadership_requirement}
12031237
- Do not make the report too thin: target 900-1300 English words; each section should include either 2-4 sentences or 3 concrete bullets.
12041238
- Fund flows, news catalysts, strategy, and risk alerts must contain actionable interpretation, not generic one-liners.
12051239
@@ -1244,24 +1278,55 @@ def _build_review_prompt(self, overview: MarketOverview, news: List) -> str:
12441278
### 4. Sector Highlights
12451279
(Analyze the drivers behind the leading industries and concept themes. State if industry rankings and tradable themes diverge.)
12461280
1247-
### 5. Hot Stocks & Limit-up Ladder
1248-
(Summarize hot stocks, limit-up clusters, consecutive-board leaders, and what they confirm or contradict about leadership.)
1249-
1250-
### 6. Outlook
1251-
(Provide the near-term outlook based on price action and news; classify catalysts as tailwinds, disturbances, or unconfirmed signals.)
1252-
1253-
### 7. Risk Alerts
1254-
(List 3-5 concrete risks to monitor.)
1255-
1256-
### 8. Strategy Plan
1257-
(Provide an offensive/balanced/defensive stance, a position-sizing guideline, one invalidation trigger, and end with “For reference only, not investment advice.”)
1281+
{tail_template}
12581282
12591283
---
12601284
12611285
Output the report content directly, no extra commentary.
12621286
"""
12631287

12641288
# A 股场景使用中文提示语
1289+
leadership_requirement = (
1290+
"- 必须区分“行业涨幅榜”和“真实交易主线”:用热门概念、人气股、涨停连板去校验板块判断,不能把行业涨幅第一直接等同于核心主线"
1291+
if is_a_share_review
1292+
else f"- 必须按{self._get_market_scope_name('zh')}可得数据分析,不要要求或编造该市场未提供的 A 股专属短线情绪数据"
1293+
)
1294+
tail_template = (
1295+
"""### 三、板块主线
1296+
(分析行业涨跌与概念题材背后的逻辑、持续性;说明二者是否一致,真正主线是谁;给出主线扩散/分歧观察点)
1297+
1298+
### 四、热门股票与连板
1299+
(概括人气股、涨停个股、连板高度和涨停原因聚集方向,用来验证或修正板块主线判断;说明高标与中军是否共振)
1300+
1301+
### 五、资金与情绪
1302+
(解读成交额、涨跌停结构、市场宽度和风险偏好;说明是普涨、结构性行情还是分化行情)
1303+
1304+
### 六、消息催化
1305+
(结合近三日新闻,提炼真正影响明日交易的催化或扰动;按“利好/扰动/待验证”分类)
1306+
1307+
### 七、明日交易计划
1308+
(给出进攻/均衡/防守结论、仓位区间、关注方向、回避方向、观察锚点和一个触发失效条件)
1309+
1310+
### 八、风险提示
1311+
(列出 3-5 个需要关注的风险点;最后补充“建议仅供参考,不构成投资建议”。)
1312+
"""
1313+
if is_a_share_review
1314+
else """### 三、板块与主题线索
1315+
(结合可得板块、主题和新闻线索分析市场主线;没有板块数据时不要编造,用指数分化和新闻催化说明方向)
1316+
1317+
### 四、资金与情绪
1318+
(解读成交活跃度、指数承接和风险偏好;说明是普涨、结构性行情还是分化行情,不使用 A 股涨跌停指标)
1319+
1320+
### 五、消息催化
1321+
(结合近三日新闻,提炼真正影响明日交易的催化或扰动;按“利好/扰动/待验证”分类)
1322+
1323+
### 六、明日交易计划
1324+
(给出进攻/均衡/防守结论、仓位区间、关注方向、回避方向、观察锚点和一个触发失效条件)
1325+
1326+
### 七、风险提示
1327+
(列出 3-5 个需要关注的风险点;最后补充“建议仅供参考,不构成投资建议”。)
1328+
"""
1329+
)
12651330
return f"""你是一位专业的A/H/美股市场分析师,请根据以下数据生成一份结构化的{self._get_market_scope_name('zh')}大盘复盘报告。
12661331
12671332
【重要】输出要求:
@@ -1273,7 +1338,7 @@ def _build_review_prompt(self, overview: MarketOverview, news: List) -> str:
12731338
- 不要重复列出已由系统注入的表格数据;正文负责解释表格背后的含义
12741339
- 正文不能过短:整篇建议 1200-1800 个中文字符;每个二级小节至少 2-4 句或 3 条要点
12751340
- 资金与情绪、消息催化、交易计划、风险提示必须给出可执行判断,不能只写一句泛泛提示
1276-
- 必须区分“行业涨幅榜”和“真实交易主线”:用热门概念、人气股、涨停连板去校验板块判断,不能把行业涨幅第一直接等同于核心主线
1341+
{leadership_requirement}
12771342
12781343
---
12791344
@@ -1312,23 +1377,7 @@ def _build_review_prompt(self, overview: MarketOverview, news: List) -> str:
13121377
### 二、指数结构
13131378
{self._get_index_hint()},说明谁在护盘、谁在拖累,以及关键支撑/压力;至少比较两个指数的强弱)
13141379
1315-
### 三、板块主线
1316-
(分析行业涨跌与概念题材背后的逻辑、持续性;说明二者是否一致,真正主线是谁;给出主线扩散/分歧观察点)
1317-
1318-
### 四、热门股票与连板
1319-
(概括人气股、涨停个股、连板高度和涨停原因聚集方向,用来验证或修正板块主线判断;说明高标与中军是否共振)
1320-
1321-
### 五、资金与情绪
1322-
(解读成交额、涨跌停结构、市场宽度和风险偏好;说明是普涨、结构性行情还是分化行情)
1323-
1324-
### 六、消息催化
1325-
(结合近三日新闻,提炼真正影响明日交易的催化或扰动;按“利好/扰动/待验证”分类)
1326-
1327-
### 七、明日交易计划
1328-
(给出进攻/均衡/防守结论、仓位区间、关注方向、回避方向、观察锚点和一个触发失效条件)
1329-
1330-
### 八、风险提示
1331-
(列出 3-5 个需要关注的风险点;最后补充“建议仅供参考,不构成投资建议”。)
1380+
{tail_template}
13321381
13331382
---
13341383

tests/test_market_analyzer_generate_text.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,55 @@ def test_review_prompt_caps_news_url_context(self):
907907
assert "URL: https://example.com/redirect?" in prompt
908908
assert ("x" * 220) not in prompt
909909

910+
def test_us_english_review_prompt_omits_a_share_hot_stock_template(self):
911+
from src.core.market_profile import US_PROFILE
912+
from src.core.market_strategy import get_market_strategy_blueprint
913+
from src.market_analyzer import MarketOverview
914+
915+
ma = self._make_market_analyzer_with_mock_generate_text(return_value="review")
916+
ma.config.report_language = "en"
917+
ma.region = "us"
918+
ma.profile = US_PROFILE
919+
ma.strategy = get_market_strategy_blueprint("us")
920+
921+
prompt = ma._build_review_prompt(
922+
MarketOverview(
923+
date="2026-05-06",
924+
hot_stocks=[{"rank": 1, "code": "AAPL", "name": "Apple"}],
925+
limit_up_stocks=[{"code": "AAPL", "name": "Apple"}],
926+
),
927+
[],
928+
)
929+
930+
assert "## Hot Stocks and Limit-up Ladder" not in prompt
931+
assert "### 5. Hot Stocks & Limit-up Ladder" not in prompt
932+
assert "Limit-up" not in prompt
933+
assert "### 5. Outlook" in prompt
934+
935+
def test_hk_chinese_review_prompt_omits_a_share_ladder_template(self):
936+
from src.core.market_profile import HK_PROFILE
937+
from src.core.market_strategy import get_market_strategy_blueprint
938+
from src.market_analyzer import MarketOverview
939+
940+
ma = self._make_market_analyzer_with_mock_generate_text(return_value="review")
941+
ma.region = "hk"
942+
ma.profile = HK_PROFILE
943+
ma.strategy = get_market_strategy_blueprint("hk")
944+
945+
prompt = ma._build_review_prompt(
946+
MarketOverview(
947+
date="2026-05-06",
948+
hot_stocks=[{"rank": 1, "code": "00700", "name": "腾讯控股"}],
949+
limit_up_stocks=[{"code": "00700", "name": "腾讯控股"}],
950+
),
951+
[],
952+
)
953+
954+
assert "## 热门个股与涨停梯队" not in prompt
955+
assert "### 四、热门股票与连板" not in prompt
956+
assert "涨停" not in prompt
957+
assert "### 四、资金与情绪" in prompt
958+
910959
def test_inject_data_adds_hot_stocks_and_limit_up_ladder(self):
911960
from src.market_analyzer import MarketOverview
912961

0 commit comments

Comments
 (0)