auto_trade_sys/docs/common/订单记录与币安对账流程.md
薇薇安 13a0e7d580 delete: 移除过时的文档与代码文件
删除了多个不再使用的文档和代码文件,包括交易更新推送、条件订单推送、REST API 文档、WebSocket API 文档及相关的策略分析文档。这些文件的移除有助于清理代码库,确保项目的整洁性与可维护性。
2026-02-20 17:49:00 +08:00

159 lines
10 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.

# 订单记录与币安对账流程
本文梳理:**订单是怎么记的**、**何时写入 entry_order_id / exit_order_id**、**如何与币安保持一致**。
---
## 一、数据模型(与币安对应的关键字段)
| 字段 | 含义 | 与币安对应 |
|------|------|------------|
| `entry_order_id` | 币安开仓订单号 (orderId) | 开仓订单唯一标识,用于与币安「开仓订单」一一对应 |
| `exit_order_id` | 币安平仓订单号 (orderId) | 平仓订单唯一标识,用于与币安「平仓订单」一一对应 |
| `client_order_id` | 币安 clientOrderId | 系统下单时用 `SYSTEM_ORDER_ID_PREFIX_时间戳_随机`,便于区分本系统单 |
| `entry_time` | 开仓时间 | 建议从币安订单/成交取真实时间,便于统计与早止盈 |
| `status` | open / closed / pending | open=持仓中closed=已平仓pending=已下单待成交 |
**「可对账」定义**(与币安一致的口径):
-`entry_order_id`(非空且不为 0
-`status = closed`,还须有 `exit_order_id`(非空且不为 0
接口默认 `reconciled_only=true` 时,只返回/统计满足上述条件的记录。
---
## 二、订单记录是在哪里写的?
### 1. 本系统开仓(策略/限价开仓)
**位置**`trading_system/position_manager.py`(开仓成功、成交确认后)
- **流程**:下单 → 等待成交REST 轮询或 WS→ 取到 `orderId`、成交价、数量 → 写库。
- **写库方式**
- 若之前已建 `status=pending`(限价先挂单):用 `Trade.update_pending_to_filled(client_order_id, account_id, entry_order_id, entry_price, quantity)``Trade.update_pending_by_entry_order_id(symbol, account_id, entry_order_id, entry_price, quantity)` 完善为 open并写入 `entry_order_id`
- 否则直接 `Trade.create(..., entry_order_id=orderId, client_order_id=..., status='open', entry_time=...)`
- **entry_order_id 来源**REST 下单返回的 `order.get("orderId")`,或成交确认时从订单查询得到。
- **entry_time**:支持传入;不传则用当前北京时间;补建/手动同步路径会从币安订单或成交取真实开仓时间。
**结论**:本系统开的仓,在正常落库且无异常时,**开仓时就会带上 entry_order_id**,与币安开仓订单一一对应。
---
### 2. User Data StreamWS 推送)补全订单号
**位置**`trading_system/user_data_stream.py`
- **开仓成交 (ORDER_TRADE_UPDATE, X=FILLED, 非 reduceOnly)**
- 若有 `clientOrderId``Trade.update_pending_to_filled(client_order_id, account_id, order_id, price, quantity)`,把对应 pending 记录完善为 open 并写入 `entry_order_id`
- 若无 clientOrderId`Trade.update_pending_by_entry_order_id(symbol, account_id, order_id, price, quantity)`,用 symbol+account 下「唯一一条 pending 且无 entry_order_id」的记录做兜底补全。
- **平仓成交 (ORDER_TRADE_UPDATE, X=FILLED, reduceOnly)**
- `Trade.set_exit_order_id_for_open_trade(symbol, account_id, order_id, entry_order_id_hint)`:给该 symbol 下当前 open 且无 exit_order_id 的记录写入平仓订单号(有 entry_order_id 时优先按 entry_order_id 精确匹配)。
- **条件单触发 (ALGO_UPDATE, X=TRIGGERED/FINISHED)**
- `ai` = 触发后的普通订单 orderId同样调用 `Trade.set_exit_order_id_for_open_trade(symbol, account_id, ai, ...)` 回写 `exit_order_id`
**结论**WS 负责在「开仓/平仓成交或条件单触发」时,把币安订单号回写到 DB保证与币安一致。
---
### 3. 本系统平仓(止损/止盈/手动平仓)
**位置**`trading_system/position_manager.py`close_position 成功后)
- **流程**:市价平仓或条件单触发 → 拿到平仓订单号 → `Trade.update_exit(trade_id, exit_price, exit_reason, pnl, pnl_percent, exit_order_id=..., exit_time_ts=..., commission=..., realized_pnl=...)`
- **exit_order_id 来源**:平仓接口返回的 `order.get('orderId')`,或从 `get_recent_trades` 按订单号汇总。
- **commission / realized_pnl**:从币安成交 `get_recent_trades` 按该订单号汇总,写入 DB统计与币安一致。
**结论**:本系统平的仓,平仓路径会写入 `exit_order_id` 及手续费/实际盈亏,与币安平仓订单一致。
---
### 4. 补建「币安有仓、DB 无记录」(状态同步)
**位置**`trading_system/position_manager.py`sync_positions_with_binance
- **何时发生**:定时同步或调用 `POST /api/account/positions/sync` 时,发现某 symbol 在币安有持仓,但 DB 没有对应 open 记录。
- **补建逻辑**(简要):
- 若配置了 `SYSTEM_ORDER_ID_PREFIX`:会查该 symbol 的开仓订单(如 get_all_orders / get_recent_trades取**同方向、时间合理**的订单作为 `entry_order_id`;若查到 `clientOrderId` 且**以系统前缀开头**则视为系统单并补建;若**明确不以系统前缀开头**则视为手动单,按配置可跳过或仍补建(如开启 SYNC_CREATE_MANUAL_ENTRY_RECORD
- 补建时调用 `Trade.create(..., entry_reason='sync_recovered' 或 'manual_entry', entry_order_id=..., entry_time=...)`**尽量从币安订单/成交取真实 entry_order_id 和 entry_time**。
- **entry_order_id 来源**`futures_get_order(entry_order_id)` 或同方向成交中最早一笔的 orderId若拿不到则可能为空后续靠「同步币安订单」补全
**结论**:补建记录会尽量带上 `entry_order_id` 和真实开仓时间;若当时拿不到,需依赖「同步币安订单」补全。
---
### 5. 同步币安订单(后端接口)
**位置**`backend/api/routes/trades.py``POST /api/trades/sync-binance`
- **作用**:按时间范围拉取币安历史订单,与 DB 对齐:**补全缺失的 entry_order_id / exit_order_id**,必要时**新建 DB 记录**。
- **开仓订单**
-`Trade.get_by_entry_order_id(order_id)` 已存在 → 跳过。
- 否则在该 symbol 下找「时间窗口内且无 entry_order_id」的记录按价格/数量匹配(允许少量误差)→ 匹配到则 `Trade.update_entry_order_id(trade_id, order_id)` 补全;匹配不到则若开启「全量同步」可 `Trade.create(..., entry_order_id=order_id, entry_reason='sync_from_binance', status='open')` 新建。
- **平仓订单 (reduceOnly)**
-`Trade.get_by_exit_order_id(order_id)` 已存在 → 视情况跳过。
- 否则找该 symbol 的 open 或「已 closed 但 exit_order_id 为空」的记录,匹配后 `Trade.update_exit(..., exit_order_id=order_id, ...)`,并可从成交拉取 commission/realized_pnl 写入。
**结论****与币安订单一致的关键一步**。DB 里已有记录但缺订单号时,用此接口可把 entry_order_id / exit_order_id 补全;若 DB 完全没有记录且开了全量同步,会按币安开仓订单新建记录,保证「订单记录」与币安可对账。
---
## 三、如何与币安订单一致?(操作与口径)
### 1. 保证「有订单号」
- **本系统开/平仓**:正常落库 + WS 回写,会自动有 `entry_order_id` / `exit_order_id`
- **补建/手动同步**:补建时已尽量从币安取 `entry_order_id``entry_time`;若仍缺订单号(如历史旧数据),可:
- 在前端「交易记录」页使用 **「同步订单」**(即 `POST /api/trades/sync-binance`),选择时间范围(如今天/7 天),必要时勾选「全量同步」;
- 同步后会补全「开仓/平仓订单号」,并可能新建缺失记录;同步结果会提示补全了多少个 entry_order_id / exit_order_id。
### 2. 只用「可对账」口径看订单与统计
- 前端「交易记录」默认 **勾选「仅可对账(与币安一致)」**`reconciled_only=true`)。
- 接口行为:
- `GET /api/trades?reconciled_only=true`:只返回「有 entry_order_id且若已平仓则有 exit_order_id」的记录。
- `GET /api/trades/stats?reconciled_only=true`:只统计上述记录,日盈亏、胜率、总盈亏与币安一致。
- 这样**订单记录与统计都只基于「能和币安一一对上」的数据**,避免无订单号或脏数据干扰。
### 3. 主动校验是否一致
- **接口**`GET /api/trades/verify-binance?days=30&limit=100`(需登录与账号)。
- **作用**:对「可对账」记录逐条用币安 `futures_get_order` 校验开仓/平仓订单是否存在、symbol/side/数量是否一致;返回一致/缺失/不一致数量及明细。
- 建议定期或在对账有疑问时调用,确认 DB 与币安一致。
---
## 四、流程简图(何时有 entry_order_id / exit_order_id
```
本系统开仓
→ 下单得到 orderId
→ Trade.create(..., entry_order_id=orderId) 或 update_pending_to_filled / update_pending_by_entry_order_id
→ [WS] ORDER_TRADE_UPDATE FILLED 可再次补全 entry_order_id若之前未写入
本系统平仓
→ 平仓得到 orderId
→ Trade.update_exit(..., exit_order_id=orderId)
→ [WS] ORDER_TRADE_UPDATE FILLED / ALGO_UPDATE 可再次补全 exit_order_id若之前未写入
币安有仓、DB 无(补建)
→ Trade.create(..., entry_order_id=从币安订单/成交取, entry_time=从币安取)
→ 若当时取不到 entry_order_id后续靠「同步币安订单」补全
同步币安订单 (POST /api/trades/sync-binance)
→ 开仓订单:匹配 DB 无 entry_order_id 的记录 → update_entry_order_id或新建 Trade(..., entry_order_id=orderId)
→ 平仓订单:匹配 open 或 closed 无 exit_order_id → update_exit(..., exit_order_id=orderId)
→ 结果DB 与币安在「谁开的、谁平的」上一致
```
---
## 五、小结
| 问题 | 答案 |
|------|------|
| 订单是怎么记录的? | 开仓:`Trade.create` 或 update pending → open并写 `entry_order_id`;平仓:`Trade.update_exit` 写 `exit_order_id`。补建与同步也会创建/更新记录。 |
| entry_order_id 从哪来? | 本系统开仓:下单/成交返回的 orderId补建币安 get_all_orders / get_recent_trades / futures_get_order缺失时同步币安订单接口按时间/价格匹配补全。 |
| exit_order_id 从哪来? | 本系统平仓:平仓接口返回的 orderIdWSORDER_TRADE_UPDATE / ALGO_UPDATE 回写;同步币安订单:按 reduceOnly 订单匹配并 update_exit。 |
| 如何与币安一致? | 1保证写库路径都尽量写入订单号2缺订单号时用「同步订单」补全3前端与统计只用「仅可对账」口径reconciled_only=true4需要时用 verify-binance 接口校验。 |