Make 永期 hedge UI clearer with dir segments and field grid.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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 =
|
||||
"<strong>" +
|
||||
(d.base || state.underlying) +
|
||||
"</strong> · " +
|
||||
acctLabel +
|
||||
"可用 <strong>" +
|
||||
fmt(d.available_usdt, 2) +
|
||||
"</strong> USDT<br/>标记 <strong>" +
|
||||
fmt(d.mark, 2) +
|
||||
"</strong> · 最新 " +
|
||||
fmt(d.last, 2) +
|
||||
" · 卖一 " +
|
||||
"</strong> 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 =
|
||||
"全仓建议 <strong>" +
|
||||
fmt(d.suggest_contracts, amtPrec) +
|
||||
" 合约张(已按交易所精度)";
|
||||
"</strong> 张 · 保证金 " +
|
||||
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 = '<span class="muted">填开仓价与张数后,输入止盈/止损可看盈亏</span>';
|
||||
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(
|
||||
"止盈预期 <strong class=\"" +
|
||||
(p >= 0 ? "hp-pnl-pos" : "hp-pnl-neg") +
|
||||
"\">" +
|
||||
(p >= 0 ? "+" : "") +
|
||||
fmt(p, 2) +
|
||||
" USDT</strong>"
|
||||
function chip(lab, px) {
|
||||
if (!(px > 0)) {
|
||||
return '<span class="hp-po-chip muted">' + lab + " —</span>";
|
||||
}
|
||||
const p = pnlAt(px);
|
||||
const cls = p >= 0 ? "hp-pnl-pos" : "hp-pnl-neg";
|
||||
const sign = p >= 0 ? "+" : "";
|
||||
return (
|
||||
'<span class="hp-po-chip">' +
|
||||
lab +
|
||||
' <strong class="' +
|
||||
cls +
|
||||
'">' +
|
||||
sign +
|
||||
fmt(p, 2) +
|
||||
"</strong></span>"
|
||||
);
|
||||
} else {
|
||||
parts.push("止盈预期 —");
|
||||
}
|
||||
if (sl > 0) {
|
||||
const p = pnlAt(sl);
|
||||
parts.push(
|
||||
"止损预期 <strong class=\"" +
|
||||
(p >= 0 ? "hp-pnl-pos" : "hp-pnl-neg") +
|
||||
"\">" +
|
||||
(p >= 0 ? "+" : "") +
|
||||
fmt(p, 2) +
|
||||
" USDT</strong>"
|
||||
);
|
||||
} 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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -41,37 +41,53 @@
|
||||
|
||||
<div id="hp-tab-perp_options" class="hp-tab-panel" role="tabpanel">
|
||||
<div class="options-dual-grid" id="hp-po-layout">
|
||||
<div class="card">
|
||||
<div class="card hp-po-perp-card">
|
||||
<h2>永续 · <span id="hp-perp-uly-label">ETH</span> <span class="muted hp-acct-tag" id="hp-perp-acct-tag">合约账户</span></h2>
|
||||
<div class="form-row hp-uly-row">
|
||||
<button type="button" class="btn-secondary hp-uly-btn active" data-uly="ETH">ETH</button>
|
||||
<button type="button" class="btn-secondary hp-uly-btn" data-uly="BTC">BTC</button>
|
||||
<select id="hp-direction">
|
||||
<option value="long">做多</option>
|
||||
<option value="short">做空</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="hp-perp-quote" class="muted hp-quote-line">加载中…</div>
|
||||
<p class="muted hp-unit-hint">单位说明:价格=USDT · 张数=交易所<strong>永续合约张</strong>(精度与 OKX 下单一致) · 盈亏=USDT</p>
|
||||
<div class="form-row" style="flex-wrap:wrap">
|
||||
<label>开仓价 <span class="hp-unit">USDT</span> <input type="number" step="any" id="hp-entry" /></label>
|
||||
<label>止盈 <span class="hp-unit">USDT</span> <input type="number" step="any" id="hp-tp" /></label>
|
||||
<label>止损 <span class="hp-unit">USDT</span> <input type="number" step="any" id="hp-sl" /></label>
|
||||
<label>张数 <span class="hp-unit">合约张</span> <input type="number" step="any" id="hp-contracts" /></label>
|
||||
<div class="hp-po-top">
|
||||
<div class="hp-oo-seg hp-po-dir-seg" role="group" aria-label="方向">
|
||||
<button type="button" class="btn-secondary hp-po-dir is-selected" data-dir="long" title="做多永续"><span class="hp-oo-check" aria-hidden="true">✓</span>做多</button>
|
||||
<button type="button" class="btn-secondary hp-po-dir" data-dir="short" title="做空永续"><span class="hp-oo-check" aria-hidden="true">✓</span>做空</button>
|
||||
</div>
|
||||
<span id="hp-po-mark" class="hp-po-mark" aria-live="polite">标记 —</span>
|
||||
</div>
|
||||
<p id="hp-perp-quote" class="muted hp-po-meta">加载中…</p>
|
||||
<div class="hp-po-fields">
|
||||
<label class="hp-po-field">
|
||||
<span class="hp-po-field-lab">开仓价 <em>USDT</em></span>
|
||||
<input type="number" step="any" id="hp-entry" placeholder="入场价" />
|
||||
</label>
|
||||
<label class="hp-po-field">
|
||||
<span class="hp-po-field-lab">张数 <em>合约张</em></span>
|
||||
<input type="number" step="any" id="hp-contracts" placeholder="数量" />
|
||||
</label>
|
||||
<label class="hp-po-field hp-po-field--tp">
|
||||
<span class="hp-po-field-lab">止盈 <em>USDT</em></span>
|
||||
<input type="number" step="any" id="hp-tp" placeholder="目标价" />
|
||||
</label>
|
||||
<label class="hp-po-field hp-po-field--sl">
|
||||
<span class="hp-po-field-lab">止损 <em>USDT</em></span>
|
||||
<input type="number" step="any" id="hp-sl" placeholder="保护价" />
|
||||
</label>
|
||||
</div>
|
||||
<div class="hp-po-summary">
|
||||
<div id="hp-perp-pnl-line" class="hp-po-pnl"></div>
|
||||
<div id="hp-sizing-line" class="muted hp-po-sizing"></div>
|
||||
</div>
|
||||
<p class="muted" id="hp-perp-pnl-line"></p>
|
||||
<p class="muted" id="hp-sizing-line"></p>
|
||||
</div>
|
||||
<div class="card hp-opt-card">
|
||||
<h2>期权(列表) · <span id="hp-opt-type-label">Put</span> <span class="muted hp-acct-tag" id="hp-opt-acct-tag">期权账户</span></h2>
|
||||
<div class="form-row hp-opt-toolbar">
|
||||
<h2>期权 · <span id="hp-opt-type-label">Put</span> <span class="muted hp-acct-tag" id="hp-opt-acct-tag">期权账户</span></h2>
|
||||
<div class="form-row hp-opt-toolbar hp-po-opt-toolbar">
|
||||
<select id="hp-exp-select"><option value="">选择到期日</option></select>
|
||||
<button type="button" class="btn-secondary hp-money-btn active" data-money="all">全部</button>
|
||||
<button type="button" class="btn-secondary hp-money-btn" data-money="itm">实值</button>
|
||||
<button type="button" class="btn-secondary hp-money-btn" data-money="otm">虚值</button>
|
||||
<button type="button" class="btn-secondary" id="hp-load-chain">刷新链</button>
|
||||
<span id="hp-index-line" class="hp-po-index" aria-live="polite">指数 —</span>
|
||||
</div>
|
||||
<div id="hp-index-line" class="muted hp-quote-line"></div>
|
||||
<div class="options-strike-table-wrap hp-strike-table-wrap--5">
|
||||
<table class="options-strike-table" id="hp-strike-table">
|
||||
<thead>
|
||||
@@ -93,8 +109,7 @@
|
||||
<label>张数 <span class="hp-unit">期权张</span> <input type="number" step="1" min="1" id="hp-sheets" value="1" /></label>
|
||||
<span class="muted" id="hp-premium-line"></span>
|
||||
</div>
|
||||
<p class="muted hp-unit-hint">单位说明:权利金结算币=<strong>USDC</strong> · 张数=期权张(整张) · 卖一/买一=价格/张.期权买入仅认真实卖一价且卖一深度>0;无深度不可开仓(链上~为参考估算).</p>
|
||||
<div id="hp-opt-bal-line" class="muted hp-quote-line hp-opt-bal-line"></div>
|
||||
<div id="hp-opt-bal-line" class="muted hp-po-meta hp-opt-bal-line"></div>
|
||||
<div class="form-row hp-action-row">
|
||||
<button type="button" class="primary" id="hp-preview-btn">计算</button>
|
||||
</div>
|
||||
@@ -285,4 +300,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/static/hedge_plan.js?v=20"></script>
|
||||
<script src="/static/hedge_plan.js?v=21"></script>
|
||||
|
||||
Reference in New Issue
Block a user