永期改为市价选约:左同期权人工选,右去掉选约条件.
左侧到期/CallPut/全部实虚值/展开全部;右侧仅资金杠杆与出场;下单走已选合约立即开仓而非条件盯盘. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+159
-70
@@ -22,10 +22,11 @@
|
|||||||
tab: pickDefaultTab(),
|
tab: pickDefaultTab(),
|
||||||
mode: showPerp ? "perp_options" : showOo ? "options_options" : "perp_options",
|
mode: showPerp ? "perp_options" : showOo ? "options_options" : "perp_options",
|
||||||
underlying: root.getAttribute("data-default-underly") || "ETH",
|
underlying: root.getAttribute("data-default-underly") || "ETH",
|
||||||
moneyFilter: root.getAttribute("data-option-primary") !== "0" ? "otm" : "itm",
|
moneyFilter: root.getAttribute("data-option-primary") !== "0" ? "all" : "itm",
|
||||||
ooMoneyFilter: "atm_otm", // 期期锁定:平值+虚值
|
ooMoneyFilter: "atm_otm", // 期期锁定:平值+虚值
|
||||||
ooRecommend: null, // atm_straddle | double_otm | null
|
ooRecommend: null, // atm_straddle | double_otm | null
|
||||||
ooStrikeExpandAll: false, // 默认 Call/Put 各 3 档
|
ooStrikeExpandAll: false, // 默认 Call/Put 各 3 档
|
||||||
|
poStrikeExpandAll: false, // 永期市价选约:默认平值+实3+虚3
|
||||||
chain: null,
|
chain: null,
|
||||||
selected: null,
|
selected: null,
|
||||||
legA: null,
|
legA: null,
|
||||||
@@ -68,7 +69,9 @@
|
|||||||
const hint = $("hp-po-pair-hint");
|
const hint = $("hp-po-pair-hint");
|
||||||
if (hint) {
|
if (hint) {
|
||||||
if (isOptionPrimary()) {
|
if (isOptionPrimary()) {
|
||||||
hint.textContent = dir === "short" ? "买 Put + 永续多" : "买 Call + 永续空";
|
hint.textContent =
|
||||||
|
(dir === "short" ? "买 Put + 永续多" : "买 Call + 永续空") +
|
||||||
|
" · 点「选择」填入右侧市价下单;比例默认 2(期权 1 → 永续 0.5)。";
|
||||||
} else {
|
} else {
|
||||||
hint.textContent = dir === "short" ? "永续空 + 保险 Call" : "永续多 + 保险 Put";
|
hint.textContent = dir === "short" ? "永续空 + 保险 Call" : "永续多 + 保险 Put";
|
||||||
}
|
}
|
||||||
@@ -162,9 +165,9 @@
|
|||||||
return fmt(v, 2) + ":1";
|
return fmt(v, 2) + ":1";
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 列表/盯盘候选杠杆门槛(与后端 effective_min_opt_leverage 对齐). */
|
/** 条件单杠杆门槛;市价下单传 0 则不筛. */
|
||||||
function optionPrimaryMinLev() {
|
function optionPrimaryMinLev() {
|
||||||
const minLev = numInput("hp-opt-leverage", opMoneyKind() === "otm" ? 200 : 100);
|
const minLev = numInput("hp-opt-leverage", 0);
|
||||||
if (!(minLev > 0)) return 0;
|
if (!(minLev > 0)) return 0;
|
||||||
if (opMoneyKind() === "otm") return Math.max(minLev, 180);
|
if (opMoneyKind() === "otm") return Math.max(minLev, 180);
|
||||||
return minLev;
|
return minLev;
|
||||||
@@ -180,17 +183,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function matchesMoneyFilter(c) {
|
function matchesMoneyFilter(c) {
|
||||||
const f = state.moneyFilter || "itm";
|
const f = state.moneyFilter || "all";
|
||||||
const m = (c.moneyness || "").toLowerCase();
|
const m = (c.moneyness || "").toLowerCase();
|
||||||
|
// 保险模式不可选虚值
|
||||||
if (!isOptionPrimary() && f === "otm") return false;
|
if (!isOptionPrimary() && f === "otm") return false;
|
||||||
// 列表:间隔+虚实值+杠杆门槛(达标才显示;启动盯盘后监控同样门槛)
|
// 市价下单:仅按 全部/实值/虚值 筛选,不做间隔/杠杆条件门槛
|
||||||
if (isOptionPrimary()) {
|
if (isOptionPrimary()) {
|
||||||
const idx = indexPx();
|
if (f === "all" || f === "any") return true;
|
||||||
const interval = numInput("hp-strike-interval", 15);
|
if (f === "itm") return m === "itm" || m === "atm";
|
||||||
if (idx && interval > 0 && Math.abs(Number(c.strike) - idx) > interval + 1e-9) {
|
if (f === "atm") return m === "atm";
|
||||||
return false;
|
if (f === "otm") return m === "otm";
|
||||||
}
|
return true;
|
||||||
if (!optionPrimaryLevOk(c)) return false;
|
|
||||||
}
|
}
|
||||||
if (f === "itm") return m === "itm" || m === "atm";
|
if (f === "itm") return m === "itm" || m === "atm";
|
||||||
if (f === "atm") return m === "atm";
|
if (f === "atm") return m === "atm";
|
||||||
@@ -198,6 +201,31 @@
|
|||||||
return m === "itm" || m === "atm";
|
return m === "itm" || m === "atm";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const PO_ATM_SIDE = 3;
|
||||||
|
|
||||||
|
function slicePoListByMoneyness(list) {
|
||||||
|
if (state.poStrikeExpandAll || !list.length) return list;
|
||||||
|
const sorted = list.slice().sort(function (a, b) {
|
||||||
|
return Number(a.strike) - Number(b.strike);
|
||||||
|
});
|
||||||
|
const itm = [];
|
||||||
|
const atm = [];
|
||||||
|
const otm = [];
|
||||||
|
sorted.forEach(function (c) {
|
||||||
|
const m = String(c.moneyness || "").toLowerCase();
|
||||||
|
if (m === "atm") atm.push(c);
|
||||||
|
else if (m === "itm") itm.push(c);
|
||||||
|
else if (m === "otm") otm.push(c);
|
||||||
|
});
|
||||||
|
const n = PO_ATM_SIDE;
|
||||||
|
const isPut = optTypeForDirection(getDirection()) === "P";
|
||||||
|
const pickedItm = isPut ? itm.slice(0, n) : itm.slice(-n);
|
||||||
|
const pickedOtm = isPut ? otm.slice(-n) : otm.slice(0, n);
|
||||||
|
return pickedItm.concat(atm, pickedOtm).sort(function (a, b) {
|
||||||
|
return Number(a.strike) - Number(b.strike);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function matchesOoMoneyFilter(c) {
|
function matchesOoMoneyFilter(c) {
|
||||||
// 期期:仅平值/虚值(禁实值)
|
// 期期:仅平值/虚值(禁实值)
|
||||||
if (!c) return false;
|
if (!c) return false;
|
||||||
@@ -280,19 +308,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function opMoneyKind() {
|
function opMoneyKind() {
|
||||||
const f = state.moneyFilter || "itm";
|
const f = state.moneyFilter || "all";
|
||||||
if (f === "otm") return "otm";
|
if (f === "otm") return "otm";
|
||||||
if (f === "atm") return "atm";
|
if (f === "atm") return "atm";
|
||||||
|
if (f === "all" || f === "any") return "all";
|
||||||
return "itm";
|
return "itm";
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyOpDefaultsFromMoney(force) {
|
function applyOpDefaultsFromMoney(force) {
|
||||||
const kind = opMoneyKind();
|
|
||||||
const levEl = $("hp-opt-leverage");
|
|
||||||
const ratioEl = $("hp-opt-perp-ratio");
|
const ratioEl = $("hp-opt-perp-ratio");
|
||||||
if (levEl && (force || !state.opLevTouched)) {
|
|
||||||
levEl.value = kind === "otm" ? "200" : "100";
|
|
||||||
}
|
|
||||||
if (ratioEl && (force || !state.opRatioTouched)) {
|
if (ratioEl && (force || !state.opRatioTouched)) {
|
||||||
// 默认 2:期权名义÷比例=永续名义(例 1 BTC 期权 → 0.5 BTC 永续)
|
// 默认 2:期权名义÷比例=永续名义(例 1 BTC 期权 → 0.5 BTC 永续)
|
||||||
ratioEl.value = "2";
|
ratioEl.value = "2";
|
||||||
@@ -326,14 +350,28 @@
|
|||||||
insMoney.classList.remove("hidden");
|
insMoney.classList.remove("hidden");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!on && state.moneyFilter === "otm") {
|
const mktMoney = $("hp-po-mkt-money");
|
||||||
|
if (mktMoney) {
|
||||||
|
if (on) {
|
||||||
|
mktMoney.removeAttribute("hidden");
|
||||||
|
mktMoney.classList.remove("hidden");
|
||||||
|
} else {
|
||||||
|
mktMoney.setAttribute("hidden", "hidden");
|
||||||
|
mktMoney.classList.add("hidden");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (on && (state.moneyFilter === "atm" || !state.moneyFilter)) {
|
||||||
|
state.moneyFilter = "all";
|
||||||
|
syncMoneyUI();
|
||||||
|
}
|
||||||
|
if (!on && (state.moneyFilter === "otm" || state.moneyFilter === "all")) {
|
||||||
state.moneyFilter = "itm";
|
state.moneyFilter = "itm";
|
||||||
syncMoneyUI();
|
syncMoneyUI();
|
||||||
}
|
}
|
||||||
if (on) applyOpDefaultsFromMoney(false);
|
if (on) applyOpDefaultsFromMoney(false);
|
||||||
const badge = $("hp-po-mode-badge");
|
const badge = $("hp-po-mode-badge");
|
||||||
if (badge) {
|
if (badge) {
|
||||||
badge.textContent = on ? "以期权为主" : "保险模式";
|
badge.textContent = on ? "市价下单" : "保险模式";
|
||||||
badge.classList.toggle("is-insurance", !on);
|
badge.classList.toggle("is-insurance", !on);
|
||||||
}
|
}
|
||||||
const title = $("hp-po-card-title");
|
const title = $("hp-po-card-title");
|
||||||
@@ -343,7 +381,7 @@
|
|||||||
if (dirLong) {
|
if (dirLong) {
|
||||||
dirLong.title = on ? "买 Call → 永续空" : "做多永续";
|
dirLong.title = on ? "买 Call → 永续空" : "做多永续";
|
||||||
if (on) {
|
if (on) {
|
||||||
dirLong.innerHTML = '<span class="hp-oo-check" aria-hidden="true">✓</span>Call';
|
dirLong.innerHTML = '<span class="hp-oo-check" aria-hidden="true">✓</span>看涨 Call';
|
||||||
} else {
|
} else {
|
||||||
dirLong.innerHTML = '<span class="hp-oo-check" aria-hidden="true">✓</span>做多';
|
dirLong.innerHTML = '<span class="hp-oo-check" aria-hidden="true">✓</span>做多';
|
||||||
}
|
}
|
||||||
@@ -351,7 +389,7 @@
|
|||||||
if (dirShort) {
|
if (dirShort) {
|
||||||
dirShort.title = on ? "买 Put → 永续多" : "做空永续";
|
dirShort.title = on ? "买 Put → 永续多" : "做空永续";
|
||||||
if (on) {
|
if (on) {
|
||||||
dirShort.innerHTML = '<span class="hp-oo-check" aria-hidden="true">✓</span>Put';
|
dirShort.innerHTML = '<span class="hp-oo-check" aria-hidden="true">✓</span>看跌 Put';
|
||||||
} else {
|
} else {
|
||||||
dirShort.innerHTML = '<span class="hp-oo-check" aria-hidden="true">✓</span>做空';
|
dirShort.innerHTML = '<span class="hp-oo-check" aria-hidden="true">✓</span>做空';
|
||||||
}
|
}
|
||||||
@@ -472,7 +510,7 @@
|
|||||||
function syncMoneyUI() {
|
function syncMoneyUI() {
|
||||||
const moneySel = $("hp-money-select");
|
const moneySel = $("hp-money-select");
|
||||||
if (moneySel && isOptionPrimary()) {
|
if (moneySel && isOptionPrimary()) {
|
||||||
moneySel.value = state.moneyFilter === "otm" ? "otm" : state.moneyFilter === "atm" ? "atm" : "itm";
|
moneySel.value = state.moneyFilter || "all";
|
||||||
}
|
}
|
||||||
document.querySelectorAll(".hp-money-btn").forEach(function (b) {
|
document.querySelectorAll(".hp-money-btn").forEach(function (b) {
|
||||||
const on = b.getAttribute("data-money") === state.moneyFilter;
|
const on = b.getAttribute("data-money") === state.moneyFilter;
|
||||||
@@ -486,14 +524,16 @@
|
|||||||
b.classList.toggle("is-selected", on);
|
b.classList.toggle("is-selected", on);
|
||||||
b.setAttribute("aria-pressed", on ? "true" : "false");
|
b.setAttribute("aria-pressed", on ? "true" : "false");
|
||||||
});
|
});
|
||||||
|
const expandCb = $("hp-strike-expand-all");
|
||||||
|
if (expandCb) expandCb.checked = !!state.poStrikeExpandAll;
|
||||||
}
|
}
|
||||||
|
|
||||||
function syncPoActionBtn() {
|
function syncPoActionBtn() {
|
||||||
const btn = $("hp-preview-btn");
|
const btn = $("hp-preview-btn");
|
||||||
if (!btn) return;
|
if (!btn) return;
|
||||||
if (isOptionPrimary()) {
|
if (isOptionPrimary()) {
|
||||||
btn.textContent = "策略启动";
|
btn.textContent = "市价下单";
|
||||||
btn.title = "按参数启动盯盘;杠杆/间隔达标后自动开仓(非现场开)";
|
btn.title = "按左侧已选合约立即开仓(买期权+对冲永续);非条件盯盘";
|
||||||
} else {
|
} else {
|
||||||
btn.textContent = "计算";
|
btn.textContent = "计算";
|
||||||
btn.title = "情景测算后再启动";
|
btn.title = "情景测算后再启动";
|
||||||
@@ -597,7 +637,7 @@
|
|||||||
updatePerpPnlHint();
|
updatePerpPnlHint();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 以期权为主:按类型/间隔自动匹配最近合约. */
|
/** 市价选约:不自动代选;仅在已选失效(换 Call/Put 或筛掉)时清空. */
|
||||||
function autoMatchPoOption(force) {
|
function autoMatchPoOption(force) {
|
||||||
if (!isOptionPrimary()) return;
|
if (!isOptionPrimary()) return;
|
||||||
const want = optTypeForDirection(getDirection());
|
const want = optTypeForDirection(getDirection());
|
||||||
@@ -608,9 +648,7 @@
|
|||||||
String(sel.opt_type || "").toUpperCase() === want &&
|
String(sel.opt_type || "").toUpperCase() === want &&
|
||||||
matchesMoneyFilter(sel);
|
matchesMoneyFilter(sel);
|
||||||
if (stillOk) return;
|
if (stillOk) return;
|
||||||
const c = pickClosestItmAtm(currentContracts("hp-exp-select"), want);
|
clearPoSelection();
|
||||||
if (c) pickContract(c);
|
|
||||||
else clearPoSelection();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function findAtmStrikeFromRows(rows, idx) {
|
function findAtmStrikeFromRows(rows, idx) {
|
||||||
@@ -1199,9 +1237,8 @@
|
|||||||
function fillExpSelect(sel, chain) {
|
function fillExpSelect(sel, chain) {
|
||||||
if (!sel) return;
|
if (!sel) return;
|
||||||
const prev = sel.value;
|
const prev = sel.value;
|
||||||
const isPoSel = sel.id === "hp-exp-select";
|
// 市价选约:到期列表不过滤(与期权页一致,默认最近一期);条件单 hidden 字段 min-hours=0
|
||||||
// 永期以期权为主:到期下拉按「最低剩余小时」过滤;期期下拉不过滤,保证明天到期可见
|
const minH = sel.id === "hp-exp-select" ? numInput("hp-min-hours", 0) : 0;
|
||||||
const minH = isPoSel && isOptionPrimary() ? numInput("hp-min-hours", 36) : 0;
|
|
||||||
sel.innerHTML = '<option value="">选择到期日</option>';
|
sel.innerHTML = '<option value="">选择到期日</option>';
|
||||||
let firstOk = null;
|
let firstOk = null;
|
||||||
let skippedNear = 0;
|
let skippedNear = 0;
|
||||||
@@ -1222,10 +1259,10 @@
|
|||||||
if (!sel.value && firstOk) {
|
if (!sel.value && firstOk) {
|
||||||
sel.value = String(firstOk.exp_time);
|
sel.value = String(firstOk.exp_time);
|
||||||
}
|
}
|
||||||
if (isPoSel && skippedNear > 0 && minH > 0) {
|
if (skippedNear > 0 && minH > 0) {
|
||||||
const tip = document.createElement("option");
|
const tip = document.createElement("option");
|
||||||
tip.disabled = true;
|
tip.disabled = true;
|
||||||
tip.textContent = "(已隐藏 " + skippedNear + " 个不足 " + minH + "h 的到期 · 可改左侧最低剩余小时)";
|
tip.textContent = "(已隐藏 " + skippedNear + " 个不足 " + minH + "h 的到期)";
|
||||||
sel.appendChild(tip);
|
sel.appendChild(tip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1270,7 +1307,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isOptionPrimary() && !matchesMoneyFilter(c)) {
|
if (isOptionPrimary() && !matchesMoneyFilter(c)) {
|
||||||
alert("不符合当前间隔/虚实值/杠杆门槛");
|
alert("不符合当前筛选(全部/实值/虚值),请切换筛选后再选");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
state.selected = c;
|
state.selected = c;
|
||||||
@@ -1300,38 +1337,38 @@
|
|||||||
const want = optTypeForDirection(getDirection());
|
const want = optTypeForDirection(getDirection());
|
||||||
const exp = currentExp("hp-exp-select");
|
const exp = currentExp("hp-exp-select");
|
||||||
const prevInst = state.selected && state.selected.inst_id;
|
const prevInst = state.selected && state.selected.inst_id;
|
||||||
|
const cols = isOptionPrimary() ? 7 : 6;
|
||||||
tbody.innerHTML = "";
|
tbody.innerHTML = "";
|
||||||
if (!exp) {
|
if (!exp) {
|
||||||
tbody.innerHTML = '<tr><td colspan="6" class="muted">请选择到期日</td></tr>';
|
tbody.innerHTML = '<tr><td colspan="' + cols + '" class="muted">请选择到期日</td></tr>';
|
||||||
if (isOptionPrimary()) clearPoSelection();
|
if (isOptionPrimary()) clearPoSelection();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const sameType = (exp.contracts || []).filter(function (c) {
|
const sameType = (exp.contracts || []).filter(function (c) {
|
||||||
return String(c.opt_type || "").toUpperCase() === want;
|
return String(c.opt_type || "").toUpperCase() === want;
|
||||||
});
|
});
|
||||||
const list = sameType.filter(matchesMoneyFilter);
|
let list = sameType.filter(matchesMoneyFilter);
|
||||||
|
if (isOptionPrimary() && state.moneyFilter === "all") {
|
||||||
|
list = slicePoListByMoneyness(list);
|
||||||
|
}
|
||||||
if (!list.length) {
|
if (!list.length) {
|
||||||
const idx = indexPx();
|
const idx = indexPx();
|
||||||
const interval = isOptionPrimary() ? numInput("hp-strike-interval", 15) : null;
|
|
||||||
let hint = "无匹配合约";
|
let hint = "无匹配合约";
|
||||||
if (isOptionPrimary() && sameType.length) {
|
if (sameType.length) {
|
||||||
hint =
|
hint =
|
||||||
"无匹配" +
|
"当前筛选下无 " +
|
||||||
(want === "C" ? "Call" : "Put") +
|
(want === "C" ? "Call" : "Put") +
|
||||||
"(已滤 " +
|
"(链上 " +
|
||||||
sameType.length +
|
sameType.length +
|
||||||
" 档)·检查间隔" +
|
" 档)·可改全部/实值/虚值或展开全部";
|
||||||
(interval != null ? "≤" + interval : "") +
|
} else {
|
||||||
"点/虚实值/杠杆≥" +
|
|
||||||
optionPrimaryMinLev();
|
|
||||||
} else if (!sameType.length) {
|
|
||||||
hint =
|
hint =
|
||||||
"该到期无 " +
|
"该到期无 " +
|
||||||
(want === "C" ? "Call" : "Put") +
|
(want === "C" ? "Call" : "Put") +
|
||||||
(idx ? " · 指数 " + fmt(idx, 2) : "") +
|
(idx ? " · 指数 " + fmt(idx, 2) : "") +
|
||||||
" · 可换到期或点刷新链";
|
" · 可换到期或点刷新链";
|
||||||
}
|
}
|
||||||
tbody.innerHTML = '<tr><td colspan="6" class="muted">' + hint + "</td></tr>";
|
tbody.innerHTML = '<tr><td colspan="' + cols + '" class="muted">' + hint + "</td></tr>";
|
||||||
if (isOptionPrimary()) clearPoSelection();
|
if (isOptionPrimary()) clearPoSelection();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1340,6 +1377,26 @@
|
|||||||
tr.className = "opt-strike-row" + (c.moneyness ? " opt-row-" + c.moneyness : "");
|
tr.className = "opt-strike-row" + (c.moneyness ? " opt-row-" + c.moneyness : "");
|
||||||
if (prevInst && c.inst_id === prevInst) tr.classList.add("opt-row-selected");
|
if (prevInst && c.inst_id === prevInst) tr.classList.add("opt-row-selected");
|
||||||
tr.setAttribute("data-inst", c.inst_id);
|
tr.setAttribute("data-inst", c.inst_id);
|
||||||
|
if (isOptionPrimary()) {
|
||||||
|
tr.innerHTML =
|
||||||
|
"<td>" +
|
||||||
|
c.strike +
|
||||||
|
"</td><td>" +
|
||||||
|
moneynessBadge(c) +
|
||||||
|
"</td><td><code class=\"opt-inst-code\">" +
|
||||||
|
(c.inst_id || "") +
|
||||||
|
'</code></td><td class="opt-px-sz">' +
|
||||||
|
fmtPxSz(c.ask, c.ask_sz, c.ask_estimated, c.tick_sz) +
|
||||||
|
'</td><td class="opt-chain-lev" title="指数÷卖一">' +
|
||||||
|
calcIndexAskLeverage(c.ask) +
|
||||||
|
'</td><td class="opt-px-sz">' +
|
||||||
|
fmtPxSz(c.bid, c.bid_sz, false, c.tick_sz) +
|
||||||
|
'</td><td><button type="button" class="btn-secondary hp-pick' +
|
||||||
|
(prevInst && c.inst_id === prevInst ? " active" : "") +
|
||||||
|
'" data-inst="' +
|
||||||
|
c.inst_id +
|
||||||
|
'">选择</button></td>';
|
||||||
|
} else {
|
||||||
tr.innerHTML =
|
tr.innerHTML =
|
||||||
"<td>" +
|
"<td>" +
|
||||||
c.strike +
|
c.strike +
|
||||||
@@ -1356,8 +1413,17 @@
|
|||||||
'" data-inst="' +
|
'" data-inst="' +
|
||||||
c.inst_id +
|
c.inst_id +
|
||||||
'">选择</button></td>';
|
'">选择</button></td>';
|
||||||
|
}
|
||||||
tbody.appendChild(tr);
|
tbody.appendChild(tr);
|
||||||
});
|
});
|
||||||
|
if (isOptionPrimary() && !state.poStrikeExpandAll && state.moneyFilter === "all") {
|
||||||
|
const hintTr = document.createElement("tr");
|
||||||
|
hintTr.innerHTML =
|
||||||
|
'<td colspan="' +
|
||||||
|
cols +
|
||||||
|
'" class="muted opt-strike-hint">默认显示平值 + 实值3档 + 虚值3档 · 勾选「展开全部」查看该到期更多行权价</td>';
|
||||||
|
tbody.appendChild(hintTr);
|
||||||
|
}
|
||||||
tbody.querySelectorAll(".hp-pick").forEach(function (btn) {
|
tbody.querySelectorAll(".hp-pick").forEach(function (btn) {
|
||||||
btn.addEventListener("click", function () {
|
btn.addEventListener("click", function () {
|
||||||
const inst = btn.getAttribute("data-inst");
|
const inst = btn.getAttribute("data-inst");
|
||||||
@@ -1707,14 +1773,20 @@
|
|||||||
if (!state.selected) throw new Error("请选用期权腿");
|
if (!state.selected) throw new Error("请选用期权腿");
|
||||||
if (isOptionPrimary()) {
|
if (isOptionPrimary()) {
|
||||||
const sized = currentOpSizing();
|
const sized = currentOpSizing();
|
||||||
if (!sized) throw new Error("请填写权利金或张数并确认卖一有效");
|
if (!sized) throw new Error("请选择期权并填写张数(或权利金预算)");
|
||||||
const optPts = numInput("hp-opt-target-pts", NaN);
|
const optPts = numInput("hp-opt-target-pts", NaN);
|
||||||
const perpPts = numInput("hp-perp-target-pts", NaN);
|
const perpPts = numInput("hp-perp-target-pts", NaN);
|
||||||
if (!(optPts > 0) || !(perpPts > 0)) throw new Error("请填写期权/永续目标位点数(须大于0)");
|
if (!(optPts > 0) || !(perpPts > 0)) throw new Error("请填写期权/永续目标位点数(须大于0)");
|
||||||
const exp = currentExp("hp-exp-select");
|
const exp = currentExp("hp-exp-select");
|
||||||
|
const premEst =
|
||||||
|
Number(state.selected.ask || 0) *
|
||||||
|
sized.sheets *
|
||||||
|
Number(state.selected.ct_mult || 0.01);
|
||||||
|
const premBudget = numInput("hp-premium-budget", 0);
|
||||||
body = {
|
body = {
|
||||||
plan_type: "perp_options",
|
plan_type: "perp_options",
|
||||||
option_primary: true,
|
option_primary: true,
|
||||||
|
market_order: 1,
|
||||||
direction: getDirection(),
|
direction: getDirection(),
|
||||||
entry: indexPx() || Number((state.market && state.market.mark) || 0),
|
entry: indexPx() || Number((state.market && state.market.mark) || 0),
|
||||||
contracts: sized.contracts,
|
contracts: sized.contracts,
|
||||||
@@ -1725,15 +1797,15 @@
|
|||||||
ct_mult: state.selected.ct_mult || 0.01,
|
ct_mult: state.selected.ct_mult || 0.01,
|
||||||
ask: state.selected.ask,
|
ask: state.selected.ask,
|
||||||
index_px: indexPx() || 0,
|
index_px: indexPx() || 0,
|
||||||
premium_budget: numInput("hp-premium-budget", 0),
|
premium_budget: premBudget > 0 ? premBudget : Math.max(premEst / 0.95, premEst),
|
||||||
option_perp_ratio: numInput("hp-opt-perp-ratio", 2),
|
option_perp_ratio: numInput("hp-opt-perp-ratio", 2),
|
||||||
option_target_points: optPts,
|
option_target_points: optPts,
|
||||||
perp_target_points: perpPts,
|
perp_target_points: perpPts,
|
||||||
strike_interval: numInput("hp-strike-interval", 15),
|
strike_interval: 0,
|
||||||
min_option_hours: numInput("hp-min-hours", 36),
|
min_option_hours: 0,
|
||||||
option_leverage: numInput("hp-opt-leverage", 100),
|
option_leverage: 0,
|
||||||
leverage: numInput("hp-perp-leverage", defaultPerpLeverage(state.underlying)),
|
leverage: numInput("hp-perp-leverage", defaultPerpLeverage(state.underlying)),
|
||||||
moneyness: opMoneyKind(),
|
moneyness: (state.selected.moneyness || opMoneyKind() || "all").toLowerCase(),
|
||||||
hours_to_expiry: exp ? hoursFromExpMs(exp.exp_time) : null,
|
hours_to_expiry: exp ? hoursFromExpMs(exp.exp_time) : null,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
@@ -1914,25 +1986,28 @@
|
|||||||
});
|
});
|
||||||
document.querySelectorAll(".hp-money-btn").forEach(function (b) {
|
document.querySelectorAll(".hp-money-btn").forEach(function (b) {
|
||||||
b.addEventListener("click", function () {
|
b.addEventListener("click", function () {
|
||||||
const m = b.getAttribute("data-money") || "itm";
|
const m = b.getAttribute("data-money") || "all";
|
||||||
if (m === "otm" && !isOptionPrimary()) {
|
if (m === "otm" && !isOptionPrimary()) {
|
||||||
alert("永期保险腿仅允许实值或平值;请在 env 将 HEDGE_PLAN_OPTION_PRIMARY=true");
|
alert("永期保险腿仅允许实值或平值;请在 env 将 HEDGE_PLAN_OPTION_PRIMARY=true");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
state.moneyFilter = m === "otm" ? "otm" : m === "atm" ? "atm" : "itm";
|
if (isOptionPrimary()) {
|
||||||
|
state.moneyFilter = m === "otm" ? "otm" : m === "itm" ? "itm" : m === "atm" ? "atm" : "all";
|
||||||
|
} else {
|
||||||
|
state.moneyFilter = m === "atm" ? "atm" : "itm";
|
||||||
|
}
|
||||||
if (isOptionPrimary()) applyOpDefaultsFromMoney(false);
|
if (isOptionPrimary()) applyOpDefaultsFromMoney(false);
|
||||||
syncMoneyUI();
|
syncMoneyUI();
|
||||||
renderListStrikes();
|
renderListStrikes();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
if ($("hp-money-select")) {
|
if ($("hp-strike-expand-all")) {
|
||||||
$("hp-money-select").addEventListener("change", function () {
|
$("hp-strike-expand-all").addEventListener("change", function () {
|
||||||
const m = $("hp-money-select").value || "otm";
|
state.poStrikeExpandAll = !!$("hp-strike-expand-all").checked;
|
||||||
state.moneyFilter = m === "otm" ? "otm" : m === "atm" ? "atm" : "itm";
|
if (state.poStrikeExpandAll && state.moneyFilter !== "all" && isOptionPrimary()) {
|
||||||
state.opLevTouched = false;
|
state.moneyFilter = "all";
|
||||||
state.opRatioTouched = false;
|
|
||||||
applyOpDefaultsFromMoney(true);
|
|
||||||
syncMoneyUI();
|
syncMoneyUI();
|
||||||
|
}
|
||||||
renderListStrikes();
|
renderListStrikes();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -2154,7 +2229,8 @@
|
|||||||
if ($("hp-preview-btn"))
|
if ($("hp-preview-btn"))
|
||||||
$("hp-preview-btn").addEventListener("click", function () {
|
$("hp-preview-btn").addEventListener("click", function () {
|
||||||
state.mode = "perp_options";
|
state.mode = "perp_options";
|
||||||
if (isOptionPrimary()) void startOptionPrimaryWatch();
|
// 以期权为主 = 市价下单(左侧人工选约后立即开);条件盯盘不再从此入口
|
||||||
|
if (isOptionPrimary()) void startPlan("perp_options", false);
|
||||||
else void runPreview();
|
else void runPreview();
|
||||||
});
|
});
|
||||||
if ($("hp-preview-btn-oo"))
|
if ($("hp-preview-btn-oo"))
|
||||||
@@ -2744,15 +2820,21 @@
|
|||||||
if (!state.selected) throw new Error("请选用期权腿");
|
if (!state.selected) throw new Error("请选用期权腿");
|
||||||
if (isOptionPrimary()) {
|
if (isOptionPrimary()) {
|
||||||
const sized = currentOpSizing();
|
const sized = currentOpSizing();
|
||||||
if (!sized) throw new Error("请填写权利金或张数并确认卖一有效");
|
if (!sized) throw new Error("请选择期权并填写张数(或权利金预算)");
|
||||||
const optPts = numInput("hp-opt-target-pts", NaN);
|
const optPts = numInput("hp-opt-target-pts", NaN);
|
||||||
const perpPts = numInput("hp-perp-target-pts", NaN);
|
const perpPts = numInput("hp-perp-target-pts", NaN);
|
||||||
if (!(optPts > 0) || !(perpPts > 0)) throw new Error("请填写期权/永续目标位点数(须大于0)");
|
if (!(optPts > 0) || !(perpPts > 0)) throw new Error("请填写期权/永续目标位点数(须大于0)");
|
||||||
const exp = currentExp("hp-exp-select");
|
const exp = currentExp("hp-exp-select");
|
||||||
const entry = indexPx() || Number((state.market && state.market.mark) || 0);
|
const entry = indexPx() || Number((state.market && state.market.mark) || 0);
|
||||||
|
const premEst =
|
||||||
|
Number(state.selected.ask || 0) *
|
||||||
|
sized.sheets *
|
||||||
|
Number(state.selected.ct_mult || 0.01);
|
||||||
|
const premBudget = numInput("hp-premium-budget", 0);
|
||||||
body = {
|
body = {
|
||||||
plan_type: "perp_options",
|
plan_type: "perp_options",
|
||||||
option_primary: true,
|
option_primary: true,
|
||||||
|
market_order: 1,
|
||||||
underlying: state.underlying,
|
underlying: state.underlying,
|
||||||
direction: getDirection(),
|
direction: getDirection(),
|
||||||
entry: entry,
|
entry: entry,
|
||||||
@@ -2767,14 +2849,14 @@
|
|||||||
index_px: entry,
|
index_px: entry,
|
||||||
exchange_symbol: (state.market && state.market.exchange_symbol) || "",
|
exchange_symbol: (state.market && state.market.exchange_symbol) || "",
|
||||||
leverage: numInput("hp-perp-leverage", defaultPerpLeverage(state.underlying)),
|
leverage: numInput("hp-perp-leverage", defaultPerpLeverage(state.underlying)),
|
||||||
option_leverage: numInput("hp-opt-leverage", 100),
|
option_leverage: 0,
|
||||||
premium_budget: numInput("hp-premium-budget", 0),
|
premium_budget: premBudget > 0 ? premBudget : Math.max(premEst / 0.95, premEst),
|
||||||
option_perp_ratio: numInput("hp-opt-perp-ratio", 2),
|
option_perp_ratio: numInput("hp-opt-perp-ratio", 2),
|
||||||
option_target_points: optPts,
|
option_target_points: optPts,
|
||||||
perp_target_points: perpPts,
|
perp_target_points: perpPts,
|
||||||
strike_interval: numInput("hp-strike-interval", 15),
|
strike_interval: 0,
|
||||||
min_option_hours: numInput("hp-min-hours", 36),
|
min_option_hours: 0,
|
||||||
moneyness: opMoneyKind(),
|
moneyness: (state.selected.moneyness || opMoneyKind() || "all").toLowerCase(),
|
||||||
hours_to_expiry: exp ? hoursFromExpMs(exp.exp_time) : null,
|
hours_to_expiry: exp ? hoursFromExpMs(exp.exp_time) : null,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
@@ -2807,7 +2889,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!fromPreviewModal) {
|
if (!fromPreviewModal) {
|
||||||
if (!window.confirm("确认启动对冲计划并真实下单?\n(将按期权账户/合约账户分别下单)")) return;
|
const confMsg = isOptionPrimary()
|
||||||
|
? "确认市价下单?\n期权 " +
|
||||||
|
(state.selected && state.selected.inst_id) +
|
||||||
|
" × " +
|
||||||
|
(body.sheets || "") +
|
||||||
|
" 张 + 永续对冲\n(期权腿币本位 / 永续腿合约账户)"
|
||||||
|
: "确认启动对冲计划并真实下单?\n(将按期权账户/合约账户分别下单)";
|
||||||
|
if (!window.confirm(confMsg)) return;
|
||||||
}
|
}
|
||||||
if (startBtn) startBtn.disabled = true;
|
if (startBtn) startBtn.disabled = true;
|
||||||
const d = await apiJson("/api/hedge-plan/start", {
|
const d = await apiJson("/api/hedge-plan/start", {
|
||||||
|
|||||||
@@ -3931,16 +3931,38 @@ html[data-theme="light"] .opt-be-dist-down {
|
|||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
min-height: 220px;
|
min-height: 220px;
|
||||||
}
|
}
|
||||||
.hedge-plan-page-wrap .hp-po-pair-hint {
|
.hedge-plan-page-wrap .hp-po-pair-hint,
|
||||||
margin-left: 8px;
|
|
||||||
font-size: 0.82rem;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
.hedge-plan-page-wrap .hp-po-chain-hint {
|
.hedge-plan-page-wrap .hp-po-chain-hint {
|
||||||
margin: 4px 0 8px;
|
margin: 4px 0 8px;
|
||||||
font-size: 0.78rem;
|
font-size: 0.78rem;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
}
|
}
|
||||||
|
.hedge-plan-page-wrap .hp-po-opt-toolbar {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 6px 8px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.hedge-plan-page-wrap .hp-po-mkt-money {
|
||||||
|
display: inline-flex;
|
||||||
|
gap: 4px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.hedge-plan-page-wrap .hp-po-mkt-money[hidden],
|
||||||
|
.hedge-plan-page-wrap .hp-po-mkt-money.hidden {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
.hedge-plan-page-wrap .hp-po-expand-label {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.hedge-plan-page-wrap .opt-inst-code {
|
||||||
|
font-size: 0.72rem;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
.hedge-plan-page-wrap .hp-po-order-card .hp-po-perp-quote-card {
|
.hedge-plan-page-wrap .hp-po-order-card .hp-po-perp-quote-card {
|
||||||
height: auto;
|
height: auto;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
|
|||||||
@@ -266,7 +266,10 @@ def validate_option_primary_moneyness(
|
|||||||
return "行权价或指数无效"
|
return "行权价或指数无效"
|
||||||
m_want = (moneyness or "atm").strip().lower()
|
m_want = (moneyness or "atm").strip().lower()
|
||||||
m_got = classify_moneyness(opt_type=o, strike=k, index_px=s)
|
m_got = classify_moneyness(opt_type=o, strike=k, index_px=s)
|
||||||
if m_want == "itm":
|
if m_want in ("all", "any", ""):
|
||||||
|
# 市价人工选约:不限制虚实值
|
||||||
|
pass
|
||||||
|
elif m_want == "itm":
|
||||||
if not is_itm_or_atm(opt_type=o, strike=k, index_px=s):
|
if not is_itm_or_atm(opt_type=o, strike=k, index_px=s):
|
||||||
return "所选须为实值或平值"
|
return "所选须为实值或平值"
|
||||||
elif m_want == "atm":
|
elif m_want == "atm":
|
||||||
@@ -280,17 +283,27 @@ def validate_option_primary_moneyness(
|
|||||||
else:
|
else:
|
||||||
return "期权类型(实/平/虚)无效"
|
return "期权类型(实/平/虚)无效"
|
||||||
|
|
||||||
interval = float(_sf(strike_interval) or DEFAULT_STRIKE_INTERVAL)
|
# 间隔/最短到期/杠杆门槛:仅条件单使用;传 0 表示市价下单跳过
|
||||||
|
interval_raw = _sf(strike_interval)
|
||||||
|
if interval_raw is None:
|
||||||
|
interval_raw = DEFAULT_STRIKE_INTERVAL
|
||||||
|
interval = float(interval_raw)
|
||||||
if interval > 0 and abs(k - s) > interval + 1e-9:
|
if interval > 0 and abs(k - s) > interval + 1e-9:
|
||||||
return f"行权价偏离指数 {abs(k - s):.1f} > 间隔 {interval:.0f}"
|
return f"行权价偏离指数 {abs(k - s):.1f} > 间隔 {interval:.0f}"
|
||||||
|
|
||||||
min_h = float(_sf(min_hours) or DEFAULT_MIN_HOURS)
|
min_h_raw = _sf(min_hours)
|
||||||
|
if min_h_raw is None:
|
||||||
|
min_h_raw = DEFAULT_MIN_HOURS
|
||||||
|
min_h = float(min_h_raw)
|
||||||
h = _sf(hours_to_expiry)
|
h = _sf(hours_to_expiry)
|
||||||
if min_h > 0 and h is not None and h < min_h:
|
if min_h > 0 and h is not None and h < min_h:
|
||||||
return f"剩余到期约 {h:.1f}h,低于最短 {min_h:.0f}h"
|
return f"剩余到期约 {h:.1f}h,低于最短 {min_h:.0f}h"
|
||||||
|
|
||||||
a = _sf(ask)
|
a = _sf(ask)
|
||||||
min_lev = effective_min_opt_leverage(m_want if m_want != "atm" else m_got or "atm", min_opt_leverage)
|
lev_cfg = _sf(min_opt_leverage)
|
||||||
|
if lev_cfg is not None and lev_cfg <= 0:
|
||||||
|
return None
|
||||||
|
min_lev = effective_min_opt_leverage(m_want if m_want not in ("atm", "all", "any", "") else (m_got or "atm"), min_opt_leverage)
|
||||||
if min_lev > 0 and a is not None and a > 0:
|
if min_lev > 0 and a is not None and a > 0:
|
||||||
lev = s / a
|
lev = s / a
|
||||||
if lev < min_lev:
|
if lev < min_lev:
|
||||||
@@ -336,24 +349,27 @@ def validate_option_primary_watch(body: dict[str, Any]) -> Optional[str]:
|
|||||||
|
|
||||||
|
|
||||||
def validate_option_primary_start(body: dict[str, Any]) -> Optional[str]:
|
def validate_option_primary_start(body: dict[str, Any]) -> Optional[str]:
|
||||||
|
market = body.get("market_order") in (True, 1, "1", "true", "yes", "on")
|
||||||
need = (
|
need = (
|
||||||
"direction",
|
"direction",
|
||||||
"contracts",
|
"contracts",
|
||||||
"opt_inst_id",
|
"opt_inst_id",
|
||||||
"sheets",
|
"sheets",
|
||||||
"exchange_symbol",
|
"exchange_symbol",
|
||||||
"premium_budget",
|
|
||||||
"option_target_points",
|
"option_target_points",
|
||||||
"perp_target_points",
|
"perp_target_points",
|
||||||
"option_perp_ratio",
|
"option_perp_ratio",
|
||||||
)
|
)
|
||||||
|
if not market:
|
||||||
|
need = need + ("premium_budget",)
|
||||||
for k in need:
|
for k in need:
|
||||||
if body.get(k) in (None, ""):
|
if body.get(k) in (None, ""):
|
||||||
return f"缺少字段: {k}"
|
return f"缺少字段: {k}"
|
||||||
try:
|
try:
|
||||||
if float(body["contracts"]) <= 0 or float(body["sheets"]) <= 0:
|
if float(body["contracts"]) <= 0 or float(body["sheets"]) <= 0:
|
||||||
return "张数必须大于 0"
|
return "张数必须大于 0"
|
||||||
if float(body["premium_budget"]) <= 0:
|
prem = _sf(body.get("premium_budget"))
|
||||||
|
if not market and (prem is None or prem <= 0):
|
||||||
return "权利金须大于 0"
|
return "权利金须大于 0"
|
||||||
if float(body["option_target_points"]) <= 0 or float(body["perp_target_points"]) <= 0:
|
if float(body["option_target_points"]) <= 0 or float(body["perp_target_points"]) <= 0:
|
||||||
return "目标位点数须大于 0"
|
return "目标位点数须大于 0"
|
||||||
@@ -374,6 +390,9 @@ def validate_option_primary_start(body: dict[str, Any]) -> Optional[str]:
|
|||||||
want = opt_type_for_view(direction)
|
want = opt_type_for_view(direction)
|
||||||
if opt_type != want:
|
if opt_type != want:
|
||||||
return f"以期权为主时做{'多' if direction == 'long' else '空'}须用 {'Call' if want == 'C' else 'Put'}"
|
return f"以期权为主时做{'多' if direction == 'long' else '空'}须用 {'Call' if want == 'C' else 'Put'}"
|
||||||
|
# 市价下单:左侧人工选约,跳过条件单(间隔/最短到期/杠杆门槛)
|
||||||
|
if market:
|
||||||
|
return None
|
||||||
moneyness = str(body.get("moneyness") or body.get("option_moneyness") or "otm").strip().lower()
|
moneyness = str(body.get("moneyness") or body.get("option_moneyness") or "otm").strip().lower()
|
||||||
from lib.hedge_plan.hedge_plan_moneyness_lib import parse_strike_from_inst
|
from lib.hedge_plan.hedge_plan_moneyness_lib import parse_strike_from_inst
|
||||||
|
|
||||||
|
|||||||
@@ -45,47 +45,55 @@
|
|||||||
|
|
||||||
<div id="hp-tab-perp_options" class="hp-tab-panel" role="tabpanel">
|
<div id="hp-tab-perp_options" class="hp-tab-panel" role="tabpanel">
|
||||||
<div class="options-dual-grid" id="hp-po-layout">
|
<div class="options-dual-grid" id="hp-po-layout">
|
||||||
<!-- 左:期权选择 -->
|
<!-- 左:市价选约(与期权下单同风格,人工选) -->
|
||||||
<div class="card hp-po-left-card hp-po-select-card">
|
<div class="card hp-po-left-card hp-po-select-card">
|
||||||
<div class="hp-po-left-stack">
|
<div class="hp-po-left-stack">
|
||||||
<div class="hp-po-inner-card hp-opt-card">
|
<div class="hp-po-inner-card hp-opt-card">
|
||||||
<h2>期权选择 · <span id="hp-opt-type-label">Call</span> <span class="muted hp-acct-tag" id="hp-opt-acct-tag">币本位</span></h2>
|
<h2>市价选约 · <span id="hp-opt-type-label">Call</span> <span class="muted hp-acct-tag" id="hp-opt-acct-tag">币本位</span></h2>
|
||||||
<div class="form-row hp-uly-row">
|
<div class="form-row hp-opt-toolbar hp-po-opt-toolbar options-chain-toolbar">
|
||||||
<button type="button" class="btn-secondary hp-uly-btn active" data-uly="ETH">ETH</button>
|
<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>
|
<button type="button" class="btn-secondary hp-uly-btn" data-uly="BTC">BTC</button>
|
||||||
</div>
|
|
||||||
<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="买 Call → 永续空"><span class="hp-oo-check" aria-hidden="true">✓</span>Call</button>
|
|
||||||
<button type="button" class="btn-secondary hp-po-dir" data-dir="short" title="买 Put → 永续多"><span class="hp-oo-check" aria-hidden="true">✓</span>Put</button>
|
|
||||||
</div>
|
|
||||||
<span class="muted hp-po-pair-hint" id="hp-po-pair-hint">买 Call + 永续空</span>
|
|
||||||
</div>
|
|
||||||
<div class="form-row hp-opt-toolbar hp-po-opt-toolbar">
|
|
||||||
<select id="hp-exp-select"><option value="">选择到期日</option></select>
|
<select id="hp-exp-select"><option value="">选择到期日</option></select>
|
||||||
|
<span 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="买 Call → 永续空"><span class="hp-oo-check" aria-hidden="true">✓</span>看涨 Call</button>
|
||||||
|
<button type="button" class="btn-secondary hp-po-dir" data-dir="short" title="买 Put → 永续多"><span class="hp-oo-check" aria-hidden="true">✓</span>看跌 Put</button>
|
||||||
|
</span>
|
||||||
<span class="hp-po-ins-money" id="hp-po-ins-money" hidden>
|
<span class="hp-po-ins-money" id="hp-po-ins-money" hidden>
|
||||||
<button type="button" class="btn-secondary hp-money-btn active" data-money="itm" title="实值+平值">实值/平值</button>
|
<button type="button" class="btn-secondary hp-money-btn active" data-money="itm" title="实值+平值">实值/平值</button>
|
||||||
<button type="button" class="btn-secondary hp-money-btn" data-money="atm" title="仅平值">仅平值</button>
|
<button type="button" class="btn-secondary hp-money-btn" data-money="atm" title="仅平值">仅平值</button>
|
||||||
</span>
|
</span>
|
||||||
<button type="button" class="btn-secondary" id="hp-recommend-opt" title="按当前类型自动匹配最近合约">自动匹配</button>
|
<span class="hp-po-mkt-money" id="hp-po-mkt-money">
|
||||||
|
<button type="button" class="btn-secondary hp-money-btn active" data-money="all" title="全部行权价(默认窗口)">全部</button>
|
||||||
|
<button type="button" class="btn-secondary hp-money-btn" data-money="itm" title="实值(含平值)">实值</button>
|
||||||
|
<button type="button" class="btn-secondary hp-money-btn" data-money="otm" title="虚值">虚值</button>
|
||||||
|
</span>
|
||||||
|
<label class="opt-strike-expand-label hp-po-expand-label" title="展开该到期全部行权价">
|
||||||
|
<input type="checkbox" id="hp-strike-expand-all"> 展开全部
|
||||||
|
</label>
|
||||||
<button type="button" class="btn-secondary" id="hp-load-chain">刷新链</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>
|
<span id="hp-index-line" class="hp-po-index" aria-live="polite">指数 —</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="muted hp-po-chain-hint">点「选择」填入右侧下单;比例默认 2(期权名义:永续名义),如期权 1 BTC → 永续约 0.5 BTC。</p>
|
<p class="muted hp-po-chain-hint" id="hp-po-pair-hint">买 Call + 永续空 · 点「选择」填入右侧市价下单;比例默认 2(期权 1 → 永续 0.5)。</p>
|
||||||
|
{# 条件单残留字段:隐藏,市价下单不用 #}
|
||||||
|
<input type="hidden" id="hp-min-hours" value="0" />
|
||||||
|
<input type="hidden" id="hp-strike-interval" value="0" />
|
||||||
|
<input type="hidden" id="hp-opt-leverage" value="0" />
|
||||||
|
<input type="hidden" id="hp-money-select" value="all" />
|
||||||
<div class="options-strike-table-wrap hp-strike-table-wrap--6">
|
<div class="options-strike-table-wrap hp-strike-table-wrap--6">
|
||||||
<table class="options-strike-table" id="hp-strike-table">
|
<table class="options-strike-table" id="hp-strike-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>行权价</th>
|
<th>行权价</th>
|
||||||
<th>实虚值</th>
|
<th>类型</th>
|
||||||
<th title="指数÷卖一">杠杆</th>
|
<th>合约</th>
|
||||||
<th>卖一/张</th>
|
<th>卖一/张</th>
|
||||||
|
<th title="指数÷卖一">杠杆</th>
|
||||||
<th>买一/张</th>
|
<th>买一/张</th>
|
||||||
<th>操作</th>
|
<th>操作</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="hp-strike-tbody">
|
<tbody id="hp-strike-tbody">
|
||||||
<tr><td colspan="6" class="muted">请刷新期权链</td></tr>
|
<tr><td colspan="7" class="muted">请刷新期权链</td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@@ -98,10 +106,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 右:下单参数 -->
|
<!-- 右:市价下单参数(无选约条件;条件单另议) -->
|
||||||
<div class="card hp-po-right-card hp-po-order-card">
|
<div class="card hp-po-right-card hp-po-order-card">
|
||||||
<h2>
|
<h2>
|
||||||
<span id="hp-po-mode-badge" class="hp-po-mode-badge">以期权为主</span>
|
<span id="hp-po-mode-badge" class="hp-po-mode-badge">市价下单</span>
|
||||||
<span id="hp-po-card-title">下单</span>
|
<span id="hp-po-card-title">下单</span>
|
||||||
· <span id="hp-perp-uly-label">ETH</span>
|
· <span id="hp-perp-uly-label">ETH</span>
|
||||||
<span class="muted hp-acct-tag" id="hp-perp-acct-tag">合约账户</span>
|
<span class="muted hp-acct-tag" id="hp-perp-acct-tag">合约账户</span>
|
||||||
@@ -109,9 +117,9 @@
|
|||||||
<details class="tip-collapse hp-rule-collapse">
|
<details class="tip-collapse hp-rule-collapse">
|
||||||
<summary class="tip-collapse-summary">规则说明</summary>
|
<summary class="tip-collapse-summary">规则说明</summary>
|
||||||
<div class="tip-collapse-body rule-tip">
|
<div class="tip-collapse-body rule-tip">
|
||||||
|
<p><strong>市价下单</strong>:左侧人工选约(同期权页:到期/Call·Put/实虚值),点「选择」后右侧下单;非条件盯盘。</p>
|
||||||
<p><strong>组合</strong>:买 Call → 永续空;买 Put → 永续多。</p>
|
<p><strong>组合</strong>:买 Call → 永续空;买 Put → 永续多。</p>
|
||||||
<p><strong>比例</strong>:期权:永续默认 <strong>2</strong>(期权名义 ÷ 比例 = 永续名义)。例:期权 1 BTC → 永续 0.5 BTC。</p>
|
<p><strong>比例</strong>:期权:永续默认 <strong>2</strong>(期权名义 ÷ 比例 = 永续名义)。例:期权 1 BTC → 永续 0.5 BTC。</p>
|
||||||
<p><strong>账户</strong>:永续腿合约账户(USDT);期权腿币本位(交易账户 USDT 买币)。</p>
|
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
<div class="hp-po-inner-card hp-po-perp-quote-card">
|
<div class="hp-po-inner-card hp-po-perp-quote-card">
|
||||||
@@ -152,37 +160,12 @@
|
|||||||
<span class="hp-po-field-lab">永续杠杆 <em>开仓</em></span>
|
<span class="hp-po-field-lab">永续杠杆 <em>开仓</em></span>
|
||||||
<input type="number" step="1" id="hp-perp-leverage" value="{{ hedge_plan_eth_leverage | default(5) }}" autocomplete="off" inputmode="numeric" data-lpignore="true" data-1p-ignore="true" data-form-type="other" title="默认取 env 永期·永续杠杆" />
|
<input type="number" step="1" id="hp-perp-leverage" value="{{ hedge_plan_eth_leverage | default(5) }}" autocomplete="off" inputmode="numeric" data-lpignore="true" data-1p-ignore="true" data-form-type="other" title="默认取 env 永期·永续杠杆" />
|
||||||
</label>
|
</label>
|
||||||
<label class="hp-po-field">
|
|
||||||
<span class="hp-po-field-lab">期权杠杆 <em>校验</em></span>
|
|
||||||
<input type="number" step="1" id="hp-opt-leverage" value="100" autocomplete="off" inputmode="numeric" data-lpignore="true" data-1p-ignore="true" data-form-type="other" />
|
|
||||||
</label>
|
|
||||||
<label class="hp-po-field">
|
<label class="hp-po-field">
|
||||||
<span class="hp-po-field-lab">比例 <em>期权:永续</em></span>
|
<span class="hp-po-field-lab">比例 <em>期权:永续</em></span>
|
||||||
<input type="number" step="any" min="0.1" id="hp-opt-perp-ratio" value="2" autocomplete="off" inputmode="decimal" data-lpignore="true" data-1p-ignore="true" data-form-type="other" title="默认2:期权名义÷比例=永续名义" />
|
<input type="number" step="any" min="0.1" id="hp-opt-perp-ratio" value="2" autocomplete="off" inputmode="decimal" data-lpignore="true" data-1p-ignore="true" data-form-type="other" title="默认2:期权名义÷比例=永续名义" />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="hp-po-section" aria-labelledby="hp-po-sec-select">
|
|
||||||
<h3 id="hp-po-sec-select" class="hp-po-section-title">选约过滤</h3>
|
|
||||||
<div class="hp-po-fields hp-po-fields--section hp-po-fields--select">
|
|
||||||
<label class="hp-po-field">
|
|
||||||
<span class="hp-po-field-lab">到期最短 <em>h</em></span>
|
|
||||||
<input type="number" step="1" id="hp-min-hours" value="36" autocomplete="off" inputmode="numeric" data-lpignore="true" data-1p-ignore="true" data-form-type="other" />
|
|
||||||
</label>
|
|
||||||
<label class="hp-po-field">
|
|
||||||
<span class="hp-po-field-lab">行权间隔 <em>点</em></span>
|
|
||||||
<input type="number" step="any" id="hp-strike-interval" value="15" autocomplete="off" inputmode="decimal" data-lpignore="true" data-1p-ignore="true" data-form-type="other" />
|
|
||||||
</label>
|
|
||||||
<label class="hp-po-field hp-po-field--type">
|
|
||||||
<span class="hp-po-field-lab">虚实值</span>
|
|
||||||
<select id="hp-money-select" aria-label="虚实值类型">
|
|
||||||
<option value="otm" selected>虚值</option>
|
|
||||||
<option value="itm">实值/平值</option>
|
|
||||||
<option value="atm">仅平值</option>
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<section class="hp-po-section" aria-labelledby="hp-po-sec-exit">
|
<section class="hp-po-section" aria-labelledby="hp-po-sec-exit">
|
||||||
<h3 id="hp-po-sec-exit" class="hp-po-section-title">出场条件</h3>
|
<h3 id="hp-po-sec-exit" class="hp-po-section-title">出场条件</h3>
|
||||||
<div class="hp-po-fields hp-po-fields--section">
|
<div class="hp-po-fields hp-po-fields--section">
|
||||||
@@ -203,7 +186,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-row hp-action-row hp-po-action-row">
|
<div class="form-row hp-action-row hp-po-action-row">
|
||||||
<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>
|
||||||
<button type="button" class="primary" id="hp-preview-btn" title="以期权为主=盯盘启动">策略启动</button>
|
<button type="button" class="primary" id="hp-preview-btn" title="按左侧已选合约市价开仓(期权+永续)">市价下单</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user