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:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user