订单跟踪BUG21

订单跟踪系统严重 Bug 分析

分析时间: 2026-02-21
涉及文件: realtime_kline_service_base.py, websocket_order_manager.py, executor.py


一、订单跟踪链路概览

flowchart LR
  subgraph place [下单]
    Place[place_order]
    Track[track_order]
    Wait[wait_for_order]
  end
  subgraph ws [WebSocket]
    WS[on_message]
    Buf[_order_msg_buffer]
    Mgr[handle_message]
  end
  subgraph resolve [结算]
    Finish[_finish / _finish_direct]
    HTTP[_http_check]
  end
  Place --> Track --> Wait
  WS --> Buf
  Buf --> Mgr
  Mgr --> Finish
  Track --> Monitor[_monitor_order]
  Monitor --> HTTP
  HTTP --> Finish
  • WS 路径orderUpdates / userFillsrealtime_kline_service_base.py 路由 → _get_ws_order_manager().handle_message()websocket_order_manager.py_on_order_update / _on_user_fill_finish()
  • HTTP/超时路径_monitor_order 早期检查(2s) + 超时后 _http_check_finish_direct()
  • 撤单后:executor 在限价单超时/撤单后调用 _get_actual_position_size() 判断部分成交与残余持仓。

以下仅列出严重与订单跟踪直接相关的问题。


二、严重 Bug 列表

1. 缓冲区回放竞态 + 异常导致订单消息永久丢失(高)

位置src/services/realtime_kline_service_base.py 约 618–626 行。

问题 1(竞态)
_order_msg_buffer 为无锁 deque。多线程下(如 WS 回调线程)可能同时:

  • 线程 A/B 都通过 if self._order_msg_buffer 进入 while self._order_msg_buffer
  • 一个线程 popleft() 后另一线程再 popleft() 可能对空 deque 抛 IndexError,导致回调崩溃、后续订单消息处理中断。

问题 2(异常丢消息)
当前逻辑是 popleft() handle_message(buffered)。若 handle_message(buffered) 抛异常,该条订单消息已被移出缓冲区且仅打日志,不会重试也不会再入队,对应订单的 WS 状态更新会永久丢失,可能造成该单一直处于 PENDING、最终依赖超时/HTTP 才结算,或价格/数量依赖 HTTP 而非更准确的 userFills。

已有记录:竞态部分见 订单跟踪bug2.md Bug 2。

修复方向

  • 用锁保护“判空 + popleft + 处理”整段,或使用“先 popleft 再处理、异常时重新放回队首/重试”策略,避免单次异常导致一条消息永久丢失;
  • 空 deque 的 popleft() 用 try/except 或“加锁后再次判空再 popleft”防止 IndexError

2. HTTP 结算覆盖已存在的 userFills 数据(中高)

位置src/trading/websocket_order_manager.py _finish_direct() 约 257–263 行。

if status == OrderStatus.FILLED:
    if px > 0:
        tracking.avg_price = px
        tracking.has_fill_price = True
    if sz > 0:
        tracking.filled_size = sz

问题
当订单已通过 userFills 累计了更精确的加权均价和 filled_size 时,tracking.has_fill_price 已为 True,但 HTTP 路径(早期检查或超时后 _http_check)若查到 filled 并调用 _finish_direct(oid, tracking, status, avg_px, total_sz),仍会无条件用 HTTP 返回的 avg_px/total_sz 覆盖 tracking.avg_pricetracking.filled_size
结果是:WS 已提供的更准确成交价/数量被 HTTP 单次查询结果覆盖,下游(如 executor 的 order_result.price / order_result.size)拿到的是 HTTP 数据而非 userFills 汇总,影响统计与风控精度。

修复方向
仅在 not tracking.has_fill_pricenot tracking.filled_size 等情况下用 HTTP 的 px/sz 回填,否则保留已有 userFills 数据。


3. 限价单超时/撤单后用缓存持仓判断部分成交与残余(高,与订单跟踪结果处理直接相关)

位置src/trading/executor.py 限价单超时分支及平仓撤单后:

  • 约 664–676 行 _get_actual_position_size() 内部使用 get_positions(),未强制刷新;
  • 约 826 行(limit_open 超时后)、1023 / 1064 行(limit_close 撤单后)在判断部分成交、残余持仓时都依赖该缓存。

问题
撤单或超时后,交易所状态已变,但 get_positions() 可能仍返回 5s TTL 内的旧缓存。

  • 缓存显示有持仓而实际已撤单清零 → 可能误建仓位或误判部分成交;
  • 缓存显示无持仓而实际有部分成交/残留 → 漏记部分成交或留下无人管理的残余仓位。

影响:与“订单跟踪结果”的后续处理直接相关,可能造成资金/仓位错误。
已有记录订单跟踪bug2.md Bug 5、Bug 6。

修复方向
_get_actual_position_size 增加 force_refresh 参数(或等效方式),在限价单超时撤单/平仓撤单后调用时传入 force_refresh=True,使用实时持仓数据。


4. 重连补查与 monitor 并发下的“busy”与重试(中)

位置src/trading/websocket_order_manager.py verify_pending_orders()_http_check()_http_busy 逻辑。

问题
重连后 verify_pending_orders() 对每个 PENDING 订单调用 _http_check(oid);若同一 oid 的 _monitor_order 也在做早期检查或超时 HTTP,会命中 oid in self._http_busy 返回 "busy"

  • verify 侧:该单被加入 retry_list,约 3 秒后重试,逻辑可接受;
  • monitor 侧:得到 "busy" 后进入 15 秒 sleep 再二轮 HTTP,可能拉长该单解析时间,在极端情况下与“强制 TIMEOUT”的节奏叠加,影响体验和日志清晰度。

当前设计不会导致重复结算(identity check),但并发控制可考虑更明确(例如同一 oid 串行化 HTTP 或共享结果),以减少重试与延迟。


三、与订单跟踪相关的已记录问题(简要)

  • Bug 8(低)订单跟踪bug2.md — 早期 HTTP 与 WS 的竞态窗口;identity check 已避免重复结算,主要影响价格精度,可与上述“HTTP 覆盖 userFills”一并考虑优化。
  • Bug 1(中高)executor.pyleg_b=None 后错误信息丢失,影响订单失败诊断,与订单结果展示相关。

四、修复优先级建议

优先级 问题 影响
P0 缓冲区回放:竞态 + 异常丢消息 订单消息丢失、回调崩溃、状态长期依赖 HTTP/超时
P0 撤单/超时后持仓查询使用缓存(Bug 5/6) 仓位误判、资金风险
P1 HTTP 覆盖 userFills 成交价/量 成交价与数量不准确,影响统计与风控
P2 重连补查与 monitor 并发 busy/重试 解析延迟与日志噪音

建议先修复 P0(缓冲区线程安全 + 异常不丢单;撤单后强制刷新持仓),再处理 P1(保留 userFills、仅在缺失时用 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