Fix coin-margin expiry breakeven to use coin premium formula.
Use K/(1-p) and K/(1+p) for coin calls/puts instead of adding ETH premium to USD strike; align T-view straddle band with coin quotes. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -614,7 +614,13 @@
|
|||||||
if (strike == null || per == null) return "—";
|
if (strike == null || per == null) return "—";
|
||||||
const k = Number(strike);
|
const k = Number(strike);
|
||||||
const d = Number(per);
|
const d = Number(per);
|
||||||
if (!Number.isFinite(k) || !Number.isFinite(d)) return "—";
|
if (!Number.isFinite(k) || !Number.isFinite(d) || d <= 0) return "—";
|
||||||
|
if (isCoinMarginMode()) {
|
||||||
|
if (d >= 1) return "—";
|
||||||
|
const lo = Math.round((k / (1 + d)) * 10) / 10;
|
||||||
|
const hi = Math.round((k / (1 - d)) * 10) / 10;
|
||||||
|
return lo.toFixed(0) + " ~ " + hi.toFixed(0);
|
||||||
|
}
|
||||||
const lo = Math.round((k - d) * 10) / 10;
|
const lo = Math.round((k - d) * 10) / 10;
|
||||||
const hi = Math.round((k + d) * 10) / 10;
|
const hi = Math.round((k + d) * 10) / 10;
|
||||||
return lo.toFixed(0) + " ~ " + hi.toFixed(0);
|
return lo.toFixed(0) + " ~ " + hi.toFixed(0);
|
||||||
@@ -623,6 +629,9 @@
|
|||||||
function formatStraddlePremiumCell(callAsk, putAsk) {
|
function formatStraddlePremiumCell(callAsk, putAsk) {
|
||||||
const per = straddleAskPerUnit(callAsk, putAsk);
|
const per = straddleAskPerUnit(callAsk, putAsk);
|
||||||
if (per == null) return '<span class="muted">不可双买</span>';
|
if (per == null) return '<span class="muted">不可双买</span>';
|
||||||
|
if (isCoinMarginMode()) {
|
||||||
|
return fmt(per, 4) + " " + (state.underlying || "ETH");
|
||||||
|
}
|
||||||
return fmtUsdc(per) + " USDC";
|
return fmtUsdc(per) + " USDC";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -889,6 +889,7 @@ def build_option_chain(
|
|||||||
strike=strike,
|
strike=strike,
|
||||||
ask_px=ask,
|
ask_px=ask,
|
||||||
mark_px=mark,
|
mark_px=mark,
|
||||||
|
quote_in_coin="_UM" not in (family or "").upper(),
|
||||||
)
|
)
|
||||||
mny = option_moneyness(opt_type=opt_type, strike=strike, index_px=idx)
|
mny = option_moneyness(opt_type=opt_type, strike=strike, index_px=idx)
|
||||||
exp_key = str(exp_ms)
|
exp_key = str(exp_ms)
|
||||||
@@ -1028,11 +1029,18 @@ def quote_option_contract(ex: ccxt.okx, inst_id: str) -> dict[str, Any]:
|
|||||||
idx = fetch_index_price(ex, uly)
|
idx = fetch_index_price(ex, uly)
|
||||||
opt_type = meta.get("optType")
|
opt_type = meta.get("optType")
|
||||||
strike = _safe_float(meta.get("stk"))
|
strike = _safe_float(meta.get("stk"))
|
||||||
|
try:
|
||||||
|
from lib.options.options_margin_mode_lib import margin_mode_from_inst_id
|
||||||
|
|
||||||
|
quote_in_coin = margin_mode_from_inst_id(inst_id) == "coin"
|
||||||
|
except Exception:
|
||||||
|
quote_in_coin = "USD_UM" not in inst_id.upper() and "-USD-" in inst_id.upper()
|
||||||
expiry_be = expiry_breakeven_from_ask(
|
expiry_be = expiry_breakeven_from_ask(
|
||||||
opt_type=str(opt_type or ""),
|
opt_type=str(opt_type or ""),
|
||||||
strike=strike,
|
strike=strike,
|
||||||
ask_px=book_ask if can_open else None,
|
ask_px=book_ask if can_open else None,
|
||||||
mark_px=mark,
|
mark_px=mark,
|
||||||
|
quote_in_coin=quote_in_coin,
|
||||||
)
|
)
|
||||||
return {
|
return {
|
||||||
"ok": True,
|
"ok": True,
|
||||||
@@ -1824,11 +1832,18 @@ def format_position_row(
|
|||||||
round(total_premium(avg, eth_amount), 8) if avg is not None and eth_amount > 0 else None
|
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_pa = _safe_float(pos.get("deltaPA"))
|
||||||
|
try:
|
||||||
|
from lib.options.options_margin_mode_lib import margin_mode_from_inst_id
|
||||||
|
|
||||||
|
quote_in_coin = margin_mode_from_inst_id(inst_id) == "coin"
|
||||||
|
except Exception:
|
||||||
|
quote_in_coin = "USD_UM" not in inst_id.upper() and "-USD-" in inst_id.upper()
|
||||||
expiry_be = expiry_breakeven_px(
|
expiry_be = expiry_breakeven_px(
|
||||||
opt_type=str(opt_type or ""),
|
opt_type=str(opt_type or ""),
|
||||||
strike=strike,
|
strike=strike,
|
||||||
avg_px=avg,
|
avg_px=avg,
|
||||||
be_px_api=_safe_float(pos.get("bePx")),
|
be_px_api=_safe_float(pos.get("bePx")),
|
||||||
|
quote_in_coin=quote_in_coin,
|
||||||
)
|
)
|
||||||
close_be = close_breakeven_idx(
|
close_be = close_breakeven_idx(
|
||||||
opt_type=str(opt_type or ""),
|
opt_type=str(opt_type or ""),
|
||||||
|
|||||||
@@ -438,10 +438,16 @@ def expiry_breakeven_from_ask(
|
|||||||
strike: float | None,
|
strike: float | None,
|
||||||
ask_px: float | None,
|
ask_px: float | None,
|
||||||
mark_px: float | None = None,
|
mark_px: float | None = None,
|
||||||
|
quote_in_coin: bool = False,
|
||||||
) -> float | None:
|
) -> float | None:
|
||||||
"""买入前预估到期平衡:权利金按卖一;无卖一时回退标记价."""
|
"""买入前预估到期平衡:权利金按卖一;无卖一时回退标记价."""
|
||||||
prem = ask_px if ask_px is not None and ask_px > 0 else mark_px
|
prem = ask_px if ask_px is not None and ask_px > 0 else mark_px
|
||||||
return expiry_breakeven_px(opt_type=opt_type, strike=strike, avg_px=prem)
|
return expiry_breakeven_px(
|
||||||
|
opt_type=opt_type,
|
||||||
|
strike=strike,
|
||||||
|
avg_px=prem,
|
||||||
|
quote_in_coin=quote_in_coin,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def expiry_breakeven_px(
|
def expiry_breakeven_px(
|
||||||
@@ -450,17 +456,37 @@ def expiry_breakeven_px(
|
|||||||
strike: float | None,
|
strike: float | None,
|
||||||
avg_px: float | None,
|
avg_px: float | None,
|
||||||
be_px_api: float | None = None,
|
be_px_api: float | None = None,
|
||||||
|
quote_in_coin: bool = False,
|
||||||
) -> float | None:
|
) -> float | None:
|
||||||
"""到期平衡点:持有至到期时标的指数盈亏为 0 的价格.优先 OKX bePx."""
|
"""到期平衡点:持有至到期时标的指数盈亏为 0 的价格.优先 OKX bePx.
|
||||||
|
|
||||||
|
USDC: Call K+p / Put K-p (p 为美元报价).
|
||||||
|
币本位: Call K/(1-p) / Put K/(1+p) (p 为币报价,与卖一同单位).
|
||||||
|
"""
|
||||||
if be_px_api is not None and be_px_api > 0:
|
if be_px_api is not None and be_px_api > 0:
|
||||||
return round(float(be_px_api), 2)
|
return round(float(be_px_api), 2)
|
||||||
if strike is None or avg_px is None:
|
if strike is None or avg_px is None:
|
||||||
return None
|
return None
|
||||||
|
try:
|
||||||
|
k = float(strike)
|
||||||
|
p = float(avg_px)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return None
|
||||||
|
if p <= 0:
|
||||||
|
return round(k, 2)
|
||||||
o = (opt_type or "").upper()
|
o = (opt_type or "").upper()
|
||||||
|
if quote_in_coin:
|
||||||
if o == "C":
|
if o == "C":
|
||||||
return round(strike + avg_px, 2)
|
if p >= 1:
|
||||||
|
return None
|
||||||
|
return round(k / (1.0 - p), 2)
|
||||||
if o == "P":
|
if o == "P":
|
||||||
return round(strike - avg_px, 2)
|
return round(k / (1.0 + p), 2)
|
||||||
|
return None
|
||||||
|
if o == "C":
|
||||||
|
return round(k + p, 2)
|
||||||
|
if o == "P":
|
||||||
|
return round(k - p, 2)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@@ -618,20 +644,30 @@ def straddle_premium_total(
|
|||||||
def straddle_breakeven_band(
|
def straddle_breakeven_band(
|
||||||
strike: float | None,
|
strike: float | None,
|
||||||
combined_ask_per_unit: float | None,
|
combined_ask_per_unit: float | None,
|
||||||
|
*,
|
||||||
|
quote_in_coin: bool = False,
|
||||||
) -> tuple[float | None, float | None]:
|
) -> tuple[float | None, float | None]:
|
||||||
"""跨式到期平衡带:下平衡 ~ 上平衡(按双卖一报价和)."""
|
"""跨式到期平衡带:下平衡 ~ 上平衡(按双卖一报价和)."""
|
||||||
if strike is None or combined_ask_per_unit is None:
|
if strike is None or combined_ask_per_unit is None:
|
||||||
return None, None
|
return None, None
|
||||||
k = float(strike)
|
k = float(strike)
|
||||||
d = float(combined_ask_per_unit)
|
d = float(combined_ask_per_unit)
|
||||||
|
if d <= 0:
|
||||||
|
return None, None
|
||||||
|
if quote_in_coin:
|
||||||
|
if d >= 1:
|
||||||
|
return None, None
|
||||||
|
return round(k / (1.0 + d), 2), round(k / (1.0 - d), 2)
|
||||||
return round(k - d, 2), round(k + d, 2)
|
return round(k - d, 2), round(k + d, 2)
|
||||||
|
|
||||||
|
|
||||||
def format_straddle_band(
|
def format_straddle_band(
|
||||||
strike: float | None,
|
strike: float | None,
|
||||||
combined_ask_per_unit: float | None,
|
combined_ask_per_unit: float | None,
|
||||||
|
*,
|
||||||
|
quote_in_coin: bool = False,
|
||||||
) -> str:
|
) -> str:
|
||||||
lo, hi = straddle_breakeven_band(strike, combined_ask_per_unit)
|
lo, hi = straddle_breakeven_band(strike, combined_ask_per_unit, quote_in_coin=quote_in_coin)
|
||||||
if lo is None or hi is None:
|
if lo is None or hi is None:
|
||||||
return ""
|
return ""
|
||||||
return f"{lo:.0f} ~ {hi:.0f}"
|
return f"{lo:.0f} ~ {hi:.0f}"
|
||||||
|
|||||||
Reference in New Issue
Block a user