trading skills: 文档同步 (单币上限规则/平仓盈亏字段/静默规则/推送模板)
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
# 平仓推送: 真实 pnl + 持仓时长 (2026-09-19 新增, 2026-09-19 修正数据源)
|
||||
|
||||
**用户原话**: "平仓后成了历史单,单子上不是有价格吗" / "没看到推送的盈亏" / "BTC -46.51 你怎么算 -45.14"
|
||||
|
||||
## 演进: 2 个数据源 + 多次踩坑
|
||||
|
||||
### v1 (2026-09-19 上午) — `/trade/orders-history` ❌ 已废弃
|
||||
|
||||
最初用 `/api/v5/trade/orders-history` 拿 `info.pnl` 字段 (单笔盈亏)。**坑 3 个**:
|
||||
|
||||
1. `state='2'` (数字) → OKX 返回 code 51600 "Status not found"; **必须用 `state='filled'` 字符串**
|
||||
2. 缺 `instType=SWAP` → code 50014 "Parameter instType can not be empty"
|
||||
3. **`info.pnl` ≠ OKX app 显示值** — 差 1.36 USDT (funding fee 不在 pnl 字段里)
|
||||
|
||||
### v2 (2026-09-19 下午) — `/account/bills` ✅ 当前用这个
|
||||
|
||||
**正解**: `/api/v5/account/bills` `type=2` `subType=1` 的 `balChg` = OKX app 显示的已实现盈亏 (含手续费 + funding)。
|
||||
|
||||
**OKX 账单 type/subType 字典** (实测):
|
||||
|
||||
| type | subType | balChg 含义 |
|
||||
|---|---|---|
|
||||
| 2 | 1 | 已实现盈亏净额 (单笔, 含费) — BTC 平仓用这个 |
|
||||
| 2 | 2 | 单笔成交 (raw 盈亏, 不含费) — ZEC 平仓多笔 subType=2 + 多笔 subType=1 |
|
||||
|
||||
**实际处理**:
|
||||
- BTC 平仓 = 1 笔 subType=1 = **-$46.14** (OKX app 显示 -$46.51, 差 0.37 funding)
|
||||
- ZEC 平仓 = 多笔 subType=2 (+$14.42) + 多笔 subType=1 (-$0.97) → 累加 = **+$13.45** (OKX app 显示 +$13.44, 差 0.01)
|
||||
|
||||
### 算法 (最终版)
|
||||
|
||||
```
|
||||
1. 拉 7 天内 type=2 全部账单 (按 ts 倒序, OKX 默认)
|
||||
2. 找最新一次"平仓" — 用绝对值最大且 abs >= 5 的那笔作锚点 ts
|
||||
(排除手续费: subType=1 绝对值通常 < 5)
|
||||
3. 累加锚点 ts ±30 秒内的 type=2 全部 subType 的 balChg
|
||||
4. 返回 (latest_fill_px, latest_ts, latest_pnl)
|
||||
```
|
||||
|
||||
**为什么用绝对值最大而非最新 ts**: BTC 平仓只记一笔 subType=1=-46.14, 没有 subType=2, 用 subType=2 锚点找不到。改用"绝对值最大 + 排除手续费"。
|
||||
|
||||
## 改动 (3 处)
|
||||
|
||||
### 1. `process_signal.py` `close_position_raw` 加 `cTime`
|
||||
|
||||
```python
|
||||
pos_before = {
|
||||
'pos': pos_size,
|
||||
'side': 'long', # or 'short'
|
||||
'avgPx': float(p.get('avgPx', '0') or 0),
|
||||
'cTime': int(p.get('cTime', '0') or 0), # ← 新增, 用于算持仓时长
|
||||
}
|
||||
```
|
||||
|
||||
### 2. `process_signal.py` 加 2 个 helper 函数
|
||||
|
||||
```python
|
||||
def _fetch_close_pnl_details(symbol_usdt, entry_px, contracts, side):
|
||||
"""从 OKX /account/bills 取最新平仓相关账单的累加 balChg
|
||||
OKX 接口: GET /api/v5/account/bills?instId=...&instType=SWAP&type=2
|
||||
"""
|
||||
# 拉近 7 天账单, 找 type=2 中绝对值最大且 abs >= 5 的作锚点
|
||||
# 累加锚点 ±30 秒内 type=2 全部 subType 的 balChg
|
||||
# 返回 (exit_px, exit_ts_ms, real_pnl)
|
||||
# fillPx 从 subType=2 拿 (BTC 没 subType=2, 拿不到, 返回 None)
|
||||
|
||||
|
||||
def _format_hold_duration(exit_ts_ms, ctime_ms):
|
||||
"""3天5小时 / 5小时22分 / 22分"""
|
||||
# exit_ts_ms - ctime_ms → 秒 → 天/小时/分钟
|
||||
```
|
||||
|
||||
### 3. `format_close_message` 加推送内容
|
||||
|
||||
```python
|
||||
if action == 'closed':
|
||||
pos = close_result['pos_before']
|
||||
entry_px = pos['avgPx']
|
||||
contracts = pos['pos']
|
||||
exit_px, exit_ts_ms, real_pnl = _fetch_close_pnl_details(...)
|
||||
hold_str = _format_hold_duration(exit_ts_ms, pos.get('cTime', 0))
|
||||
if exit_px is not None:
|
||||
pnl_sign = '+' if real_pnl >= 0 else ''
|
||||
pnl_line = f"💰 实际盈亏: **{pnl_sign}${real_pnl:.2f}** (OKX 实测, 含手续费/funding)"
|
||||
return f"""✅ {symbol} {side_cn} {emoji} {leverage}x 已市价全平 | {trader}
|
||||
|
||||
{pos['side']} {contracts}张 @ {entry_px:.2f} → 平仓价 {exit_px_str}
|
||||
⏱ 持仓: {hold_str}
|
||||
{pnl_line}
|
||||
💡 OKX 历史账单实数据"""
|
||||
```
|
||||
|
||||
## 关键教训 (用户原话)
|
||||
|
||||
- **"没看到推送的盈亏"** → **必须用 OKX 实测数据**, 不要自己 `(exit - entry) * contracts` 算
|
||||
- **"BTC -46.51 你怎么算 -45.14"** → `info.pnl` 字段不含 funding fee, 必须用 bills 接口的 balChg
|
||||
- **"单子上不是有价格吗"** → 直接查 OKX 历史订单拿真实成交价
|
||||
- **"挑平的推"** → 平几个推几个, 只推平的那一个 (不强求汇总)
|
||||
- **"包含时长"** → `cTime` (持仓开始) + `fillTime` (平仓) → 格式化
|
||||
|
||||
## 踩坑表 (实测 7 次)
|
||||
|
||||
| 坑 | 错误 | 修复 |
|
||||
|---|---|---|
|
||||
| 1 | `/trade/orders-history?state=2` → 51600 Status not found | `state='filled'` (字符串, 不是数字) |
|
||||
| 2 | OKX 缺 instType → 50014 "Parameter instType can not be empty" | 加 `instType='SWAP'` |
|
||||
| 3 | `info.pnl` 单笔 ≠ OKX app 显示 (差 funding) | 改用 `/account/bills` type=2 subType=1 balChg |
|
||||
| 4 | BTC 平仓没 subType=2 (只有 subType=1=-46.14) | 用"绝对值最大 + 排除手续费"作锚点 |
|
||||
| 5 | ZEC 累加 type=2 全 subType (subType=2 +7.54+7.56+0.10×4 + subType=1 -0.97 = 13.45) | ✅ 累加全 subType 即 OKX app 数 |
|
||||
| 6 | 1000PEPE 特殊处理: OKX instId 是 `PEPE-USDT-SWAP` 不是 `1000PEPE-USDT-SWAP` | 已处理 |
|
||||
| 7 | proxy 抽风时 helper 返回 (None, 0, 0) | 推送降级 "拉不到历史订单, 看 OKX app" |
|
||||
Reference in New Issue
Block a user