删除了多个不再使用的文档和代码文件,包括交易更新推送、条件订单推送、REST API 文档、WebSocket API 文档及相关的策略分析文档。这些文件的移除有助于清理代码库,确保项目的整洁性与可维护性。
133 lines
7.9 KiB
Markdown
133 lines
7.9 KiB
Markdown
# 订单记录简化流程(支付式闭环)
|
||
|
||
参考 `订单交易更新推送.txt`、`条件订单交易更新推送.txt`,把订单记录做成「先本地单号 → 写 DB + 下单 → 仅靠 WS 推送更新状态」的闭环,和支付系统类似。
|
||
|
||
---
|
||
|
||
## 一、目标流程(你期望的)
|
||
|
||
1. **先本地生成订单号**(如 `client_order_id = SYS_时间戳_随机`)。
|
||
2. **写 DB 与下单尽量一体**:先插入一条「待成交」记录(带 `client_order_id`),再立刻用该 id 去交易所下单(REST 或 WS,选更稳的方式);逻辑上视为「同一事务」——下单失败则把该条记录标为失败/取消。
|
||
3. **状态只跟 WS 走**:用币安 **ORDER_TRADE_UPDATE**(和必要时 **ALGO_UPDATE**)驱动所有「成交、平仓、取消」等状态与字段更新,DB 只根据推送更新,不依赖 REST 轮询结果做主数据。
|
||
|
||
这样:**一条 DB 记录 = 一次「开仓意图」或「平仓意图」**,用本地 id 和交易所 id 串联,WS 是唯一事实来源,逻辑简单、易对账。
|
||
|
||
---
|
||
|
||
## 二、币安推送里用到的字段(docs/bian)
|
||
|
||
### ORDER_TRADE_UPDATE(订单交易更新推送)
|
||
|
||
| 字段 | 含义 | 用途 |
|
||
|------|------|------|
|
||
| `e` | 事件类型 | ORDER_TRADE_UPDATE |
|
||
| `o.c` | 客户端自定义订单 ID | **clientOrderId**,我们下单时传的,用来唯一匹配「本地这条记录」 |
|
||
| `o.i` | 订单 ID | **orderId**,交易所订单号,写 entry_order_id / exit_order_id |
|
||
| `o.x` | 本次事件类型 | NEW / TRADE / CANCELED / EXPIRED 等 |
|
||
| `o.X` | 订单当前状态 | NEW / PARTIALLY_FILLED / **FILLED** / CANCELED 等 |
|
||
| `o.ap` | 订单平均成交价 | 成交后更新 entry_price 或 exit_price |
|
||
| `o.z` | 订单累计成交量 | 成交数量 |
|
||
| `o.R` | 是否只减仓 | true = 平仓单 |
|
||
| `o.rp` | 该笔实现盈亏 | 平仓时写 pnl/realized_pnl |
|
||
|
||
**开仓**:`o.R != true` 且 `o.X == FILLED` → 用 `o.c` 找到 pending 记录,更新为 open,写入 `entry_order_id=o.i`、`entry_price=o.ap`、`quantity=o.z`。
|
||
**平仓**:`o.R == true` 且 `o.X == FILLED` → 用 `o.s`(symbol) + 当前 open 记录匹配,写入 `exit_order_id=o.i`、`exit_price=o.ap`、`pnl` 等(可用 `o.rp`)。
|
||
|
||
### ALGO_UPDATE(条件订单交易更新推送)
|
||
|
||
| 字段 | 含义 | 用途 |
|
||
|------|------|------|
|
||
| `o.X` | 条件单状态 | TRIGGERED / FINISHED 表示已触发 |
|
||
| `o.ai` | 触发后普通订单 id | 平仓单触发后,用 **ai** 作为 exit_order_id,并等 ORDER_TRADE_UPDATE(ai) 拿成交价、rp |
|
||
|
||
止损/止盈是「条件单」:先下 Algo 单,触发后生成一笔普通订单,推送里给 `ai`。我们应:
|
||
1)在 ALGO_UPDATE 里用 `ai` 回写 `exit_order_id`;
|
||
2)在随后收到的 ORDER_TRADE_UPDATE(`o.i == ai`)里用 `ap/z/rp` 回写 exit_price、pnl 等,这样平仓数据也闭环。
|
||
|
||
---
|
||
|
||
## 三、闭环流程(按事件串起来)
|
||
|
||
### 开仓
|
||
|
||
```
|
||
1. 生成 client_order_id = SYS_<ts>_<rand>
|
||
2. 写 DB:INSERT 一条 status=pending, client_order_id=client_order_id, symbol/side/quantity/...
|
||
3. 下单:REST 或 WS order.place,带 newClientOrderId=client_order_id
|
||
- 若下单失败:UPDATE 该条为 status=canceled 或 failed(保证「有记录」且状态明确)
|
||
4. 之后只依赖 WS:
|
||
- 收到 ORDER_TRADE_UPDATE,o.c=client_order_id,o.X=FILLED,非 R
|
||
- → UPDATE 该条:status=open, entry_order_id=o.i, entry_price=o.ap, quantity=o.z
|
||
```
|
||
|
||
这样:**开仓是否成交、成交价/量、交易所 orderId** 全部由 WS 一次更新完成,不依赖 REST 轮询。
|
||
|
||
### 平仓(市价/限价主动平)
|
||
|
||
```
|
||
1. 不新建 DB 记录,只「选一条当前 open 记录」准备关仓
|
||
2. 下单:reduceOnly 市价/限价单(可带 newClientOrderId 便于对账)
|
||
3. 只依赖 WS:
|
||
- 收到 ORDER_TRADE_UPDATE,o.R=true,o.X=FILLED,o.s=symbol
|
||
- → 按 symbol(+ 可选 orderId/clientOrderId) 匹配那条 open
|
||
- → UPDATE:status=closed, exit_order_id=o.i, exit_price=o.ap, pnl/realized_pnl 等(可用 o.rp)
|
||
```
|
||
|
||
### 平仓(条件单触发:止损/止盈)
|
||
|
||
```
|
||
1. 不新建 DB 记录,只为当前 open 挂 Algo 单(STOP_MARKET/TAKE_PROFIT_MARKET 等)
|
||
2. 触发后只依赖 WS:
|
||
- 先收到 ALGO_UPDATE:o.X=TRIGGERED/FINISHED,o.ai=触发后订单 id
|
||
→ UPDATE 该 open:exit_order_id=o.ai(先占位)
|
||
- 再收到 ORDER_TRADE_UPDATE:o.i=o.ai,o.X=FILLED,o.R=true
|
||
→ 同一条记录:exit_price=o.ap,pnl/realized_pnl 用 o.rp,必要时补 duration/exit_time
|
||
```
|
||
|
||
这样:**条件单触发的平仓**也完全由 WS 闭环,不依赖 REST 或定时同步做主数据。
|
||
|
||
---
|
||
|
||
## 四、和当前实现的对应关系
|
||
|
||
| 目标步骤 | 当前实现 | 说明 |
|
||
|----------|----------|------|
|
||
| 本地先生成 client_order_id | ✅ 已有 | position_manager 里 `SYS_ts_rand`,并传 `newClientOrderId` |
|
||
| 先写 DB 再下单 | ✅ 已有 | 先 `Trade.create(..., status='pending', client_order_id=...)`,再下单 |
|
||
| 下单失败把记录标失败 | ⚠️ 部分 | 有失败路径,但不一定统一 UPDATE 为 canceled/failed |
|
||
| 开仓成交只靠 WS 更新 | ✅ 已有 | User Data Stream 里 ORDER_TRADE_UPDATE FILLED + 非 R → `update_pending_to_filled(client_order_id, ..., order_id, ap, z)` |
|
||
| 平仓成交只靠 WS 更新 exit_order_id | ✅ 已有 | ORDER_TRADE_UPDATE FILLED + R → `set_exit_order_id_for_open_trade(symbol, account_id, order_id)` |
|
||
| 平仓成交用 WS 更新 exit_price / pnl | ❌ 缺口 | 目前只写了 exit_order_id,**没有**在 WS 里用 `ap/rp` 调 `update_exit(..., exit_price, pnl, ...)`,条件单触发的平仓尤其缺 |
|
||
| 条件单触发用 ai 写 exit_order_id | ✅ 已有 | ALGO_UPDATE 里 `set_exit_order_id_for_open_trade(symbol, account_id, ai)` |
|
||
| 「DB + 下单」原子性 | ⚠️ 语义上的 | DB 与交易所是两套系统,无法真 2PC;只能「先写 DB 再下单,失败则把该条标为失败」 |
|
||
|
||
所以:**主流程已经接近「支付式」**,真正缺的闭环是:**WS 收到平仓成交(含条件单触发的 ai 那笔)时,不仅要写 exit_order_id,还要用推送里的 ap/rp 等把 exit_price、pnl、exit_time 等一次更新掉**。
|
||
|
||
---
|
||
|
||
## 五、建议补的一步(闭环平仓数据)
|
||
|
||
在 **User Data Stream** 里,当 `ORDER_TRADE_UPDATE` 中 `o.R == true` 且 `o.X == FILLED` 时,除现有 `set_exit_order_id_for_open_trade(symbol, account_id, order_id)` 外,建议:
|
||
|
||
- 用同一推送里的 `o.ap`、`o.z`、`o.rp`(及可选 `o.N`/`o.n` 手续费)和当前时间(或 `o.T` 成交时间),对**同一条 open 记录**再调一次 **update_exit**(或等价的「按 exit_order_id 补全 exit_price / pnl / commission」接口),把:
|
||
- exit_price
|
||
- pnl / pnl_percent(或 realized_pnl)
|
||
- commission(若有)
|
||
- exit_time
|
||
|
||
都从 WS 一次写库。这样:
|
||
|
||
- 市价/限价平仓:ORDER_TRADE_UPDATE 一次即可完成「exit_order_id + 价格 + 盈亏」闭环。
|
||
- 条件单平仓:ALGO_UPDATE 写 exit_order_id → 等 ORDER_TRADE_UPDATE(ai) 再按上面补全价格和盈亏,也闭环。
|
||
|
||
---
|
||
|
||
## 六、小结
|
||
|
||
- **你的理解**:先本地单号 → 写 DB + 下单(尽量一体)→ 只靠 WS 通知更新订单状态与成交数据,是正确且更清晰的设计。
|
||
- **当前实现**:开仓侧已经是「先生成 client_order_id、先写 pending、再下单、WS 完善」;平仓侧 **exit_order_id** 已由 WS 回写,但 **exit_price / pnl 等还未在 WS 路径里统一写库**,算一个遗漏。
|
||
- **补上这一块**(WS 平仓成交时顺带 update_exit 价格与盈亏),就能在「不依赖 REST 轮询、不依赖同步接口做主数据」的前提下,做到订单记录与币安完全以 WS 为源的闭环。
|
||
- **事务**:DB 与交易所无法真事务,只能「先 DB 再下单,失败则把该条记录标为失败/取消」,当前逻辑已接近,可再统一失败态标记。
|
||
|
||
文档参考:`docs/bian/订单交易更新推送.txt`、`docs/bian/条件订单交易更新推送.txt`。
|