fix: use isolated margin for OKX option buys (error 51019)

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-07 10:24:33 +08:00
parent 2a3f36d34f
commit 5fdfc67438
3 changed files with 56 additions and 12 deletions
+52 -8
View File
@@ -1,7 +1,9 @@
"""OKX USDⓈ 期权 API 封装(主账户 exchange_options 专用)。"""
from __future__ import annotations
import json
import math
import re
import time
from typing import Any, Callable
@@ -9,6 +11,50 @@ import ccxt
from lib.options.options_pricing_lib import is_shallow_itm, option_moneyness, option_moneyness_label
_OKX_OPTION_ERR_ZH: dict[str, str] = {
"51018": "期权账户不能持有净空头头寸",
"51019": "期权买入须使用逐仓模式(全仓模式下不能持有多头净头寸)",
}
def _okx_trade_error_message(exc: BaseException | None = None, resp: Any = None) -> str:
row: dict[str, Any] | None = None
if isinstance(resp, dict):
data = resp.get("data") or []
if data and isinstance(data[0], dict):
row = data[0]
if row is None and exc is not None:
text = str(exc)
match = re.search(r"\{.*\}", text, re.DOTALL)
if match:
try:
payload = json.loads(match.group(0))
data = payload.get("data") or []
if data and isinstance(data[0], dict):
row = data[0]
except json.JSONDecodeError:
pass
if row:
code = str(row.get("sCode") or "")
zh = _OKX_OPTION_ERR_ZH.get(code)
if zh:
return zh
msg = str(row.get("sMsg") or "").strip()
if msg:
return msg
if exc is not None:
text = str(exc).strip()
if text.lower().startswith("okx "):
text = text[4:].strip()
return text or "下单失败"
return "下单失败"
def td_mode_for_option_buy(configured: str | None = None) -> str:
"""OKX 买入期权(多头)必须使用逐仓。"""
mode = (configured or "isolated").strip().lower()
return "isolated" if mode == "cross" else mode or "isolated"
def create_options_exchange(
api_key: str,
@@ -327,7 +373,7 @@ def place_option_limit_order(
side: str,
sheets: int,
price: float,
td_mode: str = "cross",
td_mode: str = "isolated",
tick_sz: Any = None,
reduce_only: bool = False,
pos_side: str | None = None,
@@ -357,10 +403,9 @@ def place_option_limit_order(
data = (resp or {}).get("data") or []
if data and str(data[0].get("sCode")) == "0":
return {"ok": True, "data": data[0], "raw": resp, "px": px}
msg = data[0].get("sMsg") if data else str(resp)
return {"ok": False, "msg": msg or "下单失败", "raw": resp, "px": px}
return {"ok": False, "msg": _okx_trade_error_message(resp=resp), "raw": resp, "px": px}
except Exception as e:
return {"ok": False, "msg": str(e), "px": px}
return {"ok": False, "msg": _okx_trade_error_message(e), "px": px}
def place_option_market_order(
@@ -369,7 +414,7 @@ def place_option_market_order(
inst_id: str,
side: str,
sheets: int,
td_mode: str = "cross",
td_mode: str = "isolated",
reduce_only: bool = False,
pos_side: str | None = None,
) -> dict[str, Any]:
@@ -394,10 +439,9 @@ def place_option_market_order(
data = (resp or {}).get("data") or []
if data and str(data[0].get("sCode")) == "0":
return {"ok": True, "data": data[0], "raw": resp}
msg = data[0].get("sMsg") if data else str(resp)
return {"ok": False, "msg": msg or "下单失败", "raw": resp}
return {"ok": False, "msg": _okx_trade_error_message(resp=resp), "raw": resp}
except Exception as e:
return {"ok": False, "msg": str(e)}
return {"ok": False, "msg": _okx_trade_error_message(e)}
def fetch_option_positions(ex: ccxt.okx) -> list[dict[str, Any]]: