/* Copyright (c) 2025-2026 马建军. All rights reserved. * 专有软件 — 未经授权禁止复制、传播、转售。 * 详见 LICENSE.zh-CN.txt */ (function () { var trendPayload = null; function jsonPost(url, body) { return fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body || {}) }).then(function (r) { return r.json(); }); } function formData(form) { var fd = new FormData(form); var o = {}; fd.forEach(function (v, k) { o[k] = v; }); return o; } function esc(s) { return String(s == null ? '' : s) .replace(/&/g, '&') .replace(//g, '>'); } function showPreview(el, content, ok, isHtml) { if (!el) return; if (!content) { el.hidden = true; el.textContent = ''; el.innerHTML = ''; return; } el.hidden = false; el.style.color = ok === false ? 'var(--loss)' : ''; if (isHtml) { el.innerHTML = content; } else { el.innerHTML = ''; el.textContent = content; } } function fmtNum(v) { if (v == null || v === '') return '—'; return String(v); } function renderTrendPlanHtml(plan) { if (!plan) return ''; var summary = plan.summary_line || ( (plan.symbol_name || plan.symbol || '') + ' ' + (plan.direction_label || '') + ' ' + (plan.period_label || '') ); var detail = plan.detail_line || ''; var rows = plan.preview_rows || []; var html = '
' + esc(summary) + '
'; if (detail) { html += '
' + esc(detail) + '
'; } if (rows.length) { html += '' + '' + '' + ''; rows.forEach(function (row) { html += '' + '' + '' + '' + '' + '' + ''; }); html += '
档位触发/参考价手数加仓后均价止盈盈利(元)止损(元)盈亏比
' + esc(row.level) + '' + fmtNum(row.price) + '' + fmtNum(row.lots) + '' + fmtNum(row.avg_after) + '' + fmtNum(row.profit_at_tp) + '' + fmtNum(row.loss_at_sl) + '' + fmtNum(row.rr_ratio) + '
'; } else { html += '
目标手数 ' + fmtNum(plan.target_lots) + ' · 首仓 ' + fmtNum(plan.first_lots) + ' 手
'; } return html; } function formatRoll(preview) { if (!preview) return ''; var lines = []; if (preview.add_lots != null) lines.push('加仓手数:' + preview.add_lots); if (preview.new_stop_loss != null) lines.push('新止损:' + preview.new_stop_loss); if (preview.total_lots != null) lines.push('合计手数:' + preview.total_lots); if (preview.worst_loss != null) lines.push('最坏亏损:' + preview.worst_loss + ' 元'); if (preview.margin_usage_pct != null) { lines.push('滚仓后保证金占用:' + preview.margin_usage_pct + '%'); } if (preview.margin_cap_note) lines.push(preview.margin_cap_note); if (preview.message) lines.push(preview.message); return lines.length ? lines.join('\n') : JSON.stringify(preview, null, 2); } var trendForm = document.getElementById('trend-form'); var btnPreview = document.getElementById('btn-trend-preview'); var btnExec = document.getElementById('btn-trend-exec'); var previewEl = document.getElementById('trend-preview'); if (btnPreview && trendForm) { btnPreview.addEventListener('click', function () { btnPreview.disabled = true; jsonPost('/api/strategy/trend/preview', formData(trendForm)).then(function (d) { if (!d.ok) { showPreview(previewEl, d.error || '预览失败', false, false); btnExec.hidden = true; return; } trendPayload = formData(trendForm); showPreview(previewEl, renderTrendPlanHtml(d.plan), true, true); btnExec.hidden = false; }).finally(function () { btnPreview.disabled = false; }); }); } if (btnExec) { btnExec.addEventListener('click', function () { if (!trendPayload) return; btnExec.disabled = true; btnExec.textContent = '执行中…'; jsonPost('/api/strategy/trend/execute', trendPayload).then(function (d) { if (!d.ok) { alert(d.error); return; } location.reload(); }).finally(function () { btnExec.disabled = false; btnExec.textContent = '确认执行首仓'; }); }); } var rollForm = document.getElementById('roll-form'); var btnRollP = document.getElementById('btn-roll-preview'); var btnRollE = document.getElementById('btn-roll-exec'); var rollPrev = document.getElementById('roll-preview'); if (btnRollP && rollForm) { btnRollP.addEventListener('click', function () { btnRollP.disabled = true; jsonPost('/api/strategy/roll/preview', formData(rollForm)).then(function (d) { if (!d.ok) { showPreview(rollPrev, d.error, false, false); btnRollE.hidden = true; return; } showPreview(rollPrev, formatRoll(d.preview), true, false); btnRollE.hidden = false; }).finally(function () { btnRollP.disabled = false; }); }); } if (btnRollE && rollForm) { btnRollE.addEventListener('click', function () { btnRollE.disabled = true; btnRollE.textContent = '执行中…'; jsonPost('/api/strategy/roll/execute', formData(rollForm)).then(function (d) { if (!d.ok) { alert(d.error); return; } location.reload(); }).finally(function () { btnRollE.disabled = false; btnRollE.textContent = '执行滚仓'; }); }); } var btnStop = document.getElementById('btn-trend-stop'); if (btnStop) { btnStop.addEventListener('click', function () { var pid = document.querySelector('#trend-stop-form input[name=plan_id]'); jsonPost('/api/strategy/trend/stop', { plan_id: pid ? pid.value : 0 }).then(function (d) { if (!d.ok) { alert(d.error); return; } location.reload(); }); }); } })();