Files
Hermes-Skills/okx-auto-position/references/v4.5.62-2026-08-23-dedup-manual-clear-and-raw-rest-position-sizing.md
T

154 lines
7.4 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.
# v4.5.62 (2026-08-23) Trader 白名单生效后实战经验:dedup需手动清 + raw REST 仓位调控
**用户指令**: "是的,暂时只做熬鹰的,。" (v4.5.61 已生效) → 后续实战暴露 3 个新坑。
---
## 坑 1: dedup 误跳后需手动清 `recent_signals` 解锁
**症状**: 熬鹰同一币种连续两条信号(如"加仓"+"调杠杆"),第二条常被报 `⏭️ 重复信号,跳过`,但实际是**不同动作**(加仓 vs 调杠杆)本应分别处理。
**根因**:
- `recent_signals` 表按 `(symbol, trader)` 记录,**不区分动作**(open/add/reduce/close/调杠杆)
- 调杠杆信号算 action="modify",但 `parse_signal` 不区分 modify,被归入"已有同币种同 trader 记录"
**解锁脚本** (sqlite3):
```bash
# 通用: 清特定币种的所有 recent_signals (会强制下次该币种信号重新走 advisor)
sqlite3 ~/.hermes/trading/signal_dedup.db "DELETE FROM recent_signals WHERE symbol LIKE '%<SYMBOL>%';"
# 实战示例:
sqlite3 ~/.hermes/trading/signal_dedup.db "DELETE FROM recent_signals WHERE symbol LIKE '%HYPE%' AND trader='熬鹰资本';"
sqlite3 ~/.hermes/trading/signal_dedup.db "DELETE FROM recent_signals WHERE symbol LIKE '%MU%';"
sqlite3 ~/.hermes/trading/signal_dedup.db "DELETE FROM recent_signals WHERE symbol LIKE '%ETH%';"
sqlite3 ~/.hermes/trading/signal_dedup.db "DELETE FROM recent_signals WHERE symbol LIKE '%SKHYNIX%';"
# ⚠ 慎用: 全清 (所有 trader 重新走 advisor)
sqlite3 ~/.hermes/trading/signal_dedup.db "DELETE FROM recent_signals WHERE 1=1;"
sqlite3 ~/.hermes/trading/signal_dedup.db "DELETE FROM processed WHERE 1=1;"
```
**判断时机**: 看到 `⏭️ 重复信号,跳过` 但用户提示信号确实新的(如调杠杆跟新开仓是不同动作)→ 立即清库 + 重跑 process_signal。
---
## 坑 2: advisor 建议张数 vs 实际 execute 张数不一致 (幽灵执行)
**症状**: advisor 报 "1.0张" 但实际 execute 成功 25张 或 33张。
**实战案例 (本次 session)**:
- 熬鹰 HYPE long 2x 信号 → advisor 推 1.0张 → execute 实际成交 25张@2x (avgPx 80.17,占用保证金 $101)
- 熬鹰 HYPE 调杠杆 2→5x 信号 → execute 实际 HYPE long 25→33 张 (杠杆升级时**重新加仓**了)
**根因**: `okx_position_advisor.py` 计算 `contracts` 字段时按 `availBal × pct / margin_per_contract`,但 execute 路径走 ccxt 的 `create_order` 时可能**忽略 contracts 字段**或**用全仓计算**。
**判定规则** (硬):
- advisor JSON 返回 `contracts < 10` 但 execute 后 check_account 显示 `contracts > 20` → **advisor 报的 contracts 不可信**
- execute 完成后**必须跑** `check_account.py` 反查实际张数,不能信 advisor
**回复模板** (execute 后必查):
```
[实测] {symbol} {side} {X}张 avgPx={x} mark={y} upl={z}; USDT{可用} / {权益}
```
**禁止**: 直接引用 advisor 返回的 contracts 数字。
**已知幽灵币种** (本次 session 验证):
- HYPE-USDT-SWAP (advisor 1张 → 实际 25/33 张)
- SNDK-USDT-SWAP (已知 execute 失败,v4.5.38 已记录)
- 1000PEPE-USDT-SWAP (execute 漏处理,v4.5.37 已记录)
**修复方向** (下次必修):
- [ ] okx_position_advisor.py 的 execute 路径必须 raw REST 反查 contracts,与 advisor JSON 对比,不一致就 warn + 用反查值
- [ ] process_signal 主流程在 `_okx_raw_request('GET', '/api/v5/account/positions')` 后写 `actual_contracts` 字段
- [ ] advisor JSON 加 `{"execute_verified": True, "actual_contracts": X}` 字段
---
## 坑 3: 用户手动 raw REST 加仓模板 (7 成 / 8 成仓位)
**用户指令序列**:
- "这个可以跟单了" (MSTR short 1x 信号,资金够但 advisor 报性价比低跳过)
- "按 8 成来开" → manual 1 张 MSTR short @5x (实际只占 17%,没到 8 成)
- "不是,只留 2 成的余额" → 再加 3 张 MSTR short → 占用 68% (≈7 成) ✓
**通用公式**:
```
可用USDT × 目标占比 = 目标占用保证金
目标占用保证金 / 单张保证金 = 目标张数
```
**OKX USDC 永续单张保证金** (实测本次 session):
| 币种 | 杠杆 | 单张保证金 | 名义/张 |
|---|---|---|---|
| MSTR-USDT-SWAP | 1x | $269.99 | $126.80 |
| MSTR-USDT-SWAP | 5x | $25.44 | $634.00 |
| BTC-USDT-SWAP | 5x | ~$13,300 | $66,000 (1张) |
| ETH-USDT-SWAP | 5x | ~$485 | $2,420 (1张) |
**手动 execute 模板** (复制此块到 `/tmp/open_X.py`):
```python
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' # ⚠ 必须是字符串, 不要写 method=POST (NameError)
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()
# 示例: MSTR short 0.01 张 (最小变动单位, 先小试)
body = {"instId": "MSTR-USDT-SWAP", "tdMode": "cross", "side": "sell",
"posSide": "net", "ordType": "market", "sz": "0.01"}
print(json.dumps(okx_post('/api/v5/trade/order', body), ensure_ascii=False, indent=2))
```
**⚠ 注意 `name='POST'` 拼写** — 之前 1 次踩坑 (NameError: name 'POST' is not defined),method 应为字符串 `'POST'`。
**回查模板** (`scripts/check_account.py` 已封装, 直接调):
```bash
timeout 30 python3 ~/.hermes/skills/trading/okx-auto-position/scripts/check_account.py MSTR-USDT-SWAP
```
---
## 用户行为模式(本次新增)
1. **"X成来开"** → 留对应比例余额, 开对应比例仓位
- "8 成来开" = 留 2 成余额, 用 8 成余额开仓
- "7 层" / "7 成" 同义
- 实际执行: 我算的总和与字面不符时, **用户会纠正** ("不是,是只留 2 成")
2. **用户对小币种手动加仓有耐心** — MSTR 1x 信号 advisor 报"性价比低"跳过,用户主动说"跟单了"
3. **用户关心的是"是否真的下单", 不是 advisor 报了什么** — execute 后必查 check_account.py
---
## 本次净状态(白名单生效后)
- USDT $149.60 (无冻结)
- MSTR short 4.01张@5x (avgPx 126.04,浮亏-$0.21,占用 68% 仓位 = 7 成)
- 强平价 160.00 (距 $34, 安全)
- 白名单生效,只熬鹰资本触发 execute
- 其他 trader(矿工/予与/麻吉/风寻/狙击手)静默跳过
---
## 部署清单(下次 session 第一件事)
- [ ] parse_signal() 区分 modify (调杠杆) vs add (加仓), 不混进 dedup 记录
- [ ] recent_signals 表加 `action` 字段 (open/add/reduce/close/modify)
- [ ] okx_position_advisor.py execute 后 raw REST 反查, 实际 contracts 写入 advisor JSON
- [ ] process_signal 主流程加 `actual_contracts` 字段, 与 advisor JSON 对比 warn
- [ ] manual_order.py 模板沉淀到 scripts/ (避免每次重新写)
- [ ] add scripts/manual_position_sizing.py 算 7 成/8 成张数