"""盘中调度器 使用 APScheduler 管理盘前/盘中/盘后定时任务。 """ import logging import traceback from datetime import datetime from apscheduler.schedulers.asyncio import AsyncIOScheduler from apscheduler.triggers.cron import CronTrigger from app.engine.recommender import refresh_recommendations from app.api.websocket import broadcast_update logger = logging.getLogger(__name__) scheduler = AsyncIOScheduler(timezone="Asia/Shanghai") async def _run_scan(session_name: str): """执行一次扫描并推送结果""" logger.info(f"=== 定时扫描: {session_name} ({datetime.now().strftime('%H:%M:%S')}) ===") try: result = await refresh_recommendations(scan_session=session_name) rec_count = len(result.get("recommendations", [])) logger.info(f"扫描完成: {rec_count} 只推荐股票") # 通过 WebSocket 推送更新 await broadcast_update({ "type": "scan_update", "session": session_name, "count": rec_count, "timestamp": datetime.now().isoformat(), }) except Exception as e: logger.error(f"定时扫描失败 ({session_name}): {e}") from app.db.error_logger import log_error await log_error("scheduler", f"定时扫描失败 ({session_name}): {e}", detail=traceback.format_exc()) async def _generate_daily_review(): """16:10 自动生成每日复盘报告""" logger.info("=== 开始生成每日复盘报告 ===") try: from app.llm.daily_review import generate_review result = await generate_review() if result.get("status") == "ok": logger.info(f"复盘报告生成成功: {result.get('trade_date')}") else: logger.warning(f"复盘报告生成失败: {result.get('message')}") except Exception as e: logger.error(f"复盘报告生成异常: {e}") from app.db.error_logger import log_error await log_error("scheduler", f"复盘报告生成异常: {e}", detail=traceback.format_exc()) def setup_scheduler(): """配置所有定时任务(交易日时间)""" # 盘前准备 09:00 - 计算前一日市场温度和板块数据 scheduler.add_job( _run_scan, CronTrigger(hour=9, minute=0, day_of_week="mon-fri"), args=["pre_market"], id="pre_market", replace_existing=True ) # 早盘扫描 09:35-09:55 每5分钟 + 10:00 for m in range(35, 60, 5): scheduler.add_job( _run_scan, CronTrigger(hour=9, minute=m, day_of_week="mon-fri"), args=["morning_open"], id=f"morning_{m}", replace_existing=True ) scheduler.add_job( _run_scan, CronTrigger(hour=10, minute=0, day_of_week="mon-fri"), args=["morning_open"], id="morning_1000", replace_existing=True ) # 上午盘中 10:10-11:30 - 每10分钟 for h in [10, 11]: start_m = 10 if h == 10 else 0 end_m = 60 if h == 10 else 31 for m in range(start_m, end_m, 10): scheduler.add_job( _run_scan, CronTrigger(hour=h, minute=m, day_of_week="mon-fri"), args=["morning_mid"], id=f"morning_mid_{h}_{m}", replace_existing=True ) # 午后扫描 13:00-14:00 - 每10分钟 for m in range(0, 60, 10): scheduler.add_job( _run_scan, CronTrigger(hour=13, minute=m, day_of_week="mon-fri"), args=["afternoon"], id=f"afternoon_{m}", replace_existing=True ) # 尾盘扫描 14:00-14:50 - 每10分钟 for m in range(0, 51, 10): scheduler.add_job( _run_scan, CronTrigger(hour=14, minute=m, day_of_week="mon-fri"), args=["late_session"], id=f"late_{m}", replace_existing=True ) # 收盘总结 16:00(Tushare 日线数据通常在 15:30 后更新完成) scheduler.add_job( _run_scan, CronTrigger(hour=16, minute=0, day_of_week="mon-fri"), args=["post_market"], id="post_market", replace_existing=True ) # 每日复盘报告 16:10(扫描完成后生成) scheduler.add_job( _generate_daily_review, CronTrigger(hour=16, minute=10, day_of_week="mon-fri"), id="daily_review", replace_existing=True ) logger.info("盘中调度器已配置完成") def start_scheduler(): setup_scheduler() scheduler.start() logger.info("调度器已启动") def stop_scheduler(): if scheduler.running: scheduler.shutdown() logger.info("调度器已停止")