单独期权增加翻倍出场:可开关、自选倍数(默认1倍=盈利等于权利金),达标后买一限价平。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-08-12 10:31:29 +08:00
parent 8dda7500df
commit cd23ea74a6
11 changed files with 728 additions and 13 deletions
+116 -1
View File
@@ -1406,6 +1406,17 @@
}
body.target_index = tgt;
}
const peEnabled = !!(document.getElementById("opt-profit-exit-enabled") || {}).checked;
if (peEnabled) {
const multRaw = (document.getElementById("opt-profit-exit-mult") || {}).value;
const mult = parseFloat(multRaw);
if (!Number.isFinite(mult) || mult <= 0) {
alert("翻倍倍数无效");
return false;
}
body.profit_exit_enabled = true;
body.profit_exit_mult = mult;
}
const d = await apiJson("/api/options/open", {
method: "POST",
headers: { "Content-Type": "application/json" },
@@ -1484,7 +1495,42 @@
const hint = closeGateHint(closePreview);
return hint ? '<div class="muted opt-bid-invalid-hint">' + hint + "</div>" : "";
})() +
renderTargetDelegateRow(p)
renderTargetDelegateRow(p) +
renderProfitExitRow(p)
);
}
function renderProfitExitRow(p) {
const inst = p.inst_id || "";
if (p.hedge_plan_target) {
return "";
}
const enabled = !!p.profit_exit_enabled;
const mult = p.profit_exit_mult != null && Number(p.profit_exit_mult) > 0
? Number(p.profit_exit_mult)
: 1;
const state = String(p.profit_exit_state || (enabled ? "active" : "idle"));
const req = p.profit_exit_required_recycle;
let statusTxt = enabled
? ("监控中 · " + fmt(mult, 2) + "倍")
: "未开启";
if (enabled && state === "closing") statusTxt = "平仓挂单中 · " + fmt(mult, 2) + "倍";
return (
'<div class="opt-target-row opt-profit-exit-pos-row" data-inst="' + inst + '">' +
'<span class="opt-target-row-label">翻倍</span>' +
'<label class="opt-profit-exit-toggle"><input type="checkbox" class="opt-pos-profit-exit-enabled" data-inst="' +
inst + '"' + (enabled ? " checked" : "") + "> 开启</label>" +
'<input type="number" class="opt-pos-profit-exit-mult" data-inst="' + inst +
'" min="0.1" step="0.1" value="' + mult + '"' + (enabled ? "" : " disabled") + ">" +
'<button type="button" class="btn-secondary opt-profit-exit-save-btn" data-inst="' +
inst + '">应用</button>' +
'<span class="opt-target-armed">' + statusTxt + "</span>" +
'<span class="muted opt-target-row-hint">' +
(enabled
? ("1倍=盈利=权利金" + (req != null ? (" · 需回收≥" + fmtUsdc(req)) : ""))
: "开启后自选倍数;达标按买一限价平;可随时关闭") +
"</span>" +
"</div>"
);
}
@@ -1686,6 +1732,30 @@
cancelPositionTarget(btn.getAttribute("data-inst"), btn);
});
});
container.querySelectorAll(".opt-profit-exit-save-btn").forEach(function (btn) {
btn.addEventListener("click", function (e) {
e.stopPropagation();
savePositionProfitExit(btn.getAttribute("data-inst"), btn);
});
});
container.querySelectorAll(".opt-pos-profit-exit-enabled").forEach(function (cb) {
cb.addEventListener("click", function (e) { e.stopPropagation(); });
cb.addEventListener("change", function () {
const row = cb.closest(".opt-profit-exit-pos-row");
const multInp = row && row.querySelector(".opt-pos-profit-exit-mult");
if (multInp) multInp.disabled = !cb.checked;
});
});
container.querySelectorAll(".opt-pos-profit-exit-mult").forEach(function (inp) {
inp.addEventListener("click", function (e) { e.stopPropagation(); });
inp.addEventListener("keydown", function (e) {
if (e.key === "Enter") {
e.preventDefault();
e.stopPropagation();
savePositionProfitExit(inp.getAttribute("data-inst"), null);
}
});
});
container.querySelectorAll(".opt-pos-target-input").forEach(function (inp) {
inp.addEventListener("click", function (e) { e.stopPropagation(); });
inp.addEventListener("input", function () {
@@ -1776,6 +1846,39 @@
}
}
async function savePositionProfitExit(inst, btn) {
if (!inst) return;
const card = document.querySelector('.opt-pos-card[data-inst="' + inst + '"]') ||
document.querySelector('.opt-pos-accordion-item[data-inst="' + inst + '"]');
const row = card ? card.querySelector(".opt-profit-exit-pos-row") : null;
const enabledEl = row ? row.querySelector(".opt-pos-profit-exit-enabled") : null;
const multEl = row ? row.querySelector(".opt-pos-profit-exit-mult") : null;
const enabled = !!(enabledEl && enabledEl.checked);
let mult = 1;
if (enabled) {
mult = parseFloat(multEl ? multEl.value : "1");
if (!Number.isFinite(mult) || mult <= 0) {
alert("翻倍倍数无效");
return;
}
}
if (btn) btn.disabled = true;
try {
const d = await apiJson("/api/options/profit-exit", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ inst_id: inst, enabled: enabled, mult: mult }),
});
if (!d.ok) {
alert(d.msg || "保存失败");
return;
}
await refreshAllPositions();
} finally {
if (btn) btn.disabled = false;
}
}
function paintTargetMonitors(list) {
const box = document.getElementById("opt-target-monitors");
const host = document.getElementById("opt-target-monitors-list");
@@ -2349,6 +2452,18 @@
bindOptionsPosTabs();
hardenOrderAutofill();
(function bindProfitExitOpenControls() {
const peCb = document.getElementById("opt-profit-exit-enabled");
const peMult = document.getElementById("opt-profit-exit-mult");
if (!peCb || !peMult) return;
function sync() {
peMult.disabled = !peCb.checked;
if (peCb.checked && (!peMult.value || Number(peMult.value) <= 0)) peMult.value = "1";
}
peCb.addEventListener("change", sync);
sync();
})();
document.querySelectorAll('input[name="opt-size-mode"]').forEach(function (r) {
r.addEventListener("change", function () {
updateSizeInputs();