IMM Beta 导致 Adaptive Z 虚假入场信号 — BUG 分析报告

IMM Beta 导致 Adaptive Z 虚假入场信号 — BUG 分析报告

日期: 2026-03-19
严重程度: P0(已造成实际错误交易)
影响范围: 所有使用 IMM Kalman β 的交易配对
状态: 待修复


1. 事件摘要

2026-03-19 14:25:05,IMM 改造后的新代码对 ASTER/USDC:USDC|BTC/USDC:USDC 配对发出了错误的 long 入场信号,导致以 $0.68924 买入 29.0 ASTER。该信号在老代码(无 IMM 改造)中不存在。仓位于 33 秒后被均值回归退场机制平仓,亏损 $0.01。

1.1 对比日志(同一时刻 14:25:05,同一 pair,同一 z4h)

指标 新代码(IMM) 老代码(无 IMM)
z4h -1.5430 -1.5430
ema 1.4326 -1.1029
raw_std 1.0235 1.8705
reg_std 1.0666 1.8944
adaptive_z -2.7898 (139%thresh) -0.2323 (12%thresh)
触发入场 ✅ 是(错误) ❌ 否(正确)

1.2 关键观察

  • z4h 相同:当前时刻的 z4h 计算结果在两个版本中一致(-1.5430)
  • ema 差距巨大:1.4326 vs -1.1029,差距 2.54(方向都反了,一正一负)
  • std 差距巨大:1.0235 vs 1.8705,新代码的 std 仅为老代码的 55%
  • adaptive_z 公式:az = (z4h - ema) / reg_std
    • 新代码:(-1.5430 - 1.4326) / 1.0666 = -2.7898 → 触发入场
    • 老代码:(-1.5430 - (-1.1029)) / 1.8944 = -0.2323 → 不触发

1.3 事件时间线

14:25:05  新代码 tick(candle) → az=-2.7898 (139%thresh) → 入场信号触发
14:25:05  新代码 买入 ASTER 29.0 @ $0.68924
14:25:07  订单成交
14:25:29  配对关系缓存重新加载(可能是服务组件重启)
14:25:35  仓位从 DB 恢复同步(entry_adaptive_z=-2.7898)
14:25:38  退场信号触发(az=-0.2333 已远超回归目标 -0.6974)
14:25:40  平仓成交 @ $0.68926,PnL=-$0.01,持仓 33 秒

2. 根因分析

2.1 BUG 定位

文件: src/utils/analysis/analysis_core.py
行号: 566-568(calculate_cointegration_params_dual_window 函数)

_n_updates = kalman_state_out.get('n_updates', 0) if kalman_state_out else 0
_kalman_warm = kalman_result is not None and _n_updates >= IMM_KALMAN_WARMUP
beta_for_spread = kalman_result['beta'] if _kalman_warm else beta_ols  # ← BUG

IMM Kalman 热身完成后(n_updates >= 20),z4h 的 spread 计算从 OLS β 切换为 Kalman β。这一改动破坏了 z4h 时间序列的平稳性。

2.2 问题传播链

┌─────────────────────────────────────────────────────────────────────┐
│  IMM Kalman β 时变演化(如 0.9 → 0.5 → 0.64)                       │
│       ↓                                                             │
│  每次 tick 用当前 Kalman β 计算 spread → 计算 z4h                    │
│       ↓                                                             │
│  z4h 时间序列引入人为水平漂移(β 变化 → spread 结构变化 → z4h 偏移)  │
│       ↓                                                             │
│  z4h 写入 analysis_results 表(DB 数据被"污染")                     │
│       ↓                                                             │
│  strategy prime_buffer 从 DB 加载历史 z4h → EMA/STD 反映污染值       │
│       ↓                                                             │
│  实时 z4h(当前 Kalman β 恰好≈OLS β)回到"正常"值                    │
│       ↓                                                             │
│  adaptive_z = (正常z4h - 漂移ema) / 偏小std → 虚假大信号              │
└─────────────────────────────────────────────────────────────────────┘

2.3 为什么 EMA 能漂移 2.5?

策略引擎的 EMA 和 STD 跟踪的是 z4h 的历史值(存储在 SymbolBaseline.std_window 中,maxlen=72)。这些历史值有两个来源:

  1. prime_buffer:服务启动时从 analysis_results 表加载最近 144 条 z4h
  2. process_tick:实时运行时每根新 K 线追加 z4h

两个来源的 z4h 都使用了 Kalman β 计算,因此整个 buffer 都被污染。

单次 z4h 偏移量级估算

Δz4h ≈ (β_ols - β_kalman) × (log_base[-1] - mean(log_base)) / std_spread

典型值(ASTER/BTC):
- Δβ = 0.26(OLS β=0.90, Kalman β=0.64)
- BTC 趋势 3-5%: |log_base[-1] - mean(log_base)| ≈ 0.015-0.025
- spread_std ≈ 0.02-0.08

→ 单次 Δz4h ≈ 0.05 ~ 0.5(取决于行情)

单次偏移 0.05~0.5 看似不大,但:

  1. Kalman β 是时变的:在不同历史时段,β 偏差可能更极端(P_β=0.098 → β 的 95% CI 跨度 ±0.62)
  2. 偏移方向可持续:当 BTC 处于单边趋势时,Δz4h 方向一致,累积效应放大
  3. DB 历史数据跨度长:prime_buffer 加载 30 天内的数据,覆盖 Kalman β 演化全程
  4. EMA 收敛到偏移均值:如果历史 z4h 平均值偏移 +2.5,EMA 就会收敛到 +2.5

2.4 为什么当前 z4h 在两个版本中相同?

z4h 在 30 点窗口内做归一化:

z4h = (spread[-1] - mean(spread[:-1])) / std(spread[:-1])

所有 30 个点使用同一个 β(当前 Kalman β=0.6438)。归一化会吸收 β 的大部分影响——改变 β 相当于对 spread 做一个近似线性变换,mean/std 会相应调整,z4h 变化不大。

但不同时间点的 z4h 使用不同的 β(Kalman β 时变),这导致 z4h 序列不可比较。策略层的 EMA/STD 假设 z4h 是平稳序列,这一假设被破坏。


3. 影响范围

3.1 直接影响

  • 所有 Kalman 热身完成(n_updates >= 20)的交易配对,其 z4h 均受 Kalman β 影响
  • z4h 写入 analysis_results 表,DB 中 IMM 部署后的 z4h 数据均被污染
  • strategy 的 prime_buffer 加载这些数据,EMA/STD 偏移

3.2 信号影响模式

场景 adaptive_z 表现 后果
Kalman β 曾大幅偏离 → 近期回归 EMA 滞后于实际 z4h → az 虚假放大 虚假入场信号
Kalman β 稳定但与 OLS β 不同 z4h 持续偏移但 EMA 跟上 → az 正常 信号正常但回归目标偏移
Kalman β 突然跳变 z4h 突变 → az 尖峰 突发虚假信号或错失真信号

3.3 受影响代码路径

calculate_cointegration_params_dual_window()  ← β 替换点
  → spread 使用 Kalman β
    → calculate_zscore_ols() 使用该 spread
      → z4h 被 Kalman β 影响
        → 写入 analysis_results 表
        → 传给 strategy.process_tick()
          → 更新 EMA/STD buffer
            → 计算 adaptive_z
              → 产生虚假入场/退场信号

4. 修复方案

4.1 核心修复:z4h spread 始终使用 OLS β

文件: src/utils/analysis/analysis_core.py

将第 568-569 行:

beta_for_spread  = kalman_result['beta']  if _kalman_warm else beta_ols
alpha_for_spread = kalman_result['alpha'] if (_kalman_warm and use_alpha) else alpha

改为:

beta_for_spread  = beta_ols
alpha_for_spread = alpha

原则

  • z4h/spread → 始终用 OLS β(保证时间序列平稳性,供策略层 EMA/STD 消费)
  • Kalman β → 仅作为 kalman_beta 字段输出,供交易执行层用作对冲比率

4.2 修复后的过渡期处理

修复代码后,DB 中仍存有被污染的历史 z4h 数据。strategy 的 prime_buffer 会加载这些数据。

自然恢复:buffer maxlen=72,每 5 分钟更新一次,约 6 小时后 buffer 完全被新数据替换,EMA 自动修正。

加速恢复(可选):

  • 重启服务后手动清理 analysis_results 中 IMM 部署后的 z4h 数据
  • 或临时增大 min_required(prime_buffer 最小就绪条件),强制等更多正确数据积累后再产生信号

4.3 关于保留 Kalman β 用于 spread 的讨论

原设计意图是用 Kalman β(更精确的时变对冲比率)替代 OLS β 来构建 spread,提升 z4h 对均值回归的灵敏度。这一思路在单次 z4h 计算内是合理的——Kalman β 可能更好地捕捉当前市场结构。

但问题在于策略层的 adaptive z-score 模型假设 z4h 是平稳序列:

adaptive_z = (z4h - EMA(z4h)) / STD(z4h)

Kalman β 的时变性破坏了 z4h 的平稳性。每个历史 z4h 用不同的 β 计算,它们不具备统计可比性。EMA/STD 在不可比序列上计算的结果没有意义。

如果未来想让 z4h 使用时变 β,需要同时修改策略层:

  • 对 β 变化做 z4h 校准(补偿 β 变化引起的 z4h 水平漂移)
  • 或在 β 发生显著变化时重置 EMA/STD buffer
  • 或使用 β-invariant 的 z4h 定义

在当前策略架构下,z4h 必须使用稳定的 β


5. 验证方法

5.1 修复前验证

对比同一 pair 在修复前后的 z4h / ema / adaptive_z:

# 在 calculate_cointegration_params_dual_window 中临时加日志:
logger.info(
    f"[β对比] {pair_name} | "
    f"OLS β={beta_ols:.4f} Kalman β={kalman_result['beta']:.4f} "
    f"Δ={kalman_result['beta'] - beta_ols:+.4f} | "
    f"z4h_OLS={z4h_with_ols:.4f} z4h_Kalman={z4h_with_kalman:.4f} "
    f"Δz4h={z4h_with_kalman - z4h_with_ols:+.4f}"
)

5.2 修复后验证

  1. 部署修复代码后,观察 ASTER/BTC 配对的 tick 日志
  2. 确认 ema 逐步从 1.4326 向负值回归(与老代码趋同)
  3. 确认不再产生类似的虚假入场信号
  4. 6 小时后确认 EMA 已完全修正

5.3 回归测试

确认以下功能不受影响:

  • Kalman β 仍正常输出(kalman_beta 字段)
  • Gate3 体制检测(kalman_regime_score)仍正常工作
  • Kalman state 持久化/恢复仍正常
  • 对冲比率(hedge ratio)仍使用 Kalman β

6. 经验教训

  1. 信号输入的平稳性是策略层的前提:任何改变 z4h 计算方式的改动,必须验证 z4h 时间序列的平稳性不被破坏
  2. 时变参数不能直接用于构建被统计模型消费的时序:Kalman β 用于 z4h → EMA → adaptive_z 的链条中,时变性在每个环节都被放大
  3. DB 写入的 z4h 有长期记忆:prime_buffer 从 DB 加载历史数据,任何计算方式的变更都会在 DB 中留下不一致的数据
  4. 双版本对比是发现此类 BUG 的有效手段:本次 BUG 正是通过同时运行新旧代码对比发现的

Read more

AMI的优越性

世界模型(World Models)的具体例子 如下,我按类型分类,便于理解。每类都附带实际实现、演示效果和应用场景。 1. Yann LeCun / Meta 的 JEPA 系列(最直接对应“世界模型”概念) 这些是 LeCun 主张的非生成式抽象预测世界模型代表。 * I-JEPA(Image JEPA,2023) 输入一张图像,模型把不同区域(context 和 target)编码成抽象表示,然后预测 target 的表示(不在像素级别重建)。 例子:给定一张遮挡了部分物体的图片,模型能预测“被遮挡物体的大致位置和属性”,构建对物体持久性和空间关系的理解。 这是一个“原始世界模型”,能学习物理常识(如物体不会凭空消失)。 * V-JEPA / V-JEPA 2(Video JEPA,

By SHI XIAOLONG

什么是:“世界模型(World Models)”

世界模型(World Models) 是人工智能领域的一个核心概念,尤其在 Yann LeCun 等研究者推动的下一代 AI 架构中占据中心位置。它指的是 AI 系统在内部构建的对现实世界的抽象模拟或内部表示,让机器能够像人类或动物一样“理解”物理世界、预测未来、规划行动。 简单比喻 想象你闭上眼睛也能“看到”房间里的物体会如何移动、碰撞或掉落——这就是你大脑里的世界模型。AI 的世界模型就是类似的“数字孪生”(digital twin)或“内部模拟器”:它不是简单记住数据,而是学习世界的动态、因果关系和物理直觉(如重力、物体持久性、遮挡、因果等)。 为什么需要世界模型? 当前主流的大型语言模型(LLM) 擅长处理文本(统计模式预测),但存在根本局限: * 缺乏对物理世界的真正理解 → 容易“幻觉”、无法可靠规划。 * 样本效率低 → 人类/

By SHI XIAOLONG

K线周期可配置化设计方案

K线周期可配置化设计方案 1. 背景与目标 当前 Beta 套利策略的 K 线周期硬编码为 "1h",分散在多个文件中。需要: 1. 将 K 线周期从 1h 改为 2h 2. 提取为环境变量 BETA_ARB_KLINE_INTERVAL,使其可在 .env 中配置 2. 影响范围分析 2.1 需要修改的文件(共 6 个) 文件 硬编码位置 修改内容 src/trading/config.py BetaArbConfig dataclass 新增 kline_interval 字段,

By SHI XIAOLONG

对于空间环境、“信息/逻辑”(比如代码、结构、表达)秩序追求的心理特征分析

一、为什么是“空间 + 信息”同时强化? 因为你当年面对的是“双重失控”: 1️⃣ 外部世界是脏乱 + 失序的 * 空间被污染 * 行为无边界 * 基本生活秩序崩塌 👉 所以你现在会强烈要求: * 桌面干净 * 房间有序 * 物品可控 这是在修复:“物理世界必须是可控的” 2️⃣ 人的行为和逻辑也是混乱的 * 没有规则 * 没有底线 * 没有理性 👉 所以你现在会特别在意: * 表达是否清晰 * 逻辑是否自洽 * 结构是否优雅 * 代码是否干净 这是在修复:“认知世界必须是合理的” 二、你其实构建了一个“高纯度系统” 你现在的偏好,本质上是: 👉 低噪音 + 高结构 + 强控制感 具体表现就是: * 空间:极简、整洁、可预测 * 信息:清晰、压缩、无冗余 这类人有一个很明显的优势: 👉 处理复杂问题时,

By SHI XIAOLONG