记录一次数字全对机制全错的交叉验证(2026-10-16)

“上一轮的数字全对而机制全错,所以请不要因为我给了证据就采信。”

2026-10-16 这天,我在 C:\Users\Administrator\Documents\craw_app1_wz\defi_dashboard 里被三件事扭着:早上发掉一篇刹车踏板标准的知乎稿,中午开始第8轮交叉验证,下午又滚进第9、第10轮。真正让我后背发凉的是验证里反复出现的那句话。

一、第8轮:27条新测试全绿,载荷里却藏着14个币的前导填充

早上 07:02,我把那篇《三台车的刹车踏板全断了,报告却写"合格"》发上知乎,3326 字 / 98 段,独立抓取确认正文完整。但接下来的交叉验证才是今天的重头戏。

第8轮(xverify8)的目标是复核新增的两处生产代码改动和 27 条新测试。我先跑 validation/run_validation.py 的 load_data() 剔除逻辑:剔除“最后一笔成交距面板末端超过 MAX_STALE_DAYS(默认 30) 天”的币。真实数据用的是 %TEMP%\ovscripts\okx_1h_6y.json。

结果很直接:剔除 0 个币,剔除前后等权 10 币年化均为 46.1596%,零变化。也就是说,这段剔除逻辑在真实数据上是死代码。再往下看,panel_end = max(各币最后成交日) 这个定义没有抗噪性,集体断档时全体 stale=0,所有币都被保留——数据源真停更时,这个守卫会报告“一切正常”。

但真正严重的问题不在这里。load_data():110 无条件执行 ffill,14/29 个币在上市日之前被填成常数直线——SUI 填了 962 天、ARB 921、APT 766、OP 625。这直接让 screen_symbols() 的 min_history_days=365 形同虚设:APT 真实交易历史只有 1458 天,却以 2223 天通过筛选。根因是返回契约有缺陷:load_data() 不返回有效性掩码,调用方根本分不清哪段是真实成交、哪段是填充。

测试那一边更是让人冒汗。27 条新测试全部通过,但我做了注入实验:15 个错误注入里逃了 6 个。其中 I6、I7 是 charge() 成本公式和 bonferroni(),新测试套件完全不覆盖——旧测试文件有覆盖,但新的质量门声称只认这 27 条。最致命的是 test_load_data.py 自己的 docstring 写着要防“用 ffill 后的 px 做 last_valid_index() 永远返回面板末端, 剔除逻辑会静默失效”,我把这个精确回归注入进去,10 条相关测试仍然全部通过。测试结构上就抓不住这个 bug。

二、第8轮留下的教训:数字对,机制错

第8轮报告里我写“前导 ffill 污染”,还给了 SUI 962 天这些数字。结果第9轮一开始,用户直接说:数字对而机制错,ffill 只向前,填不出前导区间。我翻回去看 load_data(),确实 ffill 默认是往前的,只能填历史缺口,不可能在上市日之前填出区间——那 962 天不是 ffill 填出来的,但当时我只把数字当成了证据。

也就是说,我照着错的机制写的“修复”,很可能又是死代码。这也是为什么用户在第9轮开头反复强调:“不要因为我给了证据就采信。”数字看起来越精确,越容易把方向带偏。

三、第9轮:6处注入逃了5处,协议冻结比数字更早失效

第9轮(xverify9b)开始前,用户说自己已经查出三处同等级的错误,让我重点找第四处。这轮改动很多:新增 analysis_config.py、walkforward.py、协议 v3,strategies_regime_overlay.py 删掉一个恒等变体换成 beta 调整,run_validation.py 加了 risk_report()。

我做完注入验证,结论是:6 处注入 → 5 处逃逸,只有“恒等变体”那条测试能钉住它声称的性质。Codex 错误清单一共 7 条,最重的两条:

  1. grid_search.py:94 里 split = int(n * TRAIN_FRAC),TRAIN_FRAC 根本没定义,直接 NameError。之前说的配置统一切分是悬空引用,不是修复。“动量族被证伪”(+606%→-76%)这个结论现在根本无法用协议配置重算。
  2. 协议 v3 的冻结时间线出问题:12:06:46 首次冻结,analysis_config.py 12:12:10 才诞生,walk-forward 修 bug 后也没有重新冻结。协议里 study 的 walk-forward 数字是旧的(无闸门 -0.74% → ma50 +13.50%),实测修后是 -34.39% → -20.06%(10币)/ -42.83% → -25.83%(28币)。协议里“闸门把负超额翻正”的叙事已经不成立。

第3条同样被确证,但报告里那一段没写全,我先记着,后面还得补。

四、第10轮:重新冻结是不是在“改完答案再签名”

第10轮(xverify10)用户只发了需求,没等我出报告。这轮是只读不改,重点攻击三个问题:

  1. 反复重新冻结协议三次(amendment_v3 / v3b / v3c),是不是让“冻结”退化成“给当前答案签名”?
  2. 冷启动修复(PRE=400 / 评估区间后移)是否真的对称,还是引入了新偏差?
  3. Bonferroni 的 family_size=28 在“28 个变体 x 6 折”这种嵌套结构下取法对不对?

用户又重复了那句话:“上一轮的数字全对而机制全错,所以请不要因为我给了证据就采信。”这轮当天没跑完,我没看到最终报告,只能把问题原样记下来。

五、遗留

  • 第10轮的最终报告没写完,三个攻击点需要独立复核,尤其是“重新冻结是否消解证据价值”这个题,我自己也没想清楚。
  • 第8轮里 strategies_candidates.py 的 mtime 在 09:11 变了,内容 byte-identical,但来源是另一个进程。多进程写保护上还有 sha256 绑定的坑要补。
  • 回放时间轴那 4 个 bug 虽然当天修掉(红线符号反了、进度条尺度不同、拖动卡死、红线不更新),但跟量化这边没关系,另有记忆池记录,今天不展开。
  • 早上那篇刹车标准的稿子已经上线,不需要再管。

这天的教训就一句:数字精确不等于机制正确,拿证据之前先问自己,这个数字是怎么来的。