// static/js/backtesting.js document.addEventListener('DOMContentLoaded', () => { const strategySelect = document.getElementById('strategy-select'); const paramsContainer = document.getElementById('params-container'); const form = document.getElementById('backtest-form'); const runBtn = document.getElementById('run-backtest-btn'); const resultsContainer = document.getElementById('results-container'); const resultsSummary = document.getElementById('results-summary'); const loadingSpinner = document.getElementById('loading-spinner'); let equityChart = null; // Variabel untuk menyimpan instance grafik const resultsLog = document.getElementById('results-log'); // Muat strategi ke dropdown async function loadStrategies() { try { const response = await fetch('/api/strategies'); if (!response.ok) throw new Error('Gagal memuat strategi.'); const strategies = await response.json(); strategySelect.innerHTML = ''; strategies.forEach(strategy => { const option = document.createElement('option'); option.value = strategy.id; option.textContent = strategy.name; strategySelect.appendChild(option); }); } catch (error) { console.error('Error loading strategies:', error); strategySelect.innerHTML = ''; } } // Muat parameter saat strategi dipilih strategySelect.addEventListener('change', async () => { const strategyId = strategySelect.value; paramsContainer.innerHTML = '

Memuat parameter...

'; if (!strategyId) { paramsContainer.innerHTML = ''; return; } try { const res = await fetch(`/api/strategies/${strategyId}/params`); const params = await res.json(); paramsContainer.innerHTML = ''; // Kosongkan lagi if (params.length > 0) { params.forEach(param => { paramsContainer.innerHTML += `
`; }); } else { paramsContainer.innerHTML = '

Strategi ini tidak memiliki parameter kustom.

'; } } catch (err) { console.error('Gagal memuat parameter strategi:', err); paramsContainer.innerHTML = '

Gagal memuat parameter.

'; } }); // Jalankan backtest saat form disubmit form.addEventListener('submit', async (e) => { e.preventDefault(); loadingSpinner.classList.remove('hidden'); resultsContainer.classList.add('hidden'); runBtn.disabled = true; runBtn.textContent = 'Menjalankan...'; const formData = new FormData(form); // Kumpulkan parameter kustom const params = {}; // Ambil parameter dari form utama (SL/TP) params['sl_pips'] = parseFloat(document.getElementById('sl_atr_multiplier').value); params['tp_pips'] = parseFloat(document.getElementById('tp_atr_multiplier').value); // Kumpulkan parameter strategi kustom paramsContainer.querySelectorAll('input').forEach(input => { const value = parseFloat(input.value); // Pastikan hanya angka valid yang di-parse, selain itu ambil string params[input.name] = isNaN(value) ? input.value : value; }); formData.append('params', JSON.stringify(params)); try { const response = await fetch('/api/backtest/run', { method: 'POST', body: formData, // Kirim sebagai multipart/form-data }); const results = await response.json(); if (response.ok) { displayResults(results); } else { alert(`Error: ${results.error}`); } } catch (err) { console.error("Backtest failed:", err); alert('Gagal terhubung ke server.'); } finally { loadingSpinner.classList.add('hidden'); runBtn.disabled = false; runBtn.textContent = 'Jalankan Backtest'; } }); function displayResults(data) { resultsContainer.classList.remove('hidden'); // Enhanced display with spread costs and protection info const spreadCosts = data.total_spread_costs || 0; const netProfit = data.net_profit_after_costs || data.total_profit_usd; const instrument = data.instrument || 'UNKNOWN'; // Check if protection was applied const engineConfig = data.engine_config || {}; const instrumentConfig = engineConfig.instrument_config || {}; const maxRisk = instrumentConfig.max_risk_percent || 2.0; const maxLot = instrumentConfig.max_lot_size || 10.0; const spreadPips = instrumentConfig.typical_spread_pips || 2.0; resultsSummary.innerHTML = `

Instrument

${instrument}

Max Risk: ${maxRisk}%

Gross Profit

${data.total_profit_usd.toFixed(2)} $

Before costs

Spread Costs

-${spreadCosts.toFixed(2)} $

${spreadPips} pips spread

Net Profit

${netProfit.toFixed(2)} $

After all costs

Max Drawdown

${data.max_drawdown_percent.toFixed(2)}%

Win Rate

${data.win_rate_percent.toFixed(2)}%

Total Trades

${data.total_trades}

Max Lot: ${maxLot}

Wins

${data.wins}

Losses

${data.losses}

`; // Tampilkan grafik kurva ekuitas displayEquityChart(data.equity_curve); // Enhanced trade log with spread costs if (data.trades && data.trades.length > 0) { let logHtml = '

20 Trade Terakhir (Enhanced Engine)

'; logHtml += '
'; data.trades.forEach(trade => { const profitClass = trade.profit > 0 ? 'text-green-600' : 'text-red-600'; const spreadCost = trade.spread_cost || 0; const lotSize = trade.lot_size || 0; const reason = trade.reason || 'N/A'; logHtml += `

`; logHtml += `${trade.position_type} | `; logHtml += `Entry: ${trade.entry.toFixed(4)} | `; logHtml += `Exit: ${trade.exit.toFixed(4)} | `; logHtml += `Lot: ${lotSize.toFixed(2)} | `; logHtml += `Profit: ${trade.profit.toFixed(2)} | `; logHtml += `Spread: $${spreadCost.toFixed(2)} | `; logHtml += `Reason: ${reason}`; logHtml += `

`; }); logHtml += '
'; // Add enhanced engine info logHtml += '
'; logHtml += '
🚀 Enhanced Engine Features Applied:
'; logHtml += '
'; logHtml += `

✅ Realistic spread costs: ${spreadPips} pips per trade

`; logHtml += `

✅ ATR-based position sizing with ${maxRisk}% max risk

`; logHtml += `

✅ Instrument protection: ${maxLot} max lot size

`; logHtml += `

✅ Slippage simulation included

`; logHtml += `

💰 Total spread costs deducted: $${spreadCosts.toFixed(2)}

`; logHtml += '
'; resultsLog.innerHTML = logHtml; } else { resultsLog.innerHTML = ''; } } function displayEquityChart(equityData) { const ctx = document.getElementById('equity-chart').getContext('2d'); if (equityChart) { equityChart.destroy(); // Hancurkan grafik lama sebelum membuat yang baru } equityChart = new Chart(ctx, { type: 'line', data: { labels: Array.from({ length: equityData.length }, (_, i) => i + 1), // Label 1, 2, 3, ... datasets: [{ label: 'Equity Curve', data: equityData, borderColor: 'rgb(59, 130, 246)', backgroundColor: 'rgba(59, 130, 246, 0.1)', borderWidth: 2, fill: true, tension: 0.1, pointRadius: 0, }] }, options: { responsive: true, plugins: { legend: { display: false }, title: { display: true, text: 'Pertumbuhan Modal (Equity Curve)' } }, scales: { y: { beginAtZero: false } } } }); } loadStrategies(); });