回测处理经验
一、回测结果不及预期
场景一:没有产出交易
先别急着”调到有交易”,而是先判断为什么没有交易。常见 5 类原因:
- alpha 本身太稀疏 — ResidualZ 在回测窗里根本没到开仓阈值
- 信号到交易的映射太严 — 比如
cross_only+ 高entry_z,理论有偏离但不触发下单 - 风控/质量门禁全挡掉 — 比如
pair_missing、low_sample、missing_rate_window、高波动过滤 - 白名单和回测窗错配 —
04_test_evidence里通过的币,在05_backtest那段时间刚好没有足够触发 - 实现或时间语义有问题 — 最危险,比如阈值方向写反、
open_time/close_time错位、cross 事件判定有 bug
解决思路:按层推进,不要一口气乱改
Step 1:先做”触发漏斗”诊断
从上到下数一遍,定位死在哪一层:
- 有多少 bar ResidualZ 是有效值
- 有多少 bar 满足
abs(ResidualZ) >= entry_z - 有多少是
cross_only真正触发点 - 有多少通过
low_sample/pair_missing - 有多少通过 regime gate
- 最后剩多少真实 entry
Step 2:先放松执行层,不先改 alpha
机构一般先改最外层映射,而不是先推翻信号:
- 降低
entry_z - 把
cross_only暂时改成level做诊断 - 放宽
max_hold_bars - 暂时去掉可选的 risk overlay
如果这样就有交易,说明问题在执行映射,不在 alpha 本身。
Step 3:把”最小 baseline”和”附加风控”分开
先跑一版最小可执行策略,再逐个叠加:
- entry / exit
- sizing
- portfolio cap
- regime gate
invalid_sample处理
这样才知道是哪一层把交易打没了。
Step 4:如果最小版本也没交易,再回头看信号设计
这时才考虑:
- 这组
param_id是否过于保守 - 这个 bar 频率是否不适合
- 这组白名单是不是太窄
best_h和执行窗口是否不匹配
Step 5:始终一次只改一层
不要同时改 entry_z、entry_mode、风控门禁、白名单。
场景二:回测结果为负
核心问题:到底是哪个层面把结果打成负的。最有用的拆法是以下 6 个角度。
1. 正确性层
先排除假亏损:
- 时间语义有没有错位
- 信号方向有没有写反
- 开平仓规则有没有实现偏差
- 双引擎是否一致
- 费用、资金费率、持仓记账是否重复或漏记
如果这层没过,后面所有优化都不可信。
2. Alpha 层
先看扣费前有没有边。最关键的分叉:
| 情况 | 判断 |
|---|---|
| gross < 0 且 net < 0 | 更像 alpha 本身没用,或信号映射方向错了 |
| gross > 0 但 net < 0 | alpha 有一点边但太薄,主要死在成本和换手 |
这一步看:Q1/Q5、gamma、reversion_score、OOS 是否仍成立。
3. 执行映射层
很多负收益其实不是 alpha 错,而是交易规则把信号用坏了。重点看:
entry_z是否合理entry_mode是否过于频繁或过于稀疏exit_z、max_hold_bars、cooldown- flip 是否过多
同一个信号,交易语义不对,也会把正边打成负边。
4. 成本与微观结构层
实盘最常见的死因。重点看:
trade_count、fee_paid_sum- 单笔平均毛利 vs 单笔平均成本
- 持仓时长、换手率
- 滑点、资金费率、冲击成本
如果发现单笔毛利很小、交易特别多、一加真实费用就翻负 — 那就不是先”调更多参数”,而是先想办法降换手、拉大单笔边际。
5. 组合层
单币能赚,组合也可能亏:
- 是否少数币在拖累
- 并发过高导致资金分散
- 仓位分配是否错误
- 是否把低质量、低边际币也一起放进来了
- 组合是否过度集中在某一类市场状态
常见动作:砍掉最差币种、改 portfolio cap、调整 sizing、把 alpha 和 risk overlay 分开记账。
6. 稳健性层
哪怕当前回测为正也可能不可用;为负也要看负得是否稳定:
- 不同时间段
- 不同波动 regime
- 上涨/下跌切片
- 不同 symbol 子集
- 不同成本假设
如果只是某一小段负,可能是 regime 问题。如果各切片都负,通常就该考虑停线。
快速判断框架
| 现象 | 优先怀疑 |
|---|---|
| gross 和 net 都负 | alpha、方向、执行映射 |
| gross 正、net 负 | 换手和成本 |
| 单币多半正、组合负 | 仓位和组合分配 |
| Train/Test 好,Backtest 负 | 执行层和成本层 |
| 只有极少数参数/时间段为正 | 过拟合 |
稳健性诊断:不是第一优先,但不可跳过
稳健性诊断和前面几层的关系:
| 层级 | 回答的问题 |
|---|---|
| 正确性 / alpha / 执行 / 成本 | ”为什么现在亏” — 定位主因 |
| 时间段 / symbol 子集 / 成本假设 / 波动状态 | ”这个亏法是稳定一致的,还是只发生在某些局部条件下” — 稳健性切片 |
它的作用不是先拿来救策略,而是帮你判断:是全局都不行,还是只有某些 regime 不行;是少数币拖累,还是大部分币都边际太薄;是只要手续费一真实化就不行,还是在某些成本水平下还能活。
四类稳健性切片
1)不同时间段切片
- 前半段亏后半段赚?还是只有某个阶段特别差?
- 如果只是某一小段拖累 → regime 问题;各时间段都差不多负 → 不是偶然
2)不同 symbol 子集
- 前 5 个最强币、去掉最差 3 个币、大币 vs 小币
- 判断是不是少数币拖累组合,是否需要收缩白名单
3)不同成本假设
- 0 bps → 1 bps/side → 2 bps/side → 3 bps/side
- 判断策略到底是”无边”还是”有边但太薄”
- 这是最关键的实盘敏感性分析之一
4)不同波动状态分层
- 高波 BTC / 低波 BTC / 下跌环境 / 非下跌环境
- 判断策略是不是只在某种市场环境下有效,regime gate 是否值得保留
处理优先级
- 先确认主因 — 成本层是主因?执行层是次因?
- 再做稳健性切片 — 是不是所有子样本都一样差?有没有值得保留的局部结构?
- 最后再决定怎么处理 — 停线 / 收缩白名单 / 降换手 / 回到更慢频率 / 重开新信号族
这些切片不是”先怎么调”,而是”在决定调不调、往哪调之前,先把亏损结构拆清楚”。
实战案例:时间段切片诊断
为什么先做这个:
- 它最先回答”这次亏损是不是全回测窗都在发生”
- 如果只是某一小段特别差 → 优先查局部 regime、局部事件、局部数据问题
- 如果每一段都差 → 问题更像 baseline 自身的边际和成本结构,不是某个偶发坏月份
切片结论:
- 日历切片 3 段全部为负
- 半窗切片 2 段也全部为负
- 2025-12 负,2026-01 更负,2026-02 也仍然为负
- 前两段虽然
gross_pnl_sum为正,但都远小于fee_paid_sum - 最后一小段连
gross_pnl_sum都转负了
当前亏损不是某一个坏时间段偶然拖累,更像整个回测窗里都存在”边际太薄、费用太重”的一致性问题。
实战案例:Symbol 子集切片诊断
为什么做这个:
- 时间段切片已经证明,不是某一个坏月份拖累
- 下一步最该判断:是不是少数坏币拖垮了组合
- 如果删掉最差几个币就能明显改善 → 优先考虑收缩白名单
- 如果子集仍然都很差 → 问题更偏整体边际薄,不是坏币太多
切片结论:
- 删掉最差 1/3/5 个币,亏损会缩小,但仍明显为负
- 只保留 top_5、top_10
reversion_score的高分币,费用后还是负 - 按
best_h=10或best_h=30分组,也都还是负
当前 baseline 的负收益不是主要由少数坏币拖累,也不是”只要留下测试层最强币组就能救回来”。
实战案例:成本假设切片诊断
为什么做这个:
前两步已经基本排除了”某一段时间”或”少数坏币”是主因,接下来最该量化的是:这条线到底是”无成本时有边、真实成本后被吃掉”,还是”连低成本假设也站不住”。
实战案例:高波动状态分层诊断
为什么做这个:
- 前三步已经说明,不是某一小段时间、不是少数坏币、也不是”只差一点成本”
- 最后要判断:是不是主要高波环境把策略打坏了
- 如果亏损主要集中在高波段 →
exclude_high_vol这类 regime gate 可能还是主线 - 如果不是 → 高波过滤就不是核心解法
切片结论:
- 高波 bar 只占约 20.13%
- bar 级净值增量里,高波段不是主要亏损来源,主要亏损反而发生在
low_volbar - 在高波 regime 里结束的交易单笔更差,但只有 81 笔
- 穿过高波环境的交易也更差,但只有 95 笔
当前总体亏损并不是由少量高波穿透交易主导。
二、针对 Alpha 利润太薄的解决方案
概述
- 先判断 gross/trade 能不能随着更严格入场明显抬升;如果不能,说明 alpha 太弱,不是执行层能救。
- 再看持仓时长和出场语义,确认是不是太早进、太早出,把本来能走开的 spread 提前切掉了。
- 最后看 symbol 和 regime 切片,确认是不是低质量币和高波阶段在稀释整体边际。
实战案例:单笔毛利太薄的处理思路
这次围绕”单笔毛利太薄”的处理思路,可以概括成 5 步:
Step 1:先把问题定义清楚
不是先看组合为什么亏,而是先问:gross_pnl_per_trade 是否太低,导致手续费把边际吃掉。也就是先定位成”单笔交易经济性”问题,而不是先怀疑数据或信号方向。
Step 2:先做结构拆解,不把”毛利薄”当成全局结论
加了单笔诊断后,发现不是所有交易都薄,而是结构性变薄。最典型的是 6_to_15 bars 这档交易,毛利明显偏薄、费毛比偏高,所以拖累主要集中在某些持仓时长和某些 symbol。
Step 3:先试执行层收紧,验证是不是”交易太松”
执行层优先试了两类动作:
- 提高 entry_z
- 缩短 max_hold_bars
结果很明确:
- entry_z 往上抬到 2.85/3.0,交易基本直接消失,这条路太激进
- max_hold_bars 从 15 收到 10 有改善
- 再压到 8/6 就开始过度收紧,反而伤到有效利润
所以执行层当前最优点是:
- entry_z = 2.77
- max_hold_bars = 10
Step 4:再试截面层排查,验证是不是”少数差币拖累”
在 hold10 这个当前最好执行版本上,做了一个诊断实验:剔除 TRXUSDT、BNBUSDT、HBARUSDT。结果显示改善很明显:
- total_return: -0.00787 → -0.00533
- gross_pnl_per_trade: 0.00257 → 0.00338
- fee_to_gross_ratio: 0.155 → 0.118
这说明问题不只是”执行过松”,还包括”少数差币在稀释组合质量”。
Step 5:最后把研究结论和正式策略边界分开
这里要严格区分两件事:
- 手工剔除差币:可以作为诊断实验
- 正式策略规则:不能靠”回头看哪个币差就删哪个”
所以当前真正的研究结论是:
- 单笔毛利薄不是全局性的,而是集中在部分持仓区间和部分 symbol
- 执行层有效优化方向是”缩短持仓”,不是继续抬高 entry_z
- 截面层确实存在差币拖累
- 但最终不能停留在”手工删币”,而应该升级成事前可定义的 TopK / 打分筛选规则
总结
这次处理单笔毛利薄的思路是:
先用单笔诊断找出”薄”发生在哪,再先收执行、后查截面,最后把”人工剔除差币”的发现,上升为未来应该实现的、事前可执行的 TopK/score-based universe 规则。