d467760d5c
重写滚仓计仓与四种加仓方式(市价/斐波/突破),程序盯 mark 触价成交;风险读监控单;pending 可删不可改;手动平仓同步结束滚仓。新增 /strategy/roll/docs 说明页与顺势加仓滚仓说明.md。 Co-authored-by: Cursor <cursoragent@cursor.com>
183 lines
5.7 KiB
JavaScript
183 lines
5.7 KiB
JavaScript
(function () {
|
||
"use strict";
|
||
|
||
const form = document.getElementById("roll-form");
|
||
if (!form) return;
|
||
|
||
const symbolSel = document.getElementById("roll-symbol");
|
||
const dirInput = document.getElementById("roll-direction");
|
||
const modeSel = document.getElementById("roll-add-mode");
|
||
const riskBanner = document.getElementById("roll-risk-banner");
|
||
const previewBtn = document.getElementById("roll-preview-btn");
|
||
const submitBtn = document.getElementById("roll-submit-btn");
|
||
const previewBox = document.getElementById("roll-preview-box");
|
||
const previewText = document.getElementById("roll-preview-text");
|
||
const countdownEl = document.getElementById("roll-countdown");
|
||
|
||
let countdownTimer = null;
|
||
let previewOk = false;
|
||
let lastPreviewMode = "";
|
||
|
||
function qs(sel) {
|
||
return form.querySelector(sel);
|
||
}
|
||
|
||
function selectedOption() {
|
||
return symbolSel.options[symbolSel.selectedIndex];
|
||
}
|
||
|
||
function syncDirectionLock() {
|
||
const opt = selectedOption();
|
||
if (!opt || !opt.value) {
|
||
riskBanner.textContent = "当前风险:请选择持仓币种";
|
||
return;
|
||
}
|
||
const dir = opt.getAttribute("data-direction") || "long";
|
||
const rp = opt.getAttribute("data-risk-percent") || "—";
|
||
dirInput.value = dir;
|
||
riskBanner.textContent = "当前风险:" + rp + "%(来自监控单 #" + (opt.getAttribute("data-monitor-id") || "?") + ")";
|
||
}
|
||
|
||
function syncFieldVisibility() {
|
||
const mode = modeSel.value;
|
||
form.querySelectorAll(".roll-field-fib").forEach(function (el) {
|
||
el.style.display = mode === "fib_618" || mode === "fib_786" ? "inline-flex" : "none";
|
||
});
|
||
form.querySelectorAll(".roll-field-breakout").forEach(function (el) {
|
||
el.style.display = mode === "breakout" ? "inline-flex" : "none";
|
||
});
|
||
const fibInputs = [qs("#roll-fib-upper"), qs("#roll-fib-lower")];
|
||
const bpInput = qs("#roll-breakout");
|
||
fibInputs.forEach(function (inp) {
|
||
if (inp) inp.required = mode === "fib_618" || mode === "fib_786";
|
||
});
|
||
if (bpInput) bpInput.required = mode === "breakout";
|
||
resetPreview();
|
||
}
|
||
|
||
function resetPreview() {
|
||
previewOk = false;
|
||
submitBtn.disabled = true;
|
||
previewBox.style.display = "none";
|
||
countdownEl.style.display = "none";
|
||
if (countdownTimer) {
|
||
clearInterval(countdownTimer);
|
||
countdownTimer = null;
|
||
}
|
||
}
|
||
|
||
function formPayload() {
|
||
const fd = new FormData(form);
|
||
const obj = {};
|
||
fd.forEach(function (v, k) {
|
||
obj[k] = v;
|
||
});
|
||
return obj;
|
||
}
|
||
|
||
function runPreview() {
|
||
resetPreview();
|
||
if (!symbolSel.value) {
|
||
alert("请先选择持仓币种");
|
||
return;
|
||
}
|
||
previewBtn.disabled = true;
|
||
fetch("/strategy/roll/preview", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
||
body: JSON.stringify(formPayload()),
|
||
credentials: "same-origin",
|
||
})
|
||
.then(function (r) {
|
||
return r.json();
|
||
})
|
||
.then(function (data) {
|
||
previewBtn.disabled = false;
|
||
if (!data.ok) {
|
||
alert(data.msg || "预览失败");
|
||
return;
|
||
}
|
||
const p = data.preview || {};
|
||
lastPreviewMode = p.add_mode || modeSel.value;
|
||
previewText.innerHTML =
|
||
"<strong>" +
|
||
(p.add_mode_label || "") +
|
||
"</strong> · 约 <strong>" +
|
||
(p.add_amount_display != null ? p.add_amount_display : p.add_amount_raw) +
|
||
"</strong> 张<br>" +
|
||
"加仓参考价 " +
|
||
(p.add_price_display != null ? p.add_price_display : p.add_price) +
|
||
" · 新止损 " +
|
||
(p.new_sl_display != null ? p.new_sl_display : p.new_stop_loss) +
|
||
"<br>" +
|
||
"合并均价 " +
|
||
p.avg_entry_after +
|
||
" · 打到止损约 " +
|
||
p.loss_at_sl_usdt +
|
||
"U(风险预算 " +
|
||
(p.risk_budget_usdt != null ? p.risk_budget_usdt : "—") +
|
||
"U)";
|
||
previewBox.style.display = "block";
|
||
previewOk = true;
|
||
if (lastPreviewMode === "market") {
|
||
startCountdown(10);
|
||
} else {
|
||
submitBtn.disabled = false;
|
||
countdownEl.style.display = "none";
|
||
}
|
||
})
|
||
.catch(function () {
|
||
previewBtn.disabled = false;
|
||
alert("预览请求失败");
|
||
});
|
||
}
|
||
|
||
function startCountdown(sec) {
|
||
let left = sec;
|
||
submitBtn.disabled = true;
|
||
countdownEl.style.display = "block";
|
||
countdownEl.textContent = "市价加仓:" + left + " 秒后可执行(可取消刷新预览)";
|
||
countdownTimer = setInterval(function () {
|
||
left -= 1;
|
||
if (left <= 0) {
|
||
clearInterval(countdownTimer);
|
||
countdownTimer = null;
|
||
countdownEl.textContent = "可以执行市价加仓";
|
||
submitBtn.disabled = false;
|
||
return;
|
||
}
|
||
countdownEl.textContent = "市价加仓:" + left + " 秒后可执行";
|
||
}, 1000);
|
||
}
|
||
|
||
symbolSel.addEventListener("change", function () {
|
||
syncDirectionLock();
|
||
resetPreview();
|
||
});
|
||
modeSel.addEventListener("change", syncFieldVisibility);
|
||
form.addEventListener("input", resetPreview);
|
||
form.addEventListener("change", function (e) {
|
||
if (e.target !== previewBtn) resetPreview();
|
||
});
|
||
previewBtn.addEventListener("click", runPreview);
|
||
form.addEventListener("submit", function (e) {
|
||
if (!previewOk) {
|
||
e.preventDefault();
|
||
alert("请先点击预览");
|
||
return;
|
||
}
|
||
if (lastPreviewMode === "market" && submitBtn.disabled) {
|
||
e.preventDefault();
|
||
alert("请等待 10 秒确认倒计时结束");
|
||
return;
|
||
}
|
||
const modeLabel = modeSel.options[modeSel.selectedIndex].text;
|
||
if (!confirm("确认提交「" + modeLabel + "」?")) {
|
||
e.preventDefault();
|
||
}
|
||
});
|
||
|
||
syncDirectionLock();
|
||
syncFieldVisibility();
|
||
})();
|