订单跟踪bug15
订单跟踪严重 Bug 分析
一、订单跟踪架构简要
flowchart LR
subgraph ws [WebSocket]
MSG[orderUpdates / userFills]
end
subgraph kline [RealtimeKlineServiceBase]
ON_MSG[on_message]
BUF[_order_msg_buffer]
MGR[_get_ws_order_manager]
end
subgraph mgr [WebSocketOrderManager]
TRACK[_tracking dict]
RESOLVE[_resolve]
HTTP[_resolve_via_http]
end
MSG --> ON_MSG
ON_MSG --> MGR
MGR -->|None| BUF
MGR -->|ready| mgr
BUF -->|replay| mgr
mgr --> TRACK
mgr --> RESOLVE
mgr --> HTTP
- 订单状态来源:WebSocket
orderUpdates(终态)+userFills(成交价);超时后 HTTP 兜底;重连后verify_pending_orders()补查。 - 关键文件:
src/trading/websocket_order_manager.py、src/services/realtime_kline_service_base.py(615–641 行)、src/trading/executor.py(559–611、1554–1574 行)。
二、严重 Bug:订单消息缓冲区溢出导致状态更新丢失
位置:src/services/realtime_kline_service_base.py 第 193 行、619–634 行。
现象:
- 当
_get_ws_order_manager()为None时,orderUpdates/userFills被写入_order_msg_buffer。 - 缓冲区为
deque(maxlen=500):满 500 条后,每次append都会自动丢弃最左侧(最早)的一条。 - 日志中“最早的消息将被丢弃”描述正确,但代码没有在满时拒绝写入或扩容,只是记录错误。
后果:
- 在“订单管理器长期未就绪”或“消息先于管理器到达”的场景下(如启动顺序异常、交易模块延迟加载、长时间断线后重连前已积压大量订单推送),最早的一批订单状态更新会永久丢失。
- 受影响的订单无法再通过 WebSocket 解析,只能依赖:
- 超时 → HTTP 验证 → 可能被标为
TIMEOUT或到超时后才解析; - 或 重连后的
verify_pending_orders()(若重连事件已触发且补查成功)。
- 超时 → HTTP 验证 → 可能被标为
- 可能导致:已成交订单被误判为超时、策略层认为未成交而重复下单或错误风控。
建议修复方向(实施时再细化):
- 缓冲区满时:要么不再 append(或环形缓冲 + 明确丢弃策略),并告警/监控;要么增大容量或按 oid 去重/合并,避免关键终态更新被挤掉。
- 同时保证:管理器就绪后只回放“仍对当前追踪有效”的更新(例如只处理
_tracking中存在的 oid),避免用过期状态覆盖新订单。
三、已核对的设计(未发现严重错误)
以下行为经代码追踪后认为设计正确,未列为严重 bug,仅作说明便于后续加固:
| 点 | 结论 |
|---|---|
| 锁与解析路径 | 状态变更在 _lock 内完成,_resolve() 幂等且统一出口,_terminal_status 在设 TIMEOUT 前锁内再次检查,避免覆盖 WS 终态。 |
| 重复追踪 | 同一 oid 再次 track_order 时,旧追踪被设为 CANCELED 并 result_event.set(),旧超时线程会因 event 唤醒而退出,不会对“新”追踪误设 TIMEOUT。 |
| 多订单单条消息 | _on_order_update / _on_user_fill 中对 items/fills 的循环内,对每个需要解析的 oid 都调用了 _resolve(oid),不会只解析最后一条。 |
| 重连补查 | WebSocketReconnectedEvent 由 src/utils/websocket/enhanced_ws_manager.py 在重连就绪后发布,Executor 订阅并启动 verify_pending_orders(),逻辑正确。 |
| HTTP 并发 | _resolve_via_http 使用 tracking._http_lock,拿不到锁返回 "busy",避免同一 oid 并发 HTTP 导致重复解析。 |
| 去重 | orderUpdates / userFills 在 src/utils/message_deduplicator.py 的 _SKIP_DEDUP_CHANNELS 中,不会因去重丢失订单消息。 |
四、可加固点(非严重 bug)
- 重连补查与超时线程:
verify_pending_orders与_timeout_then_verify可能同时对同一 oid 调 HTTP,后者会得到"busy"并重试;补查二次失败后依赖超时机制。可考虑在重连补查时对busy的 oid 做短延迟再试或记录,避免“误以为未补查到”的告警。 - 缓冲区满时的策略:除上述“不丢关键更新”的修复外,可增加监控指标(例如丢弃条数、按 channel 统计),便于发现“管理器长期未就绪”的配置或启动顺序问题。
五、总结
- 严重 bug:仅 1 个——订单消息缓冲区
maxlen=500满后继续 append 会丢弃最早消息,导致部分订单的 WebSocket 状态更新永久丢失,依赖超时/HTTP 可能误判或延迟,影响策略与风控。 - 其余订单跟踪链路(锁、解析、重连补查、去重、多订单单条消息处理)经审查未见严重逻辑错误;修复缓冲区溢出并做适当监控即可显著降低订单跟踪风险。