订单跟踪系统BUG25

订单跟踪系统严重 Bug 分析报告

分析日期:2026-02-21
主要问题文件:websocket_order_manager.py


BUG 1 — 竞态条件导致定时器内存泄漏 🔴 Critical

位置: websocket_order_manager.py:360-365

代码:

timer = threading.Timer(
    self._FILL_GRACE_SEC, self._resolve, [oid]
)
timer.daemon = True
tracking._grace_timer = timer
timer.start()

问题描述:
同一 oid 被重复调用 track_order() 时,旧 tracking 被替换,但旧 _grace_timer 的引用丢失。旧定时器仍在后台运行,最终调用 _resolve(oid) 时发现 identity check 失败(tracking is not old_tracking),订单状态永久悬挂为 PENDING。大量重复追踪会导致定时器线程不断累积,最终引发线程爆炸。

严重程度: Critical

潜在影响:

  • 订单状态永久不一致,wait_for_order 无限等待直到超时
  • 高频交易场景下线程数量失控,系统资源耗尽

BUG 2 — 成交价丢失(默认值陷阱)🟠 High

位置: websocket_order_manager.py:270-283

代码:

if final_status == OrderStatus.FILLED:
    if tracking is not None and status is not None:
        if not tracking.has_fill_price:
            if px > 0:
                tracking.avg_price = px
                tracking.has_fill_price = True
            if sz > 0:
                tracking.filled_size = sz
    else:
        # WS 模式:使用 fallback 价格
        if not tracking.has_fill_price:
            tracking.avg_price = tracking._fallback_px  # ← 可能为 0.0
            tracking.filled_size = tracking._fallback_sz  # ← 可能为 0.0

问题描述:
当交易所返回的所有价格字段(avgPxlimitPxpx)均为 None0 时,_fallback_px = 0.0,最终成交记录 avg_price=0.0filled_size=0.0,后续 PnL 计算完全错误。

严重程度: High

潜在影响:

  • 订单显示成交但成交均价和数量为 0
  • PnL 计算结果失真,用户看到虚假利润数据
  • 清仓逻辑因 size=0 而失败

BUG 3 — 订单 ID 类型不一致导致追踪失效 🟠 High

位置: websocket_order_manager.py:127, 392

代码:

# track_order 中 (第127行)
oid = int(oid)

# _on_user_fill 中 (第392行)
oid = _safe_int_oid(fill_data.get("oid"))
if oid is None:
    continue

问题描述:
不同来源的 oid(字符串、浮点数、整数)转换时可能因精度问题产生不一致。例如浮点精度误差导致 int(10000000000.1) != int(10000000000.2),使 userFills 消息与 orderUpdate 消息无法正确关联,同一订单被认为是两个不同订单。

严重程度: High

潜在影响:

  • WS 终态和真实成交价无法正确关联
  • 部分成交的订单价格累计失败
  • 多币种系统中可能导致订单追踪到错误币种

BUG 4 — _resolve 在锁外调用的 TOCTOU 竞态 🔴 Critical

位置: websocket_order_manager.py:341-376

代码:

with self._lock:
    # ...设置 should_resolve = True

if should_resolve:
    self._resolve(oid)   # ← 锁已释放!此时另一线程可能已替换 _tracking[oid]

问题描述:
锁内完成状态检查和 should_resolve = True 设置后释放锁,在锁外调用 _resolve(oid) 期间,另一线程若调用 track_order(oid) 替换了 _tracking[oid],identity check 失败导致 _resolve() 返回 False,订单永久阻塞在 PENDINGwait_for_order 无限等待直到超时。

时序:

  1. 线程 A 持锁,设置 should_resolve=True
  2. 线程 A 释放锁
  3. 线程 B 调用 track_order(oid),替换 _tracking[oid] 为新 tracking
  4. 线程 A 调用 _resolve(oid),identity check 失败,返回 False
  5. 订单状态永久 PENDING

严重程度: Critical

潜在影响:

  • 高并发下自动交易订单频繁阻塞
  • 真实订单已成交但系统误判为 TIMEOUT,头寸计算错误

BUG 5 — _http_busy 集合状态不一致 🟡 Medium

位置: websocket_order_manager.py:529-557

代码:

def _http_check(self, oid: int) -> str:
    with self._lock:
        if oid in self._http_busy:
            return "busy"
        self._http_busy.add(oid)
    try:
        ...
        settled = self._resolve(oid, tracking, status, avg_px, total_sz)
        return "resolved" if settled else "stale"
    finally:
        with self._lock:
            self._http_busy.discard(oid)

问题描述:
虽然 finally 块确保清理,但如果 _resolve() 内部抛出异常后被上层吞掉,后续对相同 oid_http_check() 可能因为 tracking is not current 而返回 "stale",导致该订单的 HTTP 查询被跳过。

严重程度: Medium

潜在影响:

  • 网络异常频繁时,某些订单的 HTTP 查询被意外跳过
  • 订单最终状态无法确定

BUG 6 — orderUpdates / userFills 顺序依赖导致延迟或错误 🟠 High

位置: websocket_order_manager.py:346-365, 417-421

代码:

# _on_order_update 中
if status_str == "filled":
    tracking._ws_status = OrderStatus.FILLED
    if tracking.has_fill_price:
        should_resolve = True
    else:
        timer = threading.Timer(self._FILL_GRACE_SEC, self._resolve, [oid])
        timer.start()

# _on_user_fill 中
if tracking._ws_status is not None:
    should_resolve = True

问题描述:
系统设计假设 orderUpdates 先于 userFills 到达。若顺序颠倒:

  • userFills 先到 → _ws_status is None → 不触发 resolve,成交数据被丢弃
  • 之后 orderUpdates 到达,启动 grace timer,5 秒后才使用可能不准确的 fallback 价格

严重程度: High

潜在影响:

  • 成交确认延迟,快速市场中头寸偏离
  • userFills 推送不稳定的币种,成交价完全依赖不准确的 fallback 值

BUG 7 — verify_pending_orders 使用过期 tracking 对象 🟠 High

位置: websocket_order_manager.py:176-205

代码:

with self._lock:
    pending = [
        (oid, t) for oid, t in self._tracking.items()
        if t.status == OrderStatus.PENDING and t._ws_status is None
    ]

for i, (oid, tracking) in enumerate(pending):
    result = self._http_check(oid)
    if result in ("error", "busy"):
        retry_list.append((oid, tracking))  # ← tracking 可能已过期

time.sleep(3)
for oid, tracking in retry_list:
    result = self._http_check(oid)
    if result in ("error", "busy"):
        self._resolve(oid, tracking, OrderStatus.TIMEOUT)  # ← 使用过期 tracking!

问题描述:
重试时持有的 tracking 引用可能已被其他线程替换。调用 _resolve(oid, tracking, ...) 因 identity check 失败而永久返回 False,WebSocket 重连后的订单补查逻辑形同虚设。

严重程度: High

潜在影响:

  • WebSocket 重连后大量 PENDING 订单无法被正确解决
  • 订单状态在断线重连后永久悬挂

BUG 8 — _pos_float 无法区分真实 0 值 🟡 Medium

位置: websocket_order_manager.py:69-80

代码:

def _pos_float(raw) -> float:
    if raw is None:
        return 0.0
    try:
        val = float(raw)
        return val if val > 0 else 0.0
    except (ValueError, TypeError):
        return 0.0

问题描述:
如果交易所返回字符串 "0.0""0"(尚未更新成交价时的占位值),_pos_float() 返回 0.0,与真实的 0 成交量无法区分,导致部分成交的订单被误判为未成交。

严重程度: Medium

潜在影响:

  • 部分成交订单成交价信息丢失
  • 成交价格在暂时不可用时被永久设为 0

BUG 9 — Daemon 线程无异常捕获导致静默崩溃 🟠 High

位置: websocket_order_manager.py:142-144

代码:

threading.Thread(
    target=self._monitor_order, args=(oid, tracking), daemon=True
).start()

问题描述:
_monitor_order() 和 grace timer 回调 _resolve() 中的任何未捕获异常都会导致线程/定时器静默死亡,没有告警,没有重试,订单永久卡在 PENDING

严重程度: High

潜在影响:

  • 网络错误或 API 异常时,监控线程崩溃无人感知
  • 订单阻塞在 wait_for_order,后续交易逻辑全部串行阻塞

BUG 10 — _grace_timer 重复 cancel 调用逻辑不清晰 🟢 Low

位置: websocket_order_manager.py:262-264, 358-359

问题描述:
_resolve()_on_order_update() 中都有 tracking._grace_timer.cancel() 调用,若定时器已在执行 _resolve() 中途时调用 cancel,Python 内部可以安全处理,但代码逻辑不够清晰,存在潜在的状态不一致风险。

严重程度: Low


优先级汇总

Bug ID 位置 严重程度 修复优先级 核心风险
BUG 1 :130-139, 360-365 Critical 🔴 最高 定时器泄漏,线程爆炸
BUG 4 :341-376 Critical 🔴 最高 订单永久 PENDING
BUG 2 :270-283 High 🟠 高 成交价丢失,PnL 错误
BUG 3 :127, 392 High 🟠 高 订单 ID 关联失败
BUG 6 :346-365, 417-421 High 🟠 高 成交确认延迟或价格错误
BUG 7 :176-205 High 🟠 高 重连后订单补查失效
BUG 9 :142-144 High 🟠 高 线程静默崩溃无感知
BUG 5 :529-557 Medium 🟡 中 HTTP 查询被意外跳过
BUG 8 :69-80 Medium 🟡 中 部分成交价格丢失
BUG 10 :262-264, 358-359 Low 🟢 低 逻辑不清晰

核心结论

系统在 并发、状态同步、异常处理 三个维度存在系统性缺陷,在 高频交易 + 网络波动 场景下极易导致订单状态不一致或永久阻塞。建议优先修复 BUG 1 和 BUG 4 的竞态问题,再依次处理高优先级的成交价丢失和异常处理问题。

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