# 正向流程漏洞分析与加固方案

> 目标：弄清楚正常流程为什么会出现「币安有持仓、DB 无 open 记录」，并完善正向流程，使正常流程掌控全局。

## 一、正常流程梳理

### 1.1 开仓链路（简化）

```
strategy.open_position()
  → 创建 pending 记录 (DB, client_order_id)
  → place_order 下单币安
  → _wait_for_order_filled() REST 轮询
     → 若 FILLED：update_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 路径若在调用前崩溃，则完全依赖 WS；WS 路径若异常，则完全依赖 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 加固点 1：WS 重连后 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 加固点 2：sync_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 加固点 4：update_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 对账，使正常流程在运行中即可发现并修正漏记，而不依赖单独的补建逻辑。
