删除了多个不再使用的文档和代码文件,包括交易更新推送、条件订单推送、REST API 文档、WebSocket API 文档及相关的策略分析文档。这些文件的移除有助于清理代码库,确保项目的整洁性与可维护性。
7.9 KiB
订单记录简化流程(支付式闭环)
参考 订单交易更新推送.txt、条件订单交易更新推送.txt,把订单记录做成「先本地单号 → 写 DB + 下单 → 仅靠 WS 推送更新状态」的闭环,和支付系统类似。
一、目标流程(你期望的)
- 先本地生成订单号(如
client_order_id = SYS_时间戳_随机)。 - 写 DB 与下单尽量一体:先插入一条「待成交」记录(带
client_order_id),再立刻用该 id 去交易所下单(REST 或 WS,选更稳的方式);逻辑上视为「同一事务」——下单失败则把该条记录标为失败/取消。 - 状态只跟 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。