可复现的回测循环:把研究流程写成软件
从数据快照、参数版本到结果归档,讨论回测框架的工程边界与可复现性的实现方式。
问题不是算得快,而是算得一样
研究代码最常见的失效不是性能,而是同一份输入在不同时间得到不同结果。环境依赖、数据口径、随机数状态、隐式时区,都会让「上次跑出来的结论」无法复现。把回测当作软件工程问题处理,第一步是承认:可复现性是一种接口约束,而不是一条运行约定。
我们把一次回测拆成四个可独立校验的环节:输入快照、参数声明、执行过程、结果归档。每一环都留下可比对的结构化记录,而不是散落在笔记里的口头约定。
输入快照:先固定数据,再谈逻辑
数据层至少要回答三个问题:用的是哪个时间截面、缺失值如何处理、公司行动与复权口径是什么。建议把这三个答案写进一份 manifest,与回测代码同版本管理。
run_id: demo-20261115-01 # 示例内容 · 需核实
dataset: bars@v3
as_of: 2026-11-14T00:00:00Z
universe_rule: fixed-at-start
missing: forward-fill-limited
adjustment: split-and-dividend
manifest 的价值在于:当结论变化时,可以先判断是否只是输入变了,而不是重新怀疑整套逻辑。
参数声明:禁止从环境隐式读取
所有影响结果的参数都应显式出现在配置中,并在运行时做完整性校验。依赖环境变量、全局常量或「上次改过的默认值」,是复现失败的高频来源。
| 参数类别 | 校验方式 | 失败时行为 |
|---|---|---|
| 时间区间 | 起止有序、时区显式 | 构建期报错 |
| 随机种子 | 必填且记录 | 拒绝执行 |
| 成本假设 | 单位与符号校验 | 拒绝执行 |
| 依赖版本 | 锁文件比对 | 输出差异报告 |
结果归档:让差异可定位
输出不应只有一个数字。我们建议同时保留:输入 manifest 的哈希、代码版本标识、参数快照、逐阶段中间量与最终指标。这样当两次运行不一致时,差异可以定位到具体环节,而不必整体重跑。
verify(run_a, run_b)
├── manifest_hash equal?
├── code_revision equal?
├── params equal?
└── stage_checksums first_diff_at = ?
小结与边界
这套结构只解决「同一输入得到同一结果」的工程问题,不评价任何研究结论的优劣,也不涉及数据来源的合规判断。本文所有标识与字段均为示意,具体实施需按团队的数据治理要求调整。
示例内容 · 需核实。
需核实 · 全文为工程技术示例,不含真实业绩、案例或客户信息。免责声明