diff --git a/lib/common/static/options_panel.js b/lib/common/static/options_panel.js
index 4b05140..9b597d7 100644
--- a/lib/common/static/options_panel.js
+++ b/lib/common/static/options_panel.js
@@ -1434,15 +1434,14 @@
function fmtDist(v) {
if (v === null || v === undefined || Number.isNaN(Number(v))) return "—";
const n = Number(v);
- const sign = n > 0 ? "+" : "";
- return sign + n.toFixed(1);
+ if (n < 0) return n.toFixed(1);
+ return n.toFixed(1);
}
function distBeClass(v) {
if (v === null || v === undefined || Number.isNaN(Number(v))) return "";
const n = Number(v);
if (n > 0) return "opt-be-dist-up";
- if (n < 0) return "opt-be-dist-down";
return "";
}
diff --git a/lib/exchange/okx_options_lib.py b/lib/exchange/okx_options_lib.py
index 04edc80..36d9051 100644
--- a/lib/exchange/okx_options_lib.py
+++ b/lib/exchange/okx_options_lib.py
@@ -17,6 +17,7 @@ from lib.options.options_pricing_lib import (
is_shallow_itm,
option_moneyness,
option_moneyness_label,
+ strike_distance_to_be,
)
_OKX_OPTION_ERR_ZH: dict[str, str] = {
@@ -903,7 +904,7 @@ def build_option_chain(
"mark_px": mark,
"ask_estimated": q["ask_estimated"],
"expiry_be_px": expiry_be,
- "dist_expiry_be": idx_distance_to_be(idx, expiry_be),
+ "dist_expiry_be": strike_distance_to_be(strike, expiry_be, opt_type=opt_type),
"moneyness": mny,
"moneyness_label": option_moneyness_label(mny),
"ct_mult": _safe_float(meta.get("ctMult")) or 0.01,
@@ -1050,7 +1051,7 @@ def quote_option_contract(ex: ccxt.okx, inst_id: str) -> dict[str, Any]:
"open_block_msg": "" if can_open else open_block_msg,
"index_px": idx,
"expiry_be_px": expiry_be,
- "dist_expiry_be": idx_distance_to_be(idx, expiry_be),
+ "dist_expiry_be": strike_distance_to_be(strike, expiry_be, opt_type=str(opt_type or "")),
"ct_mult": _safe_float(meta.get("ctMult")) or 0.01,
"min_sz": int(_safe_float(meta.get("minSz")) or 1),
"tick_sz": tick_sz,
@@ -1792,6 +1793,7 @@ def format_position_row(
close_breakeven_idx,
expiry_breakeven_px,
idx_distance_to_be,
+ strike_distance_to_be,
total_premium,
)
@@ -1809,6 +1811,16 @@ def format_position_row(
opt_type = parsed_type
if strike is None:
strike = parsed_strike
+ try:
+ from lib.options.options_margin_mode_lib import margin_mode_from_inst_id, premium_ccy_for_mode
+
+ row_mode = margin_mode_from_inst_id(inst_id) if inst_id else "usdc"
+ underly = (inst_id.split("-")[0] if inst_id else "ETH") or "ETH"
+ premium_ccy = premium_ccy_for_mode(row_mode, underly)
+ except Exception:
+ row_mode = "usdc"
+ underly = (inst_id.split("-")[0] if inst_id else "ETH") or "ETH"
+ premium_ccy = "USDC"
eth_amount = round(abs(sheets) * ct_mult, 8)
premium_paid = (
round(total_premium(avg, eth_amount), 8) if avg is not None and eth_amount > 0 else None
@@ -1832,16 +1844,6 @@ def format_position_row(
ct_mult=ct_mult,
)
exp_time_ms = normalize_option_exp_ms(pos.get("expTime"), inst_id)
- try:
- from lib.options.options_margin_mode_lib import margin_mode_from_inst_id, premium_ccy_for_mode
-
- row_mode = margin_mode_from_inst_id(inst_id) if inst_id else "usdc"
- underly = (inst_id.split("-")[0] if inst_id else "ETH") or "ETH"
- premium_ccy = premium_ccy_for_mode(row_mode, underly)
- except Exception:
- row_mode = "usdc"
- underly = (inst_id.split("-")[0] if inst_id else "ETH") or "ETH"
- premium_ccy = "USDC"
return {
"inst_id": inst_id or pos.get("instId"),
"pos": sheets,
@@ -1867,7 +1869,7 @@ def format_position_row(
"avail_pos": _safe_float(pos.get("availPos")),
"expiry_be_px": expiry_be,
"close_be_px": close_be,
- "dist_expiry_be": idx_distance_to_be(idx_px, expiry_be),
+ "dist_expiry_be": strike_distance_to_be(strike, expiry_be, opt_type=str(opt_type or "")),
"dist_close_be": idx_distance_to_be(idx_px, close_be),
"raw": pos,
}
diff --git a/lib/options/options_pricing_lib.py b/lib/options/options_pricing_lib.py
index f679af7..e392b9a 100644
--- a/lib/options/options_pricing_lib.py
+++ b/lib/options/options_pricing_lib.py
@@ -531,6 +531,25 @@ def idx_distance_to_be(idx_px: float | None, be_px: float | None) -> float | Non
return round(float(be_px) - float(idx_px), 2)
+def strike_distance_to_be(
+ strike: float | None,
+ be_px: float | None,
+ *,
+ opt_type: str | None = None,
+) -> float | None:
+ """行权价到到期平衡价的价差(Call:BE−K, Put:K−BE)."""
+ if strike is None or be_px is None:
+ return None
+ k = float(strike)
+ be = float(be_px)
+ o = (opt_type or "").upper()
+ if o == "C":
+ return round(be - k, 2)
+ if o == "P":
+ return round(k - be, 2)
+ return round(abs(be - k), 2)
+
+
def format_options_breakeven_line(
*,
expiry_be_px: float | None,
diff --git a/lib/options/templates/options_panel.html b/lib/options/templates/options_panel.html
index 99fcc3c..076ae2f 100644
--- a/lib/options/templates/options_panel.html
+++ b/lib/options/templates/options_panel.html
@@ -380,4 +380,4 @@
-
+
diff --git a/tests/test_options_pricing.py b/tests/test_options_pricing.py
index b0d6cd4..a6ee8e2 100644
--- a/tests/test_options_pricing.py
+++ b/tests/test_options_pricing.py
@@ -349,6 +349,13 @@ def test_expiry_breakeven_coin_margin():
) == round(2425 / (1 - 0.0187), 2)
+def test_strike_distance_to_be():
+ from lib.options.options_pricing_lib import strike_distance_to_be
+
+ assert strike_distance_to_be(2390, 2430, opt_type="C") == 40.0
+ assert strike_distance_to_be(2450, 2411, opt_type="P") == 39.0
+
+
def test_straddle_breakeven_band_coin():
from lib.options.options_pricing_lib import straddle_breakeven_band
@@ -454,4 +461,4 @@ def test_format_position_row_breakeven():
assert row["expiry_be_px"] == 3515.6
assert row["idx_px"] == 3480.0
assert row["close_be_px"] is not None
- assert row["dist_expiry_be"] == 35.6
+ assert row["dist_expiry_be"] == 15.6