订单跟踪bug13

订单跟踪严重 Bug 分析

本文档对当前系统订单跟踪链路(WebSocket 订单管理器、K 线服务消息路由、Executor 追踪与 HTTP 兜底)进行静态分析与数据流梳理,归纳可能导致订单状态错误、永久阻塞或资源问题的严重 bug,并给出修复方向。


一、订单跟踪架构简述

sequenceDiagram
    participant Exec as Executor
    participant WSM as WebSocketOrderManager
    participant Kline as RealtimeKlineService
    participant WS as EnhancedWSManager

    Exec->>WSM: track_order(oid, coin, timeout)
    WSM->>WSM: _timeout_then_verify 线程启动
    Exec->>WSM: wait_for_order(tracking)
    WS-->>Kline: on_message(orderUpdates/userFills)
    Kline->>WSM: handle_message(msg)
    WSM->>WSM: _on_order_update / _on_user_fill
    WSM->>WSM: _resolve(oid) -> result_event.set()
    Note over WSM: 超时则 HTTP query_order_status 兜底
  • 状态解析唯一入口:_resolve()src/trading/websocket_order_manager.py),锁内改 statuspop(oid),锁外 result_event.set()
  • 订单消息来源:K 线服务的 on_message 根据 channel in ("orderUpdates", "userFills") 转发给 _get_ws_order_manager().handle_message(msg);若 manager 未就绪则写入 _order_msg_buffer,就绪后先回放 buffer 再处理当前消息。

二、严重 Bug 列表

1. 订单消息缓冲区满时丢弃最旧消息,导致 WS 终态丢失(严重)

  • 位置src/services/realtime_kline_service_base.py 第 193 行:self._order_msg_buffer: deque = deque(maxlen=500);第 627 行:manager 未就绪时 append(msg)
  • 问题:当 WebSocket 订单管理器长时间未就绪(例如交易模块启动晚、初始化异常)时,订单消息会不断入队。deque(maxlen=500) 满后,新消息会挤掉最旧消息(FIFO)。被挤掉的若是某订单的 filled/canceled/rejected 更新,该订单在管理器内会一直保持 PENDING,直到超时后 HTTP 兜底才能解析。
  • 影响:订单可能被误判为超时(TIMEOUT)或长时间阻塞(最多 timeout_seconds + _HTTP_VERIFY_BUFFER),增加延迟与误报,在 HTTP 也失败时可能两轮重试后强制 TIMEOUT。
  • 修复方向
    • 方案 A:缓冲区有界时改为丢弃消息并打日志,保证已缓冲的“旧消息”不丢(适合“宁可漏新不可丢旧”的语义);
    • 方案 B:不设 maxlen,改为按条数/字节上限告警并触发降级(例如只做 HTTP 轮询),避免静默丢消息;
    • 同时建议:manager 未就绪时尽早告警并限制 buffer 写入量,避免长时间无 manager 仍持续缓冲。

2. orderUpdates 消息中 status 字段位置未兼容(高风险)

  • 位置src/trading/websocket_order_manager.py 第 241 行:status_str = (item.get("status") or "").lower();解析逻辑仅使用 item 顶层字段。
  • 问题:当前仅从 item.get("status") 读取。若 Hyperliquid 将订单状态放在 item.order.status 或仅在嵌套对象中提供,则 status_str 始终为空,filled/canceled/rejected 分支均不会进入,该订单不会通过 WebSocket 被解析,只能依赖超时 + HTTP。
  • 影响:所有依赖 WS 实时终态的订单都会退化为“超时后 HTTP 才解析”,失去实时性并增加误判 TIMEOUT 的风险。
  • 修复方向
    • 查阅 Hyperliquid 官方文档或实际抓包确认 orderUpdates 单条 data 项的结构(顶层 status vs order.status);
    • 在解析时兼容两种位置,例如:status_str = (item.get("status") or (item.get("order") or {}).get("status") or "").lower(),并补充单测或集成测试覆盖不同结构。

3. cancel_all_open_orders 中 oid 类型未统一(中高)

  • 位置src/trading/websocket_order_manager.py 第 163 行:oid = order.get("oid"),直接传给 self._executor._cancel_order(coin, oid);Executor _cancel_ordersrc/trading/executor.py)签名为 oid: int
  • 问题:若 get_open_orders() 或底层 API 某次返回的 oid 为字符串(例如不同端点或版本),未转为 int 可能导致类型错误或底层 API 调用异常,后续循环中的挂单可能未被取消,留下孤儿单。
  • 影响:启动时“清理残留挂单”不完整,可能影响后续策略或风控假设。
  • 修复方向:与现有 _safe_int_oid 一致,对 oid 做安全转换:oid = _safe_int_oid(order.get("oid")),若为 None 则跳过该条并打日志,再调用 _cancel_order(coin, oid)

4. 重连后 verify_pending_orders 无并发限制(中)

  • 位置src/trading/websocket_order_manager.py 第 148–152 行:verify_pending_orders 对每个 pending oid 依次调用 _resolve_via_http(oid),无并发上限。
  • 问题:重连后若存在大量 PENDING 订单,会同时发起大量 HTTP 请求(每个 oid 一次 query_order_status),可能触发交易所或网关限流、连接耗尽或延迟激增。
  • 影响:重连后的补查失败或变慢,部分订单可能仍依赖超时路径才能解析。
  • 修复方向:对 _resolve_via_http(oid) 做并发限制(例如线程池或信号量,如最多 3–5 个并发),或串行 + 小延迟,避免瞬时打满 HTTP。

三、已确认无问题的设计点

  • channel 字段:Hyperliquid 文档确认 orderUpdates 推送使用 channel: "orderUpdates",与当前 msg.get("channel") 路由一致。
  • oid 类型:Executor 侧 order_id 已用 int(raw_oid) 转换;WS 解析使用 _safe_int_oid,类型一致。
  • 重复追踪:同一 oid 再次 track_order 时,旧 tracking 被设为 CANCELED 并 result_event.set()wait_for_order(old) 会正确返回 False。
  • _resolve 幂等与引用_resolve 在锁内 pop(oid) 后,在锁外对同一 tracking 引用做 set() 和日志,对象仍有效,无 use-after-free 问题。

四、建议修复优先级

优先级 Bug 原因
P0 缓冲区满丢弃最旧消息 直接导致订单 WS 终态丢失,只能等超时/HTTP
P1 orderUpdates status 字段位置 若实际在 order 下,所有订单都无法通过 WS 解析
P2 cancel_all_open_orders oid 类型 防御性统一,避免孤儿单与异常
P3 verify_pending_orders 并发限制 重连场景下的稳定性与限流风险

完成 P0/P1 后,建议在测试环境或小流量下验证:订单从挂出到 filled/canceled 的 WS 解析率、超时率,以及重连后 pending 订单的 HTTP 补查成功率。

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