Files
crypto_monitor/static/strategy_roll.js
T
dekun d467760d5c 顺势加仓 v2:程序监控滚仓、文档页与平仓同步
重写滚仓计仓与四种加仓方式(市价/斐波/突破),程序盯 mark 触价成交;风险读监控单;pending 可删不可改;手动平仓同步结束滚仓。新增 /strategy/roll/docs 说明页与顺势加仓滚仓说明.md。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-26 22:03:23 +08:00

183 lines
5.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
(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();
})();