# v4.5.27 静默 execute 失败模式 (2026-07-08 实测) ## 现象 `process_signal.py` 处理加仓信号时,advisor `--execute` 步骤因 SSL/ReadTimeout 抛异常,但 process_signal 只返回: ``` ⚠️ advisor错误: Traceback (most recent call last) File ".../urllib3/response.py", line 905, in _error_catcher ``` 此时**用户实际持仓无变化**(无冻结余额增加、无可用USDT减少),但 process_signal 已经返回"已推送"给 QQ,**QQ 上写着"已跟单 X 张"是假阳性**。 ## 真实案例(2026-07-08) - 风寻 SKHY 加仓: advisor SSL抽风,QQ推了但实际未execute - 熬鹰 SPCX long 新开仓:advisor SSL抽风,QQ推但实际未下单(连续2次都失败) - 熬鹰 SKHY 空单新开仓:同上 - 熬鹰 MU 新开仓:同上 这4个都中了,**用户实际未跟单,但 QQ 显示已跟单**。 ## 根因 1. `process_signal.py` 调 `run_advisor()` 返回 `{'error': '...'}` 2. 接着进入 `format_message(rec)` 路径,format 不检查 error 状态 3. 然后 `push_to_qq()` + `record_signal()`,**没有 execute 失败回滚** 4. 结果:QQ 拿到"已跟单 X 张"推送,但实际持仓0张变化 ## 二次校验硬约束(已写 SKILL.md v4.5.18) `scripts/process_signal.py` 流程: ``` 1. parse → fields 2. classify → signal_type 3. close 信号 → 走独立通道 4. 其他信号 → run_advisor() → rec 5. 若 rec.error: ❌ 当前: 直接 format + push,QQ看到假的"已跟单" ✅ 必须: 查 raw REST /api/v5/account/positions,若该币种 pos==0 或未变化, 推"⚠️ advisor 错误,建议仓位=X张,但 execute 失败,需手动" ``` ## 验证execute是否真下单 ```bash # 每次 advisor --execute 后,必须反查 timeout 30 python3 -c " import json,time,hmac,hashlib,base64,requests def okx(p,params=None,t=15): creds=open('/home/openclaw/.bashrc').read() k=s=pw=None for l in creds.split('\n'): if l.startswith('export OKX_API_KEY='): k=l.split('=',1)[1].strip().strip('\"').strip(\"'\") elif l.startswith('export OKX_SECRET='): s=l.split('=',1)[1].strip().strip('\"').strip(\"'\") elif l.startswith('export OKX_PASSPHRASE='): pw=l.split('=',1)[1].strip().strip('\"').strip(\"'\") ts=time.strftime('%Y-%m-%dT%H:%M:%S.000Z',time.gmtime()) msg=ts+'GET'+p+(json.dumps(params) if params else '') sig=base64.b64encode(hmac.new(s.encode(),msg.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'} px={'http':'http://127.0.0.1:7890','https':'http://127.0.0.1:7890'} return requests.get(f'https://www.okx.com{p}',params=params or {},headers=h,proxies=px,timeout=t).json() pos=okx('/api/v5/account/positions') for p in pos.get('data',[]): if float(p.get('pos','0') or 0)!=0: print(f\"{p['instId']:25s} {p['pos']:>10s}张 avgPx={p['avgPx']} mark={p['markPx']} upl={p['upl']}\") bal=okx('/api/v5/account/balance') for d in bal['data'][0].get('details',[]): if d['ccy']=='USDT': print(f\"USDT: availBal={d['availBal']} frozenBal={d['frozenBal']} eq={d['eq']}\") " ``` 如果该币种 pos 没新增 + frozenBal 没增加 → execute 失败。 ## 修复方向(待实施) **短期**:在 `process_signal.py` 加 execute 失败兜底: ```python if 'error' in rec: # advisor报错,验证一下真没execute time.sleep(3) actual_pos = fetch_position(symbol) if actual_pos is None or actual_pos == existing_pos: # 真没execute msg = f"⚠️ {symbol} {side} advisor失败且未下单: {rec['error'][:100]}" push_to_qq(msg) return f"⚠️ execute失败: {rec['error'][:50]}" ``` **长期**:advisor --execute 路径加 retry + 真错误识别(SSL抽风不算 fatal,应重试一次)。 ## 实战经验(2026-07-08) 1. **advisor SSL抽风时,等30-60秒再 retry 通常可恢复** — OKX API 暂时性网络问题 2. **小张数(< 0.5张)信号经常 fail execute** — 因为OKX最小变动单位不匹配 3. **新币种(SPCX/SKHYNIX)首次 execute 经常 fail** — 可能合约 metadata 未完全加载 4. **失败时advisor 返回 JSON 看起来正常,但无 order_id** — 这就是 v4.5.18 execute 反查硬约束的根因 ## v4.5.27 决策规则(可入主 SKILL.md) 1. advisor 报 SSL/Timeout/Connection 错误 → 自动 retry 一次,间隔 10s 2. retry 仍失败 → 推"⚠️ {symbol} execute失败,需手动"到 QQ,不推"已跟单" 3. 用户看到这条 → 自己手动 retry 或 等下次信号 4. **绝对禁止**在 advisor 报错时推"已跟单 X 张"格式