Files
tick-stock-panel/backend/app/services/repair_daily.py
T
shy3130 e15acddb2f fix(pipeline): 停机缺口检测与自动修复, 防止盘中快照数据永久损坏
场景: 用户盘中停机后次日启动并开实时行情, 实时 flush 写出"今天"分区后,
盘后管道的「今天已有数据→只刷今天」分支会让停机日的半日快照永久留存
(close=停机时刻价, volume=半日累计, 技术指标全错且污染后续 lookback)。

判据 (quote_ts 列, 仅实时 flush 写入真实毫秒时间戳):
- null → batch 拉取/盘后计算的权威历史 → 完整
- 历史交易日时刻 < 15:00 → 盘中快照 → 坏
- ≥ 15:00 → 尾盘定版 → 完整; 今天不校验
读取走 parquet 元数据 statistics, 实测 ~0.5ms/分区。

三个钩子层层设防:
- 启动自检 (main.py): boot 后 30s 后台扫描, 窗口内坏数据自动创建修复任务
  (复用 repair_daily 管道 + JobStore, ActiveJobCard 可见进度)
- 开实时门禁 (settings.py): 坏数据在 5 天窗口内 → 自动建修复任务并 409,
  前端经现有 toast 机制展示; 窗口外放行不拦
- 盘后管道自愈 (daily_pipeline.py): 分支3前完整性扫描, 坏日 → 降级分支4
  从 min(最新日, 坏日) 范围重拉; ETF/指数起点同步提前到各自族最早坏日

修复原语补强: 股票 enriched 的坏分区由 prune_enriched_partitions 先删再
当"新日期"重算 (增量重算只算不存在的日期, 分区已存在虽错也永不重算)。
分钟K缺口经修复管道 Step 2.5 增量同步天然补洞 (start=max(datetime) 幂等)。

测试: 新增 24 个 (判据/门禁/boot/端到端离线集成复刻用户场景),
全量 1002 passed。
2026-08-23 19:12:57 +08:00

57 lines
2.2 KiB
Python

"""修正 / 补全日K数据 — 完全复用盘后管道,只是日期范围由用户传入。
典型场景: 昨天没看盘 / 服务挂了一天,本地日K缺了若干天。
设计原则: 不重复盘后管道的任何逻辑。直接调用 daily_pipeline.run_now(),
通过 override_start_date 参数把"自动算日期"换成"用户指定起点",
其余 (维表 / A股日K / 除权因子 / enriched / 指数 / ETF / 错误兜底) 全部原样复用。
这样修正功能与盘后管道永远保持一致,不会出现遗漏。
落盘是 merge-upsert (按 symbol+date 去重 keep="last"), 新数据天然覆盖旧值,
不会产生重复。日K/指数/ETF 族无需先删分区; 股票 enriched 例外 — 增量重算
只算 enriched 里不存在的日期, "分区已存在但内容是盘中快照"的场景由管道内
的 prune_enriched_partitions 先删坏分区再重算 (run_now 内处理, 此处无需关心)。
"""
from __future__ import annotations
import logging
from collections.abc import Callable
from datetime import date
from app.tickflow.capabilities import CapabilitySet
from app.tickflow.repository import KlineRepository
logger = logging.getLogger(__name__)
def run_repair_daily(
repo: KlineRepository,
capset: CapabilitySet,
start_date: date,
end_date: date | None = None,
on_progress: Callable | None = None,
) -> dict:
"""修正 / 补全数据 — 复用盘后管道,日期范围由用户指定。
通过 run_now(override_start_date=start_date) 把日K/除权/指数的拉取起点
统一设为 start_date (到今天),其余流程与盘后管道完全一致。
Args:
repo: 数据仓库
capset: 权限集
start_date: 用户选定的起始日期
end_date: 保留参数(目前盘后管道固定拉到今天, 此值未使用, 为接口兼容保留)
on_progress: 进度回调
Returns:
run_now() 的完整结果 dict。
"""
today = date.today()
if start_date > today:
return {"error": "起始日期不能晚于今天"}
logger.info("repair_daily: run pipeline with override_start_date=%s", start_date)
from app.jobs.daily_pipeline import run_now
return run_now(repo, capset, on_progress=on_progress, override_start_date=start_date)