当前系统订单跟踪存在哪些严重的bug8

订单跟踪系统严重Bug分析报告(第二轮)

分析日期:2026-02-21
分析范围:git status 显示的已修改文件
分析文件:executor.py / position_manager.py / trade_repository.py / websocket_order_manager.py / enhanced_ws_manager.py / realtime_kline_service_base.py


BUG #1 🔴 WebSocket订单状态丢失竞态条件

严重等级: 严重
文件: src/trading/websocket_order_manager.py ~第167-199行
类别: 竞态条件

问题描述

_on_order_update 在锁内执行 self._tracking.pop(oid, None) 删除 tracking 对象后,_resolve_via_http 在锁外查询时获取 None,误判订单已结算,导致成交价永远不被记录,PnL计算错误。

竞态时序

T1: 线程A (_on_order_update) 在锁内 pop(oid) 删除 tracking
T2: 线程B (wait_for_order)   在锁外等待 result_event
T3: 线程C (_resolve_via_http) 查询 self._tracking.get(oid) → None
T4: _resolve_via_http 误判"已结算",返回 True
结果: 成交价未记录,PnL计算错误

影响

  • 订单成交价永远为 0 或初始值
  • PnL 计算完全失真
  • 订单状态与实际交易所状态不一致

BUG #2 🔴 成交价缓存TOCTOU漏洞

严重等级: 严重
文件: src/trading/websocket_order_manager.py ~第173-178行 & 第335-350行
类别: TOCTOU(检查时间与使用时间不一致)

问题代码

# _on_order_update(锁内)
fill = self._fill_prices.pop(oid, None)   # 弹出缓存
if fill and fill[0] > 0:
    tracking.avg_price = fill[0]

# _cache_fill(锁外,并发)
existing = self._fill_prices.get(oid)     # 读取
# ... 计算新价格 ...
self._fill_prices[oid] = (new_px, new_sz) # 覆盖写入,竞态窗口

影响

  • 多次部分成交场景下加权均价计算错误
  • _MAX_FILL_CACHE 溢出时 FIFO 删除可能删掉错误的订单

BUG #3 🟠 订单状态机转换不原子

严重等级: 高
文件: src/trading/websocket_order_manager.py ~第244-251行
类别: 状态机逻辑

问题描述

超时验证线程与成交通知线程并发时,已被 pop 的 tracking 对象被重新设为 TIMEOUT 状态,造成状态机逻辑冲突,可能导致重复追踪。

非法状态转换路径

正常: PENDING → FILLED / CANCELED / REJECTED
异常: PENDING → TIMEOUT(对象已被 pop,操作无意义)
结果: 可能重新激活已删除的订单,造成重复追踪

BUG #4 🟠 账户余额缓存陈旧导致超额下单

严重等级: 高
文件: src/trading/executor.py ~第1624-1637行
类别: 缓存策略

问题代码

def get_account_value(self, force_refresh: bool = False) -> float:
    if not force_refresh:
        with self._cache_lock:
            age = time.time() - self._position_cache_ts
            if age < self._cache_ttl and self._position_cache_ts > 0:
                return self._cached_account_value  # ❌ 可能返回严重过期的值

影响

  • WebSocket 掉线期间,缓存 TTL=5秒,返回严重过期的账户余额
  • 高频交易下允许"负余额"下单
  • Leg B 失败后回滚 Leg A,但已产生滑点损失

BUG #5 🟠 限价单撤单返回值未检查

严重等级: 高
文件: src/trading/executor.py ~第808-838行
类别: 错误处理缺失

问题代码

if result.leg_a.order_id:
    self._cancel_order(alt_coin, result.leg_a.order_id)  # ❌ 不检查返回值

# 查询实际成交量(撤单失败时数据可能不一致)
actual_filled = self._get_actual_position_size(alt_coin)

影响

  • 撤单失败时订单仍在交易所挂单
  • 继续下 Leg B,造成双腿超额持仓
  • _get_actual_position_size 可能因网络超时返回 0,真实持仓被忽视

BUG #6 🟠 仓位同步覆盖写竞态

严重等级: 高
文件: src/trading/position_manager.py ~第357-369行
类别: 竞态条件

竞态时序

T0: 内存仓位 = {alt: 1.5, base: 100}
T1: get_positions() 查询交易所,返回 {alt: 1.5}
T2: 另一线程下单 Leg A,内存更新为 {alt: 2.0}
T3: _execute_close 用旧查询结果 1.5 覆盖内存值 2.0
结果: 仓位追踪错误,后续平仓数量不足

影响

  • 仓位数量记录错误,平仓时数量偏少
  • 残余仓位无法被系统感知,形成隐藏风险敞口

BUG #7 🟡 孤儿仓位内存-DB不一致

严重等级: 中
文件: src/trading/position_manager.py ~第807-849行
类别: 数据原子性

问题描述

内存更新完成后,_repo.save_position() 在锁外进行持久化,若此时持久化失败,内存与 DB 产生分歧。重启后恢复时加载错误数据。

with self._lock:
    existing.alt_size = actual_size   # 内存更新(锁内)

# 锁外:持久化(可能失败)
self._repo.save_position(size_sync_pos)  # ❌ 无乐观锁,失败无回滚

BUG #8 🟡 TradeRepository非原子并发更新

严重等级: 中
文件: src/trading/trade_repository.py ~第170-209行
类别: 数据库操作

问题描述

多线程同时更新同一仓位的 PnL 和价格字段时,SQL 非原子执行导致中间状态被读取,财务数据可能被部分覆盖。

# SQL 字段拼接(非原子)
sql = f"UPDATE pair_positions SET {', '.join(updates)} WHERE position_id = %s"

BUG #9 🟡 WebSocket重连时订阅状态竞态

严重等级: 中
文件: src/utils/websocket/enhanced_ws_manager.py ~第587-638行
类别: 并发订阅管理

竞态时序

T1: _on_open 执行,清空 active_subscriptions(全量清空)
T2: 业务线程并发调用 add_subscriptions
T3: add_subscriptions 检查 "sub_key not in active_subscriptions"(此时为空,通过)
T4: 产生重复订阅请求,或部分订阅丢失

二级问题

ws.send() 失败后无全量重试或回滚机制,导致部分订阅在交易所侧存在但本地未记录。


BUG #10 🟠 _fill_prices 内存泄漏

严重等级: 高
文件: src/utils/websocket/enhanced_ws_manager.py ~第260-350行
类别: 内存管理

问题代码

self._fill_prices: Dict[int, Tuple[float, float]] = {}  # ❌ 无TTL的普通dict

# 淘汰策略缺陷
if len(self._fill_prices) > self._MAX_FILL_CACHE:
    del self._fill_prices[next(iter(self._fill_prices))]  # ❌ FIFO删除最早插入,非最旧数据

影响

  • 高频交易下 _fill_prices 持续增长,TTLCache 保护不覆盖此 dict
  • FIFO 策略可能删除"最近高频订单",保留"旧低频订单"
  • 应使用 OrderedDict 或带 TTL 的缓存结构

BUG #11 🟡 oid类型不一致导致dict查找失败

严重等级: 中
文件: src/trading/websocket_order_manager.py ~第43-74行
类别: 类型安全

问题描述

  • API 响应的 oid 可能为字符串
  • WebSocket 消息的 oid 可能为整数
  • self._tracking[oid] 因 key 类型不匹配而静默查找失败
def track_order(self, oid: int, coin: str, ...) -> OrderTracking:
    oid = int(oid)  # ❌ 假设调用者已保证类型,重复转换无法覆盖所有路径

BUG #12 🟠 平仓PnL计算中基准币成交价为0

严重等级: 高
文件: src/trading/position_manager.py ~第696-735行
类别: PnL计算

问题代码

base_exit_price = order_result.leg_b.price
if (not base_exit_price or base_exit_price <= 0) and position.base_symbol:
    base_exit_price = self._executor.get_all_mids().get(...)
    # ❌ get_all_mids() 失败或超时时,base_exit_price 保持为 0.0
    # ❌ 未降级到 position.base_current_price

影响

  • Leg B 平仓失败且 get_all_mids() 超时时,base_exit_price = 0.0
  • base 腿 PnL 完全丢失,可能导致 30-50% 的收益未记录

BUG #13 🟡 平仓禁用配置检查时间窗口

严重等级: 中
文件: src/trading/position_manager.py ~第260-290行
类别: 配置热更新

竞态时序

T1: is_close_disabled("COIN", "BASE") → False(允许平仓)
T2: 配置热更新,将 COIN|BASE 添加到禁用列表
T3: 执行平仓逻辑(缺少二次检查)
T4: 订单已提交到交易所
结果: 管理员紧急禁用操作失效

📊 Bug汇总与优先级

Bug# 严重等级 类别 文件 复现难度
#1 🔴 严重 竞态条件 websocket_order_manager.py
#2 🔴 严重 TOCTOU websocket_order_manager.py
#3 🟠 高 状态机 websocket_order_manager.py
#4 🟠 高 缓存过期 executor.py
#5 🟠 高 错误处理 executor.py
#6 🟠 高 竞态条件 position_manager.py
#7 🟡 中 原子性 position_manager.py
#8 🟡 中 非原子更新 trade_repository.py
#9 🟡 中 订阅竞态 enhanced_ws_manager.py
#10 🟠 高 内存泄漏 enhanced_ws_manager.py
#11 🟡 中 类型安全 websocket_order_manager.py
#12 🟠 高 PnL丢失 position_manager.py
#13 🟡 中 配置时间窗 position_manager.py

⚠️ 高危影响评估

直接影响

  • 订单状态丢失 → 仓位追踪错误
  • PnL 计算错误 → 财务报表不准确
  • 缓存不一致 → 风险管理失效
  • 并发下单失败 → 交易失败率上升

间接影响

  • 内存泄漏 → 长期运行内存溢出
  • 孤儿仓位泄漏 → 持仓被无限期占用
  • 黑名单配置失效 → 风险币种无法紧急禁用

修复优先级

优先级 Bug编号 原因
P0 🔴 #1, #2 成交价丢失,PnL完全错误,核心数据不可信
P1 🟠 #4, #5, #6, #10, #12 超额下单、内存泄漏、PnL缺失
P2 🟡 #3, #7, #8, #9, #11, #13 状态不一致、数据分歧

最高风险组合:Bug #1 + #2 + #12 同时存在时,系统长期运行后财务数据完全不可信。建议在下次部署前优先修复 P0 级别问题。

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