Perp hedge: dialog order like options; right panel shows positions/orders/history/stats.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+240
-85
@@ -15,12 +15,13 @@
|
|||||||
function pickDefaultTab() {
|
function pickDefaultTab() {
|
||||||
if (showPerp) return "perp_options";
|
if (showPerp) return "perp_options";
|
||||||
if (showOo) return "options_options";
|
if (showOo) return "options_options";
|
||||||
return "active";
|
return "perp_options";
|
||||||
}
|
}
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
tab: pickDefaultTab(),
|
tab: pickDefaultTab(),
|
||||||
mode: showPerp ? "perp_options" : showOo ? "options_options" : "perp_options",
|
mode: showPerp ? "perp_options" : showOo ? "options_options" : "perp_options",
|
||||||
|
posTab: "live",
|
||||||
underlying: root.getAttribute("data-default-underly") || "ETH",
|
underlying: root.getAttribute("data-default-underly") || "ETH",
|
||||||
moneyFilter: root.getAttribute("data-option-primary") !== "0" ? "all" : "itm",
|
moneyFilter: root.getAttribute("data-option-primary") !== "0" ? "all" : "itm",
|
||||||
ooMoneyFilter: "atm_otm", // 期期锁定:平值+虚值
|
ooMoneyFilter: "atm_otm", // 期期锁定:平值+虚值
|
||||||
@@ -29,6 +30,7 @@
|
|||||||
poStrikeExpandAll: false, // 永期手动选约:默认平值+实3+虚3
|
poStrikeExpandAll: false, // 永期手动选约:默认平值+实3+虚3
|
||||||
chain: null,
|
chain: null,
|
||||||
selected: null,
|
selected: null,
|
||||||
|
orderDialogOpen: false,
|
||||||
legA: null,
|
legA: null,
|
||||||
legB: null,
|
legB: null,
|
||||||
market: null,
|
market: null,
|
||||||
@@ -72,7 +74,7 @@
|
|||||||
if (isOptionPrimary()) {
|
if (isOptionPrimary()) {
|
||||||
hint.textContent =
|
hint.textContent =
|
||||||
(dir === "short" ? "买 Put + 永续多" : "买 Call + 永续空") +
|
(dir === "short" ? "买 Put + 永续多" : "买 Call + 永续空") +
|
||||||
" · 点「选择」填入右侧手动下单;比例默认 2(期权 1 → 永续 0.5)。";
|
" · 点「选择」弹窗下单;比例默认 2(期权 1 → 永续 0.5)。";
|
||||||
} else {
|
} else {
|
||||||
hint.textContent = dir === "short" ? "永续空 + 保险 Call" : "永续多 + 保险 Put";
|
hint.textContent = dir === "short" ? "永续空 + 保险 Call" : "永续多 + 保险 Put";
|
||||||
}
|
}
|
||||||
@@ -89,8 +91,11 @@
|
|||||||
state.direction = next;
|
state.direction = next;
|
||||||
syncPoDirUI();
|
syncPoDirUI();
|
||||||
if (!changed && !forceReload) return;
|
if (!changed && !forceReload) return;
|
||||||
state.selected = null;
|
if (state.orderDialogOpen) closeOrderDialog();
|
||||||
if ($("hp-sel-inst")) $("hp-sel-inst").textContent = "—";
|
else {
|
||||||
|
state.selected = null;
|
||||||
|
if ($("hp-sel-inst")) $("hp-sel-inst").textContent = "—";
|
||||||
|
}
|
||||||
void loadMarket().then(function () {
|
void loadMarket().then(function () {
|
||||||
renderListStrikes();
|
renderListStrikes();
|
||||||
});
|
});
|
||||||
@@ -681,7 +686,7 @@
|
|||||||
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 = "情景测算后再启动";
|
||||||
@@ -777,13 +782,110 @@
|
|||||||
return (Math.round((1 / a) * 10) / 10).toFixed(1) + "×";
|
return (Math.round((1 / a) * 10) / 10).toFixed(1) + "×";
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearPoSelection() {
|
const orderPanelHome = (function () {
|
||||||
|
const host = document.getElementById("hp-order-panel-host");
|
||||||
|
return host ? host.parentElement : null;
|
||||||
|
})();
|
||||||
|
|
||||||
|
function orderPanel() {
|
||||||
|
return $("hp-order-panel");
|
||||||
|
}
|
||||||
|
|
||||||
|
function orderPanelHost() {
|
||||||
|
return $("hp-order-panel-host");
|
||||||
|
}
|
||||||
|
|
||||||
|
function parkOrderPanel() {
|
||||||
|
const panel = orderPanel();
|
||||||
|
const host = orderPanelHost();
|
||||||
|
if (panel && host && panel.parentElement !== host) host.appendChild(panel);
|
||||||
|
if (host) {
|
||||||
|
host.hidden = true;
|
||||||
|
host.setAttribute("aria-hidden", "true");
|
||||||
|
if (orderPanelHome && host.parentElement !== orderPanelHome) {
|
||||||
|
orderPanelHome.appendChild(host);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (panel) panel.style.display = "none";
|
||||||
|
state.orderDialogOpen = false;
|
||||||
|
const tbody = $("hp-strike-tbody");
|
||||||
|
if (tbody) {
|
||||||
|
tbody.querySelectorAll(".opt-strike-row").forEach(function (r) {
|
||||||
|
r.classList.remove("opt-row-selected");
|
||||||
|
});
|
||||||
|
tbody.querySelectorAll(".hp-pick").forEach(function (b) {
|
||||||
|
b.classList.remove("active");
|
||||||
|
if (b.dataset.origText) b.textContent = b.dataset.origText;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function openOrderDialog(instId) {
|
||||||
|
const panel = orderPanel();
|
||||||
|
const host = orderPanelHost();
|
||||||
|
if (!panel || !host) return false;
|
||||||
|
if (panel.parentElement !== host) host.appendChild(panel);
|
||||||
|
if (host.parentElement !== document.body) document.body.appendChild(host);
|
||||||
|
host.hidden = false;
|
||||||
|
host.setAttribute("aria-hidden", "false");
|
||||||
|
panel.style.display = "";
|
||||||
|
state.orderDialogOpen = true;
|
||||||
|
const tbody = $("hp-strike-tbody");
|
||||||
|
if (tbody && instId) {
|
||||||
|
tbody.querySelectorAll(".opt-strike-row").forEach(function (r) {
|
||||||
|
r.classList.toggle("opt-row-selected", r.getAttribute("data-inst") === instId);
|
||||||
|
});
|
||||||
|
tbody.querySelectorAll(".hp-pick").forEach(function (b) {
|
||||||
|
if (!b.dataset.origText) b.dataset.origText = "选择";
|
||||||
|
const on = b.getAttribute("data-inst") === instId;
|
||||||
|
b.classList.toggle("active", on);
|
||||||
|
b.textContent = on ? "已选" : b.dataset.origText;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeOrderDialog() {
|
||||||
state.selected = null;
|
state.selected = null;
|
||||||
|
parkOrderPanel();
|
||||||
if ($("hp-sel-inst")) $("hp-sel-inst").textContent = "—";
|
if ($("hp-sel-inst")) $("hp-sel-inst").textContent = "—";
|
||||||
updatePremiumLine();
|
updatePremiumLine();
|
||||||
updatePerpPnlHint();
|
updatePerpPnlHint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clearPoSelection() {
|
||||||
|
state.selected = null;
|
||||||
|
if ($("hp-sel-inst")) $("hp-sel-inst").textContent = "—";
|
||||||
|
if (state.orderDialogOpen) parkOrderPanel();
|
||||||
|
updatePremiumLine();
|
||||||
|
updatePerpPnlHint();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setHpPosTab(tab) {
|
||||||
|
const next = tab === "pending" || tab === "history" || tab === "stats" ? tab : "live";
|
||||||
|
state.posTab = next;
|
||||||
|
document.querySelectorAll(".hp-pos-tab").forEach(function (btn) {
|
||||||
|
const on = btn.getAttribute("data-hp-pos-tab") === next;
|
||||||
|
btn.classList.toggle("active", on);
|
||||||
|
btn.setAttribute("aria-selected", on ? "true" : "false");
|
||||||
|
});
|
||||||
|
document.querySelectorAll("[data-hp-pos-pane]").forEach(function (pane) {
|
||||||
|
const on = pane.getAttribute("data-hp-pos-pane") === next;
|
||||||
|
pane.classList.toggle("is-active", on);
|
||||||
|
if (on) pane.removeAttribute("hidden");
|
||||||
|
else pane.setAttribute("hidden", "");
|
||||||
|
});
|
||||||
|
if (next === "live" || next === "pending") void loadActivePlans();
|
||||||
|
else if (next === "history") void loadHistory();
|
||||||
|
else if (next === "stats") void loadStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshHpPosPanel() {
|
||||||
|
void loadActivePlans();
|
||||||
|
void loadHistory();
|
||||||
|
if (state.posTab === "stats") void loadStats();
|
||||||
|
}
|
||||||
|
|
||||||
/** 手动选约:不自动代选;仅在已选失效(换 Call/Put 或筛掉)时清空. */
|
/** 手动选约:不自动代选;仅在已选失效(换 Call/Put 或筛掉)时清空. */
|
||||||
function autoMatchPoOption(force) {
|
function autoMatchPoOption(force) {
|
||||||
if (!isOptionPrimary()) return;
|
if (!isOptionPrimary()) return;
|
||||||
@@ -873,7 +975,7 @@
|
|||||||
b.classList.toggle("active", on);
|
b.classList.toggle("active", on);
|
||||||
b.setAttribute("aria-selected", on ? "true" : "false");
|
b.setAttribute("aria-selected", on ? "true" : "false");
|
||||||
});
|
});
|
||||||
["perp_options", "options_options", "active", "history", "stats"].forEach(function (id) {
|
["perp_options", "options_options"].forEach(function (id) {
|
||||||
const panel = $("hp-tab-" + id);
|
const panel = $("hp-tab-" + id);
|
||||||
if (!panel) return;
|
if (!panel) return;
|
||||||
const on = id === tab;
|
const on = id === tab;
|
||||||
@@ -884,6 +986,9 @@
|
|||||||
if (tab === "perp_options" || tab === "options_options") {
|
if (tab === "perp_options" || tab === "options_options") {
|
||||||
state.mode = tab;
|
state.mode = tab;
|
||||||
}
|
}
|
||||||
|
if (tab === "perp_options") {
|
||||||
|
refreshHpPosPanel();
|
||||||
|
}
|
||||||
const hint = $("hp-acct-hint");
|
const hint = $("hp-acct-hint");
|
||||||
if (hint) {
|
if (hint) {
|
||||||
if (tab === "options_options") {
|
if (tab === "options_options") {
|
||||||
@@ -1472,18 +1577,9 @@
|
|||||||
state.selected = c;
|
state.selected = c;
|
||||||
const el = $("hp-sel-inst");
|
const el = $("hp-sel-inst");
|
||||||
if (el) el.textContent = c.inst_id;
|
if (el) el.textContent = c.inst_id;
|
||||||
const tbody = $("hp-strike-tbody");
|
|
||||||
if (tbody) {
|
|
||||||
tbody.querySelectorAll(".opt-strike-row").forEach(function (r) {
|
|
||||||
r.classList.toggle("opt-row-selected", r.getAttribute("data-inst") === c.inst_id);
|
|
||||||
});
|
|
||||||
tbody.querySelectorAll(".hp-pick").forEach(function (b) {
|
|
||||||
b.classList.toggle("active", b.getAttribute("data-inst") === c.inst_id);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
updatePremiumLine();
|
updatePremiumLine();
|
||||||
if (isOptionPrimary()) {
|
if (isOptionPrimary()) {
|
||||||
// 手动下单以张数为主:选约后若未填张数则默认 1,并同步左右
|
// 手动下单以张数为主:选约后若未填张数则默认 1
|
||||||
if (!(orderSheets() > 0)) {
|
if (!(orderSheets() > 0)) {
|
||||||
if ($("hp-sheets")) $("hp-sheets").value = "1";
|
if ($("hp-sheets")) $("hp-sheets").value = "1";
|
||||||
if ($("hp-order-sheets")) $("hp-order-sheets").value = "1";
|
if ($("hp-order-sheets")) $("hp-order-sheets").value = "1";
|
||||||
@@ -1491,10 +1587,11 @@
|
|||||||
syncSheetsInputs("hp-order-sheets");
|
syncSheetsInputs("hp-order-sheets");
|
||||||
if (!(numInput("hp-order-sheets", 0) > 0)) syncSheetsInputs("hp-sheets");
|
if (!(numInput("hp-order-sheets", 0) > 0)) syncSheetsInputs("hp-sheets");
|
||||||
}
|
}
|
||||||
void loadMarket();
|
|
||||||
updatePerpPnlHint();
|
updatePerpPnlHint();
|
||||||
updatePoOpenSummary();
|
updatePoOpenSummary();
|
||||||
}
|
}
|
||||||
|
openOrderDialog(c.inst_id);
|
||||||
|
void loadMarket();
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderListStrikes() {
|
function renderListStrikes() {
|
||||||
@@ -1608,11 +1705,11 @@
|
|||||||
if (line) line.textContent = "";
|
if (line) line.textContent = "";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const sheets = Number(($("hp-sheets") && $("hp-sheets").value) || 1);
|
const sheets = Math.max(1, orderSheets() || Number(($("hp-sheets") && $("hp-sheets").value) || 1));
|
||||||
const ct = Number(state.selected.ct_mult || 0.01);
|
const ct = Number(state.selected.ct_mult || 0.01);
|
||||||
const ask = Number(state.selected.ask || 0);
|
const ask = Number(state.selected.ask || 0);
|
||||||
const prem = ask * sheets * ct;
|
const prem = ask * sheets * ct;
|
||||||
line.textContent = "预估权利金 ≈ " + fmt(prem, 4) + " 币(币本位)";
|
line.textContent = "预估权利金 ≈ " + fmt(prem, 4) + " 币(币本位) · 卖一 " + fmt(ask, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildStraddleRows(contracts) {
|
function buildStraddleRows(contracts) {
|
||||||
@@ -1881,7 +1978,8 @@
|
|||||||
state.underlying = next;
|
state.underlying = next;
|
||||||
syncUnderlyingUI();
|
syncUnderlyingUI();
|
||||||
if (!changed && !forceReload) return;
|
if (!changed && !forceReload) return;
|
||||||
state.selected = null;
|
if (state.orderDialogOpen) closeOrderDialog();
|
||||||
|
else state.selected = null;
|
||||||
state.legA = null;
|
state.legA = null;
|
||||||
state.legB = null;
|
state.legB = null;
|
||||||
state.ooRecommend = null;
|
state.ooRecommend = null;
|
||||||
@@ -2133,18 +2231,36 @@
|
|||||||
if (state.tab === "perp_options" || state.tab === "options_options") {
|
if (state.tab === "perp_options" || state.tab === "options_options") {
|
||||||
void loadGates();
|
void loadGates();
|
||||||
if (state.tab === "perp_options") void refreshPoStrategyStatus();
|
if (state.tab === "perp_options") void refreshPoStrategyStatus();
|
||||||
} else if (state.tab === "active") {
|
|
||||||
void loadActivePlans();
|
|
||||||
} else if (state.tab === "history") {
|
|
||||||
void loadHistory();
|
|
||||||
} else if (state.tab === "stats") {
|
|
||||||
void loadStats();
|
|
||||||
} else {
|
} else {
|
||||||
const el = $("hp-gate-line");
|
const el = $("hp-gate-line");
|
||||||
if (el) el.textContent = "";
|
if (el) el.textContent = "";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
document.querySelectorAll(".hp-pos-tab").forEach(function (btn) {
|
||||||
|
btn.addEventListener("click", function () {
|
||||||
|
setHpPosTab(btn.getAttribute("data-hp-pos-tab") || "live");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if ($("hp-pos-refresh")) {
|
||||||
|
$("hp-pos-refresh").addEventListener("click", function () {
|
||||||
|
refreshHpPosPanel();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function onOrderDialogClose() {
|
||||||
|
closeOrderDialog();
|
||||||
|
}
|
||||||
|
if ($("hp-order-close-btn")) $("hp-order-close-btn").addEventListener("click", onOrderDialogClose);
|
||||||
|
if ($("hp-order-cancel-btn")) $("hp-order-cancel-btn").addEventListener("click", onOrderDialogClose);
|
||||||
|
const orderHost = orderPanelHost();
|
||||||
|
if (orderHost) {
|
||||||
|
orderHost.addEventListener("click", function (ev) {
|
||||||
|
if (ev.target === orderHost) closeOrderDialog();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
document.addEventListener("keydown", function (ev) {
|
||||||
|
if (ev.key === "Escape" && state.orderDialogOpen) closeOrderDialog();
|
||||||
|
});
|
||||||
document.querySelectorAll(".hp-uly-btn, .hp-uly-btn-oo").forEach(function (b) {
|
document.querySelectorAll(".hp-uly-btn, .hp-uly-btn-oo").forEach(function (b) {
|
||||||
b.addEventListener("click", function () {
|
b.addEventListener("click", function () {
|
||||||
setUnderlying(b.getAttribute("data-uly") || "ETH", true);
|
setUnderlying(b.getAttribute("data-uly") || "ETH", true);
|
||||||
@@ -2617,65 +2733,92 @@
|
|||||||
return map[st] || st || "—";
|
return map[st] || st || "—";
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadActivePlans() {
|
function bindActivePlanRowActions(tbody) {
|
||||||
const tbody = $("hp-active-tbody");
|
|
||||||
if (!tbody) return;
|
if (!tbody) return;
|
||||||
|
tbody.querySelectorAll(".hp-btn-detail").forEach(function (btn) {
|
||||||
|
btn.addEventListener("click", function () {
|
||||||
|
void showPlanDetail(Number(btn.getAttribute("data-id")));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
tbody.querySelectorAll(".hp-btn-complete").forEach(function (btn) {
|
||||||
|
btn.addEventListener("click", function () {
|
||||||
|
void completeMissingLeg(Number(btn.getAttribute("data-id")));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
tbody.querySelectorAll(".hp-btn-end").forEach(function (btn) {
|
||||||
|
btn.addEventListener("click", function () {
|
||||||
|
void endActivePlan(Number(btn.getAttribute("data-id")));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPendingPlan(p) {
|
||||||
|
const st = String((p && p.status) || "").toLowerCase();
|
||||||
|
return st === "watching" || st === "pending";
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderActivePlanRows(tbody, rows, emptyMsg, colMode) {
|
||||||
|
if (!tbody) return;
|
||||||
|
if (!rows.length) {
|
||||||
|
tbody.innerHTML = '<tr><td colspan="8" class="muted">' + (emptyMsg || "暂无") + "</td></tr>";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tbody.innerHTML = "";
|
||||||
|
rows.forEach(function (p) {
|
||||||
|
const tr = document.createElement("tr");
|
||||||
|
const typeLabel = p.plan_type === "perp_options" ? "永期" : "期期";
|
||||||
|
const contracts = p.contracts_summary || "—";
|
||||||
|
const midCol =
|
||||||
|
colMode === "pending"
|
||||||
|
? p.note || activeTargetLabel(p) || "盯盘/待成交"
|
||||||
|
: activeTargetLabel(p);
|
||||||
|
tr.innerHTML =
|
||||||
|
"<td>#" +
|
||||||
|
p.id +
|
||||||
|
"</td><td>" +
|
||||||
|
typeLabel +
|
||||||
|
"</td><td>" +
|
||||||
|
(p.underlying || "") +
|
||||||
|
"</td><td class=\"hp-contracts-cell\" title=\"" +
|
||||||
|
String(contracts).replace(/"/g, """) +
|
||||||
|
"\"><code>" +
|
||||||
|
contracts +
|
||||||
|
"</code></td><td>" +
|
||||||
|
activeStatusLabel(p) +
|
||||||
|
"</td><td>" +
|
||||||
|
midCol +
|
||||||
|
"</td><td>" +
|
||||||
|
(p.opened_at || "—") +
|
||||||
|
'</td><td class="hp-hist-actions">' +
|
||||||
|
completeLegButtonHtml(p) +
|
||||||
|
'<button type="button" class="btn-secondary hp-btn-detail" data-id="' +
|
||||||
|
p.id +
|
||||||
|
'">成交细节</button> ' +
|
||||||
|
'<button type="button" class="btn-secondary hp-btn-end" data-id="' +
|
||||||
|
p.id +
|
||||||
|
'">结束计划</button></td>';
|
||||||
|
tbody.appendChild(tr);
|
||||||
|
});
|
||||||
|
bindActivePlanRowActions(tbody);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadActivePlans() {
|
||||||
|
const liveBody = $("hp-active-tbody");
|
||||||
|
const pendingBody = $("hp-pending-tbody");
|
||||||
|
if (!liveBody && !pendingBody) return;
|
||||||
try {
|
try {
|
||||||
const d = await apiJson("/api/hedge-plan/active");
|
const d = await apiJson("/api/hedge-plan/active");
|
||||||
const rows = d.plans || [];
|
const rows = d.plans || [];
|
||||||
if (!rows.length) {
|
const pending = rows.filter(isPendingPlan);
|
||||||
tbody.innerHTML = '<tr><td colspan="8" class="muted">暂无进行中的计划</td></tr>';
|
const live = rows.filter(function (p) {
|
||||||
return;
|
return !isPendingPlan(p);
|
||||||
}
|
|
||||||
tbody.innerHTML = "";
|
|
||||||
rows.forEach(function (p) {
|
|
||||||
const tr = document.createElement("tr");
|
|
||||||
const typeLabel = p.plan_type === "perp_options" ? "永期" : "期期";
|
|
||||||
const contracts = p.contracts_summary || "—";
|
|
||||||
tr.innerHTML =
|
|
||||||
"<td>#" +
|
|
||||||
p.id +
|
|
||||||
"</td><td>" +
|
|
||||||
typeLabel +
|
|
||||||
"</td><td>" +
|
|
||||||
(p.underlying || "") +
|
|
||||||
"</td><td class=\"hp-contracts-cell\" title=\"" +
|
|
||||||
String(contracts).replace(/"/g, """) +
|
|
||||||
"\"><code>" +
|
|
||||||
contracts +
|
|
||||||
"</code></td><td>" +
|
|
||||||
activeStatusLabel(p) +
|
|
||||||
"</td><td>" +
|
|
||||||
activeTargetLabel(p) +
|
|
||||||
"</td><td>" +
|
|
||||||
(p.opened_at || "—") +
|
|
||||||
'</td><td class="hp-hist-actions">' +
|
|
||||||
completeLegButtonHtml(p) +
|
|
||||||
'<button type="button" class="btn-secondary hp-btn-detail" data-id="' +
|
|
||||||
p.id +
|
|
||||||
'">成交细节</button> ' +
|
|
||||||
'<button type="button" class="btn-secondary hp-btn-end" data-id="' +
|
|
||||||
p.id +
|
|
||||||
'">结束计划</button></td>';
|
|
||||||
tbody.appendChild(tr);
|
|
||||||
});
|
|
||||||
tbody.querySelectorAll(".hp-btn-detail").forEach(function (btn) {
|
|
||||||
btn.addEventListener("click", function () {
|
|
||||||
void showPlanDetail(Number(btn.getAttribute("data-id")));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
tbody.querySelectorAll(".hp-btn-complete").forEach(function (btn) {
|
|
||||||
btn.addEventListener("click", function () {
|
|
||||||
void completeMissingLeg(Number(btn.getAttribute("data-id")));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
tbody.querySelectorAll(".hp-btn-end").forEach(function (btn) {
|
|
||||||
btn.addEventListener("click", function () {
|
|
||||||
void endActivePlan(Number(btn.getAttribute("data-id")));
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
renderActivePlanRows(liveBody, live, "暂无当前持仓", "live");
|
||||||
|
renderActivePlanRows(pendingBody, pending, "暂无当前委托", "pending");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
tbody.innerHTML = '<tr><td colspan="8" class="err">' + (e.message || e) + "</td></tr>";
|
const msg = '<tr><td colspan="8" class="err">' + (e.message || e) + "</td></tr>";
|
||||||
|
if (liveBody) liveBody.innerHTML = msg;
|
||||||
|
if (pendingBody) pendingBody.innerHTML = msg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2832,7 +2975,7 @@
|
|||||||
try {
|
try {
|
||||||
await apiJson("/api/hedge-plan/" + planId, { method: "DELETE" });
|
await apiJson("/api/hedge-plan/" + planId, { method: "DELETE" });
|
||||||
await loadHistory();
|
await loadHistory();
|
||||||
if (state.tab === "stats") await loadStats();
|
if (state.posTab === "stats") await loadStats();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
window.alert(e.message || String(e));
|
window.alert(e.message || String(e));
|
||||||
}
|
}
|
||||||
@@ -3107,16 +3250,14 @@
|
|||||||
closePreviewModal();
|
closePreviewModal();
|
||||||
const refreshHint =
|
const refreshHint =
|
||||||
d.refresh && d.refresh.msg ? "\n" + String(d.refresh.msg) : "";
|
d.refresh && d.refresh.msg ? "\n" + String(d.refresh.msg) : "";
|
||||||
|
if (state.orderDialogOpen) closeOrderDialog();
|
||||||
if (d.partial) {
|
if (d.partial) {
|
||||||
alert(
|
alert(
|
||||||
(d.msg || "半腿失败,已挂待补") +
|
(d.msg || "半腿失败,已挂待补") +
|
||||||
(d.plan_id ? "\n计划 #" + d.plan_id : "") +
|
(d.plan_id ? "\n计划 #" + d.plan_id : "") +
|
||||||
refreshHint +
|
refreshHint +
|
||||||
"\n请到「进行中的计划」补开缺失腿"
|
"\n请到右侧「当前持仓」补开缺失腿"
|
||||||
);
|
);
|
||||||
state.tab = "active";
|
|
||||||
syncTabUI();
|
|
||||||
void loadActivePlans();
|
|
||||||
} else {
|
} else {
|
||||||
alert(
|
alert(
|
||||||
"计划已启动 #" +
|
"计划已启动 #" +
|
||||||
@@ -3125,6 +3266,12 @@
|
|||||||
refreshHint
|
refreshHint
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (showPerp) {
|
||||||
|
state.tab = "perp_options";
|
||||||
|
syncTabUI();
|
||||||
|
setHpPosTab(d.partial ? "live" : "live");
|
||||||
|
}
|
||||||
|
refreshHpPosPanel();
|
||||||
void loadGates();
|
void loadGates();
|
||||||
if (isOo) void loadChain();
|
if (isOo) void loadChain();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -3164,10 +3311,18 @@
|
|||||||
updateOoBudgetLine(null);
|
updateOoBudgetLine(null);
|
||||||
bind();
|
bind();
|
||||||
syncOptionPrimaryUI();
|
syncOptionPrimaryUI();
|
||||||
|
setHpPosTab(state.posTab || "live");
|
||||||
|
parkOrderPanel();
|
||||||
void refreshAll().then(function () {
|
void refreshAll().then(function () {
|
||||||
void refreshPoStrategyStatus();
|
void refreshPoStrategyStatus();
|
||||||
|
refreshHpPosPanel();
|
||||||
});
|
});
|
||||||
setInterval(function () {
|
setInterval(function () {
|
||||||
if (state.tab === "perp_options" && isOptionPrimary()) void refreshPoStrategyStatus();
|
if (state.tab === "perp_options" && isOptionPrimary()) void refreshPoStrategyStatus();
|
||||||
}, 15000);
|
}, 15000);
|
||||||
|
setInterval(function () {
|
||||||
|
if (state.tab === "perp_options" && (state.posTab === "live" || state.posTab === "pending")) {
|
||||||
|
void loadActivePlans();
|
||||||
|
}
|
||||||
|
}, 20000);
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -3911,6 +3911,29 @@ html[data-theme="light"] .opt-be-dist-down {
|
|||||||
.hedge-plan-page-wrap #hp-po-layout > .card {
|
.hedge-plan-page-wrap #hp-po-layout > .card {
|
||||||
align-self: stretch;
|
align-self: stretch;
|
||||||
}
|
}
|
||||||
|
.hedge-plan-page-wrap .hp-po-pos-card {
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
.hedge-plan-page-wrap .hp-po-pos-card .options-pos-tab-body {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-height: 240px;
|
||||||
|
}
|
||||||
|
.hedge-plan-page-wrap .hp-po-pos-card .options-pos-pane {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.hedge-plan-page-wrap .hp-po-order-dialog .hp-po-section-title {
|
||||||
|
margin: 0 0 6px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
.hedge-plan-page-wrap .hp-po-order-dialog .hp-po-fields--section {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
.hedge-plan-page-wrap .hp-pos-tab.active,
|
||||||
|
.hedge-plan-page-wrap .opt-pos-tab.active {
|
||||||
|
border-color: #5b8cff;
|
||||||
|
color: #cfe0ff;
|
||||||
|
background: rgba(74, 124, 255, 0.28);
|
||||||
|
}
|
||||||
.hedge-plan-page-wrap .hp-po-left-stack,
|
.hedge-plan-page-wrap .hp-po-left-stack,
|
||||||
.hedge-plan-page-wrap .hp-po-right-stack {
|
.hedge-plan-page-wrap .hp-po-right-stack {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
<div class="flash" style="margin-bottom:12px">期权模块未启用,无法拉期权链.请先配置期权账户.</div>
|
<div class="flash" style="margin-bottom:12px">期权模块未启用,无法拉期权链.请先配置期权账户.</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if hedge_plan_enabled and not hedge_plan_show_perp_options and not hedge_plan_show_options_options %}
|
{% if hedge_plan_enabled and not hedge_plan_show_perp_options and not hedge_plan_show_options_options %}
|
||||||
<div class="flash" style="margin-bottom:12px">永期与期期 Tab 均已隐藏:请在 env「期权/对冲模式」切换交易模式;进行中/历史仍可查看.</div>
|
<div class="flash" style="margin-bottom:12px">永期与期期 Tab 均已隐藏:请在 env「期权/对冲模式」切换交易模式.</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="card hp-head-card">
|
<div class="card hp-head-card">
|
||||||
@@ -35,9 +35,6 @@
|
|||||||
{% if hedge_plan_show_options_options %}
|
{% if hedge_plan_show_options_options %}
|
||||||
<button type="button" class="hp-tab" role="tab" aria-selected="false" data-tab="options_options">期期对冲</button>
|
<button type="button" class="hp-tab" role="tab" aria-selected="false" data-tab="options_options">期期对冲</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<button type="button" class="hp-tab" role="tab" aria-selected="false" data-tab="active">进行中的计划</button>
|
|
||||||
<button type="button" class="hp-tab" role="tab" aria-selected="false" data-tab="history">历史记录</button>
|
|
||||||
<button type="button" class="hp-tab" role="tab" aria-selected="false" data-tab="stats">统计分析</button>
|
|
||||||
</div>
|
</div>
|
||||||
<p class="muted" id="hp-gate-line"></p>
|
<p class="muted" id="hp-gate-line"></p>
|
||||||
<p class="muted hp-acct-hint" id="hp-acct-hint">永续腿→合约账户 · 期权腿→期权账户</p>
|
<p class="muted hp-acct-hint" id="hp-acct-hint">永续腿→合约账户 · 期权腿→期权账户</p>
|
||||||
@@ -45,163 +42,220 @@
|
|||||||
|
|
||||||
<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 options-order-card">
|
||||||
<div class="hp-po-left-stack">
|
<h2>手动选约 · <span id="hp-opt-type-label">Call</span> <span class="muted hp-acct-tag" id="hp-opt-acct-tag">币本位</span></h2>
|
||||||
<div class="hp-po-inner-card hp-opt-card">
|
<div class="form-row hp-opt-toolbar hp-po-opt-toolbar options-chain-toolbar">
|
||||||
<h2>手动选约 · <span id="hp-opt-type-label">Call</span> <span class="muted hp-acct-tag" id="hp-opt-acct-tag">币本位</span></h2>
|
<button type="button" class="btn-secondary hp-uly-btn active" data-uly="ETH">ETH</button>
|
||||||
<div class="form-row hp-opt-toolbar hp-po-opt-toolbar options-chain-toolbar">
|
<button type="button" class="btn-secondary hp-uly-btn" data-uly="BTC">BTC</button>
|
||||||
<button type="button" class="btn-secondary hp-uly-btn active" data-uly="ETH">ETH</button>
|
<select id="hp-exp-select"><option value="">选择到期日</option></select>
|
||||||
<button type="button" class="btn-secondary hp-uly-btn" data-uly="BTC">BTC</button>
|
<span class="hp-oo-seg hp-po-dir-seg" role="group" aria-label="期权类型">
|
||||||
<select id="hp-exp-select"><option value="">选择到期日</option></select>
|
<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>
|
||||||
<span class="hp-oo-seg hp-po-dir-seg" role="group" aria-label="期权类型">
|
<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>
|
||||||
<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>
|
</span>
|
||||||
<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 class="hp-po-ins-money" id="hp-po-ins-money" hidden>
|
||||||
</span>
|
<button type="button" class="btn-secondary hp-money-btn active" data-money="itm" title="实值+平值">实值/平值</button>
|
||||||
<span class="hp-po-ins-money" id="hp-po-ins-money" hidden>
|
<button type="button" class="btn-secondary hp-money-btn" data-money="atm" title="仅平值">仅平值</button>
|
||||||
<button type="button" class="btn-secondary hp-money-btn active" data-money="itm" title="实值+平值">实值/平值</button>
|
</span>
|
||||||
<button type="button" class="btn-secondary hp-money-btn" data-money="atm" title="仅平值">仅平值</button>
|
<span class="hp-po-mkt-money" id="hp-po-mkt-money">
|
||||||
</span>
|
<button type="button" class="btn-secondary hp-money-btn active" data-money="all" title="全部行权价(默认窗口)">全部</button>
|
||||||
<span class="hp-po-mkt-money" id="hp-po-mkt-money">
|
<button type="button" class="btn-secondary hp-money-btn" data-money="itm" title="实值(含平值)">实值</button>
|
||||||
<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="otm" title="虚值">虚值</button>
|
||||||
<button type="button" class="btn-secondary hp-money-btn" data-money="itm" title="实值(含平值)">实值</button>
|
</span>
|
||||||
<button type="button" class="btn-secondary hp-money-btn" data-money="otm" title="虚值">虚值</button>
|
<label class="opt-strike-expand-label hp-po-expand-label" title="展开该到期全部行权价">
|
||||||
</span>
|
<input type="checkbox" id="hp-strike-expand-all"> 展开全部
|
||||||
<label class="opt-strike-expand-label hp-po-expand-label" title="展开该到期全部行权价">
|
</label>
|
||||||
<input type="checkbox" id="hp-strike-expand-all"> 展开全部
|
<button type="button" class="btn-secondary" id="hp-load-chain">刷新链</button>
|
||||||
</label>
|
<span id="hp-index-line" class="hp-po-index" aria-live="polite">指数 —</span>
|
||||||
<button type="button" class="btn-secondary" id="hp-load-chain">刷新链</button>
|
</div>
|
||||||
<span id="hp-index-line" class="hp-po-index" aria-live="polite">指数 —</span>
|
<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" />
|
||||||
|
<input type="hidden" id="hp-sheets" value="1" />
|
||||||
|
<input type="hidden" id="hp-premium-budget" value="0" />
|
||||||
|
<div class="options-strike-table-wrap hp-strike-table-wrap--6">
|
||||||
|
<table class="options-strike-table" id="hp-strike-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>行权价</th>
|
||||||
|
<th>类型</th>
|
||||||
|
<th>合约</th>
|
||||||
|
<th>卖一/张</th>
|
||||||
|
<th title="币本位:1÷卖一">杠杆</th>
|
||||||
|
<th>买一/张</th>
|
||||||
|
<th>操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="hp-strike-tbody">
|
||||||
|
<tr><td colspan="7" class="muted">请刷新期权链</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div id="hp-opt-bal-line" class="muted hp-po-meta hp-opt-bal-line"></div>
|
||||||
|
|
||||||
|
<!-- 弹窗下单(同期权页) -->
|
||||||
|
<div id="hp-order-panel-host" class="opt-order-backdrop" hidden aria-hidden="true">
|
||||||
|
<div id="hp-order-panel" class="opt-order-dialog hp-po-order-dialog" role="dialog" aria-modal="true" aria-labelledby="hp-order-dialog-title" style="display:none">
|
||||||
|
<div class="opt-order-dialog-head">
|
||||||
|
<h3 class="opt-order-title" id="hp-order-dialog-title">手动下单 · <span id="hp-perp-uly-label">ETH</span></h3>
|
||||||
|
<button type="button" class="btn-secondary" id="hp-order-close-btn" style="font-size:.72rem;padding:2px 10px">取消</button>
|
||||||
</div>
|
</div>
|
||||||
<p class="muted hp-po-chain-hint" id="hp-po-pair-hint">买 Call + 永续空 · 点「选择」填入右侧手动下单;比例默认 2(期权 1 → 永续 0.5)。</p>
|
<div class="opt-order-layout">
|
||||||
{# 条件单残留字段:隐藏,手动下单不用 #}
|
<div class="opt-order-main">
|
||||||
<input type="hidden" id="hp-min-hours" value="0" />
|
<div id="hp-sel-inst" class="options-order-inst">—</div>
|
||||||
<input type="hidden" id="hp-strike-interval" value="0" />
|
<p class="muted hp-po-meta" id="hp-premium-line"></p>
|
||||||
<input type="hidden" id="hp-opt-leverage" value="0" />
|
<div class="hp-po-quote-head">
|
||||||
<input type="hidden" id="hp-money-select" value="all" />
|
<span id="hp-po-mark" class="hp-po-mark" aria-live="polite">标记 —</span>
|
||||||
<div class="options-strike-table-wrap hp-strike-table-wrap--6">
|
<span class="muted hp-acct-tag" id="hp-perp-acct-tag">合约账户</span>
|
||||||
<table class="options-strike-table" id="hp-strike-table">
|
</div>
|
||||||
<thead>
|
<p id="hp-perp-quote" class="muted hp-po-meta">加载中…</p>
|
||||||
<tr>
|
<p id="hp-po-perp-quote-right" class="muted hp-po-meta" hidden></p>
|
||||||
<th>行权价</th>
|
<span id="hp-po-mode-badge" class="hp-po-mode-badge" hidden>手动下单</span>
|
||||||
<th>类型</th>
|
<span id="hp-po-card-title" hidden>下单</span>
|
||||||
<th>合约</th>
|
|
||||||
<th>卖一/张</th>
|
<div class="hp-po-fields hidden" id="hp-po-fields-insurance" hidden>
|
||||||
<th title="币本位:1÷卖一">杠杆</th>
|
<label class="hp-po-field">
|
||||||
<th>买一/张</th>
|
<span class="hp-po-field-lab">开仓价 <em>USDT</em></span>
|
||||||
<th>操作</th>
|
<input type="number" step="any" id="hp-entry" placeholder="入场价" autocomplete="off" inputmode="decimal" data-lpignore="true" data-1p-ignore="true" data-form-type="other" />
|
||||||
</tr>
|
</label>
|
||||||
</thead>
|
<label class="hp-po-field">
|
||||||
<tbody id="hp-strike-tbody">
|
<span class="hp-po-field-lab">张数 <em>合约张</em></span>
|
||||||
<tr><td colspan="7" class="muted">请刷新期权链</td></tr>
|
<input type="number" step="any" id="hp-contracts" placeholder="数量" autocomplete="off" inputmode="decimal" data-lpignore="true" data-1p-ignore="true" data-form-type="other" />
|
||||||
</tbody>
|
</label>
|
||||||
</table>
|
<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="目标价" autocomplete="off" inputmode="decimal" data-lpignore="true" data-1p-ignore="true" data-form-type="other" />
|
||||||
|
</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="保护价" autocomplete="off" inputmode="decimal" data-lpignore="true" data-1p-ignore="true" data-form-type="other" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="hp-po-fields-option-primary">
|
||||||
|
<section class="hp-po-section">
|
||||||
|
<h3 class="hp-po-section-title">开仓</h3>
|
||||||
|
<div class="hp-po-fields hp-po-fields--section hp-po-fields--capital">
|
||||||
|
<label class="hp-po-field">
|
||||||
|
<span class="hp-po-field-lab">张数 <em>期权张</em></span>
|
||||||
|
<input type="number" step="1" min="1" id="hp-order-sheets" value="1" 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" 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" />
|
||||||
|
</label>
|
||||||
|
<label class="hp-po-field">
|
||||||
|
<span class="hp-po-field-lab">永续杠杆 <em>env</em></span>
|
||||||
|
<input type="number" step="1" id="hp-perp-leverage" value="{{ hedge_plan_eth_leverage | default(5) }}" readonly autocomplete="off" inputmode="numeric" data-lpignore="true" data-1p-ignore="true" data-form-type="other" title="BTC=BTC_LEVERAGE ETH=ALT_LEVERAGE" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<p class="muted hp-po-max-line" id="hp-po-max-line">期权开仓最大 —</p>
|
||||||
|
<p class="muted hp-po-perp-auto" id="hp-po-perp-auto">永续开仓 —</p>
|
||||||
|
</section>
|
||||||
|
<section class="hp-po-section">
|
||||||
|
<h3 class="hp-po-section-title">出场条件</h3>
|
||||||
|
<div class="hp-oo-seg hp-po-exit-seg" role="group" aria-label="出场方式">
|
||||||
|
<button type="button" class="btn-secondary hp-po-exit-mode is-selected" data-exit="profit"><span class="hp-oo-check" aria-hidden="true">✓</span>盈利金额出场</button>
|
||||||
|
<button type="button" class="btn-secondary hp-po-exit-mode" data-exit="target"><span class="hp-oo-check" aria-hidden="true">✓</span>目标位出场</button>
|
||||||
|
</div>
|
||||||
|
<div class="hp-po-fields hp-po-fields--section" id="hp-po-exit-profit">
|
||||||
|
<label class="hp-po-field">
|
||||||
|
<span class="hp-po-field-lab">目标盈利 <em>USDT</em></span>
|
||||||
|
<input type="number" step="any" min="0.01" id="hp-profit-target" placeholder="扣双边手续费后净利" autocomplete="off" inputmode="decimal" data-lpignore="true" data-1p-ignore="true" data-form-type="other" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="hp-po-fields hp-po-fields--section hidden" id="hp-po-exit-target" hidden>
|
||||||
|
<label class="hp-po-field">
|
||||||
|
<span class="hp-po-field-lab">期权目标位 <em>相对K</em></span>
|
||||||
|
<input type="number" step="any" id="hp-opt-target-pts" placeholder="点数" autocomplete="off" inputmode="decimal" data-lpignore="true" data-1p-ignore="true" data-form-type="other" />
|
||||||
|
</label>
|
||||||
|
<label class="hp-po-field">
|
||||||
|
<span class="hp-po-field-lab">永续目标位 <em>相对K</em></span>
|
||||||
|
<input type="number" step="any" id="hp-perp-target-pts" placeholder="点数" autocomplete="off" inputmode="decimal" data-lpignore="true" data-1p-ignore="true" data-form-type="other" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</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>
|
||||||
|
<div class="opt-order-dialog-actions">
|
||||||
|
<span id="hp-po-strategy-status" class="hp-po-strategy-status" aria-live="polite"></span>
|
||||||
|
<button type="button" class="btn-primary" id="hp-preview-btn" title="卖一限价开期权+永续对冲">手动下单</button>
|
||||||
|
<button type="button" class="btn-secondary" id="hp-order-cancel-btn">取消</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row hp-pick-row">
|
|
||||||
<label>已选 <code id="hp-sel-inst">—</code></label>
|
|
||||||
<label>张数 <span class="hp-unit">期权张</span> <input type="number" step="1" min="1" id="hp-sheets" value="1" autocomplete="off" inputmode="numeric" data-lpignore="true" data-1p-ignore="true" data-form-type="other" /></label>
|
|
||||||
<span class="muted" id="hp-premium-line"></span>
|
|
||||||
</div>
|
|
||||||
<div id="hp-opt-bal-line" class="muted hp-po-meta hp-opt-bal-line"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 右:手动下单参数(无选约条件;条件单另议;开仓按盘口卖一限价,非交易所市价单) -->
|
|
||||||
<div class="card hp-po-right-card hp-po-order-card">
|
<!-- 右:持仓(当前持仓/委托/历史/统计) -->
|
||||||
<h2>
|
<div class="card options-pos-card-wrap hp-po-pos-card">
|
||||||
<span id="hp-po-mode-badge" class="hp-po-mode-badge">手动下单</span>
|
<div class="options-pos-head">
|
||||||
<span id="hp-po-card-title">下单</span>
|
<h2>持仓</h2>
|
||||||
· <span id="hp-perp-uly-label">ETH</span>
|
<div class="options-pos-head-actions">
|
||||||
<span class="muted hp-acct-tag" id="hp-perp-acct-tag">合约账户</span>
|
<button type="button" class="btn-secondary" id="hp-pos-refresh" title="刷新持仓与历史">刷新</button>
|
||||||
</h2>
|
|
||||||
<details class="tip-collapse hp-rule-collapse">
|
|
||||||
<summary class="tip-collapse-summary">规则说明</summary>
|
|
||||||
<div class="tip-collapse-body rule-tip">
|
|
||||||
<p><strong>手动下单</strong>:左侧人工选约,右侧填张数;开仓按<strong>卖一限价</strong>。先开期权(按张数买币并+5%滑点),再开永续。</p>
|
|
||||||
<p><strong>组合</strong>:买 Call → 永续空;买 Put → 永续多。永续杠杆取 env:BTC=<code>BTC_LEVERAGE</code>(默认10)/ETH=<code>ALT_LEVERAGE</code>(默认5)。</p>
|
|
||||||
<p><strong>出场</strong>:盈利金额(扣开平双边手续费后净利)或目标位点数二选一。平仓先期权(须买一流动性)再永续,再把币全部卖回 USDT。一账户同时仅一笔对冲仓。</p>
|
|
||||||
<p><strong>比例</strong>:默认 <strong>2</strong>(期权名义 ÷ 比例 = 永续名义)。</p>
|
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</div>
|
||||||
<div class="hp-po-inner-card hp-po-perp-quote-card">
|
<div class="options-pos-tabs" role="tablist" aria-label="永期持仓面板">
|
||||||
<h3 class="hp-po-section-title">永续行情</h3>
|
<button type="button" class="btn-secondary opt-pos-tab hp-pos-tab active" data-hp-pos-tab="live" role="tab" aria-selected="true">当前持仓</button>
|
||||||
<div class="hp-po-quote-head">
|
<button type="button" class="btn-secondary opt-pos-tab hp-pos-tab" data-hp-pos-tab="pending" role="tab" aria-selected="false">当前委托</button>
|
||||||
<span id="hp-po-mark" class="hp-po-mark" aria-live="polite">标记 —</span>
|
<button type="button" class="btn-secondary opt-pos-tab hp-pos-tab" data-hp-pos-tab="history" role="tab" aria-selected="false">历史记录</button>
|
||||||
|
<button type="button" class="btn-secondary opt-pos-tab hp-pos-tab" data-hp-pos-tab="stats" role="tab" aria-selected="false">统计分析</button>
|
||||||
|
</div>
|
||||||
|
<div class="options-pos-tab-body">
|
||||||
|
<div class="options-pos-pane is-active" data-hp-pos-pane="live" role="tabpanel">
|
||||||
|
<p class="muted" style="margin:0 0 8px;font-size:.82rem">进行中的对冲计划(持仓中 / 半腿待补)</p>
|
||||||
|
<div class="options-strike-table-wrap">
|
||||||
|
<table class="options-strike-table hp-history-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th><th>类型</th><th>标的</th><th>合约</th><th>状态</th><th>目标/止盈止损</th><th>开仓</th><th>操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="hp-active-tbody">
|
||||||
|
<tr><td colspan="8" class="muted">加载中…</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p id="hp-perp-quote" class="muted hp-po-meta">加载中…</p>
|
<div class="options-pos-pane" data-hp-pos-pane="pending" role="tabpanel" hidden>
|
||||||
<p id="hp-po-perp-quote-right" class="muted hp-po-meta" hidden></p>
|
<p class="muted" style="margin:0 0 8px;font-size:.82rem">盯盘中 / 待成交计划</p>
|
||||||
</div>
|
<div class="options-strike-table-wrap">
|
||||||
<div class="hp-po-fields hidden" id="hp-po-fields-insurance" hidden>
|
<table class="options-strike-table hp-history-table">
|
||||||
<label class="hp-po-field">
|
<thead>
|
||||||
<span class="hp-po-field-lab">开仓价 <em>USDT</em></span>
|
<tr>
|
||||||
<input type="number" step="any" id="hp-entry" placeholder="入场价" autocomplete="off" inputmode="decimal" data-lpignore="true" data-1p-ignore="true" data-form-type="other" />
|
<th>ID</th><th>类型</th><th>标的</th><th>合约</th><th>状态</th><th>说明</th><th>开仓</th><th>操作</th>
|
||||||
</label>
|
</tr>
|
||||||
<label class="hp-po-field">
|
</thead>
|
||||||
<span class="hp-po-field-lab">张数 <em>合约张</em></span>
|
<tbody id="hp-pending-tbody">
|
||||||
<input type="number" step="any" id="hp-contracts" placeholder="数量" autocomplete="off" inputmode="decimal" data-lpignore="true" data-1p-ignore="true" data-form-type="other" />
|
<tr><td colspan="8" class="muted">暂无委托</td></tr>
|
||||||
</label>
|
</tbody>
|
||||||
<label class="hp-po-field hp-po-field--tp">
|
</table>
|
||||||
<span class="hp-po-field-lab">止盈 <em>USDT</em></span>
|
|
||||||
<input type="number" step="any" id="hp-tp" placeholder="目标价" autocomplete="off" inputmode="decimal" data-lpignore="true" data-1p-ignore="true" data-form-type="other" />
|
|
||||||
</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="保护价" autocomplete="off" inputmode="decimal" data-lpignore="true" data-1p-ignore="true" data-form-type="other" />
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div id="hp-po-fields-option-primary">
|
|
||||||
<section class="hp-po-section" aria-labelledby="hp-po-sec-capital">
|
|
||||||
<h3 id="hp-po-sec-capital" class="hp-po-section-title">开仓</h3>
|
|
||||||
<div class="hp-po-fields hp-po-fields--section hp-po-fields--capital">
|
|
||||||
<label class="hp-po-field">
|
|
||||||
<span class="hp-po-field-lab">张数 <em>期权张</em></span>
|
|
||||||
<input type="number" step="1" min="1" id="hp-order-sheets" value="1" autocomplete="off" inputmode="numeric" data-lpignore="true" data-1p-ignore="true" data-form-type="other" title="与左侧张数同步" />
|
|
||||||
</label>
|
|
||||||
<label class="hp-po-field">
|
|
||||||
<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:期权名义÷比例=永续名义" />
|
|
||||||
</label>
|
|
||||||
<label class="hp-po-field">
|
|
||||||
<span class="hp-po-field-lab">永续杠杆 <em>env</em></span>
|
|
||||||
<input type="number" step="1" id="hp-perp-leverage" value="{{ hedge_plan_eth_leverage | default(5) }}" readonly autocomplete="off" inputmode="numeric" data-lpignore="true" data-1p-ignore="true" data-form-type="other" title="BTC=BTC_LEVERAGE(默认10) ETH=ALT_LEVERAGE(默认5),在 env「永期·永续杠杆」改" />
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
<p class="muted hp-po-max-line" id="hp-po-max-line">最大开仓 —</p>
|
</div>
|
||||||
<p class="muted hp-po-perp-auto" id="hp-po-perp-auto">永续开仓 — (按比例自动)</p>
|
<div class="options-pos-pane" data-hp-pos-pane="history" role="tabpanel" hidden>
|
||||||
{# 兼容旧 JS:权利金隐藏,市价/手动用张数定仓 #}
|
<div class="options-strike-table-wrap">
|
||||||
<input type="hidden" id="hp-premium-budget" value="0" />
|
<table class="options-strike-table hp-history-table">
|
||||||
</section>
|
<thead>
|
||||||
<section class="hp-po-section" aria-labelledby="hp-po-sec-exit">
|
<tr>
|
||||||
<h3 id="hp-po-sec-exit" class="hp-po-section-title">出场条件</h3>
|
<th>ID</th><th>类型</th><th>标的</th><th>合约</th><th>状态</th><th>合计≈U</th><th>原因</th><th>开仓</th><th>结束</th><th>操作</th>
|
||||||
<div class="hp-oo-seg hp-po-exit-seg" role="group" aria-label="出场方式">
|
</tr>
|
||||||
<button type="button" class="btn-secondary hp-po-exit-mode is-selected" data-exit="profit" title="组合净利(扣开平双边手续费)达目标金额即平"><span class="hp-oo-check" aria-hidden="true">✓</span>盈利金额出场</button>
|
</thead>
|
||||||
<button type="button" class="btn-secondary hp-po-exit-mode" data-exit="target" title="指数相对行权价达目标点数出场"><span class="hp-oo-check" aria-hidden="true">✓</span>目标位出场</button>
|
<tbody id="hp-history-tbody">
|
||||||
|
<tr><td colspan="10" class="muted">加载中…</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="hp-po-fields hp-po-fields--section" id="hp-po-exit-profit">
|
</div>
|
||||||
<label class="hp-po-field">
|
<div class="options-pos-pane" data-hp-pos-pane="stats" role="tabpanel" hidden>
|
||||||
<span class="hp-po-field-lab">目标盈利 <em>USDT</em></span>
|
<p class="muted" style="margin:0 0 8px;font-size:.82rem">按永期 / 期期分别统计</p>
|
||||||
<input type="number" step="any" min="0.01" id="hp-profit-target" placeholder="扣双边手续费后净利" autocomplete="off" inputmode="decimal" data-lpignore="true" data-1p-ignore="true" data-form-type="other" />
|
<div id="hp-stats-box" class="muted">加载中…</div>
|
||||||
</label>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="hp-po-fields hp-po-fields--section hidden" id="hp-po-exit-target" hidden>
|
|
||||||
<label class="hp-po-field">
|
|
||||||
<span class="hp-po-field-lab">期权目标位 <em>相对K</em></span>
|
|
||||||
<input type="number" step="any" id="hp-opt-target-pts" placeholder="点数" autocomplete="off" inputmode="decimal" data-lpignore="true" data-1p-ignore="true" data-form-type="other" />
|
|
||||||
</label>
|
|
||||||
<label class="hp-po-field">
|
|
||||||
<span class="hp-po-field-lab">永续目标位 <em>相对K</em></span>
|
|
||||||
<input type="number" step="any" id="hp-perp-target-pts" placeholder="点数" autocomplete="off" inputmode="decimal" data-lpignore="true" data-1p-ignore="true" data-form-type="other" />
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</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>
|
|
||||||
<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>
|
|
||||||
<button type="button" class="primary" id="hp-preview-btn" title="按左侧已选合约卖一限价开仓(期权+永续)">手动下单</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -321,52 +375,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="hp-tab-active" class="hp-tab-panel hidden" role="tabpanel" hidden>
|
|
||||||
<div class="card">
|
|
||||||
<h2>进行中的计划</h2>
|
|
||||||
<p class="muted">仅显示已启动但尚未结束的计划;可查看每条腿的当前记录状态。</p>
|
|
||||||
<div class="options-strike-table-wrap">
|
|
||||||
<table class="options-strike-table hp-history-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>ID</th><th>类型</th><th>标的</th><th>合约</th><th>状态</th><th>目标/止盈止损</th><th>开仓</th><th>操作</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="hp-active-tbody">
|
|
||||||
<tr><td colspan="8" class="muted">加载中…</td></tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="hp-tab-history" class="hp-tab-panel hidden" role="tabpanel" hidden>
|
|
||||||
<div class="card">
|
|
||||||
<h2>历史记录</h2>
|
|
||||||
<p class="muted">独立对冲计划历史(与普通交易记录分离)。点「成交细节」查看合约名与成交字段。</p>
|
|
||||||
<div class="options-strike-table-wrap">
|
|
||||||
<table class="options-strike-table hp-history-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>ID</th><th>类型</th><th>标的</th><th>合约</th><th>状态</th><th>合计≈U</th><th>原因</th><th>开仓</th><th>结束</th><th>操作</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="hp-history-tbody">
|
|
||||||
<tr><td colspan="10" class="muted">加载中…</td></tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="hp-tab-stats" class="hp-tab-panel hidden" role="tabpanel" hidden>
|
|
||||||
<div class="card">
|
|
||||||
<h2>统计分析</h2>
|
|
||||||
<p class="muted">按永期 / 期期分别统计:胜率、盈亏比、最大盈利、最大亏损、最大回撤(按结束时间累积)</p>
|
|
||||||
<div id="hp-stats-box" class="muted">加载中…</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="hp-detail-modal" class="hp-modal-backdrop" hidden>
|
<div id="hp-detail-modal" class="hp-modal-backdrop" hidden>
|
||||||
<div class="hp-modal" role="dialog" aria-modal="true" aria-labelledby="hp-detail-title">
|
<div class="hp-modal" role="dialog" aria-modal="true" aria-labelledby="hp-detail-title">
|
||||||
<div class="hp-modal-head">
|
<div class="hp-modal-head">
|
||||||
@@ -408,4 +416,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="/static/hedge_plan.js?v=50"></script>
|
<script src="/static/hedge_plan.js?v=51"></script>
|
||||||
|
|||||||
Reference in New Issue
Block a user