diff --git a/lib/common/static/instance_theme.css b/lib/common/static/instance_theme.css
index ad239bb..502e56b 100644
--- a/lib/common/static/instance_theme.css
+++ b/lib/common/static/instance_theme.css
@@ -2588,6 +2588,10 @@ html[data-theme="light"] .settings-side-export-label {
.options-page-wrap .options-strike-table code {
font-size: 0.66rem;
}
+.opt-px-sz {
+ font-variant-numeric: tabular-nums;
+ white-space: nowrap;
+}
.opt-be-dist-up {
color: #5ee89a;
}
diff --git a/lib/common/static/options_panel.js b/lib/common/static/options_panel.js
index 16cf88b..36a23e1 100644
--- a/lib/common/static/options_panel.js
+++ b/lib/common/static/options_panel.js
@@ -149,6 +149,15 @@
"指数 " + state.underlying + " ≈ " + fmt(idx, 2) + " · 实值=价内 · 虚值=价外";
}
+ function fmtPxSz(px, sz) {
+ if (px === null || px === undefined || Number.isNaN(Number(px))) return "—";
+ const price = Number(px).toFixed(4).replace(/\.?0+$/, "");
+ 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 fmtDist(v) {
if (v === null || v === undefined || Number.isNaN(Number(v))) return "—";
const n = Number(v);
@@ -197,8 +206,8 @@
"
" + c.strike + " | " +
"" + moneynessBadge(c) + " | " +
"" + c.inst_id + " | " +
- "" + fmt(c.ask, 4) + " | " +
- "" + fmt(c.bid, 4) + " | " +
+ "" + fmtPxSz(c.ask, c.ask_sz) + " | " +
+ "" + fmtPxSz(c.bid, c.bid_sz) + " | " +
"" + (c.expiry_be_px != null ? fmt(c.expiry_be_px, 0) : "—") + " | " +
'' + fmtDist(c.dist_expiry_be) + " | " +
'' +
@@ -229,7 +238,9 @@
function fillOrderPanel(d) {
const sz = d.sizing || {};
document.getElementById("opt-order-inst").textContent = d.inst_id || state.selectedInst || "";
- document.getElementById("opt-order-ask").textContent = fmt(d.ask, 4);
+ document.getElementById("opt-order-ask").textContent = fmtPxSz(d.ask, d.ask_sz);
+ const bidEl = document.getElementById("opt-order-bid");
+ if (bidEl) bidEl.textContent = fmtPxSz(d.bid, d.bid_sz);
document.getElementById("opt-order-sheets").textContent = sz.sheets != null ? sz.sheets : "—";
document.getElementById("opt-order-eth").textContent = sz.eth_amount != null ? sz.eth_amount : "—";
document.getElementById("opt-order-premium").textContent = sz.total_premium != null ? fmt(sz.total_premium, 4) + " USDC" : "—";
diff --git a/lib/exchange/okx_options_lib.py b/lib/exchange/okx_options_lib.py
index 9ecd860..31ce7f2 100644
--- a/lib/exchange/okx_options_lib.py
+++ b/lib/exchange/okx_options_lib.py
@@ -116,18 +116,27 @@ def format_option_px(px: float, tick_sz: Any) -> str:
def _fetch_book_bid_ask(ex: ccxt.okx, inst_id: str) -> tuple[float | None, float | None]:
+ bid, ask, _, _ = _fetch_book_top(ex, inst_id)
+ return bid, ask
+
+
+def _fetch_book_top(
+ ex: ccxt.okx, inst_id: str
+) -> tuple[float | None, float | None, float | None, float | None]:
try:
rows = ex.public_get_market_books({"instId": inst_id, "sz": "1"}).get("data") or []
if not rows:
- return None, None
+ return None, None, None, None
row = rows[0]
asks = row.get("asks") or []
bids = row.get("bids") or []
ask = _safe_float(asks[0][0]) if asks else None
bid = _safe_float(bids[0][0]) if bids else None
- return bid, ask
+ ask_sz = _safe_float(asks[0][1]) if asks and len(asks[0]) > 1 else None
+ bid_sz = _safe_float(bids[0][1]) if bids and len(bids[0]) > 1 else None
+ return bid, ask, bid_sz, ask_sz
except Exception:
- return None, None
+ return None, None, None, None
def _pos_side_from_position(pos: dict[str, Any] | None) -> str | None:
@@ -338,6 +347,8 @@ def build_option_chain(
ask = _safe_float(t.get("askPx"))
bid = _safe_float(t.get("bidPx"))
mark = _safe_float(t.get("markPx"))
+ ask_sz = _safe_float(t.get("askSz"))
+ bid_sz = _safe_float(t.get("bidSz"))
if ask is None and bid is None and mark is None:
continue
expiry_be = expiry_breakeven_from_ask(
@@ -356,6 +367,8 @@ def build_option_chain(
"exp_time": exp_ms,
"ask": ask,
"bid": bid,
+ "ask_sz": ask_sz,
+ "bid_sz": bid_sz,
"mark_px": mark,
"expiry_be_px": expiry_be,
"dist_expiry_be": idx_distance_to_be(idx, expiry_be),
@@ -385,12 +398,18 @@ def quote_option_contract(ex: ccxt.okx, inst_id: str) -> dict[str, Any]:
t = t_rows[0] if t_rows else {}
ask = _safe_float(t.get("askPx"))
bid = _safe_float(t.get("bidPx"))
- if ask is None or bid is None:
- book_bid, book_ask = _fetch_book_bid_ask(ex, inst_id)
+ ask_sz = _safe_float(t.get("askSz"))
+ bid_sz = _safe_float(t.get("bidSz"))
+ if ask is None or bid is None or ask_sz is None or bid_sz is None:
+ book_bid, book_ask, book_bid_sz, book_ask_sz = _fetch_book_top(ex, inst_id)
if ask is None:
ask = book_ask
if bid is None:
bid = book_bid
+ if ask_sz is None:
+ ask_sz = book_ask_sz
+ if bid_sz is None:
+ bid_sz = book_bid_sz
mark = _safe_float(t.get("markPx"))
tick_sz = meta.get("tickSz")
if ask is None and mark is not None:
@@ -413,6 +432,8 @@ def quote_option_contract(ex: ccxt.okx, inst_id: str) -> dict[str, Any]:
"meta": meta,
"ask": ask,
"bid": bid,
+ "ask_sz": ask_sz,
+ "bid_sz": bid_sz,
"mark": mark,
"index_px": idx,
"expiry_be_px": expiry_be,
diff --git a/lib/options/options_pricing_lib.py b/lib/options/options_pricing_lib.py
index 2a528e3..24a8028 100644
--- a/lib/options/options_pricing_lib.py
+++ b/lib/options/options_pricing_lib.py
@@ -28,6 +28,24 @@ def premium_per_sheet(quote_per_unit: float, ct_mult: float = 0.01) -> float:
return float(quote_per_unit) * float(ct_mult)
+def format_quote_liquidity(px: float | None, sz: float | None, *, px_decimals: int = 4) -> str | None:
+ """盘口展示:价格/张数,如 17.2/150。"""
+ if px is None:
+ return None
+ try:
+ price = f"{float(px):.{px_decimals}f}".rstrip("0").rstrip(".")
+ except (TypeError, ValueError):
+ return None
+ if sz is None:
+ return price
+ try:
+ s = float(sz)
+ size = str(int(s)) if abs(s - int(s)) < 1e-9 else str(s).rstrip("0").rstrip(".")
+ except (TypeError, ValueError):
+ return price
+ return f"{price}/{size}"
+
+
def total_premium(quote_per_unit: float, eth_amount: float, ct_mult: float = 0.01) -> float:
return float(quote_per_unit) * float(eth_amount)
diff --git a/lib/options/templates/options_panel.html b/lib/options/templates/options_panel.html
index 1d9ab1e..ea4ba7d 100644
--- a/lib/options/templates/options_panel.html
+++ b/lib/options/templates/options_panel.html
@@ -7,7 +7,7 @@
期权下单
- 报价单位为每 1 ETH/BTC;1 张 = 0.01。链展示近 14 日到期,标注实值/虚值;到期平衡按卖一预估(无卖一按标记价)。资金划转与 USDT/USDC 兑换见「系统设置 → 期权设置」。
+ 报价单位为每 1 ETH/BTC;1 张 = 0.01。卖一/买一列为 价格/张数。链展示近 14 日到期,标注实值/虚值;到期平衡按卖一预估(无卖一按标记价)。资金划转与 USDT/USDC 兑换见「系统设置 → 期权设置」。
|