feat期权链与持仓展示 Delta。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-10-14 22:14:07 +08:00
parent 48f544b1f5
commit 627c27a6f8
8 changed files with 88 additions and 5 deletions
+9
View File
@@ -3372,6 +3372,15 @@ html[data-theme="light"] .settings-side-export-label {
html[data-theme="light"] .opt-chain-lev {
color: #1a4a8a;
}
.opt-chain-delta {
font-variant-numeric: tabular-nums;
white-space: nowrap;
font-weight: 600;
color: #9ad5ff;
}
html[data-theme="light"] .opt-chain-delta {
color: #0b5cad;
}
.opt-be-dist-up {
color: #5ee89a;
}
+16 -1
View File
@@ -477,7 +477,15 @@
}
function strikeTableColspan() {
return 9;
return state.chainView === "t" ? 9 : 10;
}
function fmtDelta(v) {
if (v === null || v === undefined || v === "") return "—";
const n = Number(v);
if (!Number.isFinite(n)) return "—";
const sign = n > 0 ? "+" : "";
return sign + n.toFixed(3);
}
function syncChainViewUI() {
@@ -1308,6 +1316,7 @@
"<td><code>" + c.inst_id + "</code></td>" +
"<td class=\"opt-px-sz\">" + fmtPxSz(c.ask, c.ask_sz, c.ask_estimated) + "</td>" +
'<td class="opt-chain-lev">' + fmtChainLeverage(chainLev) + "</td>" +
'<td class="opt-chain-delta" title="Delta">' + fmtDelta(c.delta) + "</td>" +
"<td class=\"opt-px-sz\">" + fmtPxSz(c.bid, c.bid_sz) + "</td>" +
"<td>" + (c.expiry_be_px != null ? fmt(c.expiry_be_px, 0) : "—") + "</td>" +
'<td class="' + distBeClass(c.dist_expiry_be) + '">' + fmtDist(c.dist_expiry_be) + "</td>" +
@@ -1734,6 +1743,9 @@
'<span class="pos-meta-item">行权价: ' + fmt(p.strike, 0) + "</span>" +
'<span class="pos-meta-item">张数: ' + fmt(p.pos, 0) + " · 币量 " + fmt(p.eth_amount, 4) + "</span>" +
'<span class="pos-meta-item" title="名义价值÷权利金,测算用">杠杆倍数: ' + fmtPosLeverage(p) + "</span>" +
'<span class="pos-meta-item" title="OKX deltaBS(单位 Delta)">Delta: ' +
fmtDelta(p.delta != null ? p.delta : p.delta_bs) +
"</span>" +
(expAttr
? '<span class="pos-meta-item">到期倒计时: <span class="opt-expiry-cd" data-opt-exp-ms="' + expAttr + '">—</span></span>'
: "") +
@@ -1742,6 +1754,9 @@
'<div class="pos-cell"><span class="pos-label">权利金</span><span class="pos-value">' + premTxt + " " + premCcy + "</span></div>" +
'<div class="pos-cell"><span class="pos-label">开仓均价</span><span class="pos-value">' + avgTxt + "</span></div>" +
'<div class="pos-cell"><span class="pos-label">标记价</span><span class="pos-value">' + markTxt + "</span></div>" +
'<div class="pos-cell"><span class="pos-label">Delta</span><span class="pos-value" title="OKX deltaBS">' +
fmtDelta(p.delta != null ? p.delta : p.delta_bs) +
"</span></div>" +
'<div class="pos-cell"><span class="pos-label">指数价</span><span class="pos-value">' + fmt(p.idx_px, 0) + "</span></div>" +
'<div class="pos-cell"><span class="pos-label">到期平衡</span><span class="pos-value">' + fmt(p.expiry_be_px, 0) + "</span></div>" +
'<div class="pos-cell"><span class="pos-label">平掉回本</span><span class="pos-value">' + fmt(p.close_be_px, 0) + "</span></div>" +
+12
View File
@@ -884,6 +884,10 @@ def build_option_chain(
mark = q["mark_px"]
ask_sz = q["ask_sz"]
bid_sz = q["bid_sz"]
# 行情 ticker:delta;兼容个别字段名
delta = _safe_float(t.get("delta"))
if delta is None:
delta = _safe_float(t.get("deltaBS"))
expiry_be = expiry_breakeven_from_ask(
opt_type=opt_type,
strike=strike,
@@ -904,6 +908,7 @@ def build_option_chain(
"ask_sz": ask_sz,
"bid_sz": bid_sz,
"mark_px": mark,
"delta": delta,
"ask_estimated": q["ask_estimated"],
"expiry_be_px": expiry_be,
"dist_expiry_be": strike_distance_to_be(strike, expiry_be),
@@ -1843,6 +1848,9 @@ def format_position_row(
round(total_premium(avg, eth_amount), 8) if avg is not None and eth_amount > 0 else None
)
delta_pa = _safe_float(pos.get("deltaPA"))
delta_bs = _safe_float(pos.get("deltaBS"))
if delta_bs is None:
delta_bs = _safe_float(pos.get("delta"))
try:
from lib.options.options_margin_mode_lib import margin_mode_from_inst_id
@@ -1909,6 +1917,10 @@ def format_position_row(
total_premium=premium_paid,
margin_mode=row_mode,
),
# 展示用单位 Delta(BS);delta_pa 为仓位美元敏感度
"delta": delta_bs,
"delta_bs": delta_bs,
"delta_pa": delta_pa,
"raw": pos,
}
+1
View File
@@ -135,6 +135,7 @@ def _enrich_hub_protocol(snap: dict[str, Any], *, app_module: Any) -> dict[str,
"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,
"delta": p.get("delta") if p.get("delta") is not None else p.get("delta_bs"),
"mark": p.get("mark") or p.get("mark_px"),
"underlying": p.get("underlying"),
"source_label": p.get("source_label"),
+1 -1
View File
@@ -9,7 +9,7 @@
<link rel="stylesheet" href="/static/instance_theme_early.css?v=4">
<link rel="stylesheet" href="/static/account_risk_badge.css?v=4">
<link rel="stylesheet" href="/static/instance_page.css?v=26">
<link rel="stylesheet" href="/static/instance_theme.css?v=128">
<link rel="stylesheet" href="/static/instance_theme.css?v=129">
<script src="/static/account_risk_badge.js?v=4"></script>
<script src="/static/open_submit_gate.js?v=1"></script>
<meta name="theme-color" content="#0b0d14">
+1 -1
View File
@@ -19,7 +19,7 @@
<link rel="manifest" href="/static/icons/manifest.webmanifest">
<title>{{ pwa_app_name }}</title>
<link rel="stylesheet" href="/static/instance_page.css?v=26">
<link rel="stylesheet" href="/static/instance_theme.css?v=128">
<link rel="stylesheet" href="/static/instance_theme.css?v=129">
</head>
<body
+3 -2
View File
@@ -87,6 +87,7 @@
<th>合约</th>
<th>卖一/张</th>
<th title="USDC:指数÷卖一;币本位:1÷卖一(卖一为币报价)">杠杆</th>
<th title="OKX 行情 Delta(约 -1~1)">Delta</th>
<th>买一/张</th>
<th>到期平衡</th>
<th>平衡价差</th>
@@ -110,7 +111,7 @@
</tr>
</thead>
<tbody id="opt-strike-tbody">
<tr><td colspan="9" class="muted">请选择到期日</td></tr>
<tr><td colspan="10" class="muted">请选择到期日</td></tr>
</tbody>
</table>
</div>
@@ -368,4 +369,4 @@
</div>
</div>
<script src="/static/options_expiry_countdown.js?v=1"></script>
<script src="/static/options_panel.js?v=72"></script>
<script src="/static/options_panel.js?v=73"></script>
+45
View File
@@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
import unittest
from lib.exchange.okx_options_lib import format_position_row
class OptionsDeltaFieldsTests(unittest.TestCase):
def test_format_position_exposes_delta_bs(self):
row = format_position_row(
{
"instId": "ETH-USD-261015-2500-C",
"pos": "10",
"avgPx": "0.02",
"markPx": "0.025",
"idxPx": "2600",
"optType": "C",
"stk": "2500",
"deltaBS": "0.6123",
"deltaPA": "1.23",
},
ct_mult=0.1,
)
self.assertAlmostEqual(row["delta"], 0.6123, places=4)
self.assertAlmostEqual(row["delta_bs"], 0.6123, places=4)
self.assertAlmostEqual(row["delta_pa"], 1.23, places=4)
def test_format_position_falls_back_delta_field(self):
row = format_position_row(
{
"instId": "BTC-USD-261015-80000-P",
"pos": "1",
"avgPx": "0.01",
"markPx": "0.012",
"idxPx": "82000",
"optType": "P",
"stk": "80000",
"delta": "-0.45",
},
ct_mult=0.01,
)
self.assertAlmostEqual(row["delta"], -0.45, places=4)
if __name__ == "__main__":
unittest.main()