/** * 中控策略计算器:趋势回调 / 滚仓历史测算 */ (function () { const page = document.getElementById("page-calculator"); if (!page) return; let inited = false; function $(id) { return document.getElementById(id); } function esc(s) { return String(s == null ? "" : s) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """); } function num(id) { const el = $(id); if (!el) return null; const n = Number(el.value); return Number.isFinite(n) ? n : null; } function fmt(v, digits) { if (v == null || v === "") return "—"; const n = Number(v); if (!Number.isFinite(n)) return esc(v); if (digits != null) return n.toFixed(digits); return String(n); } function fmtU(v) { if (v == null || v === "") return "—"; const n = Number(v); if (!Number.isFinite(n)) return "—"; return (n >= 0 ? "+" : "") + n.toFixed(2) + "U"; } function pnlClass(v) { const n = Number(v); if (!Number.isFinite(n) || n === 0) return ""; return n > 0 ? "calc-pnl-profit" : "calc-pnl-loss"; } function syncTrendAddLabel() { const dir = ($("calc-trend-direction") && $("calc-trend-direction").value) || "long"; const lab = $("calc-trend-add-label"); if (lab) lab.textContent = dir === "short" ? "补仓下沿价" : "补仓上沿价"; } function renderTrendTable(rows) { if (!rows || !rows.length) { return '
无档位数据
'; } let html = '| 档位 | 触发价 | 张数 | 加仓后均价 | 止盈盈利 | 止损金额 | 盈亏比 | " + "
|---|---|---|---|---|---|---|
| " + esc(r.label) + " | " + "" + fmt(r.price, 4) + " | " + "" + fmt(r.contracts, 4) + " | " + "" + fmt(r.avg_entry, 4) + " | " + '' + fmtU(r.profit_u) + " | " + "" + fmtU(r.risk_u) + " | " + "" + (r.rr != null ? fmt(r.rr, 2) + ":1" : "—") + " | " + "
| 阶段 | 入场/加仓价 | 统一止损 | 本次张数 | 累计张数 | 均价 | 止损亏损 | 止盈盈利 | 盈亏比 | " + "
|---|---|---|---|---|---|---|---|---|
| " + esc(r.label) + tag + " | " + "" + fmt(r.entry_or_add_price, 4) + " | " + "" + fmt(r.stop_loss, 4) + " | " + "" + fmt(r.add_contracts, 4) + " | " + "" + fmt(r.total_contracts, 4) + " | " + "" + fmt(r.avg_entry, 4) + " | " + '' + fmtU(-Math.abs(Number(r.loss_at_sl_u) || 0)) + " | " + '' + fmtU(r.profit_at_tp_u) + " | " + "" + (r.rr != null ? fmt(r.rr, 2) + ":1" : "—") + " | " + "
' + esc(msg || "计算失败") + "
"; } async function submitTrend(e) { e.preventDefault(); const body = { direction: ($("calc-trend-direction") && $("calc-trend-direction").value) || "long", capital_usdt: num("calc-trend-capital"), risk_percent: num("calc-trend-risk"), leverage: num("calc-trend-leverage"), entry_price: num("calc-trend-entry"), stop_loss: num("calc-trend-sl"), add_upper: num("calc-trend-add-upper"), take_profit: num("calc-trend-tp"), dca_legs: num("calc-trend-dca-legs") || 5, contract_size: num("calc-trend-contract-size") || 1, }; try { const r = await fetch("/api/calculator/trend", { method: "POST", credentials: "same-origin", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); const j = await r.json(); if (!j.ok) { showErr("calc-trend-result", j.msg || "计算失败"); return; } renderTrendResult(j.data); } catch (err) { showErr("calc-trend-result", String(err)); } } async function submitRoll(e) { e.preventDefault(); const body = { direction: ($("calc-roll-direction") && $("calc-roll-direction").value) || "long", capital_usdt: num("calc-roll-capital"), risk_percent: num("calc-roll-risk"), entry_price: num("calc-roll-entry"), stop_loss: num("calc-roll-sl"), take_profit: num("calc-roll-tp"), add_legs: collectRollLegs(), legs_done: num("calc-roll-legs-done") || 0, }; try { const r = await fetch("/api/calculator/roll", { method: "POST", credentials: "same-origin", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); const j = await r.json(); if (!j.ok) { showErr("calc-roll-result", j.msg || "计算失败"); return; } renderRollResult(j.data); } catch (err) { showErr("calc-roll-result", String(err)); } } function bindOnce() { if (inited) return; inited = true; const trendForm = $("calc-trend-form"); const rollForm = $("calc-roll-form"); const dirSel = $("calc-trend-direction"); if (trendForm) trendForm.addEventListener("submit", submitTrend); if (rollForm) rollForm.addEventListener("submit", submitRoll); if (dirSel) { dirSel.addEventListener("change", syncTrendAddLabel); syncTrendAddLabel(); } bindRollLegsUI(); } window.hubCalculatorPage = { init: bindOnce, destroy: function () {}, }; })();