数据自愈BUG Cursor 2

数据自愈模块启动期 BUG 分析报告(Cursor 2)

基于 realtime_kline_service 启动日志与代码阅读的根因归纳。


1. 「尝试更大范围」未真正扩大窗口

现象(日志)

  • 出现「数据不足: 1 条(24h 窗口),尝试更大范围」后,没有再出现「加载历史数据: N 条(48h/72h 窗口)」。
  • 直接进入「第 1 轮检查...」,最终「无法确定修复目标」。

根因(代码)

orchestrator.py_load_zscore_history 的时间窗口是写死三档

needed_hours = (required_count * self.interval_minutes) / 60
time_ranges_hours = [
    math.ceil(needed_hours),           # 12
    math.ceil(needed_hours * 1.3),     # 16
    math.ceil(needed_hours * 2),       # 24
]
time_ranges_hours = sorted(set(time_ranges_hours))  # [12, 16, 24]

对 4h、要 3 条时,最大只尝试 24h。24h 仍只有 1 条时循环结束,没有 48h/72h 等更大窗口,「尝试更大范围」只是日志文案,没有对应逻辑。

涉及文件src/utils/data_healing/orchestrator.py(约 L417–425)。


2. 仅 1 条数据时不生成任何修复目标

现象(日志)

SEI、XAI、IOTA、CAKE、SUPER、FARTCOIN(vs BTC)、PEOPLE、ATOM 等出现:

  • 「数量不足(1/3), 已过时1284min」
  • 「无法确定修复目标,终止修复」

根因(代码)

  • _diagnose 里,shortfall_targets 只在「连续且无缺口且数量不足」时生成:
# 3. 数量不足检查
shortfall_targets: List[datetime] = []
if is_continuous and not gap_times and len(records) < required_count:
    shortfall_targets = self._generate_shortfall_targets(records, required_count)
  • continuity_checker少于 2 条 时直接返回 is_continuous=False
if len(records) < 2:
    logger.debug(f"数据点不足({len(records)}条),无法判断连续性")
    return False, [], completeness_pct

因此:当只有 1 条时,is_continuous 恒为 False,shortfall_targets 永远为空_merge_repair_targets 得到空列表 → 打印「无法确定修复目标」。
即:恰恰在「只有 1 条、最需要补数」的场景下,自愈逻辑不生成任何补数目标。

涉及文件src/utils/data_healing/orchestrator.py(L235–238)、src/utils/data_healing/continuity_checker.py(L55–57)。


3. 加载「时间窗」与修复「向更早补」语义不一致

(与 docs/数据自愈BUG Cursor 1.md 中的根因一致。)

  • 加载kline_time >= NOW() - N 小时,只看「最近 N 小时内」的数据。
  • 数量不足时的修复:在当前最早一根之前向更早补点(_generate_shortfall_targetsearliest_time - i * interval)。

结果:补进去的 bar 若早于当前 12h/16h/24h 窗,重载时仍用同一套时间窗,看不到刚补的那条,出现「修了也白修」、多轮修复无效。
当前日志里多数在「只有 1 条」就终止,尚未进入多轮修复;一旦修复 1 条场景,仍会撞上「补了重载看不见」的问题。

涉及文件src/utils/data_healing/orchestrator.py_load_zscore_history_generate_shortfall_targets)。


4. 同一标的在不同配对下表现不一致

现象(日志)

  • FARTCOIN vs kNEIRO:12h 窗即拿到 3 条,自愈 ready。
  • FARTCOIN vs BTC:24h 窗仍只有 1 条,自愈 failed。

说明

自愈是按 (symbol, base_symbol)analysis_results 里查的,每个配对独立。有的配对历史上只写过很少的 zscore(例如只有 1 根 4h),所以即使用 24h 窗也只有 1 条。
这不是实现错误,但说明:「数据不足」往往对应该配对历史数据本身就极少;若 1 条时还不生成修复目标(见第 2 点),这些配对就会一直 failed。


5. 「已过时 1284/1285min」与单条数据

「已过时1284min」约 21.4 小时,说明那唯一一条是约一天前的 4h bar。
对 4h 来说,新鲜度阈值是 interval_minutes * FRESHNESS_MULTIPLIER(约 600 分钟),因此会被判为过时。

问题在于:过时 + 只有 1 条 时,既没有生成 shortfall_targets(因为 is_continuous=False),stale_targets 又只在 len(records) >= required_count 时生成,因此既不会按「数量不足」补更早的 bar,也不会按「过时」补更新的 bar,结果就是直接放弃。

涉及逻辑_diagnose 中 shortfall 与 stale 的生成条件。


小结(BUG 清单)

类型 表现 根因
虚假的「尝试更大范围」 24h 仍不足就结束,没有 48h/72h time_ranges_hours 最大只到 24h,没有更大窗口
1 条数据不修 数量不足(1/3) → 无法确定修复目标 1 条时 is_continuous=False,shortfall 从不生成
修了也看不见 多轮修复后条数仍不增加 加载「时间窗内」与修复「向更早扩展」不一致,重载窗不变
过时 + 不足 3 条 已过时但无修复目标 stale 只在 record_count≥required 时生成,1 条时 shortfall 也不生成

建议修复方向(与 Cursor 1 文档一致)

  1. 1 条/0 条也生成修复目标
    len(records) < required_count 时,即使 is_continuous=False(例如只有 1 条),也生成 shortfall_targets(从当前最早一根向前补足到 required_count),或对 0 条沿用 full_timeline。

  2. 真正扩大窗口或改为「最近 N 条」

    • 要么在 _load_zscore_history 中增加更大窗口(如 48h、72h、168h);
    • 要么改为「按 ORDER BY kline_time DESC LIMIT required_count(可加最大时间范围)」的「最近 N 条」语义(推荐,见 Cursor 1 方案 A)。
  3. 修复后重载语义与「补更早」一致
    若保留时间窗,则修复后重载至少应扩大窗口以包含本次 shortfall 目标时间;或采用「最近 N 条」使补写条在重载时可见。

  4. 可选:缩小启动自愈范围 / 提前终止无效轮次
    见 Cursor 1 方案 C,减少启动耗时与无效 3 轮。


涉及文件一览

  • src/utils/data_healing/orchestrator.py:加载、诊断、shortfall/stale 生成、重载
  • src/utils/data_healing/continuity_checker.pylen(records)<2 时返回 is_continuous=False
  • src/utils/data_healing/config.pyLOAD_TIME_MARGIN
  • 参考:docs/数据自愈BUG Cursor 1.md(完整因果链与方案 A/B/C)

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