77d2effb6d
Co-authored-by: Cursor <cursoragent@cursor.com>
66 lines
2.0 KiB
Python
66 lines
2.0 KiB
Python
"""公开行情辅助:补历史到期结算指数展示(不发明成交现金)。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import logging
|
|
import urllib.error
|
|
import urllib.request
|
|
from typing import Any
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
_CACHE: dict[int, float] = {}
|
|
_CACHE_MAX = 256
|
|
|
|
|
|
def looks_binance_option(inst_id: str | None) -> bool:
|
|
return "USD_UM" in str(inst_id or "")
|
|
|
|
|
|
def eth_usdt_close_at_ms(ts_ms: int | None) -> float | None:
|
|
"""币安 ETHUSDT 1m K 线收盘价(近似期权结算指数)。失败返回 None。"""
|
|
if ts_ms is None:
|
|
return None
|
|
try:
|
|
ms = int(ts_ms)
|
|
except (TypeError, ValueError):
|
|
return None
|
|
if ms <= 0:
|
|
return None
|
|
minute = (ms // 60_000) * 60_000
|
|
cached = _CACHE.get(minute)
|
|
if cached is not None:
|
|
return cached
|
|
url = (
|
|
"https://api.binance.com/api/v3/klines"
|
|
f"?symbol=ETHUSDT&interval=1m&startTime={minute}&limit=1"
|
|
)
|
|
try:
|
|
with urllib.request.urlopen(url, timeout=4) as resp:
|
|
raw = resp.read().decode("utf-8", "replace")
|
|
rows = json.loads(raw)
|
|
if not rows:
|
|
return None
|
|
close_px = float(rows[0][4])
|
|
if close_px <= 0:
|
|
return None
|
|
if len(_CACHE) >= _CACHE_MAX:
|
|
_CACHE.clear()
|
|
_CACHE[minute] = close_px
|
|
return close_px
|
|
except (urllib.error.URLError, TimeoutError, ValueError, TypeError, IndexError) as e:
|
|
logger.debug("eth_usdt_close_at_ms failed ms=%s: %s", minute, e)
|
|
return None
|
|
|
|
|
|
def maybe_public_settle_index(g: dict[str, Any]) -> float | None:
|
|
"""库内无结算价时,币安期权到期组用公开 ETHUSDT 收盘近似。"""
|
|
if str(g.get("close_reason") or "") != "expiry":
|
|
return None
|
|
inst = g.get("option_inst_id") or g.get("option2_inst_id")
|
|
if not looks_binance_option(str(inst) if inst else None):
|
|
return None
|
|
ts = g.get("close_at_ms") or g.get("hold_close_at_ms")
|
|
return eth_usdt_close_at_ms(ts if ts is not None else None)
|