fix期权链 Delta 改为从 opt-summary 拉取。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -799,6 +799,37 @@ def fetch_option_tickers(ex: ccxt.okx, inst_family: str) -> dict[str, dict[str,
|
|||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_option_summaries(ex: ccxt.okx, inst_family: str) -> dict[str, dict[str, Any]]:
|
||||||
|
"""GET /api/v5/public/opt-summary — 含 delta / deltaBS 等希腊字母.
|
||||||
|
市场 tickers 不含 Delta,期权链展示必须走本接口.
|
||||||
|
"""
|
||||||
|
out: dict[str, dict[str, Any]] = {}
|
||||||
|
try:
|
||||||
|
rows = ex.public_get_public_opt_summary({"instFamily": inst_family}).get("data") or []
|
||||||
|
for r in rows:
|
||||||
|
if isinstance(r, dict) and r.get("instId"):
|
||||||
|
out[str(r["instId"])] = r
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def option_delta_from_summary(
|
||||||
|
summary: dict[str, Any] | None = None,
|
||||||
|
*,
|
||||||
|
ticker: dict[str, Any] | None = None,
|
||||||
|
) -> float | None:
|
||||||
|
"""优先 Black-Scholes 单位 Delta(deltaBS),其次 delta;兼容 ticker 偶发字段."""
|
||||||
|
for src in (summary, ticker):
|
||||||
|
if not isinstance(src, dict):
|
||||||
|
continue
|
||||||
|
for key in ("deltaBS", "delta", "deltaBs", "delta_bs"):
|
||||||
|
v = _safe_float(src.get(key))
|
||||||
|
if v is not None:
|
||||||
|
return v
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def build_option_chain(
|
def build_option_chain(
|
||||||
ex: ccxt.okx,
|
ex: ccxt.okx,
|
||||||
underlying: str,
|
underlying: str,
|
||||||
@@ -847,6 +878,7 @@ def build_option_chain(
|
|||||||
except Exception as e2:
|
except Exception as e2:
|
||||||
instruments_err = str(e2) or e2.__class__.__name__
|
instruments_err = str(e2) or e2.__class__.__name__
|
||||||
tickers = fetch_option_tickers(ex, family)
|
tickers = fetch_option_tickers(ex, family)
|
||||||
|
summaries = fetch_option_summaries(ex, family)
|
||||||
expiries: dict[str, list[dict[str, Any]]] = {}
|
expiries: dict[str, list[dict[str, Any]]] = {}
|
||||||
skipped_no_index = 0
|
skipped_no_index = 0
|
||||||
for meta in instruments:
|
for meta in instruments:
|
||||||
@@ -872,6 +904,7 @@ def build_option_chain(
|
|||||||
continue
|
continue
|
||||||
inst_id = str(meta.get("instId") or "")
|
inst_id = str(meta.get("instId") or "")
|
||||||
t = tickers.get(inst_id) or {}
|
t = tickers.get(inst_id) or {}
|
||||||
|
s = summaries.get(inst_id) or {}
|
||||||
q = _resolve_chain_quote(
|
q = _resolve_chain_quote(
|
||||||
ticker=t,
|
ticker=t,
|
||||||
meta=meta,
|
meta=meta,
|
||||||
@@ -884,10 +917,7 @@ def build_option_chain(
|
|||||||
mark = q["mark_px"]
|
mark = q["mark_px"]
|
||||||
ask_sz = q["ask_sz"]
|
ask_sz = q["ask_sz"]
|
||||||
bid_sz = q["bid_sz"]
|
bid_sz = q["bid_sz"]
|
||||||
# 行情 ticker:delta;兼容个别字段名
|
delta = option_delta_from_summary(s, ticker=t)
|
||||||
delta = _safe_float(t.get("delta"))
|
|
||||||
if delta is None:
|
|
||||||
delta = _safe_float(t.get("deltaBS"))
|
|
||||||
expiry_be = expiry_breakeven_from_ask(
|
expiry_be = expiry_breakeven_from_ask(
|
||||||
opt_type=opt_type,
|
opt_type=opt_type,
|
||||||
strike=strike,
|
strike=strike,
|
||||||
|
|||||||
@@ -1,10 +1,27 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from lib.exchange.okx_options_lib import format_position_row
|
from lib.exchange.okx_options_lib import format_position_row, option_delta_from_summary
|
||||||
|
|
||||||
|
|
||||||
class OptionsDeltaFieldsTests(unittest.TestCase):
|
class OptionsDeltaFieldsTests(unittest.TestCase):
|
||||||
|
def test_delta_prefers_summary_delta_bs(self):
|
||||||
|
self.assertAlmostEqual(
|
||||||
|
option_delta_from_summary({"deltaBS": "0.55", "delta": "12.3"}),
|
||||||
|
0.55,
|
||||||
|
places=4,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_delta_falls_back_to_ticker(self):
|
||||||
|
self.assertAlmostEqual(
|
||||||
|
option_delta_from_summary({}, ticker={"delta": "-0.22"}),
|
||||||
|
-0.22,
|
||||||
|
places=4,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_delta_none_when_missing(self):
|
||||||
|
self.assertIsNone(option_delta_from_summary({}, ticker={}))
|
||||||
|
|
||||||
def test_format_position_exposes_delta_bs(self):
|
def test_format_position_exposes_delta_bs(self):
|
||||||
row = format_position_row(
|
row = format_position_row(
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user