记录一次被回测连续拍醒的一天(2026-07-25)

数据连续推翻三个优化方案后我才承认:这策略赚不赚钱跟参数无关,跟行情有关。

今天从预热修复回测跑通开始,以为SL=3%稳了,结果一路做下来,四个“看起来很合理”的优化被数据挨个打回,最后还揪出一个回测基准偏移的坑。老规矩,逐行核对代码,不轻信摘要。

一、预热修复后,SL=3%的优势没了,th=3直接翻负

早上一上来,预热(warmup)修复后的回测结果先出来了。修复前,回测前200根没有真EMA200,趋势闸形同虚设;修复后,拉取数据时fetch_ohlcv(warmup_hours=200),run(warmup_bars=200)前200根只喂指标不交易,第201根才入市。助手核对了代码,total_hours = days*24 + 200 = 920,since前移,live侧也同步预拉250根对上了。

结果对比很直接:

组合 无预热 有预热(200根) 变化
th=1 SL=3% +$46.17 +$35.84 ↓22%
th=1 SL=5% +$24.85 +$35.39 ↑42%
th=3 平均 +$12.55 -$43.53 翻负
th=5 +$30.33(1笔) 0笔 信号在预热区

SL=3%还是第一,但优势从“碾压”变成“微弱领先”:$35.84 vs $35.39,回撤0.2% vs 0.3%,基本没区别。th=3全线翻负,之前它看起来还行完全是预热缺口送的——前200根没有真EMA200,趋势闸没拦住逆势单。th=5直接归零,那1笔交易原来在预热区里。live配置不用动,但别指望SL=3%有巨大边际优势。

二、Walk-forward:三段没有一组参数全盈利

跑完walk-forward,结果很残酷。三段滚动窗口(每段30天交易区,200根预热前缀),段1(04-1705-25)全线亏损,段2(05-1706-24)全线盈利,段3(06-16~07-24)th=1正、th=3负。

段 th=1 SL=3% th=3 SL=5%
段1 -$26.42 -$58.23
段2 +$23.20 +$129.36
段3 +$30.77 -$44.76

没有任何参数组合在3段都盈利。 这不是SL取3%还是5%的问题,策略本身在段1那种行情里就是亏的。助手逐行核对了切段逻辑,段i切片[i*720 : i*720+920],每段200预热取自前一段交易区,无未来泄漏,段窗口重叠8天就是预热前缀,交易区首尾相接。另外他提醒我查fetch_ohlcv分页,别让2360根被悄悄截断——这个后面反转出场实验时确认接上了。

这轮还纠正了一个认知偏差:market_regime.py里“5状态×14策略权重矩阵”,权重矩阵那部分对HL策略根本用不上,regime过滤不是几行的活。

三、Regime诊断:封regime是错的,好段也靠它们赚钱

针对段1亏损,我做了个诊断脚本diag_seg1_regime.py,给每笔交易打regime标签。发现真正亏损源是ranging和trending_down:ranging 28笔胜率46.4%,-$25.63;trending_down 12笔胜率33.3%,-$13.68。助手核对打标方法无未来泄漏(用入场时刻前的1h数据重采样4h跑detect_regime),数据自洽。

但我当时算错一笔账:以为拦截ranging+trending_down能翻正。结果跑完封regime A/B,数据把计划彻底推翻:

段 原始PnL 封ranging+trending_down后 改善
段1(坏) -$26.42 +$12.90 +$39.32 ✅
段2(好) +$23.20 -$3.49 -$26.69 ❌
段3(好) +$31.06 -$10.40 -$41.46 ❌
全局 +$27.84 -$1.00 -$28.84 ❌

封掉ranging+trending_down会把一个正期望策略打成负期望。 因为这两个regime在段2/3是赚钱的:段2 trending_down SHORT 16笔+$24.78,段3 ranging LONG 15笔+$35.68。助手核对段切片不重叠、regime标注点时序、拦截公式都正确,结论站得住——封整个regime是负和,不能这么干。

四、ADX门控A/B:提高信号门槛被证伪

接着上了v1.35信号层改造:_fetch_1h_candles返回完整OHLCV,新增_calc_adx(Wilder 14周期),_get_effective_threshold根据ADX<20返回阈值3,入场循环加ADX门控。跑A/B,单变量隔离只差ADX_GATE_ENABLED一个键。

段 OFF (v1.34) ON (ADX门控) Δ
段1 -$25.69 -$46.77 -$21.08 ❌
段2 +$25.73 +$34.05 +$8.32 ✅
段3 +$24.83 +$18.84 -$5.99 ❌
合计 +$24.87 +$6.12 -$18.75 ❌

ADX门控整体被证伪:震荡段更差,趋势段改善不够。助手核对_calc_adx是标准Wilder 14,门控在live和回测位置一致,没有顺序错配。结果就是信号层提高门槛这条路也走不通。

五、反转出场ADX感知:全局微弱正,但4/6段恶化,还发现回测基准偏移

最后试了反转出场的ADX感知:REVERSAL_ADX_THRESHOLD=30,ADX<30时反转阈值从5放宽到8。A/B结果全局+$9.71(OFF -$4.10 vs ON +$5.60),但跨段2/6改善、4/6恶化。改善集中在最差的段2(+$13.35)和段3(+$12.92),恶化集中在好段6(-$12.18)。

更关键的是助手在核对回测代码时抓到一个实质偏差:回测在第243行硬编码了RISK["SIGNAL_REVERSAL_CLOSE"] = params["threshold"] + 2,算出来是3;而live的hl_trader第70行写死了"SIGNAL_REVERSAL_CLOSE": 5。回测的OFF基准其实是3,不是live的5,从来就没有忠实复刻过v1.34。 这意味着回测夸大了relax的效果,因为live真的从5放宽到8,跨度比回测的3到8小。

这个发现把反转出场A/B的“全局正”打了个折扣——内部有效性还在,但“=v1.34行为”的叙事错了,要上线得先让回测基准对齐live。

六、遗留

反转出场A/B的完整结论还没给全,因为回测基准偏移这个坑得先修。下一步先改回测里的硬编码,让SIGNAL_REVERSAL_CLOSE跟live一致,再重跑反转出场评估。另外walk-forward已经证明策略regime-dependent,但封regime和ADX门控都走不通,现在手上没有能稳定跨段提升的手段,暂时不能动live配置。

今天四个实验,三个被彻底否决,一个效果微弱还带着基准偏移。回测器一直在干它该干的活:把乐观幻想拍回数据上。