/* Copyright (c) 2025-2026 马建军. All rights reserved. * 专有软件 — 未经授权禁止复制、传播、转售。 * 详见 LICENSE.zh-CN.txt */ (function () { var cache = null; function fmtNum(v, suffix) { if (v === null || v === undefined || v === '') return '-'; var n = Number(v); if (isNaN(n)) return String(v); var s = Number.isInteger(n) ? String(n) : n.toFixed(2); return suffix ? s + suffix : s; } function fmtMoney(v) { if (v === null || v === undefined) return '-'; return fmtNum(v) + ' 元'; } function fmtPct(v) { if (v === null || v === undefined) return '-'; return fmtNum(v) + '%'; } function setSummary(s) { var map = { total_trades: function () { return fmtNum(s.total_trades); }, win_rate: function () { return fmtPct(s.win_rate); }, avg_profit: function () { return fmtMoney(s.avg_profit); }, avg_loss: function () { return fmtMoney(s.avg_loss); }, profit_loss_ratio: function () { return fmtNum(s.profit_loss_ratio); }, consecutive_losses: function () { return fmtNum(s.consecutive_losses); }, max_drawdown: function () { var amt = fmtMoney(s.max_drawdown); var pct = s.max_drawdown_pct ? ' (' + fmtPct(s.max_drawdown_pct) + ')' : ''; return amt + pct; }, max_loss_amount: function () { return fmtMoney(s.max_loss_amount); }, max_loss_pct: function () { return fmtPct(s.max_loss_pct); }, max_profit_amount: function () { return fmtMoney(s.max_profit_amount); }, max_profit_pct: function () { return fmtPct(s.max_profit_pct); }, total_fee: function () { return fmtMoney(s.total_fee); }, emotion_count: function () { return fmtNum(s.emotion_count); }, emotion_ratio: function () { return fmtPct(s.emotion_ratio); }, }; document.querySelectorAll('#stats-summary [data-k]').forEach(function (el) { var key = el.getAttribute('data-k'); el.textContent = map[key] ? map[key]() : '-'; }); } function fillViewSelect(views, selected) { var sel = document.getElementById('stats-view-select'); if (!sel) return; sel.innerHTML = ''; views.forEach(function (v) { var opt = document.createElement('option'); opt.value = v.key; opt.textContent = v.label; if (v.key === selected) opt.selected = true; sel.appendChild(opt); }); } function cellClass(key, val) { if (key === 'total_net' || key === 'max_profit' || key === 'avg_profit') { if (val > 0) return 'text-profit'; if (val < 0) return 'text-loss'; } if (key === 'max_loss' || key === 'avg_loss' || key === 'total_fee') { return 'text-loss'; } return ''; } function renderBreakdown(key) { if (!cache || !cache.breakdowns) return; var block = cache.breakdowns[key]; var head = document.getElementById('stats-breakdown-head'); var body = document.getElementById('stats-breakdown-body'); if (!block || !head || !body) return; head.innerHTML = ''; block.columns.forEach(function (col) { var th = document.createElement('th'); th.textContent = col.label; head.appendChild(th); }); body.innerHTML = ''; if (!block.rows || !block.rows.length) { var tr = document.createElement('tr'); var td = document.createElement('td'); td.colSpan = block.columns.length; td.className = 'text-muted'; td.textContent = '暂无数据'; tr.appendChild(td); body.appendChild(tr); return; } block.rows.forEach(function (row) { var tr = document.createElement('tr'); block.columns.forEach(function (col) { var td = document.createElement('td'); var val = row[col.key]; if (col.key === 'win_rate') { td.textContent = fmtPct(val); } else if (col.key === 'label') { td.textContent = val || '-'; } else if (typeof val === 'number') { td.textContent = fmtNum(val); td.className = cellClass(col.key, val); } else { td.textContent = val != null ? val : '-'; } tr.appendChild(td); }); body.appendChild(tr); }); } function applyData(data) { cache = data; setSummary(data.summary || {}); var views = data.views || []; var sel = document.getElementById('stats-view-select'); var current = sel && sel.value ? sel.value : (views[0] && views[0].key); fillViewSelect(views, current); renderBreakdown(current); var updated = document.getElementById('stats-updated'); if (updated) { updated.textContent = data.updated_at ? '统计更新于 ' + data.updated_at.replace('T', ' ') : '统计已加载'; } } function loadStats() { fetch('/api/stats') .then(function (r) { return r.json(); }) .then(applyData) .catch(function () { var updated = document.getElementById('stats-updated'); if (updated) updated.textContent = '加载失败,请刷新页面'; }); } function bootStatsPage() { var viewSel = document.getElementById('stats-view-select'); if (viewSel && !viewSel.dataset.statsBound) { viewSel.dataset.statsBound = '1'; viewSel.addEventListener('change', function () { renderBreakdown(this.value); }); } loadStats(); } if (window.qihuoPageBoot) window.qihuoPageBoot(bootStatsPage, '#stats-summary'); else if (window.qihuoOnPageLoad) window.qihuoOnPageLoad(bootStatsPage); else document.addEventListener('DOMContentLoaded', bootStatsPage); })();