mirror of
https://github.com/ZhuLinsen/daily_stock_analysis
synced 2026-09-20 10:53:33 +08:00
* feat: add JP KR market review support * fix: clarify JP KR service boundaries * fix(review-feedback-1823): add the fields to the response schema and cover the endpoint path, not * fix(review-feedback-1823): Avoid routing JP/KR reviews through MarketLight schema * fix(review-feedback-1823): add JP/KR to that resolver when accepting these values * fix(review-feedback-1823): Keep JP/KR daily contexts from normalizing to CN and add JP/KR labels * fix(review-feedback-1823): 解决冲突后再合入 * fix(review-feedback-1823): 解决冲突并确认最终 diff 后再评审合入 * fix(review-feedback-1823): 澄清或补齐验证证据 * fix(review-feedback-1823): 解决冲突,并在冲突解决后的 head 上重新确认关键验证结果 * fix(review-feedback-1823): Make the Chinese JP/KR prompt shell region-aware * fix(review-feedback-1823): 解决冲突后再判断最终 head 是否可合入 * fix(review-feedback-1823): 基于目标分支解决冲突,并在冲突后的最终 diff 上重新确认相关后端/Web 检查 * fix(review-feedback-1823): 修复 * fix(review-feedback-1823): 解决冲突 * fix(review-feedback-1823): 解决冲突后再合入 * fix(review-feedback-1823): 解决冲突后再合入 * fix(review-feedback-1823): Make the all-markets checkbox exclusive * fix(review-feedback-1823): 解决冲突,并在冲突解决后的最新 head 上重新确认关键测试和 Web 构建结果 * fix(review-feedback-1823): 解决冲突并确保解决后的 head 重新通过对应验证 * fix(review-feedback-1823): 解决冲突,并在冲突解决后重新确认受影响的后端、Web 与文档改动仍一致 * fix(review-feedback-1823): 解决冲突,再基于最新 base 复核完整 diff,并重新确认相关后端/Web 验证结果仍成立 * fix(review-feedback-1823): 解决冲突后重新确认最终 diff 与 CI 结果 * fix(review-feedback-1823): 收敛 * fix(review-feedback-1823): 基于目标分支解决冲突并重新确认关键验证结果 * fix(review-feedback-1823): 解决 * fix(review-feedback-1823): 收敛 * fix(review-feedback-1823): 解决冲突后重新确认 diff 与 CI * fix(review-feedback-1823): 补充本次未改变哪些配置契约、对应测试覆盖和回退方式 * fix(review-feedback-1823): 收敛 * fix(review-feedback-1823): PR 描述与实际 diff scope 仍不完全对齐:完整改动包含 * fix(review-feedback-1823): 解决冲突并在冲突后的最终 diff 上重新确认 CI/关键测试结果 * fix(review-feedback-1823): 解决冲突并确认解决后的 diff 与现有 43 个文件 scope 仍一致 * fix(review-feedback-1823): 解决冲突后再合入 * fix(review-feedback-1823): 解决冲突,并在最终 head 上重新确认受影响的后端与 Web 验证结果 * fix(review-feedback-1823): 解决冲突并确认冲突解决后的最终 head 仍通过对应验证 * fix(review-feedback-1823): 处理旧 both 历史记录在 JP/KR 复盘上下文匹配中的语义兼容风险
204 lines
8.7 KiB
Python
204 lines
8.7 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Tests for market strategy blueprints."""
|
|
|
|
import unittest
|
|
from types import SimpleNamespace
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from src.core.market_strategy import get_market_strategy_blueprint
|
|
from src.market_analyzer import MarketAnalyzer, MarketOverview
|
|
|
|
|
|
class TestMarketStrategyBlueprint(unittest.TestCase):
|
|
"""Validate CN/US strategy blueprint basics."""
|
|
|
|
def test_cn_blueprint_contains_action_framework(self):
|
|
blueprint = get_market_strategy_blueprint("cn")
|
|
block = blueprint.to_prompt_block()
|
|
|
|
self.assertIn("A股市场三段式复盘策略", block)
|
|
self.assertIn("Action Framework", block)
|
|
self.assertIn("进攻", block)
|
|
|
|
def test_us_blueprint_contains_regime_strategy(self):
|
|
blueprint = get_market_strategy_blueprint("us")
|
|
block = blueprint.to_prompt_block()
|
|
|
|
self.assertIn("US Market Regime Strategy", block)
|
|
self.assertIn("Risk-on", block)
|
|
self.assertIn("Macro & Flows", block)
|
|
|
|
|
|
class TestMarketAnalyzerStrategyPrompt(unittest.TestCase):
|
|
"""Validate strategy section is injected into prompt/report."""
|
|
|
|
def test_cn_prompt_contains_strategy_plan_section(self):
|
|
analyzer = MarketAnalyzer(region="cn")
|
|
prompt = analyzer._build_review_prompt(MarketOverview(date="2026-02-24"), [])
|
|
|
|
self.assertIn("明日交易计划", prompt)
|
|
self.assertIn("A股市场三段式复盘策略", prompt)
|
|
|
|
def test_us_prompt_contains_strategy_plan_section(self):
|
|
with patch("src.market_analyzer.get_config", return_value=SimpleNamespace(report_language="en")):
|
|
analyzer = MarketAnalyzer(region="us")
|
|
|
|
prompt = analyzer._build_review_prompt(MarketOverview(date="2026-02-24"), [])
|
|
|
|
self.assertIn("Strategy Plan", prompt)
|
|
self.assertIn("US Market Regime Strategy", prompt)
|
|
|
|
def test_jp_kr_prompt_uses_region_aware_english_shell(self):
|
|
cases = [
|
|
("jp", "Japan market"),
|
|
("kr", "Korea market"),
|
|
]
|
|
|
|
for region, market_scope_name in cases:
|
|
with self.subTest(region=region), patch(
|
|
"src.market_analyzer.get_config",
|
|
return_value=SimpleNamespace(report_language="en"),
|
|
):
|
|
analyzer = MarketAnalyzer(region=region)
|
|
prompt = analyzer._build_review_prompt(MarketOverview(date="2026-02-24"), [])
|
|
|
|
self.assertIn(f"professional {market_scope_name} analyst", prompt)
|
|
self.assertIn("## Data Limits", prompt)
|
|
self.assertIn("### 3. News Catalysts", prompt)
|
|
self.assertNotIn("### 3. Fund Flows", prompt)
|
|
self.assertNotIn("### 4. Sector Highlights", prompt)
|
|
self.assertNotIn("Interpret what turnover, participation, and flow signals imply", prompt)
|
|
self.assertNotIn("professional US/A/H market analyst", prompt)
|
|
|
|
def test_us_prompt_localizes_strategy_markdown_when_report_language_is_zh(self):
|
|
with patch("src.market_analyzer.get_config", return_value=SimpleNamespace(report_language="zh")):
|
|
analyzer = MarketAnalyzer(region="us")
|
|
|
|
prompt = analyzer._build_review_prompt(MarketOverview(date="2026-02-24"), [])
|
|
|
|
self.assertIn("美股市场", prompt)
|
|
self.assertNotIn("US Market Regime Strategy", prompt)
|
|
self.assertNotIn("Strategy Blueprint", prompt)
|
|
self.assertIn("风险偏好", prompt)
|
|
|
|
def test_jp_kr_prompt_uses_region_aware_chinese_shell(self):
|
|
cases = [
|
|
("jp", "日本市场", "日本市场三段式复盘策略"),
|
|
("kr", "韩国市场", "韩国市场三段式复盘策略"),
|
|
]
|
|
|
|
for region, market_scope_name, strategy_title in cases:
|
|
with self.subTest(region=region), patch(
|
|
"src.market_analyzer.get_config",
|
|
return_value=SimpleNamespace(report_language="zh"),
|
|
):
|
|
analyzer = MarketAnalyzer(region=region)
|
|
prompt = analyzer._build_review_prompt(MarketOverview(date="2026-02-24"), [])
|
|
|
|
self.assertIn(f"专业的{market_scope_name}分析师", prompt)
|
|
self.assertIn(f"结构化的{market_scope_name}大盘复盘报告", prompt)
|
|
self.assertIn(f"## 2026-02-24 {market_scope_name}大盘复盘", prompt)
|
|
self.assertIn("## 数据边界", prompt)
|
|
self.assertIn("### 三、消息催化", prompt)
|
|
self.assertIn(strategy_title, prompt)
|
|
self.assertNotIn("### 三、板块主线", prompt)
|
|
self.assertNotIn("### 四、资金与情绪", prompt)
|
|
self.assertNotIn("解读成交额、涨跌停结构、市场宽度", prompt)
|
|
self.assertNotIn("A/H/美股市场分析师", prompt)
|
|
|
|
def test_cn_prompt_uses_english_shell_when_report_language_is_en(self):
|
|
with patch("src.market_analyzer.get_config", return_value=SimpleNamespace(report_language="en")):
|
|
analyzer = MarketAnalyzer(region="cn")
|
|
|
|
prompt = analyzer._build_review_prompt(MarketOverview(date="2026-02-24"), [])
|
|
|
|
self.assertIn("# Today's Market Data", prompt)
|
|
self.assertIn("### 1. Market Summary", prompt)
|
|
self.assertIn("A-share Three-Phase Recap Strategy", prompt)
|
|
self.assertNotIn("### 一、市场总结", prompt)
|
|
self.assertNotIn("A股市场三段式复盘策略", prompt)
|
|
|
|
def test_jp_kr_strategy_blocks_are_localized_when_report_language_is_en(self):
|
|
cases = [
|
|
("jp", "Japan Market Regime Strategy", "Macro & FX", "日本市场三段式复盘策略"),
|
|
("kr", "Korea Market Regime Strategy", "Technology Cycle", "韩国市场三段式复盘策略"),
|
|
]
|
|
|
|
for region, title, dimension, chinese_title in cases:
|
|
with self.subTest(region=region):
|
|
with patch(
|
|
"src.market_analyzer.get_config",
|
|
return_value=SimpleNamespace(report_language="en"),
|
|
):
|
|
analyzer = MarketAnalyzer(region=region)
|
|
|
|
prompt_block = analyzer._get_strategy_prompt_block()
|
|
markdown_block = analyzer._get_strategy_markdown_block("en")
|
|
|
|
self.assertIn(title, prompt_block)
|
|
self.assertIn(dimension, prompt_block)
|
|
self.assertNotIn(chinese_title, prompt_block)
|
|
self.assertNotIn("只基于可得指数", prompt_block)
|
|
self.assertIn("### 6. Strategy Framework", markdown_block)
|
|
self.assertIn(dimension, markdown_block)
|
|
self.assertNotIn("### 六、策略框架", markdown_block)
|
|
|
|
def test_jp_kr_review_prompt_roles_are_market_aware(self):
|
|
cases = [
|
|
("jp", "Japan market", "日本市场"),
|
|
("kr", "Korea market", "韩国市场"),
|
|
]
|
|
|
|
for region, english_market, chinese_market in cases:
|
|
with self.subTest(region=region, language="en"):
|
|
with patch(
|
|
"src.market_analyzer.get_config",
|
|
return_value=SimpleNamespace(report_language="en"),
|
|
):
|
|
analyzer = MarketAnalyzer(region=region)
|
|
|
|
prompt = analyzer._build_review_prompt(MarketOverview(date="2026-02-24"), [])
|
|
|
|
self.assertIn(
|
|
f"You are a professional {english_market} analyst.",
|
|
prompt,
|
|
)
|
|
self.assertNotIn("US/A/H market analyst", prompt)
|
|
|
|
with self.subTest(region=region, language="zh"):
|
|
with patch(
|
|
"src.market_analyzer.get_config",
|
|
return_value=SimpleNamespace(report_language="zh"),
|
|
):
|
|
analyzer = MarketAnalyzer(region=region)
|
|
|
|
prompt = analyzer._build_review_prompt(MarketOverview(date="2026-02-24"), [])
|
|
|
|
self.assertIn(f"你是一位专业的{chinese_market}分析师", prompt)
|
|
self.assertNotIn("A/H/美股市场分析师", prompt)
|
|
|
|
def test_market_stats_passes_market_review_purpose(self):
|
|
analyzer = MarketAnalyzer.__new__(MarketAnalyzer)
|
|
analyzer.region = "hk"
|
|
analyzer.data_manager = MagicMock()
|
|
analyzer.data_manager.get_market_stats.return_value = {
|
|
"up_count": 3,
|
|
"down_count": 2,
|
|
"flat_count": 1,
|
|
"limit_up_count": 0,
|
|
"limit_down_count": 0,
|
|
"total_amount": 12.0,
|
|
}
|
|
overview = MarketOverview(date="2026-02-24")
|
|
|
|
analyzer._get_market_statistics(overview)
|
|
|
|
analyzer.data_manager.get_market_stats.assert_called_once_with(
|
|
purpose="market_review:hk"
|
|
)
|
|
self.assertEqual(overview.up_count, 3)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|