订单跟踪bug19

订单跟踪系统严重 Bug 分析

一、订单跟踪流程概览

sequenceDiagram
    participant Exec as Executor
    participant Mgr as WebSocketOrderManager
    participant WS as WebSocket/K线服务
    participant API as HTTP API

    Exec->>API: 下限价单
    API-->>Exec: 返回 oid
    Exec->>Mgr: track_order(oid) + wait_for_order(tracking)
    Mgr->>Mgr: _timeout_loop(oid, tracking) 启动
    par WS 路径
        WS->>Mgr: handle_message(orderUpdates/userFills)
        Mgr->>Mgr: _on_order_update / _on_user_fill
        Mgr->>Mgr: _finish(oid) -> result_event.set()
    else 超时路径
        Mgr->>Mgr: result_event.wait(timeout) 超时
        Mgr->>API: _http_check(oid)
        API-->>Mgr: status
        Mgr->>Mgr: _finish_direct(oid, tracking, status) -> result_event.set()
    end
    Mgr-->>Exec: wait_for_order 返回 True/False

核心代码位置:

  • 追踪注册与等待:src/trading/executor.py_track_limit_order(约 559–610 行)调用 track_order / wait_for_order
  • 状态机与超时:src/trading/websocket_order_manager.py_finish / _finish_direct_timeout_loop_http_check
  • 消息入口:K 线服务 on_message 中根据 channel 路由到 mgr.handle_message(msg)src/services/realtime_kline_service_base.py 约 613–627 行)

二、严重 Bug 列表

1. WS "filled" 先于 track_order(oid) 到达导致长时间阻塞(严重)

现象:限价单在交易所已成交,但本地要等接近一整段 timeout_seconds(例如 600 秒)才返回结果。

原因

HTTP 下限价单返回 oid 后,到执行 track_order(oid) 之间有一段代码(解析响应、赋值 order_result.order_id、再调用 _track_limit_order)。若在这段时间内 WebSocket 已推送 orderUpdatesfilled(或先到 userFills),则:

  • handle_message_on_order_update / _on_user_fill 中会执行 tracking = self._tracking.get(oid)
  • 此时该 oid 尚未被注册,trackingNone,逻辑直接 continue这条 "filled" 被永久丢弃
  • 随后 track_order(oid) 才注册并 wait_for_order(tracking)
  • 之后没有任何路径会再对该订单调用 _finish / _finish_direct(WS 已丢),只有 _timeout_loopresult_event.wait(timeout=tracking.timeout_seconds) 超时后做 HTTP 验证
  • 因此本可即时返回的成交会被拖延最多 timeout_seconds 才由 HTTP 解析并 result_event.set()

影响:资金与仓位在交易所已更新,但策略/上层要等数百秒才得到“已成交”结果,影响体验和后续逻辑,且容易被误认为“系统卡住”。

修复方向

track_order(oid) 内、注册到 _tracking 之后,立即对该 oid 做一次 HTTP 状态查询;若已为 filled/canceled/rejected,直接用 _finish_direct 结算并 result_event.set(),避免依赖“可能已错过”的 WS 消息,同时不改变现有 WS 优先的设计。


2. 订单消息路由依赖 channel,与订阅字段 type 不一致(中高)

现象:若交易所推送的订单相关消息使用 type 而不是 channel 标识类型,则所有 orderUpdates/userFills 都不会被路由到订单管理器,所有限价单都会走“超时 + HTTP”路径。

依据

  • 订阅时使用 "type": "orderUpdates" / "type": "userFills"src/services/realtime_kline_service_base.py 约 444–445 行)
  • 路由时使用 channel = msg.get("channel", "") 且仅当 channel in ("orderUpdates", "userFills") 才交给 mgr.handle_message(约 614–615 行)
  • 若推送格式为 {"type": "orderUpdates", "data": ...} 而无 channel,则 channel 为空,消息不会进入订单分支,可能被当作 K 线等其它逻辑处理或忽略

影响:WS 订单跟踪整体失效,所有订单都需等超时后 HTTP 才结算,等同于 Bug 1 的“全局版”。

修复方向

  • 确认 Hyperliquid 实际推送中订单消息的字段名(channel 还是 type
  • 路由时同时兼容 msg.get("channel")msg.get("type"),例如:channel = msg.get("channel") or msg.get("type") or "",再判断是否为 orderUpdates / userFills

3. 订单消息缓冲区仅在“新订单消息”到达时回放(中等)

现象:断线或未就绪期间缓存的 orderUpdates/userFills 只有在再次收到 orderUpdates 或 userFills 时才会被回放;若长时间没有新订单推送,已缓存的订单状态更新会一直不处理。

依据

  • on_message 中,当 mgr is not None 时,先 while self._order_msg_buffer: ... mgr.handle_message(buffered)mgr.handle_message(msg)(约 618–625 行)
  • mgr is None,只做 self._order_msg_buffer.append(msg)(约 629 行),没有任何单独线程或定时任务在“仅 K 线消息”到达时回放缓冲区
  • 因此只有在“当前这条消息是 orderUpdates/userFills”时才会触发回放;若重连后长时间没有新订单或订单推送,缓冲区里的历史订单更新会延迟到“下一笔订单相关推送”才被处理

影响:断线/重连窗口内的订单状态可能长时间未结算,依赖重连后的 verify_pending_orders 用 HTTP 补查;若 HTTP 也异常或延迟,用户会感觉“订单卡在 PENDING”。

修复方向

  • 在 WebSocket 重连或订单管理器首次就绪时,主动对当前 _order_msg_buffer 做一次回放(与现有 while self._order_msg_buffer 逻辑一致),而不是仅依赖“下一条订单消息”
  • 和 Executor 侧 WebSocketReconnectedEvent 后调用的 verify_pending_orders 配合,可进一步减少“漏结算”窗口

4. 重连后 verify 与 _timeout_loop 的 HTTP 并发(低~中)

现象:同一 oid 可能同时被“重连后的 verify_pending_orders”和“超时线程的 _timeout_loop”做 HTTP 查询;_http_busy 会令其中一方得到 "busy" 并跳过或重试,可能造成某一轮 verify 对部分订单“未处理”。

依据

  • verify_pending_orders 遍历 PENDING 并串行调用 _http_check(oid),中间 time.sleep(1)(约 162–166、172 行)
  • _timeout_loop 在超时后也会调用 _http_check(oid)(约 406、431 行)
  • _http_checkself._http_busy 保证同一 oid 不会并发 HTTP(约 356–358、375 行),另一线程会得到 "busy"
  • verify 得到 "busy" 时把该 oid 放入 retry_list,二次重试前再 sleep(3)(约 173–174 行),而 _timeout_loop 会自己 sleep(_HTTP_RETRY_DELAY) 后重试,最终都会由某一路径完成结算,不会“重复结算”,但 verify 的第一轮可能对部分 oid 无效果

影响:逻辑正确性无问题(不会重复 finish),主要是重连后第一轮补查的“覆盖率”可能略低,依赖第二轮或 _timeout_loop 补足;可视为设计上的竞态说明而非致命 bug。

修复方向

  • 保持现状即可;若希望重连后更快收敛,可在 verify 的 retry 逻辑里对 "busy" 做短时 sleep 后再重试该 oid,而不是仅依赖固定 3 秒后的第二轮全量重试

三、小结与优先级

优先级 Bug 影响 建议
P0 WS filled 先于 track_order 被丢弃 单笔订单可能阻塞至 timeout(如 600s)才返回 在 track_order 内注册后立即做一次 HTTP 状态查询并可能直接 _finish_direct
P1 消息路由仅看 channel 若 API 用 type,则所有 WS 订单跟踪失效 路由时兼容 channel 与 type
P2 缓冲区仅在新订单消息时回放 断线/未就绪期间的订单更新可能长时间未处理 重连/管理器就绪时主动回放缓冲区
P3 verify 与 _timeout_loop 的 HTTP 竞态 重连首轮补查可能部分 oid 被 busy 跳过 可选:对 busy 做短时重试

建议优先修复 P0P1(并确认交易所实际推送格式),再视需要处理 P2/P3。

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