Files
crypto_monitor/lib/options/options_position_limit_lib.py
T
dekun 94c65cbd6e Add OKX options max active positions env gate.
Enforce concurrent option contract count on standalone and hedge buys; editable in env UI with hot reload (0 = unlimited).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-30 08:12:50 +08:00

77 lines
2.2 KiB
Python

"""OKX 期权持仓笔数上限(env: OKX_OPTIONS_MAX_ACTIVE_POSITIONS)."""
from __future__ import annotations
import os
from typing import Any, Optional
def options_max_active_positions() -> int:
"""同时持有的期权合约笔数上限;0=不限制.热更读 env."""
raw = os.getenv("OKX_OPTIONS_MAX_ACTIVE_POSITIONS", "0")
try:
v = int(float(str(raw).strip()))
except (TypeError, ValueError):
return 0
return max(0, v)
def count_live_option_positions(rows: Optional[list[dict[str, Any]]]) -> int:
if not rows:
return 0
n = 0
for r in rows:
if not isinstance(r, dict):
continue
try:
pos = float(r.get("pos") or 0)
except (TypeError, ValueError):
continue
if abs(pos) >= 1e-12:
n += 1
return n
def _inst_already_open(rows: list[dict[str, Any]], inst_id: str) -> bool:
want = (inst_id or "").strip()
if not want:
return False
for r in rows:
if str(r.get("instId") or r.get("inst_id") or "").strip() == want:
return True
return False
def option_position_limit_block_msg(
ex: Any,
*,
opening_inst_id: str = "",
max_active: Optional[int] = None,
fetch_positions=None,
) -> Optional[str]:
"""若禁止新开买期权则返回中文原因,否则 None.
- max_active<=0:不限制
- 加仓已有合约(opening_inst_id 已在持仓中):不占新笔数,放行
- 拉持仓失败:拒绝开仓(避免绕过上限)
"""
mx = options_max_active_positions() if max_active is None else int(max_active)
if mx <= 0:
return None
fetch = fetch_positions
if fetch is None:
from lib.exchange.okx_options_lib import fetch_option_positions
fetch = fetch_option_positions
try:
rows = fetch(ex)
except Exception:
rows = None
if rows is None:
return f"无法获取期权持仓,暂不可开仓(上限 {mx} 笔)"
active = count_live_option_positions(rows)
if _inst_already_open(rows, opening_inst_id):
return None
if active >= mx:
return f"期权持仓已达上限({active}/{mx}),请先平仓后再开"
return None