refactor(hedge): env-driven option-primary UI with grouped params

Move mode switch to HEDGE_PLAN_OPTION_PRIMARY, split left into capital/select/exit groups, and show perp quote above options with leverage auto-match.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-08-09 09:05:32 +08:00
parent c3c7243dd7
commit 20e0c2cb9f
8 changed files with 281 additions and 130 deletions
+66 -46
View File
@@ -37,7 +37,7 @@
ooCloseModeEnabled: root.getAttribute("data-oo-close-mode-enabled") !== "0",
ooCloseMode: "close_all",
direction: "long",
optionPrimary: true,
optionPrimary: root.getAttribute("data-option-primary") !== "0",
opLevTouched: false,
opRatioTouched: false,
tradingUsdc: null,
@@ -283,14 +283,9 @@
function syncOptionPrimaryUI() {
const on = isOptionPrimary();
document.querySelectorAll(".hp-po-mode").forEach(function (b) {
const v = b.getAttribute("data-option-primary") === "1";
b.classList.toggle("is-selected", v === on);
b.classList.toggle("active", v === on);
});
const ins = $("hp-po-fields-insurance");
const op = $("hp-po-fields-option-primary");
// 保险模式只显示开仓价/张数/止盈止损;以期权为主显示权利金等执行参数
// 保险模式只显示开仓价/张数/止盈止损;以期权为主显示三组参数(env 切换,页内不可改)
if (ins) {
ins.classList.toggle("hidden", on);
if (on) ins.setAttribute("hidden", "hidden");
@@ -303,48 +298,39 @@
else op.removeAttribute("hidden");
op.style.display = on ? "" : "none";
}
const otmBtn = document.querySelector(".hp-money-otm");
if (otmBtn) {
const insMoney = $("hp-po-ins-money");
if (insMoney) {
if (on) {
insMoney.setAttribute("hidden", "hidden");
insMoney.classList.add("hidden");
} else {
insMoney.removeAttribute("hidden");
insMoney.classList.remove("hidden");
}
}
document.querySelectorAll(".hp-money-otm").forEach(function (otmBtn) {
otmBtn.classList.toggle("hidden", !on);
if (!on) otmBtn.setAttribute("hidden", "hidden");
else otmBtn.removeAttribute("hidden");
}
});
if (!on && state.moneyFilter === "otm") {
state.moneyFilter = "itm";
syncMoneyUI();
}
if (on) applyOpDefaultsFromMoney(false);
const title = $("hp-po-card-title");
if (title) title.textContent = on ? "执行参数" : "永续";
const rightQ = $("hp-po-perp-quote-right");
if (rightQ) {
rightQ.classList.toggle("hidden", !on);
if (!on) rightQ.setAttribute("hidden", "hidden");
else rightQ.removeAttribute("hidden");
const badge = $("hp-po-mode-badge");
if (badge) {
badge.textContent = on ? "以期权为主" : "保险模式";
badge.classList.toggle("is-insurance", !on);
}
const title = $("hp-po-card-title");
if (title) title.textContent = "执行参数";
const dirLong = document.querySelector('.hp-po-dir[data-dir="long"]');
const dirShort = document.querySelector('.hp-po-dir[data-dir="short"]');
if (dirLong) dirLong.title = on ? "做多=买Call+永续空" : "做多永续";
if (dirShort) dirShort.title = on ? "做空=买Put+永续多" : "做空永续";
}
function setOptionPrimary(on, forceReload) {
const next = !!on;
const changed = next !== isOptionPrimary();
state.optionPrimary = next;
if (changed) {
state.opLevTouched = false;
state.opRatioTouched = false;
state.selected = null;
if ($("hp-sel-inst")) $("hp-sel-inst").textContent = "—";
}
syncOptionPrimaryUI();
if (!changed && !forceReload) return;
void loadMarket().then(function () {
return loadChain();
});
}
function hoursFromExpMs(expMs) {
const n = Number(expMs);
if (!n || Number.isNaN(n)) return null;
@@ -440,6 +426,37 @@
return (Math.round((k / a) * 10) / 10).toFixed(1) + "×";
}
/** 永期期权行情杠杆:指数÷卖一(与选约杠杆门一致). */
function calcIndexAskLeverage(ask) {
const idx = indexPx();
const a = Number(ask);
if (!(idx > 0) || !(a > 0)) return "—";
return (Math.round((idx / a) * 10) / 10).toFixed(1) + "×";
}
function clearPoSelection() {
state.selected = null;
if ($("hp-sel-inst")) $("hp-sel-inst").textContent = "—";
updatePremiumLine();
updatePerpPnlHint();
}
/** 以期权为主:按类型/间隔/杠杆自动匹配最近合约. */
function autoMatchPoOption(force) {
if (!isOptionPrimary()) return;
const want = optTypeForDirection(getDirection());
const sel = state.selected;
const stillOk =
!force &&
sel &&
String(sel.opt_type || "").toUpperCase() === want &&
matchesMoneyFilter(sel);
if (stillOk) return;
const c = pickClosestItmAtm(currentContracts("hp-exp-select"), want);
if (c) pickContract(c);
else clearPoSelection();
}
function findAtmStrikeFromRows(rows, idx) {
if (!rows.length) return null;
if (idx == null || Number.isNaN(Number(idx))) return rows[0].strike;
@@ -913,10 +930,6 @@
: "");
const q = $("hp-perp-quote");
if (q) q.innerHTML = quoteHtml;
const rightQ = $("hp-po-perp-quote-right");
if (rightQ && isOptionPrimary()) {
rightQ.innerHTML = "永续行情 · " + quoteHtml;
}
const contractsInput = $("hp-contracts");
if (contractsInput) {
const step = amtPrec <= 0 ? "1" : String(Math.pow(10, -amtPrec));
@@ -1130,7 +1143,8 @@
const prevInst = state.selected && state.selected.inst_id;
tbody.innerHTML = "";
if (!exp) {
tbody.innerHTML = '<tr><td colspan="5" class="muted">请选择到期日</td></tr>';
tbody.innerHTML = '<tr><td colspan="6" class="muted">请选择到期日</td></tr>';
if (isOptionPrimary()) clearPoSelection();
return;
}
const sameType = (exp.contracts || []).filter(function (c) {
@@ -1158,7 +1172,8 @@
(idx ? " · 指数 " + fmt(idx, 2) : "") +
" · 可换到期或点刷新链";
}
tbody.innerHTML = '<tr><td colspan="5" class="muted">' + hint + "</td></tr>";
tbody.innerHTML = '<tr><td colspan="6" class="muted">' + hint + "</td></tr>";
if (isOptionPrimary()) clearPoSelection();
return;
}
list.forEach(function (c) {
@@ -1171,6 +1186,8 @@
c.strike +
"</td><td>" +
moneynessBadge(c) +
'</td><td class="opt-chain-lev" title="指数÷卖一">' +
calcIndexAskLeverage(c.ask) +
'</td><td class="opt-px-sz">' +
fmtPxSz(c.ask, c.ask_sz, c.ask_estimated, c.tick_sz) +
'</td><td class="opt-px-sz">' +
@@ -1191,6 +1208,7 @@
pickContract(c);
});
});
autoMatchPoOption(false);
}
function updatePremiumLine() {
@@ -1729,7 +1747,7 @@
b.addEventListener("click", function () {
const m = b.getAttribute("data-money") || "itm";
if (m === "otm" && !isOptionPrimary()) {
alert("永期保险腿仅允许实值或平值;请先打开「以期权为主」");
alert("永期保险腿仅允许实值或平值;请在 env 将 HEDGE_PLAN_OPTION_PRIMARY=true");
return;
}
state.moneyFilter = m === "otm" ? "otm" : m === "atm" ? "atm" : "itm";
@@ -1738,11 +1756,6 @@
renderListStrikes();
});
});
document.querySelectorAll(".hp-po-mode").forEach(function (b) {
b.addEventListener("click", function () {
setOptionPrimary(b.getAttribute("data-option-primary") === "1", true);
});
});
if ($("hp-opt-leverage")) {
$("hp-opt-leverage").addEventListener("input", function () {
state.opLevTouched = true;
@@ -1789,6 +1802,13 @@
});
if ($("hp-recommend-opt")) {
$("hp-recommend-opt").addEventListener("click", function () {
if (isOptionPrimary()) {
autoMatchPoOption(true);
if (!state.selected) {
alert("当前选约条件下无匹配合约,请调整类型/间隔/杠杆或换到期");
}
return;
}
const want = optTypeForDirection(getDirection());
const c = pickClosestItmAtm(currentContracts("hp-exp-select"), want);
if (!c) {
+81 -2
View File
@@ -3470,6 +3470,34 @@ html[data-theme="light"] .opt-be-dist-down {
font-size: 0.74rem;
line-height: 1.4;
}
.hedge-plan-page-wrap .hp-po-mode-badge {
display: inline-block;
margin-right: 6px;
padding: 2px 8px;
border-radius: 6px;
font-size: 0.78rem;
font-weight: 700;
letter-spacing: 0.02em;
color: #c4b5fd;
background: rgba(139, 92, 246, 0.22);
border: 1px solid rgba(167, 139, 250, 0.35);
vertical-align: middle;
}
.hedge-plan-page-wrap .hp-po-mode-badge.is-insurance {
color: #93c5fd;
background: rgba(59, 130, 246, 0.18);
border-color: rgba(96, 165, 250, 0.35);
}
.hedge-plan-page-wrap .hp-po-section {
margin: 8px 0 10px;
}
.hedge-plan-page-wrap .hp-po-section-title {
margin: 0 0 6px;
font-size: 0.8rem;
font-weight: 650;
color: #c5cdd9;
letter-spacing: 0.02em;
}
.hedge-plan-page-wrap .hp-po-fields {
display: grid;
grid-template-columns: 1fr 1fr;
@@ -3480,11 +3508,53 @@ html[data-theme="light"] .opt-be-dist-down {
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.06);
}
.hedge-plan-page-wrap .hp-po-fields--section {
margin: 0;
}
.hedge-plan-page-wrap .hp-po-field--type {
grid-column: 1 / -1;
}
.hedge-plan-page-wrap .hp-po-type-seg {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.hedge-plan-page-wrap .hp-po-right-stack {
display: flex;
flex-direction: column;
gap: 12px;
min-width: 0;
min-height: 0;
}
.hedge-plan-page-wrap .hp-po-quote-head {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 4px;
}
.hedge-plan-page-wrap .hp-po-ins-money {
display: inline-flex;
flex-wrap: wrap;
gap: 6px;
}
.hedge-plan-page-wrap .hp-po-ins-money[hidden],
.hedge-plan-page-wrap .hp-po-ins-money.hidden {
display: none !important;
}
/* display:grid 会盖掉 [hidden];模式切换必须显式 none */
.hedge-plan-page-wrap .hp-po-fields.hidden,
.hedge-plan-page-wrap .hp-po-fields[hidden] {
.hedge-plan-page-wrap .hp-po-fields[hidden],
.hedge-plan-page-wrap #hp-po-fields-option-primary.hidden,
.hedge-plan-page-wrap #hp-po-fields-option-primary[hidden] {
display: none !important;
}
.hedge-plan-page-wrap .hp-strike-table-wrap--6 {
max-height: 280px;
min-height: 200px;
overflow-y: auto;
margin-top: 4px;
flex: 1 1 auto;
}
.hedge-plan-page-wrap .hp-po-field {
display: flex;
flex-direction: column;
@@ -3731,6 +3801,7 @@ html[data-theme="light"] .opt-be-dist-down {
margin-top: 4px;
flex: 0 0 auto;
}
.hedge-plan-page-wrap .opt-chain-lev,
.hedge-plan-page-wrap .hp-oo-table-wrap .opt-chain-lev {
text-align: center;
white-space: nowrap;
@@ -3747,12 +3818,20 @@ html[data-theme="light"] .opt-be-dist-down {
align-items: stretch;
margin-bottom: 28px;
}
.hedge-plan-page-wrap .options-dual-grid > .card {
.hedge-plan-page-wrap .options-dual-grid > .card,
.hedge-plan-page-wrap .hp-po-right-stack > .card {
height: 100%;
min-height: 0;
display: flex;
flex-direction: column;
}
.hedge-plan-page-wrap .hp-po-right-stack > .hp-po-perp-quote-card {
height: auto;
flex: 0 0 auto;
}
.hedge-plan-page-wrap .hp-po-right-stack > .hp-opt-card {
flex: 1 1 auto;
}
.hedge-plan-page-wrap .hp-action-row {
margin-top: 10px;
gap: 8px;