Wide order dialog (calc|form); enrich live cards with lev/bid/margin/time.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -631,14 +631,26 @@
|
|||||||
const profitBox = $("hp-po-exit-profit");
|
const profitBox = $("hp-po-exit-profit");
|
||||||
const targetBox = $("hp-po-exit-target");
|
const targetBox = $("hp-po-exit-target");
|
||||||
if (profitBox) {
|
if (profitBox) {
|
||||||
profitBox.classList.toggle("hidden", mode !== "profit");
|
const show = mode === "profit";
|
||||||
if (mode === "profit") profitBox.removeAttribute("hidden");
|
profitBox.classList.toggle("hidden", !show);
|
||||||
else profitBox.setAttribute("hidden", "hidden");
|
if (show) {
|
||||||
|
profitBox.removeAttribute("hidden");
|
||||||
|
profitBox.style.display = "";
|
||||||
|
} else {
|
||||||
|
profitBox.setAttribute("hidden", "hidden");
|
||||||
|
profitBox.style.display = "none";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (targetBox) {
|
if (targetBox) {
|
||||||
targetBox.classList.toggle("hidden", mode !== "target");
|
const show = mode === "target";
|
||||||
if (mode === "target") targetBox.removeAttribute("hidden");
|
targetBox.classList.toggle("hidden", !show);
|
||||||
else targetBox.setAttribute("hidden", "hidden");
|
if (show) {
|
||||||
|
targetBox.removeAttribute("hidden");
|
||||||
|
targetBox.style.display = "";
|
||||||
|
} else {
|
||||||
|
targetBox.setAttribute("hidden", "hidden");
|
||||||
|
targetBox.style.display = "none";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -904,6 +916,19 @@
|
|||||||
return n > 0 ? "+" + s : s;
|
return n > 0 ? "+" + s : s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fmtDuration(sec) {
|
||||||
|
if (sec == null || Number.isNaN(Number(sec))) return "—";
|
||||||
|
let s = Math.max(0, Math.floor(Number(sec)));
|
||||||
|
const d = Math.floor(s / 86400);
|
||||||
|
s -= d * 86400;
|
||||||
|
const h = Math.floor(s / 3600);
|
||||||
|
s -= h * 3600;
|
||||||
|
const m = Math.floor(s / 60);
|
||||||
|
if (d > 0) return d + "天 " + h + "时" + m + "分";
|
||||||
|
if (h > 0) return h + "时" + m + "分";
|
||||||
|
return m + "分";
|
||||||
|
}
|
||||||
|
|
||||||
function renderLivePosCard(card) {
|
function renderLivePosCard(card) {
|
||||||
const opt = card.option || {};
|
const opt = card.option || {};
|
||||||
const perp = card.perp || {};
|
const perp = card.perp || {};
|
||||||
@@ -911,6 +936,11 @@
|
|||||||
const optType = String(opt.opt_type || "").toUpperCase() === "P" ? "Put" : "Call";
|
const optType = String(opt.opt_type || "").toUpperCase() === "P" ? "Put" : "Call";
|
||||||
const perpSide = String(perp.side || "").toLowerCase() === "short" ? "空" : "多";
|
const perpSide = String(perp.side || "").toLowerCase() === "short" ? "空" : "多";
|
||||||
const comboCls = pnlClsNum(combo.net_usdt);
|
const comboCls = pnlClsNum(combo.net_usdt);
|
||||||
|
const liqWarn = opt.liq_ok === false;
|
||||||
|
const bidTxt =
|
||||||
|
opt.bid != null
|
||||||
|
? fmt(opt.bid, 4) + (opt.bid_sz != null ? "/" + fmt(opt.bid_sz, 0) : "")
|
||||||
|
: "—";
|
||||||
return (
|
return (
|
||||||
'<article class="hp-live-card" data-plan-id="' +
|
'<article class="hp-live-card" data-plan-id="' +
|
||||||
(card.plan_id || "") +
|
(card.plan_id || "") +
|
||||||
@@ -920,9 +950,18 @@
|
|||||||
(card.plan_id || "—") +
|
(card.plan_id || "—") +
|
||||||
" · " +
|
" · " +
|
||||||
(card.underlying || "") +
|
(card.underlying || "") +
|
||||||
'</strong><span class="muted">' +
|
"</strong></div>" +
|
||||||
(card.opened_at || "") +
|
'<div class="hp-live-time-row">' +
|
||||||
"</span></div>" +
|
"<span>开仓 <strong>" +
|
||||||
|
(card.opened_at || "—") +
|
||||||
|
"</strong></span>" +
|
||||||
|
"<span>持仓时长 <strong>" +
|
||||||
|
fmtDuration(card.hold_sec) +
|
||||||
|
"</strong></span>" +
|
||||||
|
"<span>到期剩余 <strong>" +
|
||||||
|
fmtDuration(card.remain_sec) +
|
||||||
|
"</strong></span>" +
|
||||||
|
"</div>" +
|
||||||
'<div class="hp-live-legs">' +
|
'<div class="hp-live-legs">' +
|
||||||
'<div class="hp-live-leg">' +
|
'<div class="hp-live-leg">' +
|
||||||
'<div class="hp-live-leg-title">期权 · ' +
|
'<div class="hp-live-leg-title">期权 · ' +
|
||||||
@@ -935,12 +974,23 @@
|
|||||||
"<span>张数 <strong>" +
|
"<span>张数 <strong>" +
|
||||||
fmt(opt.sheets, 0) +
|
fmt(opt.sheets, 0) +
|
||||||
"</strong></span>" +
|
"</strong></span>" +
|
||||||
|
"<span>杠杆 <strong>" +
|
||||||
|
(opt.leverage != null ? fmt(opt.leverage, 1) + "×" : "—") +
|
||||||
|
"</strong></span>" +
|
||||||
"<span>开仓 <strong>" +
|
"<span>开仓 <strong>" +
|
||||||
fmt(opt.avg_open, 4) +
|
fmt(opt.avg_open, 4) +
|
||||||
"</strong></span>" +
|
"</strong></span>" +
|
||||||
"<span>标记 <strong>" +
|
"<span>标记 <strong>" +
|
||||||
fmt(opt.mark_px, 4) +
|
fmt(opt.mark_px, 4) +
|
||||||
"</strong></span>" +
|
"</strong></span>" +
|
||||||
|
'<span class="' +
|
||||||
|
(liqWarn ? "hp-liq-warn" : "") +
|
||||||
|
'" title="' +
|
||||||
|
String(opt.liq_msg || "").replace(/"/g, """) +
|
||||||
|
'">买一/流动性 <strong>' +
|
||||||
|
bidTxt +
|
||||||
|
(liqWarn ? " · 不足" : "") +
|
||||||
|
"</strong></span>" +
|
||||||
'<span>净利 <strong class="' +
|
'<span>净利 <strong class="' +
|
||||||
pnlClsNum(opt.net_usdt) +
|
pnlClsNum(opt.net_usdt) +
|
||||||
'">' +
|
'">' +
|
||||||
@@ -958,12 +1008,18 @@
|
|||||||
"<span>张数 <strong>" +
|
"<span>张数 <strong>" +
|
||||||
fmt(perp.contracts, 4) +
|
fmt(perp.contracts, 4) +
|
||||||
"</strong></span>" +
|
"</strong></span>" +
|
||||||
|
"<span>杠杆 <strong>" +
|
||||||
|
(perp.leverage != null ? fmt(perp.leverage, 0) + "×" : "—") +
|
||||||
|
"</strong></span>" +
|
||||||
"<span>开仓 <strong>" +
|
"<span>开仓 <strong>" +
|
||||||
fmt(perp.avg_open, 2) +
|
fmt(perp.avg_open, 2) +
|
||||||
"</strong></span>" +
|
"</strong></span>" +
|
||||||
"<span>标记 <strong>" +
|
"<span>标记 <strong>" +
|
||||||
fmt(perp.mark_px, 2) +
|
fmt(perp.mark_px, 2) +
|
||||||
"</strong></span>" +
|
"</strong></span>" +
|
||||||
|
"<span>保证金 <strong>" +
|
||||||
|
(perp.margin != null ? fmt(perp.margin, 2) + " U" : "—") +
|
||||||
|
"</strong></span>" +
|
||||||
'<span>净利 <strong class="' +
|
'<span>净利 <strong class="' +
|
||||||
pnlClsNum(perp.net_usdt) +
|
pnlClsNum(perp.net_usdt) +
|
||||||
'">' +
|
'">' +
|
||||||
@@ -985,7 +1041,7 @@
|
|||||||
'">成交细节</button> ' +
|
'">成交细节</button> ' +
|
||||||
'<button type="button" class="btn-secondary hp-btn-end" data-id="' +
|
'<button type="button" class="btn-secondary hp-btn-end" data-id="' +
|
||||||
(card.plan_id || "") +
|
(card.plan_id || "") +
|
||||||
'">结束计划</button>' +
|
'">平仓</button>' +
|
||||||
"</div></article>"
|
"</div></article>"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -2864,9 +2920,9 @@
|
|||||||
async function endActivePlan(planId) {
|
async function endActivePlan(planId) {
|
||||||
if (
|
if (
|
||||||
!window.confirm(
|
!window.confirm(
|
||||||
"确认结束计划 #" +
|
"确认平仓并结束计划 #" +
|
||||||
planId +
|
planId +
|
||||||
"?\n不会自动平仓;未成交/待补腿将标为未成交取消。\n已有持仓请自行平掉。"
|
"?\n未成交/待补腿将取消。\n说明:当前为结束计划标记,交易所持仓请自行确认已平。"
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -3507,6 +3507,44 @@ html[data-theme="light"] .opt-be-dist-down {
|
|||||||
padding: 20px 22px 22px;
|
padding: 20px 22px 22px;
|
||||||
max-height: 90vh;
|
max-height: 90vh;
|
||||||
}
|
}
|
||||||
|
.opt-order-dialog.hp-po-order-dialog--wide {
|
||||||
|
width: min(96vw, 920px);
|
||||||
|
}
|
||||||
|
.hp-po-order-dialog .hp-po-order-split {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 18px;
|
||||||
|
}
|
||||||
|
@media (min-width: 760px) {
|
||||||
|
.hp-po-order-dialog .hp-po-order-split {
|
||||||
|
grid-template-columns: minmax(280px, 1fr) minmax(320px, 1.15fr);
|
||||||
|
gap: 22px;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.hp-po-order-dialog .hp-po-order-calc-pane {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 14px;
|
||||||
|
border-radius: 12px;
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.07);
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
.hp-po-order-dialog .hp-po-calc-box {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.hp-po-order-dialog .hp-po-calc-box p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.hp-po-order-dialog .hp-po-order-form-pane {
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
.hp-po-order-dialog .opt-order-main {
|
.hp-po-order-dialog .opt-order-main {
|
||||||
gap: 18px;
|
gap: 18px;
|
||||||
}
|
}
|
||||||
@@ -4134,9 +4172,28 @@ html[data-theme="light"] .opt-be-dist-down {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 8px;
|
||||||
font-size: 0.88rem;
|
font-size: 0.88rem;
|
||||||
}
|
}
|
||||||
|
.hedge-plan-page-wrap .hp-live-time-row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 6px 14px;
|
||||||
|
margin: 0 0 10px;
|
||||||
|
padding: 8px 10px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: rgba(0, 0, 0, 0.16);
|
||||||
|
font-size: 0.76rem;
|
||||||
|
color: #9aa4b2;
|
||||||
|
}
|
||||||
|
.hedge-plan-page-wrap .hp-live-time-row strong {
|
||||||
|
color: #e8ecf4;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.hedge-plan-page-wrap .hp-liq-warn,
|
||||||
|
.hedge-plan-page-wrap .hp-liq-warn strong {
|
||||||
|
color: #f5c542 !important;
|
||||||
|
}
|
||||||
.hedge-plan-page-wrap .hp-live-legs {
|
.hedge-plan-page-wrap .hp-live-legs {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
"""永期对冲 · 当前持仓:期权+永续实时卡(组合净利扣开平双边手续费)."""
|
"""永期对冲 · 当前持仓:期权+永续实时卡(组合净利扣开平双边手续费)."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta, timezone
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
from lib.hedge_plan.hedge_plan_option_primary_lib import (
|
from lib.hedge_plan.hedge_plan_option_primary_lib import (
|
||||||
estimate_combo_net_pnl,
|
estimate_combo_net_pnl,
|
||||||
fee_rate,
|
fee_rate,
|
||||||
opt_type_for_view,
|
opt_type_for_view,
|
||||||
|
option_bid_liquidity_ok,
|
||||||
perp_direction_for_view,
|
perp_direction_for_view,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
_BJ = timezone(timedelta(hours=8))
|
||||||
|
|
||||||
|
|
||||||
def _sf(v: Any) -> Optional[float]:
|
def _sf(v: Any) -> Optional[float]:
|
||||||
try:
|
try:
|
||||||
@@ -33,19 +37,52 @@ def _pick_option_row(rows: list[dict[str, Any]] | None, inst_id: str) -> Optiona
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _option_bid(ex: Any, inst_id: str, fallback: Optional[float]) -> Optional[float]:
|
def _option_bid_quote(
|
||||||
|
ex: Any, inst_id: str, fallback: Optional[float]
|
||||||
|
) -> tuple[Optional[float], Optional[float]]:
|
||||||
|
"""返回 (买一价, 买一张数)."""
|
||||||
try:
|
try:
|
||||||
from lib.exchange.okx_options_lib import fetch_option_book_depth
|
from lib.exchange.okx_options_lib import fetch_option_book_depth
|
||||||
|
|
||||||
book = fetch_option_book_depth(ex, inst_id) or {}
|
book = fetch_option_book_depth(ex, inst_id) or {}
|
||||||
bids = book.get("bids") or []
|
bids = book.get("bids") or []
|
||||||
if bids and isinstance(bids[0], dict):
|
if bids and isinstance(bids[0], dict):
|
||||||
bid = _sf(bids[0].get("px") or bids[0].get("price"))
|
bid = _sf(bids[0].get("px"))
|
||||||
|
sz = _sf(bids[0].get("sz"))
|
||||||
if bid is not None and bid > 0:
|
if bid is not None and bid > 0:
|
||||||
return bid
|
return bid, sz
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
return fallback
|
return fallback, None
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_opt_exp_ms(inst_id: str) -> Optional[int]:
|
||||||
|
"""BTC-USD-261017-84000-P → 到期日 16:00 北京时间."""
|
||||||
|
parts = str(inst_id or "").upper().split("-")
|
||||||
|
if len(parts) < 4:
|
||||||
|
return None
|
||||||
|
raw = parts[2]
|
||||||
|
if len(raw) != 6 or not raw.isdigit():
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
yy, mm, dd = int(raw[0:2]), int(raw[2:4]), int(raw[4:6])
|
||||||
|
year = 2000 + yy
|
||||||
|
dt = datetime(year, mm, dd, 16, 0, 0, tzinfo=_BJ)
|
||||||
|
return int(dt.timestamp() * 1000)
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_opened_ms(opened_at: Any) -> Optional[int]:
|
||||||
|
s = str(opened_at or "").strip()
|
||||||
|
if not s:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
# "2026-10-14 16:22:21" 按北京时间
|
||||||
|
dt = datetime.strptime(s[:19], "%Y-%m-%d %H:%M:%S").replace(tzinfo=_BJ)
|
||||||
|
return int(dt.timestamp() * 1000)
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def build_live_hedge_cards(cfg: dict[str, Any], conn: Any) -> list[dict[str, Any]]:
|
def build_live_hedge_cards(cfg: dict[str, Any], conn: Any) -> list[dict[str, Any]]:
|
||||||
@@ -79,6 +116,7 @@ def build_live_hedge_cards(cfg: dict[str, Any], conn: Any) -> list[dict[str, Any
|
|||||||
get_cs = cfg.get("get_contract_size")
|
get_cs = cfg.get("get_contract_size")
|
||||||
fetch_idx = cfg.get("fetch_index_price")
|
fetch_idx = cfg.get("fetch_index_price")
|
||||||
fr = fee_rate()
|
fr = fee_rate()
|
||||||
|
now_ms = int(datetime.now(tz=_BJ).timestamp() * 1000)
|
||||||
out: list[dict[str, Any]] = []
|
out: list[dict[str, Any]] = []
|
||||||
|
|
||||||
for plan in plans:
|
for plan in plans:
|
||||||
@@ -97,14 +135,21 @@ def build_live_hedge_cards(cfg: dict[str, Any], conn: Any) -> list[dict[str, Any
|
|||||||
ct = _sf((opt_leg or {}).get("ct_mult")) or 0.01
|
ct = _sf((opt_leg or {}).get("ct_mult")) or 0.01
|
||||||
avg_open = _sf((opt_leg or {}).get("avg_open")) or _sf((opt_raw or {}).get("avgPx")) or 0.0
|
avg_open = _sf((opt_leg or {}).get("avg_open")) or _sf((opt_raw or {}).get("avgPx")) or 0.0
|
||||||
mark_opt = _sf((opt_raw or {}).get("markPx")) or _sf((opt_raw or {}).get("last")) or avg_open
|
mark_opt = _sf((opt_raw or {}).get("markPx")) or _sf((opt_raw or {}).get("last")) or avg_open
|
||||||
bid = _option_bid(ex_opt, inst_id, mark_opt) if inst_id else mark_opt
|
bid, bid_sz = _option_bid_quote(ex_opt, inst_id, mark_opt) if inst_id else (mark_opt, None)
|
||||||
strike = _sf((opt_leg or {}).get("strike")) or 0.0
|
strike = _sf((opt_leg or {}).get("strike")) or 0.0
|
||||||
opt_type = str((opt_leg or {}).get("opt_type") or opt_type_for_view(view)).upper()
|
opt_type = str((opt_leg or {}).get("opt_type") or opt_type_for_view(view)).upper()
|
||||||
|
# 币本位杠杆≈1÷卖一/标记
|
||||||
|
lev_ref = _sf(avg_open) or _sf(mark_opt) or 0.0
|
||||||
|
opt_leverage = round(1.0 / lev_ref, 1) if lev_ref and lev_ref > 0 else None
|
||||||
|
liq_ok, liq_msg = option_bid_liquidity_ok(bid, bid_sz, need_sheets=float(sheets or 0))
|
||||||
|
|
||||||
sym = str((perp_leg or {}).get("symbol") or plan.get("exchange_symbol") or "")
|
sym = str((perp_leg or {}).get("symbol") or plan.get("exchange_symbol") or "")
|
||||||
perp_dir = str((perp_leg or {}).get("side") or plan.get("perp_direction") or perp_direction_for_view(view)).lower()
|
perp_dir = str(
|
||||||
|
(perp_leg or {}).get("side") or plan.get("perp_direction") or perp_direction_for_view(view)
|
||||||
|
).lower()
|
||||||
contracts = _sf((perp_leg or {}).get("size")) or _sf(plan.get("perp_size")) or 0.0
|
contracts = _sf((perp_leg or {}).get("size")) or _sf(plan.get("perp_size")) or 0.0
|
||||||
perp_entry = _sf((perp_leg or {}).get("avg_open")) or _sf(plan.get("entry_mark")) or 0.0
|
perp_entry = _sf((perp_leg or {}).get("avg_open")) or _sf(plan.get("entry_mark")) or 0.0
|
||||||
|
perp_lev = _sf(plan.get("leverage")) or 0.0
|
||||||
cs = 0.01
|
cs = 0.01
|
||||||
if callable(get_cs) and sym:
|
if callable(get_cs) and sym:
|
||||||
try:
|
try:
|
||||||
@@ -115,6 +160,7 @@ def build_live_hedge_cards(cfg: dict[str, Any], conn: Any) -> list[dict[str, Any
|
|||||||
perp_mark = None
|
perp_mark = None
|
||||||
perp_upl = None
|
perp_upl = None
|
||||||
live_contracts = None
|
live_contracts = None
|
||||||
|
margin = None
|
||||||
if callable(get_perp_metrics) and sym:
|
if callable(get_perp_metrics) and sym:
|
||||||
try:
|
try:
|
||||||
m = get_perp_metrics(sym, perp_dir, order_leverage=plan.get("leverage"))
|
m = get_perp_metrics(sym, perp_dir, order_leverage=plan.get("leverage"))
|
||||||
@@ -122,12 +168,15 @@ def build_live_hedge_cards(cfg: dict[str, Any], conn: Any) -> list[dict[str, Any
|
|||||||
perp_mark = _sf(m.get("mark_price") or m.get("mark"))
|
perp_mark = _sf(m.get("mark_price") or m.get("mark"))
|
||||||
perp_upl = _sf(m.get("unrealized_pnl"))
|
perp_upl = _sf(m.get("unrealized_pnl"))
|
||||||
live_contracts = _sf(m.get("contracts") or m.get("size"))
|
live_contracts = _sf(m.get("contracts") or m.get("size"))
|
||||||
|
margin = _sf(m.get("initial_margin") or m.get("margin"))
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
if perp_mark is None or perp_mark <= 0:
|
if perp_mark is None or perp_mark <= 0:
|
||||||
perp_mark = perp_entry
|
perp_mark = perp_entry
|
||||||
if live_contracts and live_contracts > 0:
|
if live_contracts and live_contracts > 0:
|
||||||
contracts = live_contracts
|
contracts = live_contracts
|
||||||
|
if (margin is None or margin <= 0) and contracts and perp_mark and perp_lev and perp_lev > 0:
|
||||||
|
margin = round(float(contracts) * float(cs) * float(perp_mark) / float(perp_lev), 2)
|
||||||
|
|
||||||
idx = None
|
idx = None
|
||||||
if callable(fetch_idx):
|
if callable(fetch_idx):
|
||||||
@@ -153,19 +202,29 @@ def build_live_hedge_cards(cfg: dict[str, Any], conn: Any) -> list[dict[str, Any
|
|||||||
contract_size=float(cs or 0.01),
|
contract_size=float(cs or 0.01),
|
||||||
fee=fr,
|
fee=fr,
|
||||||
)
|
)
|
||||||
# 币本位期权净利(币) → USDT;永续净利已是 U
|
|
||||||
opt_net_coin = float(net.get("opt_net") or 0)
|
opt_net_coin = float(net.get("opt_net") or 0)
|
||||||
opt_net_usdt = round(opt_net_coin * float(idx or 0), 4) if idx else None
|
opt_net_usdt = round(opt_net_coin * float(idx or 0), 4) if idx else None
|
||||||
perp_net = float(net.get("perp_net") or 0)
|
perp_net = float(net.get("perp_net") or 0)
|
||||||
combo_usdt = round((opt_net_usdt or 0) + perp_net, 4) if opt_net_usdt is not None else round(perp_net, 4)
|
combo_usdt = round((opt_net_usdt or 0) + perp_net, 4) if opt_net_usdt is not None else round(perp_net, 4)
|
||||||
|
|
||||||
|
opened_at = plan.get("opened_at")
|
||||||
|
opened_ms = _parse_opened_ms(opened_at)
|
||||||
|
# 两腿开仓时间统一取计划 opened_at
|
||||||
|
exp_ms = _parse_opt_exp_ms(inst_id)
|
||||||
|
hold_sec = int((now_ms - opened_ms) / 1000) if opened_ms else None
|
||||||
|
remain_sec = int((exp_ms - now_ms) / 1000) if exp_ms else None
|
||||||
|
|
||||||
out.append(
|
out.append(
|
||||||
{
|
{
|
||||||
"plan_id": plan.get("id"),
|
"plan_id": plan.get("id"),
|
||||||
"status": plan.get("status"),
|
"status": plan.get("status"),
|
||||||
"underlying": uly,
|
"underlying": uly,
|
||||||
"direction": view,
|
"direction": view,
|
||||||
"opened_at": plan.get("opened_at"),
|
"opened_at": opened_at,
|
||||||
|
"opened_ms": opened_ms,
|
||||||
|
"exp_ms": exp_ms,
|
||||||
|
"hold_sec": hold_sec,
|
||||||
|
"remain_sec": remain_sec,
|
||||||
"index_px": idx,
|
"index_px": idx,
|
||||||
"fee_rate": fr,
|
"fee_rate": fr,
|
||||||
"option": {
|
"option": {
|
||||||
@@ -177,6 +236,10 @@ def build_live_hedge_cards(cfg: dict[str, Any], conn: Any) -> list[dict[str, Any
|
|||||||
"avg_open": avg_open,
|
"avg_open": avg_open,
|
||||||
"mark_px": mark_opt,
|
"mark_px": mark_opt,
|
||||||
"bid": bid,
|
"bid": bid,
|
||||||
|
"bid_sz": bid_sz,
|
||||||
|
"leverage": opt_leverage,
|
||||||
|
"liq_ok": bool(liq_ok),
|
||||||
|
"liq_msg": liq_msg or "",
|
||||||
"net_coin": opt_net_coin,
|
"net_coin": opt_net_coin,
|
||||||
"net_usdt": opt_net_usdt,
|
"net_usdt": opt_net_usdt,
|
||||||
"live": bool(opt_raw),
|
"live": bool(opt_raw),
|
||||||
@@ -188,6 +251,8 @@ def build_live_hedge_cards(cfg: dict[str, Any], conn: Any) -> list[dict[str, Any
|
|||||||
"contract_size": cs,
|
"contract_size": cs,
|
||||||
"avg_open": perp_entry,
|
"avg_open": perp_entry,
|
||||||
"mark_px": perp_mark,
|
"mark_px": perp_mark,
|
||||||
|
"leverage": perp_lev or None,
|
||||||
|
"margin": margin,
|
||||||
"upl": perp_upl,
|
"upl": perp_upl,
|
||||||
"net_usdt": perp_net,
|
"net_usdt": perp_net,
|
||||||
"live": perp_upl is not None or (perp_mark is not None and perp_mark > 0),
|
"live": perp_upl is not None or (perp_mark is not None and perp_mark > 0),
|
||||||
|
|||||||
@@ -102,15 +102,16 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="hp-opt-bal-line" class="muted hp-po-meta hp-opt-bal-line"></div>
|
<div id="hp-opt-bal-line" class="muted hp-po-meta hp-opt-bal-line"></div>
|
||||||
|
|
||||||
<!-- 弹窗下单(挂到 body 时样式靠 .hp-po-order-dialog 自身,不依赖页 wrap) -->
|
<!-- 弹窗下单:宽屏左测算 / 右下单 -->
|
||||||
<div id="hp-order-panel-host" class="opt-order-backdrop" hidden aria-hidden="true">
|
<div id="hp-order-panel-host" class="opt-order-backdrop" hidden aria-hidden="true">
|
||||||
<div id="hp-order-panel" class="opt-order-dialog hp-po-order-dialog" role="dialog" aria-modal="true" aria-labelledby="hp-order-dialog-title" style="display:none">
|
<div id="hp-order-panel" class="opt-order-dialog hp-po-order-dialog hp-po-order-dialog--wide" role="dialog" aria-modal="true" aria-labelledby="hp-order-dialog-title" style="display:none">
|
||||||
<div class="opt-order-dialog-head">
|
<div class="opt-order-dialog-head">
|
||||||
<h3 class="opt-order-title" id="hp-order-dialog-title">手动下单 · <span id="hp-perp-uly-label">ETH</span></h3>
|
<h3 class="opt-order-title" id="hp-order-dialog-title">手动下单 · <span id="hp-perp-uly-label">ETH</span></h3>
|
||||||
<button type="button" class="hp-po-dialog-x" id="hp-order-close-btn" aria-label="关闭">×</button>
|
<button type="button" class="hp-po-dialog-x" id="hp-order-close-btn" aria-label="关闭">×</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="opt-order-layout">
|
<div class="opt-order-layout hp-po-order-split">
|
||||||
<div class="opt-order-main">
|
<aside class="hp-po-order-calc-pane" aria-label="仓位测算">
|
||||||
|
<h4 class="hp-po-section-title">仓位测算</h4>
|
||||||
<div class="hp-po-dialog-meta">
|
<div class="hp-po-dialog-meta">
|
||||||
<div id="hp-sel-inst" class="options-order-inst">—</div>
|
<div id="hp-sel-inst" class="options-order-inst">—</div>
|
||||||
<p class="muted hp-po-dialog-sub" id="hp-premium-line"></p>
|
<p class="muted hp-po-dialog-sub" id="hp-premium-line"></p>
|
||||||
@@ -118,10 +119,17 @@
|
|||||||
<p id="hp-perp-quote" class="muted hp-po-dialog-sub" hidden>加载中…</p>
|
<p id="hp-perp-quote" class="muted hp-po-dialog-sub" hidden>加载中…</p>
|
||||||
<p id="hp-po-perp-quote-right" class="muted" hidden></p>
|
<p id="hp-po-perp-quote-right" class="muted" hidden></p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="hp-po-calc-box">
|
||||||
|
<p class="muted hp-po-max-line" id="hp-po-max-line">期权开仓最大 —</p>
|
||||||
|
<p class="muted hp-po-perp-auto" id="hp-po-perp-auto">永续开仓 —</p>
|
||||||
|
<div id="hp-perp-pnl-line" class="hp-po-pnl"></div>
|
||||||
|
<div id="hp-sizing-line" class="muted hp-po-sizing"></div>
|
||||||
|
</div>
|
||||||
<span id="hp-po-mode-badge" class="hp-po-mode-badge" hidden>手动下单</span>
|
<span id="hp-po-mode-badge" class="hp-po-mode-badge" hidden>手动下单</span>
|
||||||
<span id="hp-po-card-title" hidden>下单</span>
|
<span id="hp-po-card-title" hidden>下单</span>
|
||||||
<span class="muted hp-acct-tag" id="hp-perp-acct-tag" hidden>合约账户</span>
|
<span class="muted hp-acct-tag" id="hp-perp-acct-tag" hidden>合约账户</span>
|
||||||
|
</aside>
|
||||||
|
<div class="opt-order-main hp-po-order-form-pane">
|
||||||
<div class="hp-po-fields hidden" id="hp-po-fields-insurance" hidden>
|
<div class="hp-po-fields hidden" id="hp-po-fields-insurance" hidden>
|
||||||
<label class="hp-po-field">
|
<label class="hp-po-field">
|
||||||
<span class="hp-po-field-lab">开仓价 <em>USDT</em></span>
|
<span class="hp-po-field-lab">开仓价 <em>USDT</em></span>
|
||||||
@@ -183,15 +191,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<p class="err hp-po-order-err" id="hp-po-order-err" hidden></p>
|
<p class="err hp-po-order-err" id="hp-po-order-err" hidden></p>
|
||||||
</section>
|
</section>
|
||||||
<details class="tip-collapse hp-rule-collapse hp-po-order-calc">
|
|
||||||
<summary class="tip-collapse-summary">仓位测算明细</summary>
|
|
||||||
<div class="tip-collapse-body">
|
|
||||||
<p class="muted hp-po-max-line" id="hp-po-max-line">期权开仓最大 —</p>
|
|
||||||
<p class="muted hp-po-perp-auto" id="hp-po-perp-auto">永续开仓 —</p>
|
|
||||||
<div id="hp-perp-pnl-line" class="hp-po-pnl"></div>
|
|
||||||
<div id="hp-sizing-line" class="muted hp-po-sizing"></div>
|
|
||||||
</div>
|
|
||||||
</details>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="opt-order-dialog-actions">
|
<div class="opt-order-dialog-actions">
|
||||||
<span id="hp-po-strategy-status" class="hp-po-strategy-status" aria-live="polite"></span>
|
<span id="hp-po-strategy-status" class="hp-po-strategy-status" aria-live="polite"></span>
|
||||||
@@ -432,4 +431,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="/static/hedge_plan.js?v=54"></script>
|
<script src="/static/hedge_plan.js?v=55"></script>
|
||||||
|
|||||||
Reference in New Issue
Block a user