Files
Hermes-Skills/okx-auto-position/references/v4.5.63-2026-08-26-manual-position-lock-and-symbol-mismatch.md

164 lines
7.0 KiB
Markdown

# v4.5.63 (2026-08-26) 手动下单必须手动锁 + symbol 不匹配陷阱 + ETH long 完整周期
**本次 session 在 v4.5.61 (trader 白名单) + v4.5.62 (dedup 手动清 + 手动加仓) 基础上的 3 个新教训**
---
## 教训 1: 手动 execute 的仓位必须手动 `acquire_lock`
**用户原话**: "用手动锁就行,不要改代码了。"
**症状**: 用户让 agent 手动 raw REST 下单(比如 MSTR short 4.01张),下单成功了,但 `position_locks` 表里**没有这条记录**。后续熬鹰 MSTR 平仓信号进来时,`check_signal_lock(MSTR)` 返回 None → `acquire_lock` 创建**新锁** → 但同时 `process_signal` 主流程里平仓信号被"无持仓静默"短路,不会真平仓。
**根因**: `acquire_lock()` 只在 `process_signal` 主流程的 `check_signal_lock` 里被调用,手动 execute (raw REST) 完全绕开 process_signal → 没有锁记录。
**SOP (硬规则, v4.5.63 起执行)**:
```python
# 手动 execute 后, 立即手动锁 (用户偏好: 不要改代码, 用脚本)
import sys
sys.path.insert(0, '/home/openclaw/.hermes/skills/trading/okx-auto-position/scripts')
from signal_tracker import acquire_lock
# ⚠ symbol 必须与 process_signal 用的 symbol 一致 (无 '-USDT-SWAP' 后缀)
ok, msg = acquire_lock("MSTR", "熬鹰资本", "short", reason="手动加锁", auto=False)
print(f"ok={ok}, msg={msg}")
```
**手动锁模板** (`/tmp/lock_<SYMBOL>.py`):
```python
import sys
sys.path.insert(0, '/home/openclaw/.hermes/skills/trading/okx-auto-position/scripts')
from signal_tracker import acquire_lock
ok, msg = acquire_lock("<SYMBOL>", "<TRADER>", "<side>", reason="手动加锁", auto=False)
print(f"ok={ok}, msg={msg}")
```
**实战 (本次 session)**: 用户说"用手动锁就行" → 我手动锁 MSTR → 后续熬鹰 ETH / HYPE 信号正常走流程。
---
## 教训 2: `acquire_lock` 的 symbol 必须与 process_signal 一致 (无后缀)
**陷阱**: 用户手动 execute MSTR-USDT-SWAP 时, 习惯性把 instId 直接传给 `acquire_lock`,变成 `acquire_lock("MSTR-USDT-SWAP", ...)`,但 `process_signal` 用的是 `fields.get('symbol', '?')`,**extract 出来的 symbol 不带 -USDT-SWAP 后缀**(例如 "MSTR")。
**结果**: position_locks 表里出现两条记录:
```
MSTR|熬鹰资本|short|...|新信号自动锁|1 ← process_signal 自动锁的
MSTR-USDT-SWAP|熬鹰资本|short|...|手动加锁|0 ← 手动加的
```
**后续 `get_lock("MSTR")` 查到第一条(自动锁), `get_lock("MSTR-USDT-SWAP")` 查到第二条**。手动锁的那条永远不被命中,失去意义。
**正确做法**:
```python
# ✅ 对
acquire_lock("MSTR", "熬鹰资本", "short", ...)
acquire_lock("ETH", "熬鹰资本", "long", ...)
acquire_lock("HYPE", "熬鹰资本", "long", ...)
# ❌ 错
acquire_lock("MSTR-USDT-SWAP", "熬鹰资本", "short", ...) # 永远查不到
acquire_lock("ETH-USDT-SWAP", "熬鹰资本", "long", ...)
```
**验证脚本**:
```bash
sqlite3 ~/.hermes/trading/signal_history.db "SELECT * FROM position_locks;"
# 应该看到 <SYMBOL> 不带 -USDT-SWAP
```
---
## 教训 3: ETH long 完整周期: 开仓 → 调杠杆 → 后续 + 自动平仓 (ghost close)
**完整时间线** (本次 session):
1. 熬鹰 ETH long 2x 信号 → process_signal execute → **ETH long 1.4张@3x** (avgPx 2486.74)
2. 熬鹰 ETH 调杠杆 3→4x → process_signal 推"性价比低"无 execute
3. 熬鹰 ETH long 5x (新一轮信号) → process_signal 推但**未 execute**(用户资金不够再开新仓)
4. 熬鹰 ETH 平仓信号 → process_signal 报"⏭️ 平仓信号无持仓静默"
5. 用户质问: "你的 ETH long 怎么平了?"
6. 实际查 check_account.py: ETH long 已自动平仓,USDT $149.60
**关键问题**: 平仓信号被"无持仓静默",但实际平仓确实发生了 —— **ghost close** (v4.5.54 已记录的同类型 bug)。
**实际平仓价**: markPx 2457.34 → 平仓时 markPx (查不到但 USDT 从 $30.39 → $149.60 跨度太大, 不止 ETH 一笔)。USDT 增加 $0.06 几乎可忽略,**说明 ETH 平仓价 ≈ avgPx 2486.74**,盈亏接近 0。
**教训 (v4.5.63 强化)**:
- 任何"无持仓静默"信号 → **必跑 check_account.py 确认**
- 平仓 ghost close 实际可能发生了,USDT 变化能反向验证
- 不要假设 process_signal 报"无持仓"就是真无持仓,这是 v4.5.51 第9次违规的延伸
---
## 教训 4: v4.5.61 trader 白名单闸 + v4.5.62 dedup 手动清 实战组合
**实战**:
- 白名单闸: 矿工Jasonleo 20+ reduce 信号全部 `⏭️ 跳过: trader [矿工Jasonleo] 不在白名单`,完全静默,不推QQ
- dedup 手动清: 熬鹰连续 add+modify 触发 `⏭️ 重复信号,跳过` → 我用 sqlite3 清 recent_signals 解锁
**白名单 + dedup 清库 组合执行模板** (本次 session 实跑 5 次):
```bash
# 1. 删 recent_signals (解锁)
sqlite3 ~/.hermes/trading/signal_dedup.db "DELETE FROM recent_signals WHERE symbol LIKE '%HYPE%' AND trader='熬鹰资本';"
# 2. 重新跑 process_signal
python3 ~/.hermes/skills/trading/okx-auto-position/scripts/process_signal.py << 'EOF'
【熬鹰资本】
📈 注意, 大佬加仓了
【币种】: HYPEUSDT|永续|2x
【方向】: 做多 🟩
【仓位】: 5881.62 HYPE
【开仓价】: 79.93753
【当前价】: 80.03194
【保证金】: 235,358.72 USDT(全仓)
【收益额】: +555.26 USDT(+0.24%)
EOF
```
**部署清单** (下次必修):
- [ ] parse_signal() 区分 modify (调杠杆) vs add (加仓), 不混进 dedup 记录
- [ ] recent_signals 表加 `action` 字段 (open/add/reduce/close/modify)
- [ ] process_signal 主流程"无持仓静默"后必须 check_account.py 反查
- [ ] acquire_lock 必须能在手动 execute 路径触发 (或者用户手动跑 acquire_lock)
- [ ] 文档明确"手动锁 symbol 必须与 process_signal 一致"
---
## 本次 session 净状态 (2026-08-26 收)
| 项 | 值 |
|---|---|
| USDT 可用 | $149.60 |
| 权益 | $149.60 (无冻结) |
| ETH long | 已自动平仓 (盈亏 ≈ 0) |
| MSTR short | 4.01张@5x, avgPx 126.04, 浮亏-$0.21 |
| 占用 | 68% (≈7 成) |
| 强平价 | MSTR 160.00 (距 $34, 安全) |
| 白名单闸 | ✅ 只跟熬鹰资本 |
| MSTR 锁 | ✅ 已加熬鹰资本 short (手动) |
| ETH 锁 | ✅ 已加熬鹰资本 long (手动) |
---
## 用户偏好强化 (本次新增)
1. **"用手动锁就行, 不要改代码了"** — 修改 `signal_tracker.py` 加自动锁不是用户想要的
2. **"按 X 成来开" / "只留 X 成"** — 同义, 都表示**余额占比**而非**仓位占比** (用户原话: "不是, 是只留 2 成的余额" 纠正了我)
3. **"这个可以跟单了"** — 即使 advisor 报"性价比低", 用户主动要求 manual execute
4. **"不是 X 几层, 是只留 X 成余额"** — 用户在意最终占用 vs 余额比例, 不在意绝对张数
---
## 跨 session 索引
- v4.5.51 (lock trace): 持仓来源追溯三步
- v4.5.54 (trader='未知' 闸 + ghost close): 同根问题
- v4.5.60 (BTC 来源误归属): 锁追溯必查 `position_locks`
- v4.5.61 (trader 白名单): 当前生效
- v4.5.62 (dedup 手动清 + 手动加仓): 已沉淀 manual_order.py
- **v4.5.63 (本次)**: 手动锁 + symbol 一致性 + ETH ghost close + 用户"留 X 成余额"偏好