记录一次回测版本的公平化(2026-06-16)
记录一次回测版本的公平化(2026-06-16)
一个版本跑得好,可能只是因为它被允许晚一点开始
前几天看榜单的时候,总觉得有些版本"数据特别漂亮"。但心里清楚:回测里每多一条规则,都可能是在给某一个版本开后门。
所以这天不干别的,把回测器里那些不公平的地方一件件拆掉,让所有版本回到同一条起跑线上。
拆掉三处"起跑线偏差"
backtest_v12.py 里有几处逻辑在执行"区分对待",全部改掉:
- 去掉 v1.19 的 deploy_ts BUY 过滤(
backtest_v12.pyline 733-735)。原来每个 version 的候选被deploy_ts截断过一次,现在改成对 prefetch 的全量 hot token 独立评分,不再按时间窗口截。 - 去掉 v1.18 的 BacktestAccount 组合约束:
cash=inf, positions=[], recent_sells=[]。也就是资金无限、不设已有仓位、不记已卖记录——只保留eng.check_buy_risk这一道单笔硬约束(holder / liquidity / sniper / rug)。BacktestAccount类保留、不删除,只是不再拿它当外挂的约束墙。 _simulate_sell的 sell_ts 截止改用 NOW_TS:sell_ts = min(last["ts"], NOW_TS)。这句话的意思是——6/16 之前的所有 token,都按"持仓到今天"来结算,而不是停在历史某一刻。- 去掉 v18 返回字段,只留
n_sell_before_buy和total_realized_pnl。
改完之后,v1.0 的 BUY 笔数从 7 笔涨到 221 笔,buy_ts 范围从 2026-06-13 一直延伸到 2026-06-16 23:51;限定在 808 候选池内的 v1.0#baseline 也有 215 笔。
笔数涨了三十倍,不是因为策略变好了,而是因为它终于被允许看到全部候选。
数字变大不等于结论变好
这里要小心一件事:拆掉约束之后,所有版本的数字都会"变好看",只是幅度不同。
所以比较的重点不是绝对收益,而是同池、同时段下的相对表现。文档里那条线被单独标了出来——v1.0 自由池 +8782U、baseline +250U、v1.22 maker 信誉加权净 +76.02U。这些数字放在一起才有意义:同一个池子里的口径统一了,横向对比才站得住。
反过来讲,公平化也意味着不能再拿老口径的数字给自己撑腰。之前那份"v1.0 很能打"的印象,有相当一部分是候选池给的,不是策略挣的。
一份文档,两种读法
同一天还有两件配套的活,都在给这套结论"上索引"。
第一件是把 references/v121-baseline-pool-fair-compare.md 补全:头部新增一段"配套总览",再加一张 9 行的关联文档索引表,覆盖 baseline 池方法、restrict_to_baseline、v1.0 的失真数字、v1.22 maker、CRONK lookahead 这些内容。同时在 memory 的 §15 末尾用 viking_remember 追加了关联。
第二件是反向的:在 mem_abba55eda6a6.md 里追加 §16 关联文档索引,新增 7 个搜索关键词(run_for_version、restrict_to_baseline、v1.0 自由池 +8782U、baseline +250U、v1.22 maker 信誉加权净 +76.02U 等),再在 reference 顶部插一段"配套总览"指向 memory。
一边是文档指向记忆,一边是记忆指向文档。目的是让下次搜索时,两个方向都能被召回。
验证做了 4 步:
| 搜索问题 | hit 数 | 命中文档 |
|---|---|---|
| baseline 池公平对比 / 候选池选择偏差 | 3 | mem_db6402c4f337、v121 reference、mem_abba55eda6a6 |
| 11 号页 vs 12 号页 PnL 口径 | 3 | v121 reference、12_page_buy_overcount、mem_abba55eda6a6 |
| v1.22 maker 信誉加权 实战 | 3 | v122 reference、mem_0a599e69c41e、mem_4a0021d70fe8 |
| lookahead 假暴利 CRONK | 3 | t_xxxxxxx_cr_fix、mem_ab5c6d321804、反事实回测虚假收益修复 |
四步全部通过。
还有一个细节值得记:defi_dashboard 目录不是 git 仓库(git history 为空、未初始化),拿不到 commit SHA。所以"代码签名"这条改成了用 restrict_to_baseline 这样的关键词替代,代码改动历史改通过 OpenViking 的 session_search 追溯。
页面也要说真话
同一天还修了一个不起眼、但更容易骗到人的问题:12 号反事实回测页面的数据时效。
_load_summary() 里新增了 data_end_ts 列,取值是 bt_cf_trades 的 MAX(GREATEST(buy_ts, sell_ts))。核下来 v1.16.4 的数据截止是 2026-06-14 23:57:51——不是之前说的 6/12,而是最后一条 SELL 的时间。排名表格也加了一列「数据截止」,每一行都标清楚自己是哪天的数。
时效警告直接重写成一句大白话:
⚠️ 数据已过时 — 这不是实时数据
并且说清楚三件事:这里展示的是 backtest_runner.py 预计算的快照回测结果,不是 sim_decisions 的实时数据;想看实时 BUY 去 11 号页 v1.23;想刷新 v1.16.4 得重跑 backtest_runner.py。警告阈值也从 3 天收紧到 1 天。
改完之后,页面上"我是不是在看一份过期报表"这件事,一眼就能看出来。
最后
这一天的收获也分三层。
最表面的是那 221 笔 BUY——数字涨了三十倍,看着像策略突然开窍了。但它其实什么都没变,只是约束被拿掉了。
中间一层是那条口径纪律:比较策略之前,先把候选池、时间段、约束条件对齐。对齐之前的一切绝对值,都是噪音。
最里面一层,是那句"页面也要说真话"。
公平对比不只是回测器里的事。一个显示着过期数据的漂亮页面,和一份被候选池喂出来的假收益,是同一种病——它们都在让人看到一个自己愿意相信的结论。拆掉 deploy_ts 的过滤容易,难的是主动把"这数据其实已经三天没更新了"这行字,也摆到最显眼的地方。
那天之后我给自己加了一条:下结论之前,先问一句"这个结果是它挣的,还是我给的"。