Files
daily_stock_analysis/bot/commands/market.py
Alfred 88fdaf7933 feat: 大盘复盘可选除 A 股外的其它大盘(Issue #299) (#348)
* feat: 大盘复盘可选 A 股/美股区域 (Issue #299)

- 新增 MARKET_REVIEW_REGION 配置:cn/us/both
- 新增 market_profile 抽象,按 region 切换新闻搜索、Prompt、模板
- 数据层 get_main_indices(region):yfinance 支持 us 返回 SPX/IXIC/DJI/VIX
- MarketAnalyzer 支持 region 参数,美股时跳过涨跌统计与板块
- region=both 时顺序执行 A 股+美股复盘并合并报告
- 更新 .env.example、full-guide、README、CHANGELOG

* Issue #299: 审查建议修复与优化

- bot: 透传 market_review_region 到 MarketAnalyzer
- yfinance: 提取 _fetch_yf_ticker_data 公共方法,消除 cn/us 重复逻辑
- market_analyzer: 修正 mood_index 匹配逻辑,美股搜索 stock_name 为 US market
- market_analyzer: region=us 时 LLM 提示语改为英文模板
- market_review: both 模式增加 A股/美股生成进度日志
- tests: 新增 _get_us_main_indices 单元测试(mock yfinance)

* Issue #299: 落实审查建议

- config: _parse_market_review_region 解析到非法值时打印 warning 日志
- market_review: both 模式报告分隔符增加描述性说明
- market_analyzer: 指数表格中 amount=0.0 时显示 N/A,避免用户误解

* docs: 优化 MARKET_REVIEW_REGION 描述措辞,避免歧义

- 移除「仅炒美股用户可设为 us」表述
- 明确 both 模式可同时复盘 A 股与美股
- 更新 full-guide、README、CHANGELOG、config 注释、env.example

* style: 移除 bot market 命令中冗余的 or 'cn' 回退
2026-02-19 11:45:47 +08:00

123 lines
3.7 KiB
Python

# -*- coding: utf-8 -*-
"""
===================================
大盘复盘命令
===================================
执行大盘复盘分析,生成市场概览报告。
"""
import logging
import threading
from typing import List
from bot.commands.base import BotCommand
from bot.models import BotMessage, BotResponse
logger = logging.getLogger(__name__)
class MarketCommand(BotCommand):
"""
大盘复盘命令
执行大盘复盘分析,包括:
- 主要指数表现
- 板块热点
- 市场情绪
- 后市展望
用法:
/market - 执行大盘复盘
"""
@property
def name(self) -> str:
return "market"
@property
def aliases(self) -> List[str]:
return ["m", "大盘", "复盘", "行情"]
@property
def description(self) -> str:
return "大盘复盘分析"
@property
def usage(self) -> str:
return "/market"
def execute(self, message: BotMessage, args: List[str]) -> BotResponse:
"""执行大盘复盘命令"""
logger.info(f"[MarketCommand] 开始大盘复盘分析")
# 在后台线程中执行复盘(避免阻塞)
thread = threading.Thread(
target=self._run_market_review,
args=(message,),
daemon=True
)
thread.start()
return BotResponse.markdown_response(
"✅ **大盘复盘任务已启动**\n\n"
"正在分析:\n"
"• 主要指数表现\n"
"• 板块热点分析\n"
"• 市场情绪判断\n"
"• 后市展望\n\n"
"分析完成后将自动推送结果。"
)
def _run_market_review(self, message: BotMessage) -> None:
"""后台执行大盘复盘"""
try:
from src.config import get_config
from src.notification import NotificationService
from src.market_analyzer import MarketAnalyzer
from src.search_service import SearchService
from src.analyzer import GeminiAnalyzer
config = get_config()
notifier = NotificationService(source_message=message)
# 初始化搜索服务
search_service = None
if config.bocha_api_keys or config.tavily_api_keys or config.brave_api_keys or config.serpapi_keys:
search_service = SearchService(
bocha_keys=config.bocha_api_keys,
tavily_keys=config.tavily_api_keys,
brave_keys=config.brave_api_keys,
serpapi_keys=config.serpapi_keys,
news_max_age_days=config.news_max_age_days,
)
# 初始化 AI 分析器
analyzer = None
if config.gemini_api_key or config.openai_api_key:
analyzer = GeminiAnalyzer()
# 读取配置中的市场区域,与定时任务/CLI 保持一致
region = getattr(config, 'market_review_region', 'cn')
# 执行复盘
market_analyzer = MarketAnalyzer(
search_service=search_service,
analyzer=analyzer,
region=region,
)
review_report = market_analyzer.run_daily_review()
if review_report:
# 推送结果
report_content = f"🎯 **大盘复盘**\n\n{review_report}"
notifier.send(report_content, email_send_to_all=True)
logger.info("[MarketCommand] 大盘复盘完成并已推送")
else:
logger.warning("[MarketCommand] 大盘复盘返回空结果")
except Exception as e:
logger.error(f"[MarketCommand] 大盘复盘失败: {e}")
logger.exception(e)