auto_trade_sys/docs/订单与统计一致性说明.md
薇薇安 b9392e096c feat(trades): 添加对账校验接口以验证交易记录准确性
新增 `GET /api/trades/verify-binance` 接口,允许用户校验与币安的交易记录一致性。该接口支持指定时间范围和校验条数,返回校验结果的汇总和详细信息,确保策略执行分析所依赖的数据与交易所一致。
2026-02-16 14:02:55 +08:00

133 lines
11 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` / `SYSTEM_ORDER_ID_PREFIX`)后,以下内容的状态如下。
---
## 一、已能保证的部分
### 1. 持仓与币安一致
- **仪表板「当前持仓」**:数据来自 **币安实时持仓**`get_open_positions()`),与币安页面一致(仅受 `POSITION_MIN_NOTIONAL_USDT` 过滤影响)。
- **补建逻辑**:配置了 `SYSTEM_ORDER_ID_PREFIX` 时,**仅当**能明确查到开仓订单且 `clientOrderId` 非空且**不以**该前缀开头时视为手动单并跳过其余含历史单、查不到订单、cid 为空)一律补建并加入监控,避免漏掉系统单。
### 2. 订单记录与币安可对账
- **开仓**:交易系统**所有开仓单**(非 reduce_only在配置了 `SYSTEM_ORDER_ID_PREFIX` 时都会带 `newClientOrderId = 前缀_时间戳_随机`(如 `SYS_xxx`),成交后写入 DB 的 `entry_order_id``client_order_id`,与币安一致。
- **成交后落库**:开仓成交后 `position_manager` 会调用 `Trade.create(..., entry_order_id=..., client_order_id=...)`,订单记录与币安一一对应;若当时落库失败(超时/崩溃),后续同步会通过「补建」把缺失记录补上并纳入监控。
- **平仓**:平仓时保存 `exit_order_id``Trade.update_exit` 会做 `get_by_exit_order_id` 防重复。
- **同步**
- `POST /api/account/positions/sync` 与进程内定时同步:仅当开仓订单 `clientOrderId` **明确非**系统前缀时跳过补建,其余一律补建(含历史单、未带前缀的单),保证「币安有仓且为系统单」都会被监控。
- `POST /api/trades/sync-binance`:用 `get_by_exit_order_id(order_id)` 判断是否已同步,避免重复;开仓侧用 `get_by_entry_order_id(order_id)` 判断是否已存在。
- 因此:**每条 DB 记录都可与币安订单一一对应**(通过 `entry_order_id` / `exit_order_id`),订单记录与币安在「谁开的、谁平的」上一致。
### 3. 统计口径与去重
- **统计**:来自 `Trade.get_all(..., account_id)`,只统计该账号的 DB 记录。
- **净盈亏**`get_net_pnl(t)` 逻辑为:
- 若有 `realized_pnl`:用 `realized_pnl`,再若 `commission_asset == 'USDT'` 则减去 `commission`
- 否则用 `pnl`(按价格差算)。
- 胜率、总盈亏、盈亏比等均基于上述净盈亏汇总,**不会因为重复同步同一条平仓而重复计入**(由 `exit_order_id` 唯一性保证)。
---
## 二、仍依赖「谁在写」的部分
### 1. 手续费commission
| 场景 | 是否写入 commission | 说明 |
|--------------------|----------------------|------|
| 交易系统内平仓 | ✅ 是 | 从 `get_recent_trades``exit_order_id` 汇总 commission写入 `update_exit`。 |
| 仪表板「平仓」按钮 | ✅ 是 | 同上,取成交里的 commission/realizedPnl 写入。 |
| 持仓同步positions/sync | ✅ 是(已补全) | 更新 closed 前按 `exit_order_id` 拉取 `get_recent_trades`,汇总 commission/realizedPnl 并写入。 |
| 订单同步trades/sync-binance | ✅ 是(已补全) | 更新平仓记录前按订单号拉取成交,写入 commission/realized_pnl。 |
因此:**所有标记为已平仓的记录都会尽量带上手续费与实际盈亏**,统计与币安一致。
### 2. 实际盈亏realized_pnl
- **交易系统平仓、仪表板平仓**:从币安成交取 `realizedPnl` 并写入。
- **持仓同步、订单同步**:已补全逻辑,同样按 `exit_order_id` 拉取成交并写入 `realized_pnl`
- 统计中若存在 `realized_pnl` 会优先使用并再扣 USDT 手续费,否则用价格差 `pnl`
---
## 三、订单记录与统计「与币安一致」的根治方式
- **问题**DB 中可能存在币安上看不到的订单(补建脏数据、重复单、无订单号记录),导致系统统计与币安实际盈亏偏差(如系统显示盈利、币安实际亏损)。
- **根治**:接口支持 **仅可对账** 口径:
- **可对账** 定义:有 `entry_order_id`(开仓订单号),且若已平仓则还有 `exit_order_id`(平仓订单号),能与币安订单一一对应。
- **GET /api/trades**、**GET /api/trades/stats** 均支持查询参数 **`reconciled_only`**(默认 **true**
- `reconciled_only=true`:只返回/只统计上述可对账记录,**日盈亏与策略统计与币安一致**。
- `reconciled_only=false`:返回/统计全部 DB 记录(含无订单号的补建等),可能与币安不一致。
- 前端「交易记录」页默认勾选「仅可对账(与币安一致)」,可取消勾选查看全部记录。
## 四、小结:能否「直接保证」?
- **持仓与币安一致**:可以,当前实现已保证(实时持仓 + 按前缀补建)。
- **订单记录与币安可对账**:可以,`entry_order_id` / `exit_order_id` 与防重复逻辑已保证一一对应、不重复。
- **统计与币安一致**:使用 **仅可对账** 口径(`reconciled_only=true`,默认)时,日盈亏、胜率、总盈亏等只基于可对账记录,与币安一致;同一笔平仓只被记录一次,手续费与实际盈亏已在关仓路径补全。
---
## 五、下单路径与「意外订单」排查
### 1. 会向币安下单的入口(汇总)
| 类型 | 入口 | 触发条件 | 是否可能「意外」开仓 |
|------|------|----------|----------------------|
| **开仓** | `trading_system/position_manager.open_position` | 仅由策略在「通过风控 + 有信号」后调用;`risk_manager.should_trade` 会检查:持仓数 ≥ MAX_OPEN_POSITIONS 则 return False且检查该 symbol 是否已有持仓 | 否。持仓数/已有仓检查均基于币安 `get_open_positions()`,到上限或已有该 symbol 即跳过,不会下单。 |
| **开仓** | `backend/api/routes/account.py` 限价下单接口 | 用户在前端/API 主动发起的「手动限价开仓」 | 否,需显式调用。 |
| **平仓** | `position_manager.close_position` | 仅对 `active_positions` 中存在的 symbol 调用(止损/止盈/手动);`check_stop_loss_take_profit` 只遍历 `active_positions` | 否,只平我们监控的仓。 |
| **平仓** | backend 平仓/一键平仓 | 用户主动平仓 | 否,需显式调用。 |
| **止盈/止损单** | `position_manager._ensure_exchange_sltp_orders` | 仅在两处调用:(1) 开仓成功后为该 symbol 挂 SL/TP(2) 补建「仅币安有、DB 无」的持仓时,若开启 SYNC_CREATE_MANUAL_ENTRY_RECORD为补建记录补挂 SL/TP。挂前会先取消该 symbol 下同类型 Algo 单STOP_MARKET/TAKE_PROFIT_MARKET再下新单 | 不会产生「开仓」;可能覆盖用户在该 symbol 上已有的同类型保护单(属预期:补建后统一用系统计算的 SL/TP。 |
结论:**没有逻辑会在「不该开仓」时自动下开仓单**;止盈/止损挂单仅对「我们已纳入监控的持仓」补挂或覆盖,不会对「仅币安有、且未补建」的持仓主动挂单(因未进 `active_positions` 不会走到 `_ensure_exchange_sltp_orders`)。
### 2. 日志里「总是想下一些单」是否正常?
- **策略扫描日志**(如 `处理交易对: SIRENUSDT (UP 31.32%, 市场状态: ranging)`、`SIRENUSDT 4H趋势中性信号质量可能降低`、`SIRENUSDT 持仓数量已达上限16/16跳过开仓`
表示:每轮扫描会**评估**各交易对并可能尝试开仓,但 **`持仓数量已达上限16/16跳过开仓``risk_manager.should_trade` 已 return False****不会调用 `open_position`,也不会向币安下任何单**。这是正常、预期行为。
- **挂止盈/止损相关日志**(如「已挂币安保护单」「挂止盈单失败」等)
表示:仅对 **当前已在 `active_positions` 中且具备 SL/TP 价格的持仓** 在交易所侧挂或更新保护单;每次挂前会先取消同类型旧单再挂新单,不会重复堆积同一 symbol 的多组 SL/TP。
若希望减少「想下单」类日志的干扰,可将「跳过开仓」类日志级别调低(如改为 debug或仅在有实际下单时再打 info。
---
## 六、如何确认订单记录的准确性
策略执行情况分析依赖订单记录,必须保证「能对上的」记录与币安一致。可按下面步骤把握。
### 1. 日常使用:只用「可对账」口径
- 前端「交易记录」**保持勾选「仅可对账(与币安一致)」**(默认即勾选)。
- 看日盈亏、胜率、策略统计时,接口已默认 `reconciled_only=true`,只统计有 `entry_order_id` 且已平仓有 `exit_order_id` 的记录,与币安可一一对应。
- 分析策略执行、复盘盈亏时,以这批记录为准,避免被无订单号或补建脏数据干扰。
### 2. 主动校验:调用「对账校验」接口
- **接口**`GET /api/trades/verify-binance`(需登录并带 `X-Account-Id` 指定账号)。
- **参数**`days`(校验最近 N 天,默认 30、`limit`(最多校验条数,默认 100
- **作用**:对当前账号在时间范围内的「可对账」记录,逐条用币安 `futures_get_order` 校验:
- 开仓订单订单是否存在、symbol/side/数量是否与 DB 一致;
- 平仓订单(已平仓且存在 `exit_order_id`):同上,并确认为 reduceOnly。
- **返回**`summary`(一致/缺失/不一致数量)+ `details`(每条记录的 `entry_verified`/`exit_verified` 及说明)。便于快速发现「对不上」的记录。
**示例**(校验最近 30 天、最多 100 条):
```http
GET /api/trades/verify-binance?days=30&limit=100
X-Account-Id: 1
Authorization: Bearer <token>
```
`entry_missing``exit_missing` 不为 0说明 DB 里有的订单号在币安查不到(可能错写、错账号或历史清理);若 `entry_mismatch`/`exit_mismatch` 不为 0说明订单存在但 symbol/side/数量等与 DB 不一致,需排查写入或同步逻辑。
### 3. 发现对不上时怎么处理
- **仅做策略分析**:继续以「仅可对账」口径为准;有问题的记录因缺订单号或校验不通过,不会进入默认统计。
- **希望 DB 与币安尽量一致**:可先执行 `POST /api/trades/sync-binance`(按「最近 N 天」从币安拉订单并更新/补全平仓与 `exit_order_id`);再跑一次 `GET /api/trades/verify-binance` 看是否仍存在缺失或不一致,再针对单条记录排查或人工修正。
总结:**日常用「仅可对账」统计 + 定期或按需调 `verify-binance` 校验**,即可把订单记录的准确性把握住,策略执行分析所依赖的数据与币安一致。