diff --git a/lib/common/static/hedge_plan.js b/lib/common/static/hedge_plan.js index 60100f4..a4f7555 100644 --- a/lib/common/static/hedge_plan.js +++ b/lib/common/static/hedge_plan.js @@ -31,6 +31,7 @@ ooSheetsMode: "same_sheets", ooCloseModeEnabled: root.getAttribute("data-oo-close-mode-enabled") !== "0", ooCloseMode: "close_all", + direction: "long", tradingUsdc: null, fundingUsdc: null, tradeBudgetUsdc: null, @@ -40,6 +41,32 @@ previewPlanType: null, }; + function getDirection() { + return (state.direction || "long").toLowerCase() === "short" ? "short" : "long"; + } + + function syncPoDirUI() { + const dir = getDirection(); + document.querySelectorAll(".hp-po-dir").forEach(function (b) { + const on = (b.getAttribute("data-dir") || "") === dir; + b.classList.toggle("is-selected", on); + b.classList.toggle("active", on); + }); + } + + function setDirection(dir, forceReload) { + const next = (dir || "long").toLowerCase() === "short" ? "short" : "long"; + const changed = next !== getDirection(); + state.direction = next; + syncPoDirUI(); + if (!changed && !forceReload) return; + state.selected = null; + if ($("hp-sel-inst")) $("hp-sel-inst").textContent = "—"; + void loadMarket().then(function () { + renderListStrikes(); + }); + } + function $(id) { return document.getElementById(id); } @@ -428,7 +455,7 @@ } async function loadMarket() { - const dir = ($("hp-direction") && $("hp-direction").value) || "long"; + const dir = getDirection(); const d = await apiJson( "/api/hedge-plan/market?base=" + encodeURIComponent(state.underlying) + @@ -441,26 +468,20 @@ const tag = $("hp-perp-acct-tag"); if (tag) tag.textContent = acctLabel; const amtPrec = d.amount_precision != null ? Number(d.amount_precision) : 4; + const markEl = $("hp-po-mark"); + if (markEl) markEl.textContent = "标记 " + fmt(d.mark, 2); const q = $("hp-perp-quote"); if (q) { q.innerHTML = - "" + - (d.base || state.underlying) + - " · " + - acctLabel + "可用 " + fmt(d.available_usdt, 2) + - " USDT
标记 " + - fmt(d.mark, 2) + - " · 最新 " + - fmt(d.last, 2) + - " · 卖一 " + + " USDT · 卖一 " + fmt(d.ask, 2) + " · 买一 " + fmt(d.bid, 2) + " · 面值 " + fmt(d.contract_size, 4) + - " · 张精度 " + + " · 精度 " + amtPrec + " 位"; } @@ -473,20 +494,18 @@ if (sz) { if (d.full_margin_sizing) { const s = d.full_margin_sizing; - sz.textContent = - "全仓建议(" + - acctLabel + - "):保证金 " + - fmt(s.margin_capital, 2) + - " USDT × " + - s.leverage + - "x → 名义 " + - fmt(s.notional_value, 2) + - " USDT · 建议 " + + sz.innerHTML = + "全仓建议 " + fmt(d.suggest_contracts, amtPrec) + - " 合约张(已按交易所精度)"; + " 张 · 保证金 " + + fmt(s.margin_capital, 2) + + " × " + + s.leverage + + "x · 名义 " + + fmt(s.notional_value, 2) + + " USDT"; } else { - sz.textContent = "非全仓或不具备保证金数据时仅手动填张数;永期开仓需全仓."; + sz.textContent = "非全仓时请手动填张数;永期开仓需全仓"; } } const entry = $("hp-entry"); @@ -507,9 +526,9 @@ const sl = Number(($("hp-sl") && $("hp-sl").value) || NaN); const contracts = Number(($("hp-contracts") && $("hp-contracts").value) || NaN); const cs = Number((state.market && state.market.contract_size) || 0.01); - const dir = (($("hp-direction") && $("hp-direction").value) || "long").toLowerCase(); + const dir = getDirection(); if (!(entry > 0) || !(contracts > 0) || !(cs > 0)) { - el.textContent = "填写开仓价与张数后,输入止盈/止损可看永续盈亏金额(USDT)"; + el.innerHTML = '填开仓价与张数后,输入止盈/止损可看盈亏'; return; } function pnlAt(exitPx) { @@ -517,34 +536,25 @@ if (dir === "short") return (entry - exitPx) * coins; return (exitPx - entry) * coins; } - const parts = []; - if (tp > 0) { - const p = pnlAt(tp); - parts.push( - "止盈预期 = 0 ? "hp-pnl-pos" : "hp-pnl-neg") + - "\">" + - (p >= 0 ? "+" : "") + - fmt(p, 2) + - " USDT" + function chip(lab, px) { + if (!(px > 0)) { + return '' + lab + " —"; + } + const p = pnlAt(px); + const cls = p >= 0 ? "hp-pnl-pos" : "hp-pnl-neg"; + const sign = p >= 0 ? "+" : ""; + return ( + '' + + lab + + ' ' + + sign + + fmt(p, 2) + + "" ); - } else { - parts.push("止盈预期 —"); } - if (sl > 0) { - const p = pnlAt(sl); - parts.push( - "止损预期 = 0 ? "hp-pnl-pos" : "hp-pnl-neg") + - "\">" + - (p >= 0 ? "+" : "") + - fmt(p, 2) + - " USDT" - ); - } else { - parts.push("止损预期 —"); - } - el.innerHTML = parts.join(" · "); + el.innerHTML = chip("止盈", tp) + chip("止损", sl); } function fillExpSelect(sel, chain) { @@ -569,12 +579,8 @@ "/api/hedge-plan/options-chain?underlying=" + encodeURIComponent(state.underlying) ); state.chain = d; - const uly = d.underlying || state.underlying; const idx = $("hp-index-line"); - if (idx) { - idx.textContent = - "指数 " + uly + " " + fmt(d.index_px, 2) + " · " + (d.inst_family || "") + " · 实值含平值"; - } + if (idx) idx.textContent = "指数 " + fmt(d.index_px, 2); const ooIdx = $("hp-oo-index"); if (ooIdx) ooIdx.textContent = "指数 " + fmt(d.index_px, 2); setOptionsBalance(d); @@ -622,8 +628,7 @@ function renderListStrikes() { const tbody = $("hp-strike-tbody"); if (!tbody) return; - const dir = ($("hp-direction") && $("hp-direction").value) || "long"; - const want = optTypeForDirection(dir); + const want = optTypeForDirection(getDirection()); const exp = currentExp("hp-exp-select"); const prevInst = state.selected && state.selected.inst_id; tbody.innerHTML = ""; @@ -904,7 +909,7 @@ if (!entry || !tp || !sl || !contracts) throw new Error("请完整填写开仓/止盈/止损/张数"); body = { plan_type: "perp_options", - direction: ($("hp-direction") && $("hp-direction").value) || "long", + direction: getDirection(), entry: entry, tp: tp, sl: sl, @@ -1017,16 +1022,11 @@ renderListStrikes(); }); }); - const dir = $("hp-direction"); - if (dir) { - dir.addEventListener("change", function () { - state.selected = null; - if ($("hp-sel-inst")) $("hp-sel-inst").textContent = "—"; - void loadMarket().then(function () { - renderListStrikes(); - }); + document.querySelectorAll(".hp-po-dir").forEach(function (b) { + b.addEventListener("click", function () { + setDirection(b.getAttribute("data-dir") || "long", true); }); - } + }); ["hp-entry", "hp-tp", "hp-sl", "hp-contracts"].forEach(function (id) { const el = $(id); if (el) el.addEventListener("input", updatePerpPnlHint); @@ -1507,7 +1507,7 @@ body = { plan_type: "perp_options", underlying: state.underlying, - direction: ($("hp-direction") && $("hp-direction").value) || "long", + direction: getDirection(), entry: entry, tp: tp, sl: sl, @@ -1561,6 +1561,7 @@ syncTabUI(); syncUnderlyingUI(); syncMoneyUI(); + syncPoDirUI(); syncOoSizeModeUI(); syncOoCloseModeUI(); updateOoBudgetLine(null); diff --git a/lib/common/static/instance_theme.css b/lib/common/static/instance_theme.css index 8d4fd2c..8c9a274 100644 --- a/lib/common/static/instance_theme.css +++ b/lib/common/static/instance_theme.css @@ -3366,8 +3366,10 @@ html[data-theme="light"] .opt-be-dist-down { } .hedge-plan-page-wrap .hp-oo-size-mode.is-selected, .hedge-plan-page-wrap .hp-oo-close-mode.is-selected, +.hedge-plan-page-wrap .hp-po-dir.is-selected, .hedge-plan-page-wrap .hp-oo-size-mode.active, -.hedge-plan-page-wrap .hp-oo-close-mode.active { +.hedge-plan-page-wrap .hp-oo-close-mode.active, +.hedge-plan-page-wrap .hp-po-dir.active { border-color: var(--accent, #00d4ff); color: var(--text, #fff); background: rgba(0, 212, 255, 0.16); @@ -3376,11 +3378,118 @@ html[data-theme="light"] .opt-be-dist-down { } .hedge-plan-page-wrap .hp-oo-size-mode.is-selected .hp-oo-check, .hedge-plan-page-wrap .hp-oo-close-mode.is-selected .hp-oo-check, +.hedge-plan-page-wrap .hp-po-dir.is-selected .hp-oo-check, .hedge-plan-page-wrap .hp-oo-size-mode.active .hp-oo-check, -.hedge-plan-page-wrap .hp-oo-close-mode.active .hp-oo-check { +.hedge-plan-page-wrap .hp-oo-close-mode.active .hp-oo-check, +.hedge-plan-page-wrap .hp-po-dir.active .hp-oo-check { display: inline; color: var(--accent, #00d4ff); } +.hedge-plan-page-wrap .hp-po-top { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 12px 16px; + margin: 8px 0 4px; +} +.hedge-plan-page-wrap .hp-po-dir-seg { + flex: 1 1 180px; + max-width: 220px; +} +.hedge-plan-page-wrap .hp-po-mark { + font-size: 0.95rem; + font-weight: 700; + font-variant-numeric: tabular-nums; + color: #3dd68c; + white-space: nowrap; +} +.hedge-plan-page-wrap .hp-po-meta { + margin: 2px 0 8px; + font-size: 0.74rem; + line-height: 1.4; +} +.hedge-plan-page-wrap .hp-po-fields { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 10px 12px; + margin: 8px 0 6px; + padding: 10px 12px; + border-radius: 8px; + background: rgba(255, 255, 255, 0.03); + border: 1px solid rgba(255, 255, 255, 0.06); +} +.hedge-plan-page-wrap .hp-po-field { + display: flex; + flex-direction: column; + gap: 4px; + min-width: 0; + margin: 0; + font-size: 0.78rem; +} +.hedge-plan-page-wrap .hp-po-field-lab { + color: #9aa4b2; + font-size: 0.72rem; +} +.hedge-plan-page-wrap .hp-po-field-lab em { + font-style: normal; + color: #6b7388; + margin-left: 2px; +} +.hedge-plan-page-wrap .hp-po-field input { + width: 100%; + min-width: 0; + box-sizing: border-box; +} +.hedge-plan-page-wrap .hp-po-field--tp input { + border-color: rgba(61, 214, 140, 0.45); +} +.hedge-plan-page-wrap .hp-po-field--sl input { + border-color: rgba(255, 138, 138, 0.45); +} +.hedge-plan-page-wrap .hp-po-summary { + display: flex; + flex-direction: column; + gap: 6px; + margin-top: 4px; +} +.hedge-plan-page-wrap .hp-po-pnl { + display: flex; + flex-wrap: wrap; + gap: 8px; +} +.hedge-plan-page-wrap .hp-po-chip { + display: inline-flex; + align-items: baseline; + gap: 4px; + padding: 4px 8px; + border-radius: 6px; + background: rgba(255, 255, 255, 0.04); + border: 1px solid rgba(255, 255, 255, 0.08); + font-size: 0.76rem; +} +.hedge-plan-page-wrap .hp-po-sizing { + font-size: 0.74rem; + line-height: 1.4; +} +.hedge-plan-page-wrap .hp-po-opt-toolbar { + align-items: center; +} +.hedge-plan-page-wrap .hp-po-index { + margin-left: auto; + font-size: 0.9rem; + font-weight: 700; + font-variant-numeric: tabular-nums; + color: #3dd68c; + white-space: nowrap; +} +@media (max-width: 720px) { + .hedge-plan-page-wrap .hp-po-fields { + grid-template-columns: 1fr; + } + .hedge-plan-page-wrap .hp-po-index { + margin-left: 0; + } +} .hedge-plan-page-wrap #hp-oo-close-mode-row.hidden { display: none !important; } diff --git a/lib/hedge_plan/templates/hedge_plan_panel.html b/lib/hedge_plan/templates/hedge_plan_panel.html index 2251c23..f09d316 100644 --- a/lib/hedge_plan/templates/hedge_plan_panel.html +++ b/lib/hedge_plan/templates/hedge_plan_panel.html @@ -41,37 +41,53 @@
-
+

永续 · ETH 合约账户

-
-
加载中…
-

单位说明:价格=USDT · 张数=交易所永续合约张(精度与 OKX 下单一致) · 盈亏=USDT

-
- - - - +
+
+ + +
+ 标记 — +
+

加载中…

+
+ + + + +
+
+
+
-

-

-

期权(列表) · Put 期权账户

-
+

期权 · Put 期权账户

+
+ 指数 —
-
@@ -93,8 +109,7 @@ -

单位说明:权利金结算币=USDC · 张数=期权张(整张) · 卖一/买一=价格/张.期权买入仅认真实卖一价且卖一深度>0;无深度不可开仓(链上~为参考估算).

-
+
@@ -285,4 +300,4 @@ - +