// 高级功能:多策略对比、统计分析、参数热力图 // ===== 多策略对比功能 ===== async function initComparison() { // 加载策略列表到两个下拉框 const response = await fetch('/api/equity/list'); const result = await response.json(); if (result.success) { const select1 = document.getElementById('compare-strategy1'); const select2 = document.getElementById('compare-strategy2'); select1.innerHTML = ''; select2.innerHTML = ''; result.files.forEach(file => { const option1 = document.createElement('option'); const option2 = document.createElement('option'); option1.value = file.filename; option2.value = file.filename; option1.textContent = file.strategy; option2.textContent = file.strategy; select1.appendChild(option1); select2.appendChild(option2); }); } document.getElementById('compare-btn').addEventListener('click', compareStrategies); } async function compareStrategies() { const file1 = document.getElementById('compare-strategy1').value; const file2 = document.getElementById('compare-strategy2').value; if (!file1 || !file2) { alert('请选择两个策略进行对比'); return; } // 加载两个策略的数据 const [data1, data2] = await Promise.all([ fetch(`/api/equity/${file1}`).then(r => r.json()), fetch(`/api/equity/${file2}`).then(r => r.json()) ]); if (data1.success && data2.success) { // 显示统计数据 displayStrategyStats('strategy1-stats', data1.stats); displayStrategyStats('strategy2-stats', data2.stats); displayComparisonDiff('comparison-diff', data1.stats, data2.stats); // 绘制对比图表 renderComparisonChart(data1.data, data2.data, file1, file2); } } function displayStrategyStats(elementId, stats) { const element = document.getElementById(elementId); element.innerHTML = `

初始资金: ¥${stats.initial_asset.toLocaleString()}

最终资金: ¥${stats.final_asset.toLocaleString()}

总收益率: ${(stats.total_return * 100).toFixed(2)}%

最大回撤: ${(stats.max_drawdown * 100).toFixed(2)}%

回测天数: ${stats.num_days}天

`; } function displayComparisonDiff(elementId, stats1, stats2) { const element = document.getElementById(elementId); const returnDiff = (stats1.total_return - stats2.total_return) * 100; const ddDiff = (stats1.max_drawdown - stats2.max_drawdown) * 100; element.innerHTML = `

收益率差: ${returnDiff > 0 ? '+' : ''}${returnDiff.toFixed(2)}%

回撤差: ${ddDiff > 0 ? '+' : ''}${ddDiff.toFixed(2)}%

策略${returnDiff > 0 ? '1' : '2'}收益更高

策略${ddDiff < 0 ? '1' : '2'}回撤更小

`; } function renderComparisonChart(data1, data2, label1, label2) { const ctx = document.getElementById('comparison-chart').getContext('2d'); if (comparisonChart) { comparisonChart.destroy(); } // 采样数据 const step = Math.max(1, Math.floor(Math.max(data1.length, data2.length) / 200)); const labels = data1.filter((_, i) => i % step === 0).map(row => row.trade_date); const assets1 = data1.filter((_, i) => i % step === 0).map(row => row.total_asset); const assets2 = data2.filter((_, i) => i % step === 0).map(row => row.total_asset); comparisonChart = new Chart(ctx, { type: 'line', data: { labels: labels, datasets: [ { label: label1.replace('_equity.csv', ''), data: assets1, borderColor: '#667eea', backgroundColor: 'rgba(102, 126, 234, 0.1)', borderWidth: 2, fill: false, tension: 0.4 }, { label: label2.replace('_equity.csv', ''), data: assets2, borderColor: '#f093fb', backgroundColor: 'rgba(240, 147, 251, 0.1)', borderWidth: 2, fill: false, tension: 0.4 } ] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'top', }, title: { display: true, text: '多策略资金曲线对比', font: { size: 16 } } }, scales: { y: { title: { display: true, text: '总资产 (元)' } } } } }); } // ===== 统计分析功能 ===== async function initAnalytics() { // 加载交易文件列表 const response = await fetch('/api/trades/list'); const result = await response.json(); if (result.success) { const select = document.getElementById('analytics-file-select'); select.innerHTML = ''; result.files.forEach(file => { const option = document.createElement('option'); option.value = file.filename; option.textContent = `${file.time_display} - ${file.strategy_params}`; select.appendChild(option); }); } document.getElementById('analytics-file-select').addEventListener('change', loadAnalyticsData); document.getElementById('analytics-refresh-btn').addEventListener('click', initAnalytics); } async function loadAnalyticsData() { const filename = document.getElementById('analytics-file-select').value; if (!filename) return; const response = await fetch(`/api/trades/${filename}`); const result = await response.json(); if (result.success) { renderProfitDistribution(result.data); renderDrawdownAnalysis(result.data); } } function renderProfitDistribution(data) { const ctx = document.getElementById('profit-distribution-chart').getContext('2d'); if (profitDistChart) { profitDistChart.destroy(); } // 统计盈亏分布 const profits = data.map(row => row.profit_pct * 100); const bins = [-50, -40, -30, -20, -10, 0, 10, 20, 30, 40, 50]; const counts = new Array(bins.length - 1).fill(0); profits.forEach(p => { for (let i = 0; i < bins.length - 1; i++) { if (p >= bins[i] && p < bins[i + 1]) { counts[i]++; break; } } }); profitDistChart = new Chart(ctx, { type: 'bar', data: { labels: bins.slice(0, -1).map((b, i) => `${b}~${bins[i+1]}%`), datasets: [{ label: '交易次数', data: counts, backgroundColor: counts.map((_, i) => bins[i] < 0 ? 'rgba(220, 53, 69, 0.7)' : 'rgba(40, 167, 69, 0.7)'), borderColor: counts.map((_, i) => bins[i] < 0 ? '#dc3545' : '#28a745'), borderWidth: 1 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, title: { display: true, text: '收益率分布直方图' } }, scales: { y: { beginAtZero: true, title: { display: true, text: '交易笔数' } } } } }); } function renderDrawdownAnalysis(data) { const ctx = document.getElementById('drawdown-chart').getContext('2d'); if (drawdownChart) { drawdownChart.destroy(); } // 计算累计盈亏和回撤 let cumProfit = 0; let maxProfit = 0; const cumProfits = []; const drawdowns = []; data.forEach(row => { cumProfit += row.profit_amount; cumProfits.push(cumProfit); maxProfit = Math.max(maxProfit, cumProfit); drawdowns.push(cumProfit - maxProfit); }); drawdownChart = new Chart(ctx, { type: 'line', data: { labels: data.map((_, i) => i + 1), datasets: [ { label: '累计盈亏', data: cumProfits, borderColor: '#667eea', backgroundColor: 'rgba(102, 126, 234, 0.1)', borderWidth: 2, fill: true, yAxisID: 'y' }, { label: '回撤', data: drawdowns, borderColor: '#dc3545', backgroundColor: 'rgba(220, 53, 69, 0.1)', borderWidth: 2, fill: true, yAxisID: 'y' } ] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'top' }, title: { display: true, text: '累计盈亏与回撤分析' } }, scales: { y: { title: { display: true, text: '金额 (元)' } } } } }); } // ===== 参数热力图功能 ===== async function initHeatmap() { // 加载优化结果文件列表 const response = await fetch('/api/optimization/list'); const result = await response.json(); if (result.success) { const select = document.getElementById('heatmap-file-select'); select.innerHTML = ''; result.files.forEach(file => { const option = document.createElement('option'); option.value = file.filename; option.textContent = `${file.time_display} (${file.num_results}组参数)`; select.appendChild(option); }); } document.getElementById('heatmap-generate-btn').addEventListener('click', generateHeatmap); } async function generateHeatmap() { const filename = document.getElementById('heatmap-file-select').value; if (!filename) { alert('请选择优化结果文件'); return; } const response = await fetch(`/api/optimization/${filename}`); const result = await response.json(); if (result.success) { const xParam = document.getElementById('heatmap-x-param').value; const yParam = document.getElementById('heatmap-y-param').value; const metric = document.getElementById('heatmap-metric').value; renderHeatmap(result.data, xParam, yParam, metric); } } function renderHeatmap(data, xParam, yParam, metric) { // 获取唯一的X和Y值 const xValues = [...new Set(data.map(row => row[xParam]))].sort((a, b) => a - b); const yValues = [...new Set(data.map(row => row[yParam]))].sort((a, b) => a - b); // 创建热力图矩阵 const matrix = []; for (let y of yValues) { const row = []; for (let x of xValues) { const point = data.find(d => d[xParam] === x && d[yParam] === y); row.push(point ? point[metric] : null); } matrix.push(row); } // 使用Chart.js的matrix插件或简单的热力图 const ctx = document.getElementById('heatmap-chart').getContext('2d'); if (heatmapChart) { heatmapChart.destroy(); } // 简化版:使用散点图模拟热力图 const points = []; data.forEach(row => { if (row[xParam] !== undefined && row[yParam] !== undefined && row[metric] !== null) { points.push({ x: row[xParam], y: row[yParam], v: row[metric] }); } }); // 找出最大最小值用于颜色映射 const values = points.map(p => p.v); const minVal = Math.min(...values); const maxVal = Math.max(...values); heatmapChart = new Chart(ctx, { type: 'scatter', data: { datasets: [{ label: metric, data: points, backgroundColor: points.map(p => { const ratio = (p.v - minVal) / (maxVal - minVal); const r = Math.floor(220 * (1 - ratio) + 40 * ratio); const g = Math.floor(53 * (1 - ratio) + 167 * ratio); const b = Math.floor(69 * (1 - ratio) + 69 * ratio); return `rgba(${r}, ${g}, ${b}, 0.7)`; }), borderColor: '#fff', borderWidth: 1, pointRadius: 8 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, title: { display: true, text: `参数热力图 - ${metric}`, font: { size: 16 } }, tooltip: { callbacks: { label: function(context) { return `${metric}: ${context.raw.v.toFixed(4)}`; } } } }, scales: { x: { title: { display: true, text: xParam } }, y: { title: { display: true, text: yParam } } } } }); } // 初始化所有高级功能 document.addEventListener('DOMContentLoaded', function() { // 延迟加载,等待主功能初始化完成 setTimeout(() => { initComparison(); initAnalytics(); initHeatmap(); }, 500); });