Files
Hermes-Skills/okx-auto-position/references/post-execute-verification-checklist.md
T

6.1 KiB
Raw Blame History

execute 后判定成败的完整 self-check (2026-07-08)

触发场景

用户原话(本次 session): "要是我没发现,就一直不推了吗? 重试机制呢"

问题链路: 熬鹰 ETH short 减仓信号 → 紧跟"已平仓提醒"信号 → process_signal dedup 误跳过 → agent 没自动平 → ETH 涨到 1795 → 用户 ETH short 浮亏扩大到 -$6.58 → 用户质问"自动平仓的retry在哪?"

三大failure mode必须能识别

1. execute 静默失败(advisor stdout 看起来 OK,实际没下单)

症状:

  • advisor --execute 返回完整 JSON(contracts/margin/tp_price 等字段齐全)
  • 但 raw REST 查持仓:没新增任何仓
  • frozenBal 也没变化

已知受害币种(2026-07-08 实测):

  • SPCX-USDT-SWAP (新开仓 long/short 都失败)
  • MU-USDT-SWAP (新开仓静默失败,加仓成功)
  • SKHYNIX-USDT-SWAP (新开仓失败,加仓成功)

agent 必须做的 self-check(在推QQ之前):

# 1. 立刻 raw REST 查持仓
curl -s -X GET "https://www.okx.com/api/v5/account/positions?instId={SYMBOL}-USDT-SWAP" \
  -H "OK-ACCESS-KEY: ..." -H "OK-ACCESS-SIGN: ..." \
  -H "OK-ACCESS-TIMESTAMP: ..." -H "OK-ACCESS-PASSPHRASE: ..." | jq .

# 2. 看 abs(pos) 是否对应 contracts × leverage
# 3. 看 frozenBal 变化值 = contracts × markPx / leverage
# 4. 若两者都不匹配 → 静默失败 → 立即 raw REST 手动重下

2. execute 返回 order_id 但实际 Rejected

症状: stdout 有 order_id 字段,但实际 status=Rejected。

agent 必须做的 self-check:

# OKX 私有 API 反查订单状态
curl -s -X GET "https://www.okx.com/api/v5/trade/order?instId={SYMBOL}-USDT-SWAP&ordId={ID}" \
  -H "OK-ACCESS-KEY: ..." ... | jq '.data[0].state'
# state 值: filled / live / canceled / Rejected

禁止: 看到 stdout 有 order_id 就推" 下单成功" —— 必须反查 state。

3. 平仓信号被 dedup 跳过(本次 session 实测)

症状: process_signal 返回 ⏭️ 重复信号跳过,但实际持仓仍存在,平仓信号没触发市价平仓。

agent 必须做的 self-check(强化 v4.5.12 章节):

# 当 process_signal 返回 ⏭️ 重复信号跳过时:
# 1. raw REST 查同币种持仓
# 2. 若有持仓 + 信号是平仓类型 + 持仓方向与信号同方向 → 立即手动 raw REST 平仓
# 3. 不等下次信号,不等用户质问

关键判断逻辑(process_signal.py 应该自动化,但 agent 也得会手动):

process_signal 返回值:  ⏭️ 重复信号跳过
↓ raw REST 查持仓
持仓: pos = -2.89 (short)
信号类型: 平仓(close)
信号方向: short
方向一致? ✅ 同方向 → 立即 raw REST 市价全平 reduceOnly=true
方向不一致? ⛔ 反向 → 不动,推"⏭️ 反向持仓不跟平"
无持仓?     ⏭️ → 不动,推"⏭️ 无持仓可平"

三层验证 checklist (agent 每次 execute 后必跑)

第 1 层:raw REST 反查持仓(< 1秒)

import json,time,hmac,hashlib,base64,requests
# 用 v4.5.2 提供的 okx_get 函数模板
result = okx_get('/api/v5/account/positions', {'instId': f'{symbol}-USDT-SWAP'})
for p in result.get('data', []):
    pos = abs(float(p.get('pos', 0) or 0))
    if pos > 0.001:
        print(f"✅ 实际持仓: {symbol} {p['posSide']} {pos}张 @ {p['avgPx']}")
        break
else:
    print(f"⚠️ {symbol} 无持仓 - execute 可能静默失败")

第 2 层:raw REST 反查余额变化(< 1秒)

result = okx_get('/api/v5/account/balance')
for d in result.get('data', [{}])[0].get('details', []):
    if d['ccy'] == 'USDT':
        frozen = float(d.get('frozenBal', 0))
        avail = float(d.get('availBal', 0))
        print(f"USDT: avail={avail:.2f} frozen={frozen:.2f}")
        # 期望: frozen ≥ contracts × markPx / leverage
        # 若 frozen < 期望 → 静默失败

第 3 层:订单 status 反查(若 advisor stdout 有 order_id)(< 1秒)

result = okx_get('/api/v5/trade/order', {'instId': f'{symbol}-USDT-SWAP', 'ordId': order_id})
if result.get('data'):
    state = result['data'][0].get('state')
    if state == 'filled':
        print(f"✅ 订单已成交: {order_id}")
    elif state == 'live':
        print(f"⏳ 订单挂单中: {order_id}")
    elif state == 'Canceled':
        print(f"🚫 订单已撤: {order_id}")
    elif state == 'Rejected':
        print(f"❌ 订单被拒: {order_id} - 查 OKX App 原因")

实战决策表

第 1 层 第 2 层 第 3 层 综合判定 agent 动作
有持仓 frozen ≥ 期望 filled 成功 推"已跟单 X 张"
有持仓 frozen ≥ 期望 live 挂单中 推" 挂单中 @ price"
无持仓 frozen < 期望 无 order_id 静默失败 raw REST 手动重下
无持仓 frozen < 期望 Rejected 拒单 raw REST 重下 + 调小 size
无持仓 frozen < 期望 Canceled 🚫 撤单 raw REST 重下
有持仓但反向 - - dedup 反向 不动,推"⏭️ 反向持仓不跟"
平仓信号 + 有同向持仓 - - 应平仓 raw REST 市价全平

用户质问 → 强制改进

每次用户质问"为什么没自动处理"时,都应触发本 checklist 复盘:

  1. 是哪个 failure mode 触发了?
  2. 当时为什么没识别?
  3. 把识别规则加进 skill 或 process_signal.py

已经触发的质问(2026-07-08):

  • "你分析这些没有用" → v4.5.2 章节
  • "回复侧不要数据" → v4.5.3 章节
  • "要是我没发现,就一直不推了吗? 重试机制呢" → 本 reference
  • "执行成功但agent说没成" → v4.5.0 + 本 reference 第 1 层

修复优先级

未来如果重写 process_signal.py,这 3 层验证应该作为 post-execute 钩子内置到脚本里,不是依赖 agent 手动跑:

# 建议的 process_signal.py 流程
def process_signal(text):
    ... # 解析 + advisor + execute
    
    # post-execute hook (新增)
    if signal_type == 'open' or signal_type == 'add':
        time.sleep(1)
        verify_execution(symbol, expected_contracts, expected_margin)

一句话总结

不要相信 stdout。要相信 raw REST 反查持仓 + 余额变化。