diff --git a/crypto_monitor_binance/templates/index.html b/crypto_monitor_binance/templates/index.html
index 3953607..3c1dcb4 100644
--- a/crypto_monitor_binance/templates/index.html
+++ b/crypto_monitor_binance/templates/index.html
@@ -834,7 +834,7 @@
-
+
-
+
-
+
-
+
-
+
{% include 'embed_boot_scripts.html' %}
diff --git a/static/instance_theme.css b/static/instance_theme.css
index 98f60da..f117fac 100644
--- a/static/instance_theme.css
+++ b/static/instance_theme.css
@@ -1305,6 +1305,12 @@ html[data-theme="light"] .detail-actions {
align-items: center;
}
+#roll-form[data-add-mode="fib_618"] #roll-preview-btn,
+#roll-form[data-add-mode="fib_786"] #roll-preview-btn,
+#roll-form[data-add-mode="breakout"] #roll-preview-btn {
+ display: none !important;
+}
+
#strategy-roll-panel .roll-risk-banner {
margin-bottom: 8px;
color: #8fc8ff;
@@ -1341,12 +1347,30 @@ html[data-theme="light"] #strategy-roll-panel .roll-section-title {
color: #dde2ff;
}
+#roll-preview-box.roll-preview-box.is-error {
+ border-color: #8a3a4a;
+ background: #1a1218;
+ color: #ffb4b4;
+}
+
+#roll-preview-box.roll-preview-box.is-preview {
+ border-color: #3a5a8a;
+ background: #141a28;
+ color: #dde2ff;
+}
+
html[data-theme="light"] #roll-preview-box.roll-preview-box {
background: #f6f9fc !important;
border-color: #b8c8d8 !important;
color: #1a2838 !important;
}
+html[data-theme="light"] #roll-preview-box.roll-preview-box.is-error {
+ background: #fff5f5 !important;
+ border-color: #d8a0a8 !important;
+ color: #8a2030 !important;
+}
+
#roll-countdown.roll-countdown {
margin-top: 6px;
color: #ffb347;
diff --git a/static/strategy_roll.js b/static/strategy_roll.js
index c4c8d02..85779e8 100644
--- a/static/strategy_roll.js
+++ b/static/strategy_roll.js
@@ -42,10 +42,21 @@
const previewBox = document.getElementById("roll-preview-box");
const previewText = document.getElementById("roll-preview-text");
const countdownEl = document.getElementById("roll-countdown");
+ const trendLocked = submitBtn && submitBtn.getAttribute("data-trend-locked") === "1";
let countdownTimer = null;
let previewOk = false;
let lastPreviewMode = "";
+ let allowMonitorSubmit = false;
+
+ function isMarketMode() {
+ return (modeSel.value || "market") === "market";
+ }
+
+ function isMonitorMode() {
+ const m = modeSel.value || "market";
+ return m === "fib_618" || m === "fib_786" || m === "breakout";
+ }
function selectedOption() {
return symbolSel.options[symbolSel.selectedIndex];
@@ -64,6 +75,57 @@
"当前风险:" + rp + "%(来自监控单 #" + (opt.getAttribute("data-monitor-id") || "?") + ")";
}
+ function syncSubmitButton() {
+ if (!submitBtn || trendLocked) return;
+ if (isMonitorMode()) {
+ submitBtn.disabled = false;
+ return;
+ }
+ submitBtn.disabled = !previewOk || !!countdownTimer;
+ }
+
+ function clearMessageBox() {
+ if (!previewBox) return;
+ previewBox.style.display = "none";
+ previewBox.classList.remove("is-error", "is-preview");
+ if (previewText) previewText.textContent = "";
+ if (countdownEl) countdownEl.style.display = "none";
+ }
+
+ function showReject(msg) {
+ if (!previewBox || !previewText) return;
+ previewBox.style.display = "block";
+ previewBox.classList.remove("is-preview");
+ previewBox.classList.add("is-error");
+ previewText.textContent = msg || "无法执行";
+ if (countdownEl) countdownEl.style.display = "none";
+ }
+
+ function showPreviewResult(p) {
+ if (!previewBox || !previewText) return;
+ previewBox.style.display = "block";
+ previewBox.classList.remove("is-error");
+ previewBox.classList.add("is-preview");
+ previewText.innerHTML =
+ "" +
+ (p.add_mode_label || "") +
+ " · 约 " +
+ (p.add_amount_display != null ? p.add_amount_display : p.add_amount_raw) +
+ " 张
" +
+ "加仓参考价 " +
+ (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) +
+ "
" +
+ "合并均价 " +
+ p.avg_entry_after +
+ " · 打到止损约 " +
+ p.loss_at_sl_usdt +
+ "U(风险预算 " +
+ (p.risk_budget_usdt != null ? p.risk_budget_usdt : "—") +
+ "U)";
+ }
+
function syncFieldVisibility() {
syncRollFormMode(form, modeSel.value || "market");
resetPreview();
@@ -71,13 +133,13 @@
function resetPreview() {
previewOk = false;
- if (submitBtn) submitBtn.disabled = true;
- if (previewBox) previewBox.style.display = "none";
- if (countdownEl) countdownEl.style.display = "none";
+ allowMonitorSubmit = false;
+ clearMessageBox();
if (countdownTimer) {
clearInterval(countdownTimer);
countdownTimer = null;
}
+ syncSubmitButton();
}
function formPayload() {
@@ -89,78 +151,98 @@
return obj;
}
- function runPreview() {
- resetPreview();
- if (!symbolSel.value) {
- alert("请先选择持仓币种");
- return;
- }
- previewBtn.disabled = true;
- fetch("/strategy/roll/preview", {
+ function requestPreview() {
+ return 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 (r) {
+ return r.json();
+ });
+ }
+
+ function runPreview() {
+ resetPreview();
+ if (!symbolSel.value) {
+ showReject("请先选择持仓币种");
+ return;
+ }
+ if (previewBtn) previewBtn.disabled = true;
+ requestPreview()
.then(function (data) {
- previewBtn.disabled = false;
+ if (previewBtn) previewBtn.disabled = false;
if (!data.ok) {
- alert(data.msg || "预览失败");
+ showReject(data.msg || "预览失败");
return;
}
const p = data.preview || {};
lastPreviewMode = p.add_mode || modeSel.value;
- previewText.innerHTML =
- "" +
- (p.add_mode_label || "") +
- " · 约 " +
- (p.add_amount_display != null ? p.add_amount_display : p.add_amount_raw) +
- " 张
" +
- "加仓参考价 " +
- (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) +
- "
" +
- "合并均价 " +
- p.avg_entry_after +
- " · 打到止损约 " +
- p.loss_at_sl_usdt +
- "U(风险预算 " +
- (p.risk_budget_usdt != null ? p.risk_budget_usdt : "—") +
- "U)";
- previewBox.style.display = "block";
+ showPreviewResult(p);
previewOk = true;
if (lastPreviewMode === "market") {
startCountdown(10);
- } else if (submitBtn) {
- submitBtn.disabled = false;
- countdownEl.style.display = "none";
+ } else {
+ syncSubmitButton();
}
})
.catch(function () {
- previewBtn.disabled = false;
- alert("预览请求失败");
+ if (previewBtn) previewBtn.disabled = false;
+ showReject("预览请求失败,请稍后重试");
+ });
+ }
+
+ function runMonitorSubmit() {
+ if (!symbolSel.value) {
+ showReject("请先选择持仓币种");
+ return;
+ }
+ if (submitBtn) submitBtn.disabled = true;
+ requestPreview()
+ .then(function (data) {
+ if (submitBtn && !trendLocked) submitBtn.disabled = false;
+ if (!data.ok) {
+ showReject(data.msg || "无法提交监控");
+ return;
+ }
+ const p = data.preview || {};
+ const modeLabel = modeSel.options[modeSel.selectedIndex].text;
+ const summary =
+ "约 " +
+ (p.add_amount_display != null ? p.add_amount_display : p.add_amount_raw) +
+ " 张 · 触发参考价 " +
+ (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);
+ if (!confirm("确认提交「" + modeLabel + "」?\n" + summary)) {
+ return;
+ }
+ allowMonitorSubmit = true;
+ form.requestSubmit();
+ })
+ .catch(function () {
+ if (submitBtn && !trendLocked) submitBtn.disabled = false;
+ showReject("校验请求失败,请稍后重试");
});
}
function startCountdown(sec) {
let left = sec;
if (submitBtn) submitBtn.disabled = true;
- countdownEl.style.display = "block";
- countdownEl.textContent = "市价加仓:" + left + " 秒后可执行(可取消刷新预览)";
+ if (countdownEl) {
+ countdownEl.style.display = "block";
+ countdownEl.textContent = "市价加仓:" + left + " 秒后可执行(修改表单将取消预览)";
+ }
countdownTimer = setInterval(function () {
left -= 1;
if (left <= 0) {
clearInterval(countdownTimer);
countdownTimer = null;
- countdownEl.textContent = "可以执行市价加仓";
- if (submitBtn) submitBtn.disabled = false;
+ if (countdownEl) countdownEl.textContent = "可以执行市价加仓";
+ syncSubmitButton();
return;
}
- countdownEl.textContent = "市价加仓:" + left + " 秒后可执行";
+ if (countdownEl) countdownEl.textContent = "市价加仓:" + left + " 秒后可执行";
}, 1000);
}
@@ -173,16 +255,25 @@
form.addEventListener("change", function (e) {
if (e.target !== previewBtn) resetPreview();
});
- previewBtn.addEventListener("click", runPreview);
+ if (previewBtn) previewBtn.addEventListener("click", runPreview);
form.addEventListener("submit", function (e) {
- if (!previewOk) {
+ if (isMonitorMode()) {
+ if (allowMonitorSubmit) {
+ allowMonitorSubmit = false;
+ return;
+ }
e.preventDefault();
- alert("请先点击预览");
+ runMonitorSubmit();
return;
}
- if (lastPreviewMode === "market" && submitBtn && submitBtn.disabled) {
+ if (!previewOk) {
e.preventDefault();
- alert("请等待 10 秒确认倒计时结束");
+ showReject("请先点击「预览」并通过校验");
+ return;
+ }
+ if (submitBtn && submitBtn.disabled) {
+ e.preventDefault();
+ showReject("请等待 10 秒确认倒计时结束后再执行市价加仓");
return;
}
const modeLabel = modeSel.options[modeSel.selectedIndex].text;
diff --git a/strategy_templates/strategy_roll_panel.html b/strategy_templates/strategy_roll_panel.html
index 3d52d7f..4e02a36 100644
--- a/strategy_templates/strategy_roll_panel.html
+++ b/strategy_templates/strategy_roll_panel.html
@@ -6,7 +6,7 @@
仅人工提交;须先在「实盘下单」有同向持仓。仅以损定仓模式可用。
做多/做空各最多滚仓 3 次(仅计已成交腿);止盈锁定首仓不变。
风险比例读取所选监控单,不可手改;打到新止损时合并持仓亏损 ≈ 1 个风险单位(当前基数 × 监控 risk%)。
- 斐波/突破为程序监控(mark 价穿越触发),触价后市价加仓;同时仅允许 1 条监控中腿,提交后不可修改,可删除。
+ 斐波/突破为程序监控(mark 价穿越触发),触价后市价加仓;填写后直接点「执行滚仓」(无需预览)。同时仅允许 1 条监控中腿,提交后不可修改,可删除。
手动平仓后滚仓监控自动结束;已成交腿历史保留供复盘。
→ 顺势加仓完整逻辑说明
{% if roll_trend_active %}当前有运行中的趋势回调计划,请先结束后再滚仓。{% endif %}
@@ -44,11 +44,11 @@
-
-
+
+
-