6931a7b541
架构(简化,避per-account闭包注入): - live_orchestrator live_step(account_id)自包含: 恢复cash/positions/pending→fetch_day raw当日→engine.step→存状态 - run_live_step(db)遍历live accounts调live_step; scheduler register_live_step_job全局20:30 job - app startup注册全局job; routes create mode=live存account running(不跑回放) - TODO(分期项): prev_close昨日raw/listing_days IPO算/realized_pnl恢复 - 113/113通过, live_orchestrator import OK
83 lines
2.6 KiB
Python
83 lines
2.6 KiB
Python
"""模拟盘 C 实走调度(APScheduler 定时 + 启动恢复,spec §9.2)。
|
||
|
||
每日 20:30 触发 step(拉当日 bar + engine.step)。APScheduler 是 pip 依赖,
|
||
lazy import;本机无则单测 mock get_scheduler。
|
||
"""
|
||
import logging
|
||
import sqlite3
|
||
|
||
logger = logging.getLogger(__name__)
|
||
_scheduler = None
|
||
|
||
|
||
def get_scheduler():
|
||
global _scheduler
|
||
if _scheduler is None:
|
||
from apscheduler.schedulers.background import BackgroundScheduler
|
||
_scheduler = BackgroundScheduler()
|
||
return _scheduler
|
||
|
||
|
||
def start_scheduler():
|
||
s = get_scheduler()
|
||
if not s.running:
|
||
s.start()
|
||
return s
|
||
|
||
|
||
def register_paper_job(account_id: int, hour: int = 20, minute: int = 30,
|
||
job_func=None) -> str:
|
||
"""注册每日定时 job,返回 job_id(存 paper_accounts.scheduler_job_id)。"""
|
||
s = start_scheduler()
|
||
job_id = f"paper_{account_id}"
|
||
s.add_job(
|
||
job_func or _default_step,
|
||
"cron", # 字符串 trigger,避免函数内 import CronTrigger
|
||
hour=hour, minute=minute,
|
||
args=[account_id], id=job_id, replace_existing=True,
|
||
)
|
||
return job_id
|
||
|
||
|
||
def remove_paper_job(account_id: int) -> None:
|
||
s = get_scheduler()
|
||
job_id = f"paper_{account_id}"
|
||
try:
|
||
s.remove_job(job_id)
|
||
except Exception:
|
||
logger.warning("remove_job %s not found", job_id)
|
||
|
||
|
||
def _default_step(account_id: int) -> None:
|
||
"""部署时注入真 step(akshare 拉当日 + engine.step);默认 stub。"""
|
||
logger.info("paper account %s daily step (stub)", account_id)
|
||
|
||
|
||
def restore_live_jobs(db_path: str, job_func=None) -> int:
|
||
"""容器启动:遍历 status=running & mode=live 重建 job(spec §9.2 H-3)。
|
||
|
||
简化架构:实走用全局 job(register_live_step_job),此函数保留兼容老 per-account 设计。
|
||
"""
|
||
with sqlite3.connect(db_path) as conn:
|
||
rows = conn.execute(
|
||
"SELECT id FROM paper_accounts WHERE status='running' AND mode='live'"
|
||
).fetchall()
|
||
for (aid,) in rows:
|
||
register_paper_job(aid, job_func=job_func)
|
||
return len(rows)
|
||
|
||
|
||
def register_live_step_job(db_path: str, hour: int = 20, minute: int = 30) -> str:
|
||
"""全局每日 step job:20:30 遍历所有 live accounts 调 run_live_step(C-S3 简化架构)。
|
||
|
||
job_func 用 lambda 闭包 db_path(add_job 只传无参 callable)。
|
||
"""
|
||
from .live_orchestrator import run_live_step # lazy 避循环 import
|
||
s = start_scheduler()
|
||
s.add_job(
|
||
lambda: run_live_step(db_path),
|
||
"cron", hour=hour, minute=minute,
|
||
id="live_step_global", replace_existing=True,
|
||
)
|
||
return "live_step_global"
|