第 33 篇开始从历史角度看模拟盘是否稳定。
第 34 篇回到每日运行前的输入检查:如果目标股票里有价格缺失,系统应该明确报出缺口,而不是让策略在None、0或旧价格上继续跑。
缺价格不是小事
模拟盘里最危险的 bug 往往不是程序直接崩溃,而是数据缺了但程序继续运行。
如果某只股票没有最新价格,调仓金额、风险敞口、权益快照都会跟着失真。早期系统宁可保守一点,把这类问题标成 blocker。
缺口对象
第 34 章新增app/data_gaps.py。
@dataclass(frozen=True) class DataGap: symbol: str trade_date: date field: str severity: str @dataclass(frozen=True) class DataGapPlan: gaps: tuple[DataGap, ...] severity: str第一版只检查last_price,后面可以继续扩展到成交量、复权因子、停牌状态等字段。
构造缺口计划
函数输入是价格快照和必需 symbol 列表:
def build_price_gap_plan( price_snapshot: PriceSnapshot, *, required_symbols: list[str], ) -> DataGapPlan:实现时先去重,再比较必需列表和快照里已有价格。
required = list(dict.fromkeys(required_symbols)) missing = sorted(set(required) - set(price_snapshot.prices))每个缺失 symbol 都变成一个 blocker:
DataGap( symbol=symbol, trade_date=price_snapshot.trade_date, field="last_price", severity="blocker", )这个设计很朴素,但它让缺口从“日志里可能有一行”变成了可测试、可汇总、可进入运维检查清单的数据结构。
常见的数据缺口不只有价格缺失。日频策略里还可能遇到成交量缺失、复权因子缺失、停牌状态缺失、股票池成分更新延迟等问题。第一版只检查last_price,是因为价格缺口会直接影响权益、仓位和调仓金额,是模拟盘最该先挡住的一类问题。
当前联动运行结果
paper-ops-check会故意构造一个缺价格场景:必需 symbol 包含000001.SZ和600519.SH,但价格源只返回000001.SZ。
uv run python -m scripts.chapter_examples paper-ops-check因此缺口计划输出severity=blocker,gap_symbols=['600519.SH']。这类结果会直接进入第 35 篇的运维检查清单,阻止系统在行情不完整时继续执行。
测试缺口输出
测试场景里,价格快照只有000001.SZ,必需列表还有600000.SH和300001.SZ。
uv run pytest tests/test_data_gaps.py断言结果:
assert plan.severity == "blocker" assert gap_symbols(plan) == ["300001.SZ", "600000.SH"] assert plan.gaps[0].field == "last_price"如果所有必需价格都存在,则返回severity == "ok"且gaps == ()。
本章更新与代码仓库
本章更新内容:
- 新增
app/data_gaps.py。 - 实现
DataGap和DataGapPlan。 - 对必需 symbol 和价格快照做缺口检查。
- 缺失价格统一标记为 blocker。
- 新增
gap_symbols()便于测试和展示。 - 增加
paper-ops-check联动示例,展示缺价格如何变成 blocker 级别数据缺口。 - 补充价格、成交量、复权因子、停牌状态等常见数据缺口背景。
- 新增
tests/test_data_gaps.py,覆盖缺价格和数据完整两种场景。
代码仓库:
https://github.com/ax2/zi-quant-platform本章代码:
git clone https://github.com/ax2/zi-quant-platform.git cd zi-quant-platform git checkout chapter-34 uv sync --extra dev uv run pytest tests/test_data_gaps.py第 34 章提交为5c94461,tag 为chapter-34。
本篇小结
行情缺口要尽早变成结构化结果。
第 34 篇把缺失价格从隐性风险变成DataGapPlan。下一篇会把运行时间窗、历史摘要、数据缺口和健康报告组合成一张最终的运维检查清单。