auto_trade_sys/docs/正向流程漏洞分析与加固方案.md
薇薇安 e4e6e64608 feat(trade, binance_client, position_manager, user_data_stream): 增强待处理记录对账逻辑
在 `models.py` 中新增 `get_pending_recent` 方法,用于获取最近的待处理交易记录。`binance_client.py` 中添加 `get_order_by_client_order_id` 方法,以支持按 `client_order_id` 查询订单。`position_manager.py` 中实现 `_reconcile_pending_with_binance` 方法,增强对待处理记录的对账能力。`user_data_stream.py` 中在重连前执行待处理记录对账,确保系统在断线期间的交易状态得到及时更新。这些改进提升了系统的稳定性与交易记录的准确性。
2026-02-21 11:09:01 +08:00

173 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 正向流程漏洞分析与加固方案
> 目标弄清楚正常流程为什么会出现「币安有持仓、DB 无 open 记录」,并完善正向流程,使正常流程掌控全局。
## 一、正常流程梳理
### 1.1 开仓链路(简化)
```
strategy.open_position()
→ 创建 pending 记录 (DB, client_order_id)
→ place_order 下单币安
→ _wait_for_order_filled() REST 轮询
→ 若 FILLEDupdate_pending_to_filled()update_open_fields()
→ 若超时/CANCELED撤单返回 None
```
### 1.2 两条更新路径
| 路径 | 触发点 | 说明 |
|------|--------|------|
| **REST 路径** | `open_position``_wait_for_order_filled` 返回 ok | 进程存活、未超时 |
| **WS 路径** | `UserDataStream._on_order_trade_update` 收到 ORDER_TRADE_UPDATE | 需 listenKey 连接正常 |
正常情况下REST 和 WS 都会尝试更新;`update_pending_to_filled` 幂等,重复更新无害。
---
## 二、遗漏场景分析
### 2.1 进程崩溃 / 重启(最主要)
**场景**
- 创建 pending下单币安进入 `_wait_for_order_filled` 轮询
- 进程在轮询过程中崩溃OOM、kill、异常退出
- 订单在币安成交
- 重启后:
- REST 路径不会再执行(`open_position` 已结束)
- WS 不会重放历史推送,`ORDER_TRADE_UPDATE` 已丢失
**结果**pending 长期残留币安有持仓DB 无 open。
### 2.2 WS 断线期间成交
**场景**
- listenKey 失效 / 网络抖动WS 断开
- 在断开期间订单成交,未收到 ORDER_TRADE_UPDATE
- REST 路径:若 `open_position` 仍在运行,轮询会拿到 FILLED可以更新若已超时返回则不会更新
**结论**只有在「WS 断线 + REST 已超时返回」时,才会出现漏记。`open_position` 中限价单超时后会撤单,一般不留下持仓;但若存在「撤单与成交」的竞态,仍可能漏记。
### 2.3 listenKey 失效
- 60 分钟无 keepalive 会失效
- 文档建议 24 小时主动重连
- 失效期间新连接不会重放历史事件
效果同 2.2。
### 2.4 重连空窗期
- 断线 → 60s 后重连 → 新 listenKey
- 空窗期内的成交事件永久丢失
- 若该期间 REST 也未完成轮询(例如进程崩溃),则必然漏记
### 2.5 update_pending_to_filled 异常
-`Trade.update_pending_to_filled` 抛异常,`_on_order_trade_update` 会 catch 并打日志pending 保持
- REST 路径若在调用前崩溃,则完全依赖 WSWS 路径若异常,则完全依赖 REST
- 任一路径失败且另一路径也失败,则漏记
### 2.6 竞态:撤单 vs 成交
- 限价单超时,调用 `cancel_order`
- 若撤单请求发出时订单刚好成交,撤单可能失败
- 当前逻辑:超时则 `return None`pending 保留,不会执行 `update_pending_to_filled`
- **结果**pending 残留 + 币安已成交,属于漏记
---
## 三、当前 sync_positions_with_binance 的覆盖范围
| 情况 | 是否处理 |
|------|----------|
| DB open、币安无 | ✅ 更新 DB 为 closed |
| DB open、币安有 | ✅ 加载到 active_positions |
| 币安有、DB 无 open | ❌ 不处理(依赖 SYNC_RECOVER_MISSING_POSITIONS |
| DB pending、币安订单已 FILLED | ❌ 不处理 |
当前同步逻辑不包含「pending 对账」:不会主动查币安订单状态,也不会把已成交的 pending 转为 open。
---
## 四、正向流程加固方案
### 4.1 思路
不依赖补建SYNC_RECOVER_MISSING_POSITIONS在正向流程中补齐「pending 对账」能力,使:
- 有 pending 且有 client_order_id / entry_order_id 时,主动查币安订单状态
- 若已 FILLED则执行 `update_pending_to_filled`,将 pending 转为 open
### 4.2 加固点 1WS 重连后 pending 对账(推荐)
**位置**`user_data_stream.py``_run_ws` 重连成功后
**逻辑**
- 重连成功后,查询当前账号下 status=pending 且有 client_order_id 的记录(可限制如 24h 内)
- 对每条记录调用币安 REST`futures_get_order(symbol, orderId)` 或按 client_order_id 查
- 若 status=FILLED调用 `Trade.update_pending_to_filled`
**意义**:补齐 WS 断线期间丢失的 ORDER_TRADE_UPDATE。
### 4.3 加固点 2sync_positions_with_binance 中增加 pending 对账(推荐)
**位置**`position_manager.sync_positions_with_binance`
**逻辑**
- 在现有「DB open vs 币安持仓」同步之外,增加:
- 查询 `Trade.get_pending_recent(account_id, limit=50, max_age_sec=86400)`(需在 models 中新增)
- 对每条 pending若存在 client_order_id 或 entry_order_id查币安订单
- 若 FILLED`update_pending_to_filled``update_pending_by_entry_order_id`
**意义**周期性兜底覆盖进程重启、WS 漏推等场景。
### 4.4 加固点 3撤单后校验是否已成交可选
**位置**`position_manager.open_position`,在 `cancel_order` 之后
**逻辑**
- 撤单后(或撤单异常时),再查一次订单状态
- 若 status=FILLED则按 REST 路径正常执行 `update_pending_to_filled`,避免竞态漏记
**意义**:消除「撤单与成交」竞态导致的漏记。
### 4.5 加固点 4update_pending_to_filled robustness
- 保持幂等(当前已满足)
- 异常时记录清晰日志,便于排查
- 可选:对瞬时 DB 异常做有限次重试
---
## 五、实现优先级建议
| 优先级 | 加固点 | 影响 | 复杂度 |
|--------|--------|------|--------|
| P0 | sync_positions_with_binance 中 pending 对账 | 覆盖进程重启、WS 漏推等主要漏记 | 中 |
| P0 | WS 重连后 pending 对账 | 覆盖断线期间的漏推 | 中 |
| P1 | 撤单后校验是否已成交 | 消除竞态漏记 | 低 |
| P2 | update_pending_to_filled 重试与日志 | 提升可靠性 | 低 |
---
## 六、需要的 DB / API 支持
1. **Trade 模型**
- `get_pending_recent(account_id, limit, max_age_sec)`:返回指定时间范围内的 pending 记录,用于对账
2. **币安 API**
-`clientOrderId` 查询:`futures_get_all_orders(symbol, limit)` 过滤,或使用支持 `origClientOrderId` 的接口(如有)
-`orderId` 查询:`futures_get_order(symbol, orderId)`(已有)
3. **多账号**:以上逻辑需按 `account_id` 隔离,保证对账时使用对应账号的 client / API。
---
## 七、小结
- 主要漏记来自:**进程崩溃 + 订单已成交**,以及 **WS 断线期间的成交**
- 正向流程目前缺少「pending 对账」:不会主动用币安订单状态修正 pending。
- 加固方向:在 **WS 重连后****sync_positions_with_binance** 中加入 pending 对账,使正常流程在运行中即可发现并修正漏记,而不依赖单独的补建逻辑。