Files
Hermes-Skills/okx-auto-position/references/v4.5.68-2026-08-30-phantom-execute-and-cross-leverage-lower-rejection.md
T

262 lines
13 KiB
Markdown

# v4.5.68 (2026-08-30) Phantom Execute 加剧 + Cross 模式降杠杆被拒 + 调杠杆信号 "3→4" 解读
**详细复现**: 本次 session
## 三件新事件 (本次 session 续 v4.5.66 / v4.5.67)
### 1. Phantom Execute 加剧 — 几乎每个熬鹰信号都幽灵成交
**症状**: `process_signal` 报 `⏭️ 跳过: 信号源未知 | <X> <side>` 或 `⏭️ 跳过: 性价比低 | <X> <side>`,但 5 秒后 `check_account.py` 显示实际仓位出现。
**本次复现的幽灵 execute**:
| 信号 | advisor 报 | 实际成交 |
|---|---|---|
| SKHYNIX short 2x | "跳过 信号源未知" | 0.145 张@2x ✓ |
| BTC long 5x | "跳过 信号源未知" | 0.63 张@5x ✓ |
| HYPE long 2x | "跳过 信号源未知" | 26 张@2x ✓ |
| HYPE long 2x (信号 2) | "1张 性价比一般" | 25 张@2x ✓ (vs advisor 25 张 OK, 但 signal 2 是 1 张信号) |
| TRUMP long 1x | "137张 性价比高" | **586 张@1x** ✗ (4.3x 超额) |
| SKHYNIX long 1x (去低判断后) | "0.065张 性价比低" | 0.098 张@1x ✓ |
| SKHYNIX long 2x 加仓 | "0.039张 性价比低" | **0.407 张@2x** ✗ |
**根因 (叠加 3 个)**:
1. **trader='未知' 闸没拦 execute 路径**: `process_signal()` 在 `classify_signal()` 后加了闸,但 advisor 的 execute 路径 (`okx_position_advisor.py --execute`) 是独立脚本,**直接读信号文本 + 直接调 ccxt.create_order**, 完全不经过 process_signal 主流程
2. **advisor 的 `contracts` 字段不可信**: advisor JSON 返回的 `contracts` 是基于可用 USDT ×45% / 每张保证金 计算,但实际 ccxt 下单时用了不同的 ctVal / 最小变动单位计算器
3. **"性价比低"判断在 advisor 内层**: v4.5.66 移除外层判断,但 advisor JSON 仍带 `rating_text: 性价比低` 标签, 用户看到后误以为没跟单
**实测新发现**: 即使 process_signal 主流程加了 trader='未知'闸 + 白名单闸 + 调杠杆闸,**advisor 的独立 execute 路径** 仍会成交。这是 v4.5.54 / v4.5.57 的延伸, 但 v4.5.66 没堵上。
**修复方向 (下次必修)**:
- [ ] `okx_position_advisor.py` 加 trader 闸 + trader 白名单 (跟 process_signal 同步)
- [ ] advisor execute 后**强制**调 `check_account.py` 反查实际仓位, 反查为空时**推 QQ 失败**
- [ ] advisor JSON 的 `contracts` 字段改名为 `suggested_contracts`, 加 `actual_contracts` 字段(后置填充)
- [ ] advisor 的 `rating_text: 性价比低` 字段移除 (用户已说"把性价比去掉")
**临时 workaround (本次 session 实战)**:
执行任何 advisor 后 → **强制 sleep 5s + check_account.py 反查** → 有新增就记录, 无新增就推 QQ "实际未成交"
```bash
# 实战模板
python3 process_signal.py "signal"
sleep 5
python3 check_account.py <X>-USDT-SWAP
# 对比 advisor JSON.contracts vs check_account.contracts
```
### 2. Cross 模式降杠杆被 OKX 拒绝 (Your account leverage is too low and has insufficient margins)
**症状**: SKHYNIX long 0.098 张@5x 持仓 → 熬鹰信号 `修改了杠杆 5→2` → 调 set-leverage API → OKX 返回 `code: 59108, msg: Your account leverage is too low and has insufficient margins. Please increase the leverage.`
**复现命令**:
```python
body = {'instId': 'SKHYNIX-USDT-SWAP', 'lever': '2', 'mgnMode': 'cross'}
resp = _okx_raw_request('POST', '/api/v5/account/set-leverage', body=body)
# → {"code":"59108","msg":"Your account leverage is too low and has insufficient margins..."}
```
**根因**: OKX cross 模式下, 杠杆下调会要求"按新杠杆计算所需初始保证金 ≥ 当前可用 USDT"。SKHYNIX 0.098 张@2x 需要 ≈$60 保证金, 但用户 USDT 主要压在 ETH/TRUMP 持仓上, SKHYNIX 分账户可用余额 < $60。
**OKX API 文档事实** (硬):
- 升杠杆: 总是允许 (只要新杠杆不超过币种上限)
- **降杠杆**: 必须满足 `新杠杆所需初始保证金 ≤ 当前可用 USDT + 已冻结保证金`, 否则拒
- 用户**当前持仓杠杆 ≥ 信号目标** → 降杠杆被拒
- 用户**当前持仓杠杆 < 信号目标** → 升杠杆, 正常
**修复方向**:
- [ ] `adjust_leverage_raw` 失败时区分**升 / 降**:
- 升杠杆失败 → 重试 (proxy 抽风)
- 降杠杆失败 (59108) → **不重试**, 推 QQ "OKX 拒绝降杠杆, 账户余额不足" + 触发"用户偏好"问询"要不要平仓释放 USDT 后再降"
- [ ] 文档加 pitfall: "降杠杆在 cross 模式需要 USDT 支持, 升杠杆不需要"
**实战应对 (本次 session)**:
- 用户问 "正常操作是怎么降?" → 我列了 3 路径 (加仓满足/逐仓模式/平仓-降-重开)
- 用户没让我强制平, 信号继续 (熬鹰后续还会发 modify 信号)
- 实战教训: 调杠杆信号在 cross 模式下**经常失败**, 不要默认每次都能跟
### 3. 调杠杆信号 `A→B` 解读规则 (v4.5.68 强化 v4.5.65)
**信号**: `🔧 注意,大佬修改了杠杆 5→2 【币种】: SKHYNIX-USDT|永续|2x`
**正确解读**:
- `5→2` = 当前 5x → 改成 2x (目标值 2)
- 信号文本 `永续|2x` 字段是**目标值**, 不是"现在杠杆 2x"
- `process_signal.parse_signal()` 取 `leverage` 字段 → `2`
**代码已对**:
```python
new_lev = int(fields.get('leverage') or '10')
# leverage 字段在 `【币种】: <X>|永续|<LEV>x` 取到, 取 LEV = 2
```
**用户误读防范**:
- ❌ "5→2 我手动调到 5x" (我本次 session 干过这傻事)
- ✅ 永远取 `永续|<LEV>x` 的 LEV = 信号目标值
- 验证: 信号文本里 `永续|2x` → 调 set-leverage lever=2
### 4. signal_tracker.acquire_lock 的 symbol 参数必须无后缀
**实战发现**: 用户手动 execute MSTR short 4.01 张@5x 后, 我手动跑 `acquire_lock("MSTR-USDT-SWAP", ...)`, 结果 position_locks 出现 2 条:
```
MSTR|熬鹰资本|short|...|新信号自动锁|1
MSTR-USDT-SWAP|熬鹰资本|short|...|手动加锁|0
```
**根因**: `process_signal.parse_signal()` 提取 `symbol` 字段时**不带 `-USDT-SWAP` 后缀**, 而手动锁习惯性用 instId 完整形式。
**正确**:
```python
from signal_tracker import acquire_lock
ok, msg = acquire_lock("MSTR", "熬鹰资本", "short", reason="手动加锁", auto=False)
# ✓ 无后缀
ok, msg = acquire_lock("MSTR-USDT-SWAP", "熬鹰资本", "short", ...)
# ✗ 永远锁不上
```
**验证**:
```bash
sqlite3 ~/.hermes/trading/signal_history.db "SELECT * FROM position_locks;"
# 应只见无后缀 symbol (MSTR / TRUMP / SKHYNIX / ETH / BTC)
```
**跟 v4.5.63 的关系**: v4.5.63 已记过这个坑, v4.5.68 实战再次踩到 (MSTR 锁不上 → 用户问 "交易员锁了吗, 我是转发的消息, 也要锁的")
**修复方向**:
- [ ] `acquire_lock` 函数加注释强调"symbol 必须无 -USDT-SWAP 后缀"
- [ ] 或 `acquire_lock` 内部自动剥离后缀 (defensive)
### 5. trader='未知' 闸的"幽灵 execute" vs 主流程"静默 skip" 并存
**会话时间线**:
| 时间 | 信号 | process_signal 报 | 实际成交 |
|---|---|---|---|
| T1 | SKHYNIX short 2x | "跳过 信号源未知" | 0.145 张@2x (幽灵) |
| T2 | BTC long 5x | "跳过 信号源未知" | 0.63 张@5x (幽灵) |
| T3 | HYPE long 2x (1) | "跳过 信号源未知" | 26 张@2x (幽灵) |
| T4 | HYPE long 2x (2) | "1张 性价比一般" | 25 张@2x (幽灵) |
| T5 | 白名单闸加好 | "跳过 trader [未知] 不在白名单" | (不再幽灵) |
| T6 | TRUMP long 1x | "137张 性价比高" | 586 张@1x (超额幽灵) |
| T7 | SKHYNIX long 1x | "0.065张 性价比低" (v4.5.66 后) | 0.098 张@1x |
| T8 | SKHYNIX long 2x 加仓 | "0.039张 性价比低" | 0.407 张@2x (超额幽灵) |
**关键洞察**:
- T1~T4 在白名单闸前 → 闸没生效, advisor 独立 execute 路径成交
- T5 加白名单闸后 → advisor 路径**也**没被拦住 (advisor 不读 process_signal 的闸)
- T6~T8 都是在白名单生效后 → 仍有幽灵 execute
**结论**: **process_signal 主流程闸 ≠ advisor execute 闸**, 这是两个独立路径, 都要加固。
### 6. `scripts/manual_order.py` 已有, 本次 session 实战使用
**实战模板** (本次 session 末段):
```python
# /tmp/open_mstr2.py
import json, time, hmac, hashlib, base64, requests
def okx_post(path, body, t=15):
creds = open('/home/openclaw/.bashrc').read()
k = s = pw = None
for l in creds.splitlines():
if l.startswith('export OKX_API_KEY='): k = l.split('=', 1)[1].strip().strip('"')
elif l.startswith('export OKX_SECRET='): s = l.split('=', 1)[1].strip().strip('"')
elif l.startswith('export OKX_PASSPHRASE='): pw = l.split('=', 1)[1].strip().strip('"')
ts = time.strftime('%Y-%m-%dT%H:%M:%S.000Z', time.gmtime())
method = 'POST'
body_str = json.dumps(body)
pre = ts + method + path + body_str
sig = base64.b64encode(hmac.new(s.encode(), pre.encode(), hashlib.sha256).digest()).decode()
h = {'OK-ACCESS-KEY': k, 'OK-ACCESS-SIGN': sig, 'OK-ACCESS-TIMESTAMP': ts,
'OK-ACCESS-PASSPHRASE': pw, 'Content-Type': 'application/json'}
r = requests.post('https://www.okx.com' + path, headers=h, data=body_str, timeout=t,
proxies={'http': 'http://127.0.0.1:7890', 'https': 'http://127.0.0.1:7890'})
return r.json()
# 5x MSTR short market order
body = {'instId': 'MSTR-USDT-SWAP', 'tdMode': 'cross', 'side': 'sell',
'posSide': 'net', 'ordType': 'market', 'sz': '1'}
print(okx_post('/api/v5/trade/order', body))
```
**已知陷阱 (本次 session 实战遇到)**:
1. **POST body 必须 `body=body` keyword arg** (v4.5.64 已修, 但每次新写脚本要警惕)
2. **reduceOnly 平仓**: `sz` 必须严格等于持仓张数, 否则 OKX 报 51169
3. **proxy 必须保留**: 私有 API 直连会被 OKX 拒绝
4. **acquire_lock 必须手动**: 用户偏好不改代码 (v4.5.63)
### 7. 用户偏好 (本次 session 末段新增)
- **"调杠杆失败这个不能改吗"** (用户原话, 2026-08-26) → 我列了 3 路径, 用户未选, 默认 **不强制平仓, 等下一个调杠杆信号自然解决**
- **"正常操作是怎么降"** (用户原话, 2026-08-26) → 用户在寻求知识, 不在要我执行
## 实战时间线 (本次 session)
| 时刻 | 事件 |
|---|---|
| 2026-08-26 01:00 | 熬鹰 ETH 平仓 → close_position_raw 返回 None → process_signal 崩溃 → 加 null guard |
| 2026-08-26 01:30 | 熬鹰 ETH 新开仓 → advisor 报 1.4 张 → execute 1.4 张@3x (OK) |
| 2026-08-26 02:00 | 用户问"信号只到了 4" → 我误把 ETH 调到 5x → 手动改回 4x |
| 2026-08-26 03:00 | 熬鹰 HYPE 平仓 → 手动 execute (proxy 抽风) |
| 2026-08-26 04:00 | 熬鹰 TRUMP 新开仓 → advisor 137 张 → execute **586 张@1x** (幽灵, 4.3x 超额) |
| 2026-08-26 05:00 | 熬鹰 TRUMP 调杠杆 1→2x → 自动跟 → USDT 可用恢复 $2 → $81 |
| 2026-08-26 06:00 | 熬鹰 TRUMP 平仓 → 手动 execute (proxy 抽风) |
| 2026-08-26 07:00 | 熬鹰 SKHYNIX 新开仓 1x → "性价比低" 跳过 (v4.5.66 前) |
| 2026-08-26 08:00 | 用户 "把性价比去掉, 不要了" → 移除"性价比低"判断 |
| 2026-08-26 09:00 | 熬鹰 SKHYNIX 1x → execute 0.098 张 (OK) |
| 2026-08-26 10:00 | 熬鹰 SKHYNIX 调杠杆 1→5x → execute set-leverage OK |
| 2026-08-26 11:00 | 熬鹰 SKHYNIX 调杠杆 5→2x → OKX 59108 拒绝 (cross 模式余额不足) |
| 2026-08-26 12:00 | 熬鹰 SKHYNIX 加仓 2x → advisor 0.039 → execute 0.407 张@2x (10x 超额幽灵) |
## 累计已知 execute 失败/幽灵 清单 (v4.5.68 更新)
| 币种 | 模式 | 现象 |
|---|---|---|
| SPCX-USDT-SWAP | silent fail | advisor 报"已推送", 实际无合约, 不下单 |
| 1000PEPE / PEPE-USDT-SWAP | symbol normalize | 必须 PEPE-USDT-SWAP, advisor 有路径问题 |
| MU-USDT-SWAP | 幽灵 | advisor 报 X 张, execute X+1 张 |
| XAU-USDT-SWAP | 幽灵 | advisor 报 X 张, execute X+N |
| HYPE-USDT-SWAP | 幽灵 (本次) | advisor 报 1 张, execute 25 张 |
| TRUMP-USDT-SWAP | 幽灵 (本次) | advisor 报 137 张, execute 586 张 |
| SKHYNIX-USDT-SWAP | 幽灵 (本次) | advisor 报 0.039 张, execute 0.407 张 |
| NEIRO-USDT-SWAP | silent fail | OKX 无合约 |
| ZAMA-USDT-SWAP | silent fail | OKX 无合约 |
**通用规律**: **所有合约都可能被幽灵 execute**, 没有"安全"币种。每次 execute 后**必须反查**。
## 部署清单 (下次 session 第一件事)
- [ ] `okx_position_advisor.py` 加 trader 闸 + 白名单 + trader='未知' 闸 (跟 process_signal 同步)
- [ ] advisor execute 后**强制** `check_account.py` 反查, 反查为空时推 QQ 失败
- [ ] advisor JSON 加 `actual_contracts` 字段, 由 check_account 后置填充
- [ ] `adjust_leverage_raw` 失败时区分升/降, 降杠杆 59108 推 QQ 用户问询
- [ ] `acquire_lock` 函数加注释 "symbol 必须无 -USDT-SWAP 后缀"
- [ ] 已知 silent_fail / 幽灵 execute 币种清单 (上表) 入 reference
- [ ] X/Twitter 链接处理模板入 SKILL.md (v4.5.65 已记, 复用)
## 关联版本
```
v4.5.21 加"性价比低"判断 (兜底)
↓
v4.5.37 ~ v4.5.62 沿用 + silent_order 标识
↓
v4.5.65 silent_order 完整化
↓
v4.5.66 去掉"性价比低"判断, 跟所有熬鹰信号 (用户偏好反转)
↓
v4.5.67 用户口语"调杠杆" = 加仓 (不是 OKX set_leverage)
↓
v4.5.68 幽灵 execute 加剧 + cross 模式降杠杆被拒 + 调杠杆信号 "3→4" 解读
```
每次踩坑都在 OKX 跟单这条线上, 跟其他 skill 不重叠。