当前系统订单跟踪存在哪些严重的bug5
订单跟踪严重 Bug 分析
一、订单跟踪链路概览
sequenceDiagram
participant Exec as Executor
participant WSM as WebSocketOrderManager
participant WS as WebSocket
participant PM as PositionManager
participant Repo as TradeRepository
Exec->>WSM: track_order(oid, coin, timeout)
WSM->>WSM: _timeout_then_verify 线程启动
Exec->>WSM: wait_for_order(tracking)
Note over WS: orderUpdates / userFills
WS->>RealtimeKline: on_message
RealtimeKline->>WSM: handle_message(msg)
WSM->>WSM: _on_order_update / _on_user_fill
WSM->>WSM: result_event.set()
Exec->>Exec: 更新 order_result,返回
Exec->>PM: 返回 order_result
PM->>Repo: save_position + save_order
- 追踪入口:
executor._track_limit_order对限价单调用track_order+wait_for_order,依赖 WebSocket 的orderUpdates/userFills或超时后 HTTP 补查。 - 消息来源:
realtime_kline_service_base.on_message根据channel in ("orderUpdates", "userFills")转发给_ws_order_manager.handle_message。 - 持久化:
position_manager在开仓/平仓成功后调用_repo.save_position与_repo.save_order(先 position 后 order)。
二、严重 / 高优先级 Bug
1. 持久化部分写入导致数据不一致(严重)
位置:position_manager.py 开仓约 179–183 行、平仓约 537–548 行。
现象:先执行 _repo.save_position(position),再执行 _repo.save_order(..., leg_a/leg_b)。若 save_order 抛异常(如 DB 短暂不可用),position 已写入 DB,订单未写入,导致:
- 库中存在仓位记录但无对应订单明细;
- 对账、审计、统计会漏单或报错。
建议:
- 将“写 position + 写 order(s)”放在同一事务中(同一 connection、同一 transaction),任一步失败则整体回滚;或
- 先写 order(s) 再写 position,并在文档中约定“有 position 必有 order”,便于监控与修复脚本识别“有仓无单”的脏数据。
2. order_id 类型未统一,存在隐蔽风险(高)
位置:executor._parse_order_response 约 1133–1140 行;models.OrderResult.order_id 标注为 int | None。
现象:Hyperliquid API 返回的 oid 在 JSON 中可能是数字或字符串,代码直接 result.order_id = filled.get("oid") / resting.get("oid"),未做类型归一化。后果:
WebSocketOrderManager内已用_safe_int_oid/int(oid)做查找,故 _tracking 查找通常正常;- 但若其他地方用
order_result.order_id做 key、比较或传给query_order_status等,在 str/int 混用场景下可能出错或静默行为异常; - 类型标注与运行时不一致,增加维护成本。
建议:在解析下单响应时统一将 oid 转为 int(转换失败时记录日志并置为 None),并保证 OrderResult.order_id 始终为 int | None。
3. 订单管理器未就绪时订单推送被丢弃(高)
位置:realtime_kline_service_base.on_message 约 612–624 行。
现象:当 channel in ("orderUpdates", "userFills") 时,若 _trading_orchestrator._executor._ws_order_manager 为 None 或未就绪,消息被丢弃并只打 warning。可能场景:
- 服务启动顺序导致 WebSocket 先连上并开始收包,而 executor/order_manager 尚未初始化;
- 断线重连后,在
verify_pending_orders执行前已有订单状态推送到达,但此时若存在某种“未就绪”状态,推送仍会被丢弃。
后果:断线期间已成交的订单无法通过 WS 及时结算,只能依赖超时后的 HTTP 补查,用户等待时间变长,或偶发误判为超时。
建议:
- 明确启动顺序,保证在开始接收交易相关 WebSocket 消息前
_ws_order_manager已创建; - 若无法保证,可考虑在“未就绪”时对 orderUpdates/userFills 做有限缓冲(如按 oid 缓存最近 N 条),在 order_manager 就绪后回放一次(需防重放导致重复处理);
- 或至少在日志中区分“未就绪丢弃”与“正常处理”,便于排查。
4. HTTP 订单状态解析对未知 status 一律视为 TIMEOUT(中高)
位置:websocket_order_manager._parse_order_response 约 296–315 行。
现象:仅显式处理 filled / canceled / margincanceled / rejected / open,其余(如交易所返回 partial 或其他新状态)统一返回 OrderStatus.TIMEOUT。若交易所对“部分成交”等返回非 open 且非 filled 的 status,会被误判为超时,进而:
- 主线程认为订单“超时”,可能触发撤单 + 市价平仓或回滚;
- 实际订单可能仍在挂单或部分成交,造成逻辑与交易所状态不一致。
建议:查阅交易所文档,对 partial 等已知状态做显式映射(如视为仍 PENDING 或单独状态);对真正未知的 status 再落为 TIMEOUT,并打 warning 带上原始 raw_status,便于后续扩展。
三、中优先级 / 设计层面问题
5. 重连/清理时取消订阅格式与 order 订阅不匹配(中)
位置:enhanced_ws_manager 约 800–806 行(强制清理时按 sub_key 构造 unsubscribe 的 subscription)。
现象:sub_key 为 (type, coin, interval),取消订阅时构造 {type, coin, interval}。而 orderUpdates/userFills 的订阅格式为 {type, user},无 coin/interval。因此对 order 类频道发出的 unsubscribe 是错误格式,服务端可能无法正确识别,仅起到“本地清理 active_subscriptions”作用。
影响:重连后仍会用 self.subscriptions 里的完整对象重新 subscribe,所以订单推送一般能恢复;主要影响是清理阶段与服务端状态可能不一致,以及未来若依赖“先取消再订阅”的语义时可能出问题。
建议:按订阅类型分支构造 unsubscribe 的 payload(例如对 orderUpdates/userFills/user 使用 type+user,对 candle 等使用 type+coin+interval),或统一用“完整 subscription 对象”作为 key/取消参数,避免只用 (type, coin, interval)。
6. 订单消息与 K 线消息共用同一连接与去重逻辑(中)
位置:订单消息在 message_deduplicator 中已排除(orderUpdates/userFills 不参与去重),但订单与 K 线仍在同一 WebSocket 连接、同一 on_message 中处理。
现象:若 K 线处理阻塞或抛错,可能拖慢或影响订单消息处理(取决于异常是否被吞掉)。当前实现里订单分支在 K 线解析之前且 return,因此影响有限,但架构上订单与高吞吐 K 线混在一起,存在潜在延迟或相互影响风险。
建议:中长期可考虑将订单通道与行情通道分离(例如独立连接或独立 worker),或至少保证订单分支的异常不导致整条消息被跳过且无监控。
四、已确认无问题的设计点
- result_event 与 _tracking 的配对:所有
_tracking.pop(oid)的路径都伴随tracking.result_event.set(),不会造成wait_for_order永久阻塞。 - 同一 oid 多路径结算:
_resolve_via_http与_timeout_then_verify、verify_pending_orders之间通过二次拿锁和get(oid)检查,避免重复结算或状态覆盖。 - userFills 与 orderUpdates 顺序:先到 userFills 则累计;先到 orderUpdates filled 则用 limitPx 或已累计值,后到的 userFills 进 cache,由
wait_for_order内 pop 并回填,逻辑一致。 - 重连后补查:
WebSocketReconnectedEvent触发verify_pending_orders,对当前所有 PENDING 做 HTTP 补查,能弥补断线期间遗漏的成交。
五、建议修复顺序
- 优先:修复“position 与 order 持久化”的事务/顺序(Bug 1),避免数据不一致。
- 高:统一
order_id类型并在解析处归一化为 int(Bug 2);加强“订单管理器未就绪”时的策略与可观测性(Bug 3)。 - 中高:扩展
_parse_order_response对partial等状态的语义(Bug 4)。 - 中:修正重连/清理时 order 相关订阅的取消格式(Bug 5);视需求评估订单与 K 线通道分离(Bug 6)。
以上为当前代码库下订单跟踪相关的严重与重要 bug 分析及修复建议。