Backtest 失败后的标准处置 SOP
文档目的:为量化研究线在 05_backtest 结果不理想时,提供一套可审计、可复现、可治理的标准处置流程,避免“无方向优化”“回头调参”“静默改口径”。
适用范围:适用于具备明确谱系与阶段治理的研究线,尤其适用于如下 stage workflow:
00_mandate01_data_ready02_signal_ready03_train_freeze04_test_evidence05_backtest
适用对象:Research、Quant Dev、Reviewer、PM / Risk / Model Governance。
1. 核心原则
1.1 Backtest 是“交易可行性审计层”,不是“反向发明信号层”
05_backtest 的职责是验证:
- 上游已冻结的 Universe、参数、阈值、信号与执行规则,
- 在真实费用、真实持仓语义、真实容量假设下,
- 是否具备可交易性。
它不负责:
- 看到结果差后反向重写主信号;
- 回头重估
best_h、whitelist、train/test cut; - 用账户级结果去倒推 signal formula。
1.2 失败先冻结,再解释
任何不理想 backtest,第一动作都不是“优化”,而是:
- 冻结本次失败结果;
- 做失败分类;
- 明确 rollback stage;
- 写 allowed / forbidden modifications;
- 再决定是
RETRY、RESEARCH_AGAIN、NO_GO还是CHILD_LINEAGE。
1.3 只能在“失败所属层”修改
- 工程问题,在工程层修;
- 执行问题,在执行层修;
- 研究证据问题,回到
03/04修; - 主问题变了,开新谱系。
严禁跨层补丁式优化。
1.4 少量、受控、可解释的 retry
每次 retry 必须满足:
- 有且仅有一个明确假设;
- 有且仅有一组受控修改;
- 有明确预期改善指标;
- 同一失败类型的 controlled retry 不得无限持续。
建议纪律:同一失败类型最多 1–2 次 controlled retry;再无改善,则升级为 RESEARCH_AGAIN、NO_GO 或 CHILD_LINEAGE。
2. 失败触发条件
满足以下任一条件时,应触发本 SOP:
05_backtest账户级net_pnl_sum <= 0,或total_return <= 0;- 毛利为正但净利为负,且费用显著吞噬 alpha;
- 回撤、容量、换手、滑点表现明显超出项目容忍区间;
- 双引擎语义不一致、复算不一致、结果不可复现;
- 样本外 / holdout / paper trading 与预期偏离过大;
- 结果只能在狭窄参数点成立,缺乏稳健性。
3. 标准流程总览
步骤 0:冻结失败结果(Failure Freeze)
步骤 1:正确性排查(Correctness Triage)
步骤 2:统计证据排查(Evidence Triage)
步骤 3:经济性与执行排查(Economics / Execution Triage)
步骤 4:主假设复审(Thesis Review)
步骤 5:决策分流(Decision Fork)
步骤 6:受控回退或停线(Rollback / Stop / New Lineage)
4. 步骤 0:冻结失败结果
4.1 必须冻结的内容
一旦 backtest 失败,必须生成 failure package,至少包括:
lineage_idrun_id- 代码版本 / commit hash
- 数据版本 / 数据时间范围
- Universe 版本
- 参数版本
- 时间切分方式
- 手续费 / 滑点 / 借券 / 资金规模假设
- 撮合语义 / 引擎版本
- 账户级关键结果
- stage aggregate 结果
- 已知异常与未决问题
4.2 推荐产物
建议固定输出以下文件:
failure_freeze.mdrepro_manifest.jsonfailure_classification.mdrollback_decision.yamlretry_plan.md(仅当决策为RETRY时)
4.3 Failure Freeze 最小模板
lineage_id: <lineage>
run_id: <run>
stage: 05_backtest
status: failed_frozen
code_version: <git_sha>
data_version: <data_tag>
universe_version: <universe_tag>
parameter_version: <param_tag>
execution_semantics: <description>
fee_model: <description>
slippage_model: <description>
capital_assumption: <description>
key_results:
gross_pnl_sum: ...
fee_paid_sum: ...
net_pnl_sum: ...
total_return: ...
max_drawdown: ...
turnover: ...
known_issues:
- ...
open_questions:
- ...5. 步骤 1:正确性排查(Correctness Triage)
这一步先回答“结果对不对”,而不是“结果赚不赚”。
5.1 检查清单
A. 可复现性
- 同一配置重跑,结果是否一致;
- 固定种子 / 固定输入后,输出是否稳定;
- 关键汇总文件与逐笔成交是否一致。
B. 双引擎 / 交叉引擎一致性
- 至少使用两套回测或执行语义进行交叉验证;
- 检查
semantic_gap或等价字段; - 若两引擎结果方向相反或偏差巨大,优先归类为工程问题。
C. 时间与因果关系
- 信号是否使用了未来信息;
T信号与T+1收益是否正确对齐;- 标准化窗口、回归窗口、持仓窗口是否存在泄漏。
D. 数据质量
- 对齐是否正确;
- 缺失、停牌、异常值、复权、退市、可交易性过滤是否正确;
- Pair / spread / hedge leg 的单位是否一致。
E. 执行状态机正确性
- entry / exit / stop / cancel 的状态机是否正确;
cross_only是否被实现成stateful cross_only;max_hold、stop、mean_reached等 close reason 是否符合合同。
F. 成本与单位边界
- stage aggregate 的单位是否与账户级单位明确区分;
- 手续费是否按真实名义仓位计算;
- 单腿 / 双腿费用是否与 contract 一致。
5.2 判定标准
若此步发现:
- 结果不可复现;
- 双引擎语义不一致;
- 存在未来函数;
- 数据错位;
- 单位换算错误;
- 成本模型实现错误;
则归类为:
ENG_FAIL
5.3 ENG_FAIL 处理规则
- rollback_stage:缺陷所在 stage(通常为
01_data_ready、02_signal_ready或05_backtest实现层) - 决策:
RETRY - 允许修改:数据链路、状态机、撮合逻辑、费用逻辑、实现 bug
- 禁止修改:Universe、信号表达式、上游冻结对象、研究主问题
6. 步骤 2:统计证据排查(Evidence Triage)
只有 correctness 通过后,才检查“信号统计上到底还在不在”。
6.1 检查目标
04_test_evidence的核心结论是否真的稳健;- 当前 backtest 失败,是不是 earlier-stage 证据本来就弱,只是到
05才暴露; - 是否存在严重 multiple testing / data snooping;
- 上游 freeze discipline 是否被破坏。
6.2 检查清单
A. 结构证据是否稳定
- 主假设对应的 test metrics 是否在 test / holdout 上稳定;
- 不是只在 train 或少量片段上成立;
- 不是只靠极端样本支撑。
B. 样本外纪律是否被破坏
- 阈值、白名单、
best_h是否只用 train 冻结; - backtest 阶段是否出现“看了结果再回头改 freeze 对象”的行为;
- 是否在同一份 OOS 上反复调参。
C. 稳健性
- 相邻参数是否不是断崖式失效;
- 不同时间切片、不同 regime、不同 symbol bucket 是否大致一致;
- 不应只有一个窄点好看。
D. 研究问题是否发生漂移
- 当前线是否还在回答原 Mandate 的主问题;
- 还是已经变成另一个问题,只是名字没换。
6.3 判定标准
若此步发现:
04_test_evidence本身就不稳;- 样本外纪律被破坏;
best_h、whitelist、threshold 在05被反向重估;- backtest 的失败实质上是 research evidence 的失败;
则归类为:
RESEARCH_FAIL
6.4 RESEARCH_FAIL 处理规则
- rollback_stage:
03_train_freeze或04_test_evidence - 决策:
RESEARCH_AGAIN - 允许修改:研究设计、证据口径、切片验证、稳健性验证、freeze discipline
- 禁止修改:在同一份 backtest / OOS 上继续调参直到好看
7. 步骤 3:经济性与执行排查(Economics / Execution Triage)
若 correctness 正确、统计上仍有一定边,则进入经济性与执行层排查。
7.1 先拆 gross 与 net,不要先看总收益
至少拆以下指标:
gross_pnl_sumfee_paid_sumslippage_sumnet_pnl_sumgross/tradefee/tradenet/trade- turnover
- 平均持仓时长
- close reason 分布
- 容量 / 资金规模敏感性
7.2 重点检查项
A. 信号有边,但太薄
典型症状:
gross > 0net < 0gross/trade明显小于fee/trade
B. 换手过高
典型症状:
- 交易次数很多;
- 单笔利润很小;
- 零成本或低成本表现尚可,但真实费率下崩掉。
C. 持仓规则没有在消费主信号语义
例如:
- 研究主张是 mean reversion;
- 但实盘执行几乎全部由
max_hold退出; - 实际上变成了“事件 + 固定持有期”而不是“回归即退出”。
D. 规模 / 容量不匹配
例如:
- 小 alpha 在小资金下可活;
- 在正式 pair budget 下被成本吃掉;
- 策略并非完全无效,而是在当前成本 / 容量假设下不可实施。
7.3 判定标准
若此步发现:
- 信号方向基本正确或略有边;
- 但交易成本、滑点、容量、换手、持有期规则吃掉了收益;
- 或当前执行语义明显偏离研究语义;
则归类为:
EXEC_FAIL
7.4 EXEC_FAIL 处理规则
- rollback_stage:通常仍为
05_backtest,必要时回退到 execution contract 设计层 - 决策:
RETRY - 允许修改:
- exit 语义
- entry 触发节奏
- cooldown / re-entry suppression
- 持仓时长 / stop / take-profit 映射
- 成本包络测试
- 资金规模 / 容量分析
- 订单执行与撮合假设
- 禁止修改:
- topicA whitelist
best_h重估- train/test 切分
- 因 backtest 结果不好而回头改主信号
- 将对侧信号静默加回
- 事后增加 gate 美化收益
8. 步骤 4:主假设复审(Thesis Review)
当 correctness 正确、实现无误、执行与成本也不能解释全部失败时,要回到更高层:
8.1 问四个问题
- 这个策略赚的是什么钱?
- 这套机制是否仍然存在?
- 它是不是只在狭窄历史片段上成立?
- 市场结构、费用结构、拥挤度是否已经使原假设失效?
8.2 典型症状
- 不同时间段都不稳;
- 稍微改参数就断崖失效;
- 核心统计证据也弱;
- 无法用成本或执行规则解释;
- 研究主问题本身不成立,或已经不再值得投入。
此时归类为:
THESIS_FAIL
8.3 THESIS_FAIL 处理规则
- rollback_stage:不再 rollback 原线
- 决策:
NO_GO - 允许动作:停线、记录结论、沉淀负样本经验
- 禁止动作:继续在原线补丁式优化
9. 步骤 5:决策分流(Decision Fork)
每次失败处理后,只允许输出以下四种正式决策之一:
9.1 RETRY
用于:
ENG_FAILEXEC_FAIL- 且问题边界明确,可在受控范围内修复
要求:
- 写明 rollback stage
- 写明 allowed modifications
- 写明 forbidden modifications
- 写明下一轮唯一核心假设
- 写明预期改善指标
9.2 RESEARCH_AGAIN
用于:
RESEARCH_FAIL- backtest 失败只是更晚暴露 earlier-stage 证据不稳
要求:
- 回到
03/04 - 重新审视 train/test freeze discipline
- 重做证据层,而不是继续拿
05调参
9.3 NO_GO
用于:
THESIS_FAIL- 或明确判断当前线不值得继续投入
要求:
- 明确停线理由
- 记录可复用经验
- 禁止隐性重启原线
9.4 CHILD_LINEAGE
用于:
- 研究主问题、交易语义、Universe 合同发生变化
- 已不再是原题的小修小补
典型例子:
- 从“双边 OB/OS”转成“只做 3A 超涨回落”;
- 从“结构诊断”转成“执行 alpha”;
- 从“Universe only conditioning”转成“逐 bar gate conditioning”。
要求:
- 新命名
- 新 mandate
- 新 contract
- 与原线清晰隔离
10. 失败分类与回退矩阵
| 失败类型 | 典型症状 | rollback stage | 默认决策 | 允许修改 | 禁止修改 |
|---|---|---|---|---|---|
ENG_FAIL | 不可复现、未来函数、引擎不一致、单位错 | 缺陷所在 stage | RETRY | 数据、实现、状态机、成本逻辑 | 主信号、Universe、freeze 对象 |
EXEC_FAIL | gross 有边,net 被成本/换手/退出吃掉 | 05_backtest | RETRY | 执行规则、退出映射、cooldown、容量/成本测试 | best_h、whitelist、signal formula |
RESEARCH_FAIL | 04_test_evidence 不稳、样本外纪律破坏 | 03/04 | RESEARCH_AGAIN | 证据层验证、切片、freeze discipline | 在同一 OOS 上继续调参 |
THESIS_FAIL | 机制不成立、广泛失效、脆弱 | 无 | NO_GO | 停线、沉淀结论 | 补丁式续命 |
| 研究主问题改变 | 研究题目或合同改变 | 新线 | CHILD_LINEAGE | 新谱系设计 | 污染原线 |
11. Retry 的标准写法
每次 retry 只能写成如下结构:
11.1 唯一核心假设
例如:
当前失败主因是纯 hard exit,导致 mean reversion 语义没有被消费。
11.2 受控修改集
例如:
- 只修改 exit 语义;
- 不改 signal source;
- 不改 Universe;
- 不改
best_h; - 不回头改白名单。
11.3 预期改善指标
例如:
mean_reached占比上升;- 平均持有时长下降;
gross/trade上升;fee/gross比例下降;- 账户级
net_pnl_sum改善。
11.4 Retry 模板
decision_state: RETRY
failure_class: EXEC_FAIL
rollback_stage: 05_backtest
next_hypothesis: >
当前失败主因是退出语义将均值回归策略退化为硬持有期事件交易。
allowed_modifications:
- exit_semantics
- cooldown
- reentry_suppression
- execution_cost_envelope
forbidden_modifications:
- whitelist
- best_h_reestimation
- train_test_resplit
- signal_formula_rewrite
expected_improvements:
- higher_mean_reached_ratio
- lower_turnover
- higher_gross_per_trade
- lower_fee_to_gross_ratio12. 禁止动作清单
以下动作默认视为违规:
- 因为 backtest 结果差,回头重估
best_h - 在同一份 OOS / backtest 上反复挑参数直到好看
- 在
05阶段偷偷更换 Universe - 把对侧信号加回来,掩盖单边策略问题
- 用新 gate、美化过滤器掩盖主信号经济性不足
- 用 stage aggregate 替代账户级收益结论
- 在 summary 文档中混淆单位边界
- 不冻结失败版本,直接开始下一轮修改
13. 推荐的最小审计产物
13.1 failure_classification.md
建议固定写:
- 本次失败属于哪一类
- 证据是什么
- 为什么不是其他失败类型
- rollback 到哪一层
- 下一步允许改什么、不允许改什么
13.2 rollback_decision.yaml
decision_state: RETRY | RESEARCH_AGAIN | NO_GO | CHILD_LINEAGE
failure_class: ENG_FAIL | EXEC_FAIL | RESEARCH_FAIL | THESIS_FAIL
rollback_stage: 01_data_ready | 02_signal_ready | 03_train_freeze | 04_test_evidence | 05_backtest | none
allowed_modifications:
- ...
forbidden_modifications:
- ...
owner: <name>
reviewer: <name>
signoff_required: true13.3 retry_plan.md
至少包括:
- 本轮唯一假设
- 修改项
- 预期改善指标
- 成功 / 失败判定标准
- 最大 retry 次数
14. 将机构常见五阶段流程映射到本 SOP
阶段一:Sanity Checks & Attribution
映射到:
步骤 0:冻结步骤 1:正确性排查步骤 3:成本 / 归因 / 容量排查
阶段二:Hypothesis Review
映射到:
步骤 2:统计证据排查步骤 4:主假设复审
阶段三:Optimization & Refinement
映射到:
- 仅在
RETRY条件下受控进行; - 必须限定在失败所属层;
- 不是无限调参。
阶段四:Robustness Testing
映射到:
- retry 之后必须重新走未触碰样本的验证;
- 检查参数高原而不是孤峰;
- 检查 regime / time split / bucket 稳定性。
阶段五:Decision Fork
映射到:
RETRYRESEARCH_AGAINNO_GOCHILD_LINEAGE
15. 套用到你当前项目的落地建议
对当前 topicA -> topicC-3A event_hardexit 这条线,若已确认:
- 双引擎一致;
- 不是实现 bug;
- gross 为正但 net 为负;
- close reason 几乎全是
max_hold;
则当前最合理的正式分类通常应写成:
EXEC_FAIL-primary
建议写法:
- rollback_stage:
05_backtest - decision_state:
RETRY - allowed_modifications:
exit semanticsturnover suppressioncooldowncost envelopesizing / capacity sensitivity
- forbidden_modifications:
topicA whitelistbest_h re-estimationtrain/test resplit- 将
OS重新并入正式执行 - 事后添加
topicDgate 美化收益
下一轮 retry 的目标不应再写成“继续证明 OB 回落存在”,而应写成:
验证当前失败是否主要由执行退出语义与交易经济学失配导致。
16. 最终结论模板
每次 backtest 失败,最终结论建议固定为以下格式:
16.1 结论摘要
- 本次失败类型:
- 主要证据:
- rollback stage:
- 决策状态:
16.2 失败归因
- 是否为工程失败:
- 是否为研究失败:
- 是否为执行失败:
- 是否为主假设失败:
16.3 允许修改 / 禁止修改
- allowed modifications:
- forbidden modifications:
16.4 下一步动作
- 若
RETRY:唯一核心假设与指标 - 若
RESEARCH_AGAIN:回退到03/04的具体任务 - 若
NO_GO:停线与经验沉淀 - 若
CHILD_LINEAGE:新线 mandate 的差异点