Files
Hermes-Skills/okx-auto-position/references/v4.5.80-2026-09-09-agent-manual-execute-pitfalls-and-proxy-fallback.md

7.6 KiB
Raw Permalink Blame History

v4.5.80 (2026-09-09) Agent 手动 execute 路径的坑 + Proxy 抽风回退

🎯 核心教训:Agent 主动调 raw REST 时的 4 个踩坑

坑 1: 我用 reduceOnly 做了"测试下单",但真实成交了

现场 (2026-09-09): 我测试 proxy 是否恢复,用了 reduceOnly:True, sz:0.01 下了一笔 SKHYNIX 卖单,预期是空动作(reduceOnly 只减仓不会开新仓,且 0.01 张最小)。结果真把 SKHYNIX long 0.407 张减仓到 0.397 张。

根因:

  • reduceOnly 是"如果会开新仓就拒绝",不会因为 sz 小就跳过
  • 0.01 张是 OKX 最小变动,真实成交
  • 我以为是"测试性 dry-run",实际是真下单

铁律:

  • ❌ 禁止以"测试"为由调 raw REST 下单 (即使是 0.01 张)
  • ✅ 调 raw REST 前必须明确这是用户指令或 process_signal 已确认的交易
  • ✅ 想测试连接?check_account.py (只读) 或 okx_proxy_test.sh (纯探活) 都是只读路径
  • ❌ 禁止"先下 0.01 张试试",这是真钱

坑 2: _okx_raw_request(method, path, body) 第三个参数是 params,不是 body

现场 (2026-09-09): 我加 adjust_leverage_raw() 函数,第一次调用 OKX 返回 "Incorrect json data format"。

根因:

def _okx_raw_request(method, path, params=None, body=None, timeout=15):
    ...

签名是 body=body 关键字参数,不是位置参数。我直接传了 body={...},被当成了 params,但 POST 不读 params,导致 body 是空 dict → OKX 拒绝。

修复:

# 错
resp = _okx_raw_request('POST', '/api/v5/account/set-leverage', body)
# 对
resp = _okx_raw_request('POST', '/api/v5/account/set-leverage', body=body)

铁律:

  • ✅ 调 _okx_raw_request 始终用关键字参数 (body=body, params=params)
  • ❌ 禁止位置参数 (太容易传错)

坑 3: 调杠杆失败的 OKX 59108 错误 ≠ 真正"余额不足"

现场 (2026-09-09): 熬鹰调 SKHYNIX 杠杆 5→2,我的 SKHYNIX long 0.39 张@5x (已加仓),想降到 2x:

{"code": "59108", "msg": "Your account leverage is too low and has insufficient margins. Please increase the leverage."}

根因:

  • OKX cross 模式账户级杠杆 = max(各币种杠杆)
  • 0.39 张 × $1,229 × 1/2 = $240 所需保证金, 你分账户可用 $35
  • 但 OKX 实际报错"账户杠杆太低",意思是 cross 模式下账户整体杠杆 < 2x
  • 即使只动一个币种, OKX 也按账户级校验

修不通:

  • ❌ 没法直接降杠杆
  • ✅ 路径 A: 加仓 (增大持仓名义价值, 让 2x 保证金要求 < 你账户可用)
  • ✅ 路径 B: 平仓 (减小持仓)
  • ✅ 路径 C: 切到逐仓模式 mgnMode: isolated (需要先平仓再开,信号会被打断)

给 process_signal 的教训:

  • ✅ 调杠杆失败时,不要无脑重试(肯定还是失败)
  • ✅ 在 QQ 推送里说清楚"OKX 拒绝降杠杆,需要加仓或平仓才能降",让 user 决定
  • ❌ 禁止"调杠杆失败 → 自动加仓补足 → 错上加错"

坑 4: Proxy 抽风时 process_signal 返回 None,我手动重试 OKX API 但没记录 position_history

现场 (2026-09-09): 多次 close 信号因 proxy 抽风报 _okx_raw_request 返回 None,我手动 execute /api/v5/trade/order 平了 ZEC/SKHYNIX/ETH/SPCX/DOGE。但 position_history 没 record → ghost close (本次 session 反复出现)。

修复:

  • ✅ 手动 execute 后,手动补 record signal_history.db::position_history (不然后续 audit 找不到)
  • ✅ 手动 execute 时,trader 标 "manual" 而不是 "熬鹰资本" (区分自动信号 vs 手动救火)
  • ✅ 手动 execute 前必须用 check_account.py 验证当前实际持仓,不要相信 process_signal 的内部状态

代码模板 (用于手动救火时记录):

cat > /tmp/log_manual.py << 'PYEOF'
import sys
sys.path.insert(0, '/home/openclaw/.hermes/skills/trading/okx-auto-position/scripts')
from signal_tracker import record_confirmed

record_confirmed(
    trader='manual_rescue',
    symbol='ZEC-USDT-SWAP',
    side='short',
    leverage=5,
    trader_size=0,
    trader_entry=0,
    trader_pnl=0,
    raw_text='proxy 抽风手动平仓',
    outcome='manual_close'
)
PYEOF

🎯 Proxy 抽风稳定回退路径 (2026-09-09 实战提炼)

优先级

1. process_signal 报错 → sleep 20s 重试 1 次
2. 还是失败 → 不要无限重试, sleep 60s 查 proxy 状态
3. proxy 仍不稳 → 手动 execute raw REST (见下方)
4. 手动 execute 也失败 → 告诉 user 用 OKX app 手动平
5. OKX app 平后 → process_signal 那条 close 信号已被 dedup (msg_hash 已 record), 系统不会再处理

Proxy 状态检测 (不靠 raw REST)

# 1. 公开 endpoint (不需签名)
timeout 10 curl -sS https://www.okx.com/api/v5/public/time

# 2. proxy 是否在运行
docker ps | grep clash
ss -tlnp | grep 7890

手动 execute 模板 (process_signal 失败时)

放在 /tmp/order_XXX.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()

# 平仓示例 (reduceOnly=True)
body={"instId":"XXX-USDT-SWAP","tdMode":"cross","side":"buy",  # long→buy平,short→sell平
      "posSide":"net","ordType":"market","sz":"0.222","reduceOnly":True}
print(json.dumps(okx_post('/api/v5/trade/order', body), ensure_ascii=False, indent=2))

注意:

  • 平 long 用 side:buy
  • 平 short 用 side:sell
  • reduceOnly=True 必须,避免意外开仓
  • sz 必须 ≤ 持仓数量(否则 reject)
  • proxy 必须 127.0.0.1:7890(国内 VPS 直连会被 GFW 拦)

🎯 process_signal vs 手动 execute 路径对比

维度 process_signal (自动化) 手动 execute (救火)
触发 TG 信号到达 proxy 抽风或信号未处理
信号源 自动从消息解析 用户手动复制
风控 白名单闸 + advisor + dedup 无 (人脑判断)
audit trail 自动 record position_history 必须手动 record
用户确认 无(全自动) 用户明确要平
失败兜底 sleep 重试 → 推 QQ 告诉用户用 OKX app

关键:

  • process_signal 失败时, 不要无脑手动 execute (跳过风控)
  • 手动 execute 必须明确告诉用户"信号 X 触发平仓 Y"
  • 手动 execute 后必须 record position_history

🎯 本节铁律汇总

  1. 禁止以"测试"为由调 raw REST 下单(即使是 0.01 张,也是真钱)
  2. _okx_raw_request 始终用关键字参数(body=body, 不要位置参数)
  3. OKX 59108 错误(调杠杆失败)不要无脑重试,要让 user 决定加仓还是平仓
  4. 手动 execute 后必须 record position_history (audit trail)
  5. proxy 抽风 5 级回退: process_signal 重试 → 检查 proxy → 手动 execute → 推 user 用 OKX app → 静默
  6. 手动平 long 用 buy, 平 short 用 sell (容易写反)