/** * 实盘下单:币种 + 止盈止损填完后拉 /api/order_defaults 快照,在开仓按钮前显示预估盈亏比。 * 仅价格 / 百分比模式;固定盈亏比模式沿用「预估止盈」,不显示预估盈亏比。 */ (function (global) { "use strict"; let debounceMs = 400; let minRr = 1.5; let debounceTimer = null; let fetchSeq = 0; function $(id) { return document.getElementById(id); } function num(v) { const n = Number(v); return Number.isFinite(n) ? n : null; } function formatRr(rr) { if (rr === null || typeof rr === "undefined") return "—"; const n = Number(rr); if (!Number.isFinite(n)) return "—"; const body = Number.isInteger(n) ? String(n) : String(parseFloat(n.toFixed(2))); return body + ":1"; } function calcRr(direction, entry, sl, tp) { const e = num(entry); const s = num(sl); const t = num(tp); if (e === null || s === null || t === null) return null; if (direction === "short") { if (s <= e || t >= e) return null; return (e - t) / (s - e); } if (s >= e || t <= e) return null; return (t - e) / (e - s); } function calcRrFromPct(slPct, tpPct) { const sl = num(slPct); const tp = num(tpPct); if (sl === null || tp === null || sl <= 0 || tp <= 0) return null; return tp / sl; } function currentMode() { return ($("sltp-mode") && $("sltp-mode").value) || "fixed_rr"; } function currentDirection() { return ($("order-direction") && $("order-direction").value) || "long"; } function currentSymbol() { return (($("order-symbol") && $("order-symbol").value) || "").trim(); } function inputsComplete(m) { const dir = currentDirection(); if (!currentSymbol() || !dir) return false; if (m === "pct") { const sl = num($("order-sl-pct") && $("order-sl-pct").value); const tp = num($("order-tp-pct") && $("order-tp-pct").value); return sl !== null && tp !== null && sl > 0 && tp > 0; } const sl = num($("order-sl") && $("order-sl").value); const tp = num($("order-tp") && $("order-tp").value); return sl !== null && tp !== null && sl > 0 && tp > 0; } function hidePreview() { const el = $("order-rr-preview"); if (el) el.style.display = "none"; } function paint(rr, state) { const el = $("order-rr-preview"); if (!el) return; const m = currentMode(); if (m === "fixed_rr" || (m !== "price" && m !== "pct")) { el.style.display = "none"; return; } el.style.display = ""; if (state === "empty") { el.textContent = "预估盈亏比:—"; el.style.color = "#8fc8ff"; return; } if (state === "loading") { el.textContent = "预估盈亏比:计算中…"; el.style.color = "#8fc8ff"; return; } if (state === "fetch_fail") { el.textContent = "预估盈亏比:取价失败"; el.style.color = "#ff8f8f"; return; } if (rr === null) { el.textContent = "预估盈亏比:无效"; el.style.color = "#ff8f8f"; return; } el.textContent = "预估盈亏比:" + formatRr(rr); el.style.color = rr >= minRr ? "#4cd97f" : "#ff8f8f"; } function refreshNow() { const m = currentMode(); if (m === "fixed_rr") { hidePreview(); return; } if (!inputsComplete(m)) { paint(null, "empty"); return; } const sym = currentSymbol(); const dir = currentDirection(); const seq = ++fetchSeq; paint(null, "loading"); fetch( "/api/order_defaults?symbol=" + encodeURIComponent(sym) + "&direction=" + encodeURIComponent(dir) ) .then(function (r) { return r.json(); }) .then(function (data) { if (seq !== fetchSeq) return; if (!data.ok) { paint(null, "fetch_fail"); return; } if (m === "pct") { const rr = calcRrFromPct( $("order-sl-pct") && $("order-sl-pct").value, $("order-tp-pct") && $("order-tp-pct").value ); paint(rr, rr === null ? "invalid" : "ok"); return; } const entry = num(data.last_price != null ? data.last_price : data.price); if (entry === null) { paint(null, "fetch_fail"); return; } const sl = num($("order-sl") && $("order-sl").value); const tp = num($("order-tp") && $("order-tp").value); const rr = calcRr(dir, entry, sl, tp); paint(rr, rr === null ? "invalid" : "ok"); }) .catch(function () { if (seq !== fetchSeq) return; paint(null, "fetch_fail"); }); } function schedule() { clearTimeout(debounceTimer); debounceTimer = setTimeout(refreshNow, debounceMs); } function wire(opts) { opts = opts || {}; if (opts.minRr != null && Number.isFinite(Number(opts.minRr))) { minRr = Number(opts.minRr); } if (opts.debounceMs != null && Number.isFinite(Number(opts.debounceMs))) { debounceMs = Number(opts.debounceMs); } [ "order-symbol", "order-direction", "sltp-mode", "order-sl", "order-tp", "order-sl-pct", "order-tp-pct", "order-fixed-rr", ].forEach(function (id) { const el = $(id); if (!el || el._rrPreviewBound) return; el._rrPreviewBound = true; el.addEventListener("input", schedule); el.addEventListener("change", schedule); }); schedule(); } global.ManualOrderRrPreview = { wire: wire, schedule: schedule, refresh: refreshNow, calcRr: calcRr, calcRrFromPct: calcRrFromPct, formatRr: formatRr, }; })(typeof window !== "undefined" ? window : globalThis);