astock-agent/backend/app/engine/scheduler.py
2026-04-17 01:03:27 +08:00

126 lines
4.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""盘中调度器
使用 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:00Tushare 日线数据通常在 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("调度器已停止")