diff --git a/okx-auto-position/scripts/process_signal.py b/okx-auto-position/scripts/process_signal.py index 7c6f903..7251233 100644 --- a/okx-auto-position/scripts/process_signal.py +++ b/okx-auto-position/scripts/process_signal.py @@ -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()