启动数据自愈系统BUG分析3

数据自愈系统严重 BUG — 完整因果链分析

分析日期: 2026-02-15
涉及文件: src/utils/data_healing/orchestrator.py, continuity_checker.py, config.py, src/services/realtime_kline_service_base.py

概览

orchestrator.pycontinuity_checker.pyconfig.py 以及调用方 realtime_kline_service_base.py 中发现了 3 个严重 BUG,它们协同作用,使得数据自愈系统在启动时基本无法正确工作。


BUG #1(致命):时间间隔配置不匹配 — 连续性检查完全失效

因果链

阶段 详情
输入 _run_data_healing() 调用 DataHealingOrchestrator(repair_timeframe='4h'),然后 heal_and_prepare(required_count=144)
状态变化 编排器以 4h 周期 加载 K 线和计算 zscore,每条记录间隔 240 分钟
调用路径 heal_and_prepare()_load_zscore_history()checker.check_continuity()
出错点 continuity_checker.py:31: self.expected_interval = timedelta(minutes=EXPECTED_INTERVAL_MINUTES) = 5 分钟
根因 config.py:10 硬编码 EXPECTED_INTERVAL_MINUTES = 5,而实际运行用 4h (240 分钟) 间隔的数据

具体推演

数据库中的 zscore_4h 记录时间间隔:240 分钟
连续性检查器的预期间隔:5 分钟 + 1 分钟容差 = 6 分钟

实际间隔 240min > 预期间隔 6min → 每对相邻记录之间被判定存在 ~47 个"缺失"时间点

后果链:

  1. 连续性检查永远返回 is_continuous=False — 即使数据完全连续
  2. 生成大量虚假缺失时间点(间隔 5 分钟),如 [08:05, 08:10, 08:15, ..., 12:00]
  3. 这些 5 分钟间隔的时间点被传入 RepairExecutor.repair()
  4. repair() 尝试用 timeframe='4h' 的 K 线计算这些 5 分钟点的 zscore → 窗口提取失败(因为 4h K 线无法精确对齐到 5 分钟点位)
  5. 修复返回 repaired_count=0 → 认为"修复无进展",提前退出循环
  6. 最终质量评估基于错误的 missing_count数据质量被严重低估

⚠️ CAUTION: 这意味着 即使数据库中有完美连续的 4h zscore 数据,系统也会判定为"不连续"并尝试无效修复,最终可能以 degradedfailed 状态启动。


BUG #2(严重):缺少数据新鲜度检查 — 过时数据误判为"就绪"

因果链

阶段 详情
输入 系统停机 N 小时后重启,heal_and_prepare() 开始执行
状态变化 _load_zscore_history() 从 DB 加载数据,最新记录时间为 N 小时前
调用路径 heal_and_prepare()check_continuity() → 判断 is_continuous=True(假设 BUG#1 不存在)→ L210: if is_continuous and len(records) >= required_count: break
出错点 orchestrator.py:210: 仅检查数量内部连续性,不检查数据是否新鲜
根因 没有将 最新记录时间当前时间 进行比较

具体推演

场景:系统停机了 12 小时后重启
数据库中有 144 条连续记录,最新的 kline_time = 12 小时前

检查结果:
  len(records)=144 >= required_count=144  ✅
  is_continuous=True                       ✅
  → 直接 break,返回 status='ready'        ❌

后果:

  • 系统以 ready 状态启动,使用 12 小时前的过时数据
  • 在等待新的 WebSocket 数据填充之前的这段时间,策略引擎基于严重过时的 zscore 做出交易决策
  • 可能导致错误的开仓/平仓操作

⚠️ WARNING: 这个 BUG 在长时间停机(如维护、部署)后重启时尤其危险。


BUG #3(严重):时间窗口与数据量需求严重不匹配 — 4h 数据永远加载不足

因果链

阶段 详情
输入 heal_and_prepare(required_count=144)_load_zscore_history(144)
状态变化 函数尝试在 [12h, 24h, 48h] 的时间范围内查找 144 条 zscore_4h 记录
调用路径 orchestrator.py:372: time_ranges = [12, 24, 48]
出错点 144 条 × 4 小时间隔 = 576 小时 (24 天) 的数据,但最大查询窗口只有 48 小时
根因 required_count=144 是按 5 分钟间隔设计的(144×5min = 12h),但 repair_timeframe='4h' 意味着需要 144×4h = 576h

具体推演

required_count = 144
repair_timeframe = 4h → 每条记录间隔 4 小时

理论需要数据跨度: 144 × 4h = 576h = 24天
实际最大查询窗口: 48h

48h 内最多能有: 48/4 = 12 条 4h 数据

后果链:

  1. _load_zscore_history 遍历所有时间范围 [12h, 24h, 48h]
  2. 48h 窗口内只能获取最多 12 条记录
  3. len(rows)=12 < required_count=144 → 条件 rows and len(rows) >= required_count 永远为 False
  4. 最终 records = rows(最多 12 条),永远无法满足 144 条的需求
  5. 后续连续性检查在只有 12 条记录情况下计算完整度 = 12/144 = 8.3%F 级
  6. _determine_status 返回 failed(因为 8.3% < 60%

⚠️ CAUTION: 即使数据库中有完整的 24 天 4h zscore 数据,系统也无法加载到足够数量,导致自愈必然失败


BUG 联合效应

三个 BUG 形成"死亡三角",使得数据自愈系统在某些情况下完全失效:

graph TD
    A["系统启动<br>repair_timeframe='4h'<br>required_count=144"] --> B["_load_zscore_history()"]
    B --> C{"48h 窗口内<br>能找到 ≥144 条?"}
    C -->|"不可能(最多12条)"| D["BUG #3<br>数据量永远不足"]
    D --> E["传入 ≤12 条记录"]
    E --> F["check_continuity()"]
    F --> G{"每对记录间隔<br>240min vs 5min+1min?"}
    G -->|"240 >> 6"| H["BUG #1<br>生成大量虚假缺失点"]
    H --> I["repair() 尝试按5min<br>时间点修复4h数据"]
    I --> J["修复失败<br>repaired_count=0"]
    J --> K["质量评估: F级<br>completeness ≈ 8%"]
    K --> L["status = 'failed'"]

    style D fill:#ff6b6b,color:#fff
    style H fill:#ff6b6b,color:#fff
    style L fill:#ff6b6b,color:#fff

即使假设修复了 BUG #1 和 #3,BUG #2 仍然存在:

graph TD
    A["假设 BUG#1 #3 已修复<br>数据加载正确"] --> B{"数据内部连续?"}
    B -->|"Yes"| C{"数据量 ≥ 144?"}
    C -->|"Yes"| D["直接 break ✅"]
    D --> E["返回 status='ready'"]
    E --> F{"最新记录是否新鲜?"}
    F -->|"未检查!"| G["BUG #2<br>可能使用 N 小时前的旧数据"]
    G --> H["策略引擎基于过时数据交易"]

    style G fill:#ff6b6b,color:#fff
    style H fill:#ff6b6b,color:#fff

修复建议

BUG #1 修复方向

EXPECTED_INTERVAL_MINUTES 不应硬编码为 5,应该从 repair_timeframe 动态推导:

# config.py 中删除硬编码的 EXPECTED_INTERVAL_MINUTES = 5
# 改为在 orchestrator 初始化时动态计算
TIMEFRAME_TO_MINUTES = {'5m': 5, '1h': 60, '4h': 240}
self.expected_interval_minutes = TIMEFRAME_TO_MINUTES[repair_timeframe]

BUG #2 修复方向

heal_and_prepare() 的连续性判断后增加新鲜度检查:

if is_continuous and len(records) >= required_count:
    # 新增:检查数据是否新鲜
    latest_time = max(r['kline_time'] for r in records)
    now = self._get_db_now()
    staleness = (now - latest_time).total_seconds() / 60
    if staleness > expected_interval_minutes * 2:
        # 数据过时,生成缺失时间点并继续修复
        ...

BUG #3 修复方向

_load_zscore_history 的时间窗口应根据 required_count 和实际间隔动态计算:

# 根据实际时间间隔计算所需时间跨度
needed_hours = (required_count * self.expected_interval_minutes) / 60
# 加 20% 余量
time_ranges = [needed_hours * 1.2]

Read more

跑步的技巧(滚动落地)

“滚动落地(rolling contact / rolling foot strike)”不是一种教条式的“脚法”,而是一种 让冲击沿着整只脚、整条后链逐级传递的落地机制。 它的核心不是“你先用哪儿着地”,而是: 你的脚落地之后,冲击是不是像轮子一样滚过去,而不是像锤子一样砸下去。 这就是滚动落地的本质。 一、什么叫“滚动落地”? 你可以把它理解成两种完全不同的落地方式: 1. 砸地(撞击式) 脚像锤子一样拍到地上: * 要么后跟先砸 * 要么前掌先戳 * 冲击集中在一个点 * 一个结构瞬间吃掉大部分载荷 结果就是: * 后跟砸 → 膝盖难受 * 前掌戳 → 前脚掌磨烂 * 都不是长跑友好模式 这叫 撞击式着地(impact strike)。 2. 滚地(滚动式) 脚像轮胎一样“滚”过地面: * 不是某一点硬砸 * 而是外侧中足先轻触 * 再向前滚到前掌 * 最后从大脚趾蹬离

By SHI XIAOLONG

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