(function (global) { "use strict"; function fmt(v, d) { if (v === null || v === undefined || Number.isNaN(Number(v))) return "—"; return Number(v).toFixed(d == null ? 2 : d); } function fmtDisplay(v, fallback) { if (v !== null && v !== undefined && String(v).trim() !== "") return String(v); if (fallback !== undefined) return fmtDisplay(fallback); return "—"; } function fmtOptionPx(v, tickSz) { if (v === null || v === undefined || Number.isNaN(Number(v))) return "—"; const n = Number(v); const tick = Number(tickSz); if (!tickSz || Number.isNaN(tick) || tick <= 0) { let s = n.toFixed(4).replace(/\.?0+$/, ""); return s || "0"; } let decimals = 0; if (tick < 1) decimals = Math.max(0, -Math.round(Math.log10(tick))); else if (String(tick).indexOf(".") >= 0) decimals = String(tick).split(".")[1].length; let s = n.toFixed(decimals); // 仅裁小数尾零;整数 tick(BTC=5)时绝不能把 1370 裁成 137 if (decimals > 0) s = s.replace(/\.?0+$/, ""); return s || "0"; } function fmtUsdc(v) { if (v === null || v === undefined || Number.isNaN(Number(v))) return "—"; return Number(v).toFixed(2); } function posPremiumCcy(p) { const ccy = String((p && p.premium_ccy) || "").trim().toUpperCase(); if (ccy) return ccy; const mode = String((p && p.margin_mode) || "").toLowerCase(); const inst = String((p && p.inst_id) || ""); if (mode === "coin" || (inst.indexOf("-USD-") >= 0 && inst.indexOf("_UM") < 0)) { return (inst.split("-")[0] || "ETH").toUpperCase() || "ETH"; } return "USDC"; } function fmtPremiumAmt(v, ccy) { if (v === null || v === undefined || Number.isNaN(Number(v))) return "—"; const n = Number(v); const unit = String(ccy || "USDC").toUpperCase(); if (unit === "ETH" || unit === "BTC") { let s = n.toFixed(8).replace(/\.?0+$/, ""); return s || "0"; } return fmtUsdc(n); } function spotPxOf(p) { const n = Number(p && (p.idx_px != null ? p.idx_px : p.idxPx != null ? p.idxPx : p.index_px)); return Number.isFinite(n) && n > 0 ? n : null; } function fmtCoinUsdtDual(coinAmt, spotPx, ccy, signed) { if (coinAmt === null || coinAmt === undefined || Number.isNaN(Number(coinAmt))) return "—"; const n = Number(coinAmt); const unit = String(ccy || "ETH").toUpperCase(); if (unit !== "ETH" && unit !== "BTC") { const sign = signed && n > 0 ? "+" : ""; return sign + fmtUsdc(n) + "U"; } const absCoin = Math.abs(n).toFixed(8).replace(/\.?0+$/, "") || "0"; const coinSign = n < 0 ? "-" : signed && n > 0 ? "+" : ""; const coinTxt = coinSign + absCoin + " " + unit; const px = Number(spotPx); if (!Number.isFinite(px) || !(px > 0)) return coinTxt; const u = n * px; const absU = Math.abs(u).toFixed(2); const uSign = u < 0 ? "-" : signed && u > 0 ? "+" : ""; return coinTxt + " / " + uSign + absU + "U"; } function fmtNetPnlDual(net, p) { const ccy = posPremiumCcy(p); if (ccy === "USDC") { if (net == null || Number.isNaN(Number(net))) return "—"; return fmtUsdc(Number(net)) + "U"; } return fmtCoinUsdtDual(net, spotPxOf(p), ccy, true); } function optTypeLabel(t) { return (t || "").toUpperCase() === "P" ? "看跌 Put" : "看涨 Call"; } function sourceText(p) { const lab = (p && p.source_label) || "纯期权"; const src = (p && p.source) || "option"; let pid = p && p.source_plan_id; if (pid == null && p && p.hedge_plan_target && p.hedge_plan_target.plan_id != null) { pid = p.hedge_plan_target.plan_id; } if (src !== "option" && pid != null && pid !== "") return lab + " #" + pid; return lab; } function sourceBadgeHtml(p) { const src = (p && p.source) || "option"; const cls = src === "options_options" ? "opt-source-badge opt-source-badge--oo" : src === "perp_options" ? "opt-source-badge opt-source-badge--po" : "opt-source-badge opt-source-badge--plain"; return '' + sourceText(p) + ""; } function pnlCls(upl, hub) { if (upl > 0) return hub ? "pnl-pos" : "pos-pnl-profit"; if (upl < 0) return hub ? "pnl-neg" : "pos-pnl-loss"; return ""; } function fmtPxSz(px, sz, tickSz) { if (px === null || px === undefined || Number.isNaN(Number(px))) return "—"; let price = fmtOptionPx(px, tickSz); if (sz === null || sz === undefined || sz === "" || Number.isNaN(Number(sz))) return price; const s = Number(sz); const size = Math.abs(s - Math.round(s)) < 1e-9 ? String(Math.round(s)) : String(s); return price + "/" + size; } /** 买盘深度:价格/流动性;仅展示平仓所需档位(买一不够才出买二…). */ function fmtCloseLevels(preview, tickSz) { if (preview && preview.bid_invalid) { return "暂无有效买盘"; } const levels = ((preview && preview.levels) || []).slice(0, 5); if (!levels.length) return "—"; return levels.map(function (x, idx) { const levelNo = x.level != null ? x.level : idx + 1; const liq = x.available_sheets != null ? x.available_sheets : x.sz; return "买" + levelNo + " " + fmtPxSz(x.px, liq, tickSz); }).join(" · "); } function closeGateHint(preview) { if (!preview) return ""; if (preview.bid_invalid || preview.manual_close_blocked) { return preview.bid_invalid_reason || "当前买一无效,禁止买一平仓"; } const gate = preview.close_gate || {}; if (preview.close_gate_blocked || (gate.ready === false && !gate.passed)) { return "目标门控: " + (preview.close_gate_msg || gate.msg || "门控未过(见 env 目标平仓门控)"); } return ""; } function netPnlFromPos(p) { const preview = (p && p.close_preview) || {}; if (preview.bid_invalid) { const upl = p && p.upl != null ? Number(p.upl) : NaN; return Number.isFinite(upl) ? upl : null; } if (preview.estimated_pnl != null && !Number.isNaN(Number(preview.estimated_pnl))) { return Number(preview.estimated_pnl); } const covered = Number(preview.covered_sheets); const recv = Number(preview.total_received); const prem = Number(p && p.premium_paid); if ( preview.total_received != null && Number.isFinite(covered) && covered > 0 && !Number.isNaN(recv) && !Number.isNaN(prem) ) { return recv - prem; } const upl = p && p.upl != null ? Number(p.upl) : NaN; return Number.isFinite(upl) ? upl : null; } function netRoiFromPos(p, net) { const preview = (p && p.close_preview) || {}; if (preview.estimated_pnl_ratio_pct != null && !Number.isNaN(Number(preview.estimated_pnl_ratio_pct))) { return Number(preview.estimated_pnl_ratio_pct); } const prem = Number(p && p.premium_paid); if (net == null || Number.isNaN(prem) || prem <= 0) return null; return (net / prem) * 100; } function fmtClosePreview(preview, premiumPaid, hub, p) { if (!preview || preview.total_received == null) return "—"; const ccy = posPremiumCcy(p); const recvTxt = fmtPremiumAmt(preview.total_received, ccy); let cls = ""; const prem = Number(premiumPaid); const recv = Number(preview.total_received); if (!Number.isNaN(prem) && !Number.isNaN(recv)) { if (recv > prem) cls = " " + pnlCls(1, hub); else if (recv < prem) cls = " " + pnlCls(-1, hub); } return '' + recvTxt + " " + ccy + ""; } function expiryCdHtml(expMs) { const ms = expMs != null && expMs !== "" ? String(expMs) : ""; if (!ms) return "—"; return ''; } function renderCardInner(p, opts) { opts = opts || {}; const hub = !!opts.hub; const readOnly = !!opts.readOnly; const hidePnl = !!opts.hidePnl; const net = hidePnl ? null : netPnlFromPos(p); const roi = hidePnl ? null : netRoiFromPos(p, net); const uplCls = hidePnl ? "" : pnlCls(net, hub); const sideCls = (p.opt_type || "").toUpperCase() === "P" ? "pos-side-short" : "pos-side-long"; const expMs = p.exp_time_ms != null ? p.exp_time_ms : p.exp_time; const expAttr = expMs != null && expMs !== "" ? String(expMs) : ""; const closePreview = p.close_preview || {}; const tickSz = p.tick_sz; const premCcy = posPremiumCcy(p); const premTxt = fmtDisplay( p.premium_paid_fmt, p.premium_paid != null ? fmtPremiumAmt(p.premium_paid, premCcy) : null ); const avgTxt = p.avg_px != null ? fmtOptionPx(p.avg_px, tickSz) : fmtDisplay(p.avg_px_fmt); const markTxt = p.mark_px != null ? fmtOptionPx(p.mark_px, tickSz) : fmtDisplay(p.mark_px_fmt); let headActions = ""; if (!readOnly) { const closeSheets = p.avail_pos != null && Number(p.avail_pos) > 0 ? p.avail_pos : p.pos; headActions = '
' + '' + "
"; } const pnlCells = hidePnl ? "" : '
净盈亏' + (net == null ? "—" : fmtNetPnlDual(net, p)) + "
" + '
收益率' + (roi == null ? "—" : fmt(roi, 2) + "%") + "
"; return ( '
' + '
' + (p.inst_id || "") + "" + '' + optTypeLabel(p.opt_type) + "" + sourceBadgeHtml(p) + "
" + headActions + "
" + '
' + '持仓来源: ' + sourceText(p) + "" + '行权价: ' + fmt(p.strike, 0) + "" + '张数: ' + fmt(p.pos, 0) + " · 币量 " + fmt(p.eth_amount, 4) + "" + (expAttr ? '到期倒计时: ' + expiryCdHtml(expAttr) + "" : "") + "
" + '
' + '
权利金' + premTxt + " " + premCcy + "
" + '
开仓均价' + avgTxt + "
" + '
标记价' + markTxt + "
" + '
指数价' + fmt(p.idx_px, 0) + "
" + '
到期平衡' + fmt(p.expiry_be_px, 0) + "
" + '
平掉回本' + fmt(p.close_be_px, 0) + "
" + pnlCells + '
买盘深度' + fmtCloseLevels(closePreview, tickSz) + "
" + '
按买盘回收' + (closePreview.bid_invalid ? '暂无有效买盘' : fmtClosePreview(closePreview, hidePnl ? null : p.premium_paid, hub, p)) + "
" + "
" + (function () { const hint = closeGateHint(closePreview); return hint ? '
' + hint + "
" : ""; })() + (p.target_index != null ? (function () { const eth = p.eth_amount != null ? Number(p.eth_amount) : (Number(p.pos) > 0 ? Number(p.pos) * Number(p.ct_mult || 0.01) : null); const strike = Number(p.strike); const tgt = Number(p.target_index); const prem = Number(p.premium_paid); const idx = Number(p.idx_px); let profit = null; let value = null; if (Number.isFinite(tgt) && Number.isFinite(strike) && eth > 0) { const o = String(p.opt_type || "").toUpperCase(); const intrinsic = o === "C" ? Math.max(0, tgt - strike) : o === "P" ? Math.max(0, strike - tgt) : null; if (intrinsic != null) { value = Math.round(intrinsic * eth * 100) / 100; if (!hidePnl && Number.isFinite(prem)) { let premUsd = prem; if (premCcy !== "USDC" && Number.isFinite(idx) && idx > 0) premUsd = prem * idx; profit = Math.round((value - premUsd) * 100) / 100; } } } const valueUnit = premCcy !== "USDC" ? " U(估)" : " USDC"; const profitTxt = profit == null ? "—" : ((profit > 0 ? "+" : "") + fmtUsdc(profit) + (premCcy !== "USDC" ? " U(估)" : " USDC")); const profitCls = profit > 0 ? " pnl-pos" : profit < 0 ? " pnl-neg" : ""; const hedgeTarget = p.hedge_plan_target || null; const managed = hedgeTarget && hedgeTarget.managed_by === "hedge_plan"; const profitSpan = hidePnl ? "" : '预估盈利 ' + profitTxt + ""; return ( '
' + '' + (managed ? "对冲计划 #" + hedgeTarget.plan_id : "委托") + "" + '目标 ' + fmt(p.target_index, 1) + "" + '价值 ' + (value == null ? "—" : fmtUsdc(value) + valueUnit) + "" + profitSpan + '' + (managed ? "进行中 · 由对冲计划监控,到位后仅平盈利腿" : "监控中 · 到位按买一限价平") + "
" ); })() : "") ); } function renderCard(p, opts) { opts = opts || {}; const hub = !!opts.hub; const extraCls = hub ? " hub-pos-card hub-opt-pos-card" : " opt-pos-card"; return ( '
' + renderCardInner(p, opts) + "
" ); } global.OptionsPositionCards = { renderCardInner: renderCardInner, renderCard: renderCard, }; })(typeof window !== "undefined" ? window : globalThis);