订单跟踪系统BUG32

订单跟踪系统 Bug 分析报告(第二期)

分析日期:2026-02-22
分析范围:src/trading/websocket_order_manager.pysrc/trading/position_manager.pysrc/trading/trade_repository.py


严重 Bug 清单(按优先级排列)

🔴 P0 致命 Bug

BUG 1: exit_price 每次 save_position() 被重置为 0

文件: src/trading/trade_repository.py

每次调用 save_position() 时,alt_exit_pricebase_exit_price 都硬编码初始为 0,且 ON CONFLICT DO UPDATE 子句中没有更新这两个字段。平仓后若任何代码再次调用 save_position(),exit_price 会被覆盖为 0,导致 PnL 计算永久错误

根因

-- ON CONFLICT 子句缺少以下两个字段的更新
-- alt_exit_price = EXCLUDED.alt_exit_price
-- base_exit_price = EXCLUDED.base_exit_price

影响:平仓收益统计完全失真,exit_price 丢失。


BUG 2: 竞态导致订单状态重复解析

文件: src/trading/websocket_order_manager.py:228-288

_resolve() 在删除 _tracking[oid] 后,grace timer 线程仍可能持有旧的 tracking 引用并尝试二次解析。identity check 虽然有保护,但若 grace timer 在 timer.start() 之前被抢占,整个解析链会出现不可预期的状态跳变,可能导致订单成交被漏记

竞态场景

Thread A: _on_order_update() 设置 _ws_status=FILLED,检查 has_fill_price=False
Thread B: _on_user_fill() 接收 fill 数据,调用 _resolve() 成功,弹出 _tracking[oid]
Thread A: timer.start() 执行,2秒后 _resolve() 因 identity check 失败返回 False
结果:订单已从 _tracking 移除,但部分 fill 数据未被累计

影响:订单成交价格错误,持仓入场均价失真。


BUG 3: PnL 计算精度缺失

文件: src/trading/position_manager.py:732-825

_calculate_realized_pnl() 中,若 alt_exit_price <= 0alt_entry_price <= 0alt_pnl 直接保持为 0.0 而非抛出异常或阻断计算。最终 PnL = 0 + base_pnl - fee,统计数据严重失真

问题代码

alt_pnl = 0.0
if alt_exit_price <= 0 or position.alt_entry_price <= 0:
    logger.warning(...)  # 只记录警告,不阻断
if alt_exit_price > 0 and position.alt_entry_price > 0:
    alt_pnl = (alt_exit_price - position.alt_entry_price) * position.alt_size
# alt_pnl 仍为 0.0,但不会报错

影响:realized_pnl 严重低估,财务统计完全错误。


🟠 P1 高危 Bug

BUG 4: Grace Timer 竞态 — 成交价格可能错误

文件: src/trading/websocket_order_manager.py:354-364

竞态场景

Thread A: 设置 _ws_status=FILLED,检查 has_fill_price=False,准备启动 timer
Thread B: 同时收到 fill 数据,调用 _resolve() 成功,弹出 _tracking[oid]
Thread A: timer.start() 执行,2秒后 _resolve() identity check 失败,timer 未被清理

问题代码

if tracking._grace_timer is None:
    timer = threading.Timer(...)
    tracking._grace_timer = timer
    timer.start()  # ← timer 启动后 tracking 可能已被从 _tracking 移除

影响:grace timer 游离,后续同 oid 的新订单可能被错误关联,成交价格累计错误。


BUG 5: _http_check_http_busy set 可能永久泄露

文件: src/trading/websocket_order_manager.py:522-559

_resolve() 内部抛出未预期异常,finally 块虽会执行 _http_busy.discard(oid),但 _resolve() 已经部分修改了状态,tracking 与实际 DB 状态出现不一致。

影响

  • tracking 状态与 DB 不一致
  • 若异常路径绕过 finally,oid 永久被标记为 http_busy,无法再触发 HTTP 补查

BUG 6: 成交去重逻辑不完整

文件: src/trading/websocket_order_manager.py:401-412

_fill_key() 依赖 tid 或内容哈希(px + sz + time)。若两个 WebSocket 连接推送完全相同的成交数据,第二笔会被误判为重复并 continue 跳过,不执行 _accumulate_fill()

问题代码

fid = _fill_key(fill_data)
if fid in tracking._fill_ids:
    continue  # ← 跳过,不累计成交
tracking._fill_ids.add(fid)
self._accumulate_fill(tracking, fill_px, fill_sz)

影响:成交累计金额偏低,avg_fill_price 偏差,导致持仓均价计算错误。


BUG 7: 孤儿仓位收纳 — 内存与 DB 修改不原子

文件: src/trading/position_manager.py:882-924

锁内修改 existing.alt_size,锁外才执行 _repo.save_position()。若持久化失败,内存已更新但 DB 未更新,下次重启后数据回滚,仓位大小与实际不符。

问题流程

with self._lock:
    existing.alt_size = actual_size  # ← 内存已修改

# 锁已释放,其他线程可能再次修改 existing
if already_exists:
    self._repo.save_position(size_sync_pos)  # ← 若此处失败,内存与DB不一致

影响:重启恢复时仓位大小错误,可能引发多次重复建仓或错误平仓。


BUG 8: verify_pending_orders 持有过期 tracking 引用

文件: src/trading/websocket_order_manager.py:156-185

with self._lock:
    pending = [(oid, t) for oid, t in self._tracking.items() ...]
# 锁释放后,t 可能已被 _resolve() 从 _tracking 中移除
# _http_check() 重新从 _tracking.get(oid) 获取,得到 None 或新对象
# identity check 触发 "stale" 返回

影响:挂起中的订单可能永远不被 resolve,长期处于 PENDING 状态,占用追踪资源。


BUG 9: cancel_all_open_orders 单个失败中断全局清理

文件: src/trading/websocket_order_manager.py:187-204

_cancel_order() 对某个订单失败时,外层 except 直接捕获并退出循环,后续挂单无法继续清理

问题代码

for order in open_orders:
    self._executor._cancel_order(coin, oid)  # ← 若此处异常
    # ↓ 以下所有订单均不被处理
except Exception as e:
    logger.error(f"启动清理挂单失败: {e}")
    # 整个函数退出

影响:系统启动时留有残余挂单,干扰后续策略执行,可能引发意外成交。


BUG 10: close_position 平仓禁用配置热更新竞态

文件: src/trading/position_manager.py:263-293

TOCTOU(Time-of-Check to Time-of-Use)竞态:

1. 检查 is_close_disabled() → False(此时未禁用)
2. 配置被热更新为 close_disabled=True
3. 继续执行,触发实际平仓操作

影响:被禁止平仓的标的意外被平仓,违反风控规则。


🟡 P2 中危 Bug

BUG 11: sync_with_exchange 锁外获取数据 + 锁内修改不一致

文件: src/trading/position_manager.py:1174-1176

exchange_positions 在锁外获取,但 pos.alt_size 在锁内修改,DB 同步在锁外执行。期间 pos.alt_size 可能被其他线程再次更改,导致入库数据陈旧

影响:仓位同步数据有窗口期,监控面板展示的仓位大小可能与实际不符。


BUG 12: peak_pnl_pct 重复赋值掩盖潜在逻辑错误

文件: src/trading/position_manager.py:1057-1058

_load_position_from_db_row() 内已赋值,外部又覆盖一次。若将来两处取值来源不同,会产生静默的数据不一致。

影响:当前逻辑正确,但代码可读性差,隐藏未来维护风险。


优先修复建议

优先级 Bug 核心风险
🔴 P0 立即 BUG 1:exit_price 被覆盖 PnL 数据永久丢失
🔴 P0 立即 BUG 3:PnL 计算为 0 财务统计完全错误
🔴 P0 立即 BUG 2:竞态重复解析 订单成交漏记
🟠 P1 本周 BUG 4:Grace Timer 竞态 成交价格累计错误
🟠 P1 本周 BUG 5:_http_busy 泄露 订单永久不被补查
🟠 P1 本周 BUG 6:成交去重不完整 持仓均价偏差
🟠 P1 本周 BUG 7:孤儿仓位不原子 重启后仓位数据错误
🟠 P1 本周 BUG 8:过期 tracking 引用 订单永久 PENDING
🟠 P1 本周 BUG 9:cancel 异常中断循环 启动时残余挂单
🟠 P1 本周 BUG 10:平仓配置竞态 意外触发平仓
🟡 P2 计划 BUG 11:仓位同步窗口期 监控数据陈旧
🟡 P2 计划 BUG 12:重复赋值 代码维护风险

修复思路概要

BUG 1 修复

ON CONFLICT DO UPDATE 中补充:

alt_exit_price = EXCLUDED.alt_exit_price,
base_exit_price = EXCLUDED.base_exit_price

BUG 2 & 4 修复

_resolve() 中增加 grace timer 取消的双重保护,并确保 timer 回调在检查 identity 之前先持有锁。

BUG 3 修复

当 exit_price 或 entry_price 无效时,应抛出异常或返回 None 以阻断 PnL 写入,而非静默置为 0。

BUG 9 修复

cancel_all_open_orders 中的异常捕获移入循环内部,确保单个取消失败不中断其他订单的清理。

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