Files
eth_hedge_sim/backend/app/exchange/okx/parse.py
T
2026-07-25 08:43:09 +08:00

77 lines
2.5 KiB
Python

"""OKX 合约 ID / 到期解析(交易所专属)。"""
from __future__ import annotations
import re
from datetime import datetime, timezone
from typing import Any
_DATE_RE = re.compile(r"^\d{6}$")
def safe_float(v: Any) -> float | None:
if v is None or v == "":
return None
try:
return float(v)
except (TypeError, ValueError):
return None
def parse_option_inst_id(inst_id: str) -> tuple[str | None, float | None, str | None]:
"""ETH-USD_UM-YYMMDD-STRIKE-C → (YYMMDD, strike, C|P)."""
parts = (inst_id or "").strip().split("-")
if len(parts) < 5:
return None, None, None
ymd = parts[-3]
strike = safe_float(parts[-2])
opt = parts[-1].upper()
if not _DATE_RE.fullmatch(ymd) or strike is None or opt not in ("C", "P"):
return None, None, None
return ymd, strike, opt
def expiry_ms_from_ymd(ymd: str) -> int:
"""OKX 期权到期:当日 08:00 UTC = 上海 16:00。"""
yy, mm, dd = int(ymd[0:2]), int(ymd[2:4]), int(ymd[4:6])
dt = datetime(2000 + yy, mm, dd, 8, 0, 0, tzinfo=timezone.utc)
return int(dt.timestamp() * 1000)
def rows_to_option_contracts(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
"""
归一化为策略层可用的中性结构:
{inst_id, expiry_ymd, strike, side, ct_mult}
"""
out: list[dict[str, Any]] = []
for row in rows:
if not isinstance(row, dict):
continue
state = str(row.get("state") or "live").lower()
if state and state != "live":
continue
inst_id = str(row.get("instId") or "")
y, stk, opt = parse_option_inst_id(inst_id)
if y is None or stk is None or opt is None:
exp = safe_float(row.get("expTime"))
if exp:
ms = int(exp) if exp > 10_000_000_000 else int(exp * 1000)
y = datetime.fromtimestamp(ms / 1000, tz=timezone.utc).strftime("%y%m%d")
stk = safe_float(row.get("stk"))
opt_raw = str(row.get("optType") or "").upper()
opt = opt_raw if opt_raw in ("C", "P") else None
if not inst_id or not y or stk is None or opt not in ("C", "P"):
continue
ct = safe_float(row.get("ctMult"))
out.append(
{
"inst_id": inst_id,
"expiry_ymd": y,
"expiry_ms": expiry_ms_from_ymd(y),
"strike": float(stk),
"side": opt,
"ct_mult": float(ct) if ct and ct > 0 else None,
}
)
return out