okx-auto-position: 余额不足显式提示 + exit code != 0

**Bug**: 8/4 20:32 SNDK 加仓信号 done, 但 OKX 没持仓 — 用户以为是 bug
**原因**: process_signal.py 余额不足时只推'pushed_add_insufficient'消息, 不标 failed
  signal_queue.db 标 done → 用户/agent 都以为是 bug

**Fix**:
1. process_signal.py 新增 _get_usdt_free() helper (实时查 OKX USDT free)
2. 余额不足时 QQ 推送加前缀 '⚠️ 跟单失败: 余额不足 ⚠️' + USDT 余额显示
3. process_signal.py main() 加 exit code 2 (跟单失败时) → signal_queue.retry 标 failed (而非 done)
4. retry < 3 仍会重试, 但 failed 状态可区分 done (signal_queue.db 加 status='failed')

**注意**: advisor 实际会给 0.056 张 SNDK (USDT 26.82 保证金), 不是 0 张 — 但用 99.7% 余额不健康
  → 用户应该知道是'满仓'而非'跟单成功'
This commit is contained in:
2026-08-05 12:17:04 +08:00
parent 9f2ae6a45f
commit f00b6962e2
+37 -3
View File
@@ -602,6 +602,22 @@ def format_close_message(fields, close_result):
# ─── 执行订单 ────────────────────────────────────────────────────────────
def _get_usdt_free():
"""实时查 OKX USDT free (失败返回 0)."""
try:
from okx_position_advisor import load_credentials, create_exchange
creds = load_credentials()
ex = create_exchange(creds)
bal = ex.fetch_balance()
# 兼容 ccxt OKX 多账户结构
usdt_info = bal.get('USDT', {}) if isinstance(bal, dict) else {}
free = usdt_info.get('free', 0) if isinstance(usdt_info, dict) else 0
return float(free or 0)
except Exception as e:
print(f"[warn] _get_usdt_free err: {e}", file=__import__('sys').stderr)
return 0.0
def execute_order(symbol, side, leverage, rec):
"""执行开仓订单"""
cmd = [
@@ -809,14 +825,28 @@ def process_signal(text):
trader_pnl=float(fields.get('pnl', '0').replace(',', '')),
raw_text=text, outcome='auto_executed_add')
else:
# 余额不足/执行失败 → 推消息不执行
msg = format_message(fields, rec, signal_type)
# 余额不足/执行失败 → 推消息不执行 (2026-08-05: 余额不足显式提示)
exec_err = (exec_result or {}).get('error', 'unknown') if exec_result else 'no result'
# 看是余额不足还是其他错
is_insufficient = '余额不足' in str(exec_err) or 'insufficient' in str(exec_err).lower()
# 实时查 USDT free (用 okx_position_advisor helper)
usdt_free = _get_usdt_free()
if is_insufficient:
msg = format_message(fields, rec, signal_type)
msg = f"⚠️ **跟单失败: 余额不足** ⚠️\n\n{msg}\n\n💡 你 USDT ${usdt_free:.2f} 不足以开 1 张 {symbol} 3x 杠杆 (约需 ~$46k)\n"
outcome = 'pushed_add_insufficient'
else:
msg = format_message(fields, rec, signal_type)
msg = f"⚠️ **跟单失败: 执行错误** ⚠️\n\n{msg}\n\n❌ 错误: {exec_err}\n"
outcome = 'auto_executed_failed'
_tracker_record(trader=trader, symbol=symbol, side=side,
leverage=int(leverage) if leverage else 10,
trader_size=float(fields.get('size', '0').replace(',', '')),
trader_entry=float(fields.get('entry', '0').replace(',', '')),
trader_pnl=float(fields.get('pnl', '0').replace(',', '')),
raw_text=text, outcome='pushed_add_insufficient')
raw_text=text, outcome=outcome)
# 2026-08-05: 在 main() 标记 status=failed (signal_queue.db) — 让 cron retry 时知道
_exec_failed = exec_err
elif signal_type == 'reduce':
# 减仓信号: 锁了同方向 → 按大佬减仓比例, 自动减我们的同向持仓
try:
@@ -908,6 +938,10 @@ def main():
result = process_signal(text)
print(result)
# 2026-08-05: 余额不足/执行失败 → exit code != 0 → signal_queue.retry 标 failed
import sys as _sys
if '跟单失败' in str(result):
_sys.exit(2)
if __name__ == '__main__':
main()