"""对冲计划与单独期权开仓互斥门控. 默认开启:有进行中对冲计划时禁止单独开期权;有纯期权持仓时禁止启动对冲计划. 关闭 HEDGE_PLAN_OPTIONS_MUTUAL_EXCLUSIVE 后两边可同时开. """ from __future__ import annotations import os from typing import Any, Callable, Optional def _env_bool(key: str, default: bool = False) -> bool: v = (os.getenv(key) or "").strip().lower() if not v: return default return v in ("1", "true", "yes", "on") def mutual_exclusive_enabled() -> bool: return _env_bool("HEDGE_PLAN_OPTIONS_MUTUAL_EXCLUSIVE", True) def block_standalone_option_open_msg(conn: Any) -> Optional[str]: """若应拦截单独开期权,返回中文原因;否则 None.""" if not mutual_exclusive_enabled(): return None try: from lib.hedge_plan.hedge_plan_db import count_active_plans, init_hedge_plan_tables init_hedge_plan_tables(conn) if count_active_plans(conn) > 0: return "存在进行中对冲计划,禁止单独开期权(可在 env「对冲与期权互斥门控」关闭)" except Exception: return None return None def _pos_nonzero(raw: dict[str, Any]) -> bool: try: return abs(float(raw.get("pos") or 0)) > 1e-12 except (TypeError, ValueError): return False def has_standalone_option_position(conn: Any, raw_positions: list[dict[str, Any]] | None) -> bool: """交易所期权持仓中,是否存在未挂在进行中对冲计划腿上的仓位.""" if not raw_positions: return False from lib.instance.instance_dashboard_lib import _resolve_options_source for p in raw_positions: if not isinstance(p, dict) or not _pos_nonzero(p): continue inst = str(p.get("instId") or p.get("inst_id") or "").strip() if not inst: continue source, _, _ = _resolve_options_source(conn, inst) if source == "option": return True return False def block_hedge_plan_start_msg( conn: Any, *, fetch_positions: Optional[Callable[[Any], Any]] = None, exchange: Any = None, raw_positions: list[dict[str, Any]] | None = None, ) -> Optional[str]: """若应拦截启动对冲计划,返回中文原因;否则 None.""" if not mutual_exclusive_enabled(): return None rows = raw_positions if rows is None: if fetch_positions is None or exchange is None: return None try: rows = fetch_positions(exchange) or [] except Exception: return None try: if has_standalone_option_position(conn, rows): return "存在单独期权持仓,禁止启动对冲计划(可在 env「对冲与期权互斥门控」关闭)" except Exception: return None return None