记录一次 OOS tracker 双口径深夜扫描与 HL 只读对账(2026-09-29)
记录一次 OOS tracker 双口径深夜扫描与 HL 只读对账(2026-09-29)
A1 判 HALT,A2 判 NO_ACTION,同一天、同一套 factor_ic,结论完全相反——但并不是谁错了,而是它俩卡的门完全不一样。
2026-09-29 这天,从凌晨 00:40 到早上 05:56,我基本住在 factor_ic OOS tracker 的扫描输出里。两个 arm 轮流跑,中间还插了一次 Hyperliquid 的只读对账。最后没毕业、没阻断,但有几个数字和判断值得记下来,尤其是那个 stdout 的 maxDD 百分号 bug 和 A2 样本池六轮一动不动。
一、A2 那边:n=19 冻结六轮,NO_ACTION 一路到底
A2 的扫描从凌晨 00:40 的第 75 轮开始,我在终端里守着看它输出表。Gate1 样本数 19,阈值是 ≥30(A2 更严),直接 ❌;Gate2 binomial p 0.6476(win 8/19,胜率 42.1%),远大于 0.05,❌;Gate3 年化 Sharpe −7.477,❌;Gate4 maxDD 写库是 4.2782,按真实值 ≤30% 是 ✅,但 stdout 打印 427.8%,后面再说这个 bug;regime 上升率 **66.7%**(n=18),≥50% ✅ OK。
决策路径很干脆:regime=OK → Gate1 fail(19<30) → NO_ACTION。没有 HALT,没有 MID,没有 BLOCK,没有 GRADUATE,也没写 pending_log(库内没那表)。
我当时以为是偶发,结果接下来每小时跑一轮,结果像复制粘贴一样:
- 01:43 第 76 轮:n=19,同样 NO_ACTION
- 02:46 第 77 轮:n=19,同样 NO_ACTION
- 03:48 第 78 轮:n=19,同样 NO_ACTION
- 04:50 第 79 轮:n=19,同样 NO_ACTION
- 05:53 第 80 轮:n=19,同样 NO_ACTION
加上前一晚 23:38 的第 74 轮,n=19 连续冻结了六轮,所有指标逐位复刻,间隔里没有一次新的 LONG 平仓进样本。这基本可以判断 A2 的样本池卡住了,不是扫描逻辑出问题,是根本没有新事件喂进来。后面几轮我甚至不用细看表,扫一眼 n=19 就知道又是 NO_ACTION。
二、A1 那边:regime 回落率 37.0%,直接 HALT
同一个 factor_ic 的 A1 arm(SHORT 视角)跑出来的是另一番景象。当天我跑了至少三次单次扫描,时间点大概在 01:45、04:53、05:56,结果完全一致:
| Gate | 实测 | 阈值 | 判定 |
|---|---|---|---|
| Gate1 样本数 n | 31 | ≥20 | ✅ |
| Gate2 binomial p | 0.7201(win 14/31) | <0.05 | ❌ 不显著 |
| Gate3 年化 Sharpe | −2.732(×√504) | ≥1.0 | ❌ |
| Gate4 maxDD | stdout 打印 320.9%,写库真值 3.2089 | ≤30% | ❌(按打印值) |
| regime 回落率 | 37.0%(n=27) | ≥50% | ❌ → HALT |
A1 的 Gate1 样本数是够的(31 ≥ 20),但卡在 regime 上:回落率 37.0%,低于 50%,flag 直接给 HALT,判定 on_halt,也就是 factor_ic 信号源失效。这里要特别强调一句:当天网络正常,主网 K 线回落率是联网实测出来的,regime 不是 UNKNOWN 降级,是真实计算值。也就是说 A1 的问题不是样本不够,是因子本身的 forward return 持续低于随机,信号源挂了。
三、一个 stdout 的百分号 bug,差点把人带偏
当天最值得记的坑是 maxDD 的打印。A2 的 Gate4 在 stdout 里显示 **427.8%**,但写进库的是 4.2782;A1 打印 **320.9%**,写库是 3.2089。这俩都对得上:就是把真值乘以 100 打印出来了,典型格式化 bug。
如果只盯着终端屏幕,A2 的 Gate4 会被误判成 427.8% 远超 30% 阈值,直接 fail,但实际按写库真值 4.2782% 是 pass 的;A1 那边真值 3.2089% 同样是 pass,但它决策本来就被 regime HALT 拦住了,所以这个 bug 不影响最终动作。结论是:凡是看这个 tracker 的 stdout,Gate4 那一行必须回库核对,不能直接信打印值。 脚本里也没人去修这个显示,估计是知道但优先级排后。
四、中间插的 HL 只读对账:无 ghost,只有 3 个 orphan
大概在 02:46 到 03:48 之间,我还按用户指令去 Hyperliquid 做了一次只读对账巡检。跑之前先清空系统代理变量:
1 | HTTPS_PROXY="" HTTP_PROXY="" http_proxy="" https_proxy="" ALL_PROXY="" all_proxy="" |
然后执行 C:\ProgramData\anaconda3\python.exe -u hl_reconcile.py,默认 dry-run,不带 --apply。结果很干净:
- Ghost 仓位:0 ——
hl_smart_money.db(3 OPEN) /hl_testnet.db(0 OPEN) 全部链上存在,含方向反检查。 - Orphan:3,都在
hl_testnet.db,分别是 SOL/BTC/ETH,链上有但 DB 无,脚本设计只告警不修复。 - OPEN 数量守恒:两个库各自账户等式逐笔重建残差历史≈0。
- 两库自报 account 行
initial/current≠10000是已知记账损坏,不属于 ghost/orphan。
结论是无 ghost,不建议 --apply,那三个 orphan 按设计也不自动修,留着人工确认就行。
五、执行细节:代理、sandbox 和零副作用
当天我反复确认脚本的边界,因为白天有几次 Bash 调用需要带 dangerouslyDisableSandbox:true,否则 regime 拉不到主网 K 线。网络正常时,regime 都能得到真实值,不是 UNKNOWN。
副作用方面,每次跑完我都会做 tick isolation 和 sample pool 的 SQL 交叉核对。A2 只写 factor_ic_oos.db 的 oos_ticks 和 _prereg 下的 alerts 日志,不写 pending_log(因为库内没那张表);A1 写 factor_ic_oos.db、alerts、halt_log,新 tick id=487(SHORT)落库。两条 tracker 都没碰生产策略文件、没重启进程、没触发任何毕业/阻断动作,那些只有 factor_ic_oos_apply.py --confirm 才可能做。 这点在每次报告里我都确认过。
六、遗留与下一步
凌晨六小时扫下来的状态暂时定格,但有三件事还挂着:
- A2 的 n=19 还在冻结。需要查为什么 LONG 平仓样本不再进来,是行情本身没 trigger,还是上游数据源有延迟。
- A1 的 regime HALT 需要持续观察。37.0% 的回落率离 50% 阈值还差一大截,factor_ic 在 SHORT 视角的信号源短期内大概率起不来。
- HL 的 3 个 orphan(SOL/BTC/ETH)没有自动修复。虽然只告警、不写库,但得人工确认是不是应该补录,还是测试网正常残留。
- maxDD 的 stdout 乘 100 bug 还没修,不过写库值是对的,目前不影响决策,先记着别信打印值就行。