Files
FxMathQuantWebApp/js/report-generator.js
T
2025-12-24 13:07:51 +03:30

273 lines
9.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* HTML Report Generator - Creates interactive performance reports
*/
class ReportGenerator {
constructor(strategy, strategyName) {
this.strategy = strategy;
this.name = strategyName || 'Strategy Report';
}
generate() {
const m = this.strategy.metrics;
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${this.name} - Performance Report</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Inter', -apple-system, sans-serif;
background: linear-gradient(135deg, #0a0e27 0%, #1a1f3a 100%);
color: #ffffff;
padding: 40px 20px;
}
.container { max-width: 1200px; margin: 0 auto; }
h1 { font-size: 32px; margin-bottom: 10px; color: #667eea; }
.subtitle { color: #a0aec0; margin-bottom: 30px; }
.metrics-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 40px;
}
.metric-card {
background: #1a1f3a;
padding: 20px;
border-radius: 12px;
border: 1px solid #2d3748;
}
.metric-label {
font-size: 12px;
color: #718096;
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 8px;
}
.metric-value {
font-size: 28px;
font-weight: 700;
}
.positive { color: #48bb78; }
.negative { color: #f56565; }
.neutral { color: #667eea; }
.chart-container {
background: #1a1f3a;
padding: 30px;
border-radius: 12px;
margin-bottom: 30px;
border: 1px solid #2d3748;
}
.rules-section {
background: #1a1f3a;
padding: 30px;
border-radius: 12px;
margin-bottom: 30px;
border: 1px solid #2d3748;
}
.rule {
padding: 10px;
margin: 5px 0;
background: #0a0e27;
border-radius: 6px;
font-family: 'Courier New', monospace;
font-size: 14px;
}
table {
width: 100%;
border-collapse: collapse;
background: #1a1f3a;
border-radius: 12px;
overflow: hidden;
}
th, td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #2d3748;
}
th {
background: #0a0e27;
color: #a0aec0;
font-weight: 600;
text-transform: uppercase;
font-size: 11px;
}
tr:hover { background: #252b4a; }
.footer {
text-align: center;
margin-top: 40px;
color: #718096;
font-size: 14px;
}
</style>
</head>
<body>
<div class="container">
<h1>${this.name}</h1>
<p class="subtitle">Generated by FxMathQuant Web - AI-Powered Strategy Generator</p>
<div class="metrics-grid">
<div class="metric-card">
<div class="metric-label">Profit Factor</div>
<div class="metric-value ${m.profitFactor >= 1.5 ? 'positive' : 'negative'}">
${m.profitFactor.toFixed(2)}
</div>
</div>
<div class="metric-card">
<div class="metric-label">Win Rate</div>
<div class="metric-value neutral">${m.winRate.toFixed(1)}%</div>
</div>
<div class="metric-card">
<div class="metric-label">Total Trades</div>
<div class="metric-value neutral">${m.totalTrades}</div>
</div>
<div class="metric-card">
<div class="metric-label">Max Drawdown</div>
<div class="metric-value negative">${m.maxDrawdown.toFixed(2)}%</div>
</div>
<div class="metric-card">
<div class="metric-label">Total Profit</div>
<div class="metric-value ${m.totalProfit >= 0 ? 'positive' : 'negative'}">
$${m.totalProfit.toFixed(2)}
</div>
</div>
<div class="metric-card">
<div class="metric-label">Final Balance</div>
<div class="metric-value positive">$${m.finalBalance.toFixed(2)}</div>
</div>
<div class="metric-card">
<div class="metric-label">Avg Win</div>
<div class="metric-value positive">$${m.avgWin.toFixed(2)}</div>
</div>
<div class="metric-card">
<div class="metric-label">Avg Loss</div>
<div class="metric-value negative">$${Math.abs(m.avgLoss).toFixed(2)}</div>
</div>
</div>
<div class="chart-container">
<h2 style="margin-bottom: 20px;">Equity Curve</h2>
<canvas id="equityChart"></canvas>
</div>
<div class="rules-section">
<h2 style="margin-bottom: 20px;">Strategy Rules</h2>
<p style="margin-bottom: 15px; color: #a0aec0;">BUY when ALL of the following conditions are true:</p>
${this.generateRulesHTML()}
<div style="margin-top: 20px; padding: 15px; background: #0a0e27; border-radius: 6px;">
<strong>Parameters:</strong><br>
ATR Period: ${this.strategy.atrPeriod} |
SL Multiplier: ${this.strategy.slMultiplier.toFixed(2)} |
TP Multiplier: ${this.strategy.tpMultiplier.toFixed(2)}
</div>
</div>
${this.generateTradesTable()}
<div class="footer">
<p>&copy; 2025 FxMathQuant. All rights reserved.</p>
<p>This report was generated automatically by AI-powered genetic algorithms.</p>
</div>
</div>
<script>
// Equity Curve Chart
const ctx = document.getElementById('equityChart').getContext('2d');
new Chart(ctx, {
type: 'line',
data: {
labels: ${JSON.stringify(m.equity.map((_, i) => i))},
datasets: [{
label: 'Account Balance',
data: ${JSON.stringify(m.equity)},
borderColor: '#667eea',
backgroundColor: 'rgba(102, 126, 234, 0.1)',
tension: 0.4,
fill: true
}]
},
options: {
responsive: true,
plugins: {
legend: { display: false },
tooltip: {
backgroundColor: '#1a1f3a',
titleColor: '#fff',
bodyColor: '#a0aec0',
borderColor: '#2d3748',
borderWidth: 1
}
},
scales: {
y: {
beginAtZero: false,
grid: { color: '#2d3748' },
ticks: { color: '#a0aec0' }
},
x: {
grid: { color: '#2d3748' },
ticks: { color: '#a0aec0' }
}
}
}
});
</script>
</body>
</html>`;
}
generateRulesHTML() {
return this.strategy.rules.map((rule, index) => {
let ruleText = '';
if (rule.type === 'simple') {
ruleText = `${rule.left.price.toUpperCase()}[${rule.left.shift}] ${rule.operator} ${rule.right.price.toUpperCase()}[${rule.right.shift}]`;
} else {
ruleText = `(${rule.left.price1.toUpperCase()}[${rule.left.shift1}] ${rule.left.op} ${rule.left.price2.toUpperCase()}[${rule.left.shift2}]) ${rule.operator} (${rule.right.price.toUpperCase()}[${rule.right.shift}] × ${rule.right.multiplier})`;
}
return `<div class="rule">${index + 1}. ${ruleText}</div>`;
}).join('');
}
generateTradesTable() {
const trades = this.strategy.metrics.trades || [];
if (trades.length === 0) return '';
// Show first 50 trades
const displayTrades = trades.slice(0, 50);
return `
<div style="background: #1a1f3a; padding: 30px; border-radius: 12px; border: 1px solid #2d3748;">
<h2 style="margin-bottom: 20px;">Trade History ${trades.length > 50 ? '(First 50 trades)' : ''}</h2>
<table>
<thead>
<tr>
<th>#</th>
<th>Type</th>
<th>Entry</th>
<th>Exit</th>
<th>Profit</th>
<th>Reason</th>
</tr>
</thead>
<tbody>
${displayTrades.map((trade, i) => `
<tr>
<td>${i + 1}</td>
<td>${trade.type}</td>
<td>${trade.entry.toFixed(5)}</td>
<td>${trade.exit.toFixed(5)}</td>
<td class="${trade.profit >= 0 ? 'positive' : 'negative'}">
$${trade.profit.toFixed(2)}
</td>
<td>${trade.reason.replace('_', ' ')}</td>
</tr>
`).join('')}
</tbody>
</table>
</div>`;
}
}