101 lines
3.3 KiB
Python
101 lines
3.3 KiB
Python
"""盘中调度器
|
||
|
||
使用 APScheduler 管理盘前/盘中/盘后定时任务。
|
||
"""
|
||
|
||
import logging
|
||
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}")
|
||
|
||
|
||
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
|
||
)
|
||
|
||
logger.info("盘中调度器已配置完成")
|
||
|
||
|
||
def start_scheduler():
|
||
setup_scheduler()
|
||
scheduler.start()
|
||
logger.info("调度器已启动")
|
||
|
||
|
||
def stop_scheduler():
|
||
if scheduler.running:
|
||
scheduler.shutdown()
|
||
logger.info("调度器已停止")
|