单独期权增加翻倍出场:可开关、自选倍数(默认1倍=盈利等于权利金),达标后买一限价平。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user