From d743185f0516fe7523bdb72efd08fb5d4fa6c83a Mon Sep 17 00:00:00 2001 From: dekun Date: Mon, 5 Oct 2026 06:47:49 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8C=81=E4=BB=93=E5=8D=A1=E4=B8=8E=E4=B8=AD?= =?UTF-8?q?=E6=8E=A7=E5=BF=AB=E7=85=A7=E5=A2=9E=E5=8A=A0=E6=9C=9F=E6=9D=83?= =?UTF-8?q?=E6=9D=A0=E6=9D=86=E5=80=8D=E6=95=B0=E5=B1=95=E7=A4=BA=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- lib/common/static/options_panel.js | 20 +++++++++++++ lib/common/static/options_position_cards.js | 33 +++++++++++++++++++++ lib/exchange/okx_options_lib.py | 7 +++++ lib/hub/hub_bridge_register.py | 14 +++++++++ lib/options/options_history_lib.py | 19 ++++++++++++ lib/options/templates/options_panel.html | 2 +- 6 files changed, 94 insertions(+), 1 deletion(-) diff --git a/lib/common/static/options_panel.js b/lib/common/static/options_panel.js index 53dd931..944fab3 100644 --- a/lib/common/static/options_panel.js +++ b/lib/common/static/options_panel.js @@ -1076,6 +1076,25 @@ return "约 " + Number(v).toFixed(1) + "×"; } + function calcPosLeverage(p) { + const direct = Number(p && (p.contract_leverage != null ? p.contract_leverage : p.leverage)); + if (Number.isFinite(direct) && direct > 0) return direct; + const idx = Number(p && p.idx_px); + const amt = Number(p && p.eth_amount); + const prem = Number(p && p.premium_paid); + if (!Number.isFinite(idx) || !Number.isFinite(amt) || !Number.isFinite(prem) || amt <= 0 || prem <= 0) { + return null; + } + if (isCoinPos(p)) { + return Math.round((amt / prem) * 10) / 10; + } + return Math.round((idx * amt) / prem * 10) / 10; + } + + function fmtPosLeverage(p) { + return fmtLeverage(calcPosLeverage(p)); + } + /** 链上展示:USDC=指数÷卖一(美元);币本位卖一为币报价 → 1÷卖一. */ function calcAskLeverage(indexPx, askPx) { if (indexPx == null || askPx == null) return null; @@ -1691,6 +1710,7 @@ '持仓来源: ' + sourceText(p) + "" + '行权价: ' + fmt(p.strike, 0) + "" + '张数: ' + fmt(p.pos, 0) + " · 币量 " + fmt(p.eth_amount, 4) + "" + + '杠杆倍数: ' + fmtPosLeverage(p) + "" + (expAttr ? '到期倒计时: —' : "") + diff --git a/lib/common/static/options_position_cards.js b/lib/common/static/options_position_cards.js index be407ab..ef1ea5e 100644 --- a/lib/common/static/options_position_cards.js +++ b/lib/common/static/options_position_cards.js @@ -88,6 +88,38 @@ return ""; } + function isCoinPos(p) { + const mode = String((p && p.margin_mode) || "").toLowerCase(); + if (mode === "coin") return true; + if (mode === "usdc") return false; + const ccy = String((p && p.premium_ccy) || "").toUpperCase(); + if (ccy === "USDC") return false; + if (ccy === "BTC" || ccy === "ETH") return true; + const inst = String((p && p.inst_id) || ""); + return inst.indexOf("-USD-") >= 0 && inst.indexOf("_UM") < 0; + } + + function calcPosLeverage(p) { + const direct = Number(p && (p.contract_leverage != null ? p.contract_leverage : p.leverage)); + if (Number.isFinite(direct) && direct > 0) return direct; + const idx = Number(p && p.idx_px); + const amt = Number(p && p.eth_amount); + const prem = Number(p && p.premium_paid); + if (!Number.isFinite(idx) || !Number.isFinite(amt) || !Number.isFinite(prem) || amt <= 0 || prem <= 0) { + return null; + } + if (isCoinPos(p)) { + return Math.round((amt / prem) * 10) / 10; + } + return Math.round((idx * amt) / prem * 10) / 10; + } + + function fmtPosLeverage(p) { + const lev = calcPosLeverage(p); + if (lev == null || Number.isNaN(Number(lev))) return "—"; + return "约 " + Number(lev).toFixed(1) + "×"; + } + function fmtPxSz(px, sz, tickSz) { if (px === null || px === undefined || Number.isNaN(Number(px))) return "—"; let price = fmtOptionPx(px, tickSz); @@ -224,6 +256,7 @@ '持仓来源: ' + sourceText(p) + "" + '行权价: ' + fmt(p.strike, 0) + "" + '张数: ' + fmt(p.pos, 0) + " · 币量 " + fmt(p.eth_amount, 4) + "" + + '杠杆倍数: ' + fmtPosLeverage(p) + "" + (expAttr ? '到期倒计时: ' + expiryCdHtml(expAttr) + "" : "") + diff --git a/lib/exchange/okx_options_lib.py b/lib/exchange/okx_options_lib.py index d8c5c41..9e12863 100644 --- a/lib/exchange/okx_options_lib.py +++ b/lib/exchange/okx_options_lib.py @@ -1806,6 +1806,7 @@ def format_position_row( ) -> dict[str, Any]: from lib.options.options_pricing_lib import ( close_breakeven_idx, + equivalent_contract_leverage, expiry_breakeven_px, idx_distance_to_be, strike_distance_to_be, @@ -1891,6 +1892,12 @@ def format_position_row( "close_be_px": close_be, "dist_expiry_be": strike_distance_to_be(strike, expiry_be), "dist_close_be": idx_distance_to_be(idx_px, close_be), + "contract_leverage": equivalent_contract_leverage( + index_px=idx_px, + eth_amount=eth_amount, + total_premium=premium_paid, + margin_mode=row_mode, + ), "raw": pos, } diff --git a/lib/hub/hub_bridge_register.py b/lib/hub/hub_bridge_register.py index 51a19bd..aa26165 100644 --- a/lib/hub/hub_bridge_register.py +++ b/lib/hub/hub_bridge_register.py @@ -99,6 +99,19 @@ def _enrich_hub_protocol(snap: dict[str, Any], *, app_module: Any) -> dict[str, if premium_usdt is not None: has_premium = True premium_total_usdt += float(premium_usdt) + contract_leverage = p.get("contract_leverage") + if contract_leverage is None: + try: + from lib.options.options_pricing_lib import equivalent_contract_leverage + + contract_leverage = equivalent_contract_leverage( + index_px=p.get("idx_px") or p.get("index_px"), + eth_amount=p.get("eth_amount"), + total_premium=premium_paid, + margin_mode=p.get("margin_mode"), + ) + except Exception: + contract_leverage = None slim_pos.append( { "inst_id": p.get("inst_id"), @@ -108,6 +121,7 @@ def _enrich_hub_protocol(snap: dict[str, Any], *, app_module: Any) -> dict[str, "premium_paid": premium_paid, "premium_paid_usdt": round(float(premium_usdt), 4) if premium_usdt is not None else None, "premium_ccy": p.get("premium_ccy"), + "contract_leverage": contract_leverage, "mark": p.get("mark") or p.get("mark_px"), "underlying": p.get("underlying"), "source_label": p.get("source_label"), diff --git a/lib/options/options_history_lib.py b/lib/options/options_history_lib.py index 0dec61a..eca8798 100644 --- a/lib/options/options_history_lib.py +++ b/lib/options/options_history_lib.py @@ -29,6 +29,25 @@ def enrich_position_row_display( if premium_override is not None: row["premium_paid"] = premium_override row["premium_paid_fmt"] = format_premium_amount(row.get("premium_paid"), ccy=premium_ccy) + try: + from lib.options.options_pricing_lib import equivalent_contract_leverage + + def _sf(v: Any) -> float | None: + if v is None or v == "": + return None + try: + return float(v) + except (TypeError, ValueError): + return None + + row["contract_leverage"] = equivalent_contract_leverage( + index_px=_sf(row.get("idx_px")), + eth_amount=_sf(row.get("eth_amount")), + total_premium=_sf(row.get("premium_paid")), + margin_mode=row_mode, + ) + except Exception: + row.setdefault("contract_leverage", None) return row diff --git a/lib/options/templates/options_panel.html b/lib/options/templates/options_panel.html index a4a3545..a547ea8 100644 --- a/lib/options/templates/options_panel.html +++ b/lib/options/templates/options_panel.html @@ -364,4 +364,4 @@ - +