// Dashboard JavaScript class TradingBotDashboard { constructor() { this.charts = {}; this.refreshInterval = null; this.init(); } init() { this.setupEventListeners(); this.initializeCharts(); this.startAutoRefresh(); this.loadInitialData(); } setupEventListeners() { // Refresh button document.getElementById('refreshBtn').addEventListener('click', () => { this.refreshAllData(); }); // Emergency close button document.getElementById('emergencyCloseBtn').addEventListener('click', () => { this.showEmergencyModal(); }); // Emergency modal document.getElementById('confirmEmergencyClose').addEventListener('click', () => { this.emergencyCloseAll(); }); document.getElementById('cancelEmergencyClose').addEventListener('click', () => { this.hideEmergencyModal(); }); // Close modal on outside click document.getElementById('emergencyModal').addEventListener('click', (e) => { if (e.target.id === 'emergencyModal') { this.hideEmergencyModal(); } }); } initializeCharts() { // PnL Chart const pnlCtx = document.getElementById('pnlChart').getContext('2d'); this.charts.pnl = new Chart(pnlCtx, { type: 'line', data: { labels: [], datasets: [{ label: 'Daily PnL (SOL)', data: [], borderColor: '#10b981', backgroundColor: 'rgba(16, 185, 129, 0.1)', tension: 0.4, fill: true }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { labels: { color: '#ffffff' } } }, scales: { x: { ticks: { color: '#9ca3af' }, grid: { color: 'rgba(156, 163, 175, 0.2)' } }, y: { ticks: { color: '#9ca3af' }, grid: { color: 'rgba(156, 163, 175, 0.2)' } } } } }); // Positions Chart const positionsCtx = document.getElementById('positionsChart').getContext('2d'); this.charts.positions = new Chart(positionsCtx, { type: 'doughnut', data: { labels: ['Active', 'Closed'], datasets: [{ data: [0, 0], backgroundColor: ['#3b82f6', '#6b7280'], borderWidth: 0 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { labels: { color: '#ffffff' } } } } }); } async loadInitialData() { try { await Promise.all([ this.loadStatus(), this.loadRiskMetrics(), this.loadPositions(), this.loadHistory(), this.loadConfiguration() ]); } catch (error) { console.error('Error loading initial data:', error); this.showError('Failed to load dashboard data'); } } async refreshAllData() { const refreshBtn = document.getElementById('refreshBtn'); refreshBtn.classList.add('animate-spin'); try { await this.loadInitialData(); this.showSuccess('Dashboard refreshed successfully'); } catch (error) { console.error('Error refreshing data:', error); this.showError('Failed to refresh dashboard'); } finally { refreshBtn.classList.remove('animate-spin'); } } startAutoRefresh() { // Refresh data every 30 seconds this.refreshInterval = setInterval(() => { this.loadStatus(); this.loadRiskMetrics(); this.loadPositions(); }, 30000); } async loadStatus() { try { const response = await fetch('/api/status'); const data = await response.json(); // Update bot status const statusElement = document.getElementById('botStatus'); statusElement.innerHTML = ` ${data.bot.status} `; statusElement.className = `font-bold status-${data.bot.status}`; } catch (error) { console.error('Error loading status:', error); } } async loadRiskMetrics() { try { const response = await fetch('/api/risk'); const data = await response.json(); // Update quick stats document.getElementById('activePositions').textContent = data.positionSummary.activePositions; document.getElementById('dailyPnL').textContent = `${data.dailyStats.netPnL.toFixed(4)} SOL`; document.getElementById('winRate').textContent = `${data.dailyStats.winRate}%`; document.getElementById('riskLevel').textContent = data.riskLevel; // Update PnL chart this.updatePnLChart(data.dailyStats); // Update positions chart this.updatePositionsChart(data.positionSummary); } catch (error) { console.error('Error loading risk metrics:', error); } } async loadPositions() { try { const response = await fetch('/api/positions'); const data = await response.json(); this.updatePositionsTable(data.positions); } catch (error) { console.error('Error loading positions:', error); } } async loadHistory() { try { const response = await fetch('/api/history'); const data = await response.json(); this.updateHistoryTable(data.history); } catch (error) { console.error('Error loading history:', error); } } async loadConfiguration() { try { const response = await fetch('/api/config'); const data = await response.json(); this.updateConfigurationGrid(data); } catch (error) { console.error('Error loading configuration:', error); } } updatePnLChart(dailyStats) { const chart = this.charts.pnl; // Add current data point const now = new Date(); const timeLabel = now.toLocaleTimeString(); chart.data.labels.push(timeLabel); chart.data.datasets[0].data.push(dailyStats.netPnL); // Keep only last 20 data points if (chart.data.labels.length > 20) { chart.data.labels.shift(); chart.data.datasets[0].data.shift(); } chart.update(); } updatePositionsChart(positionSummary) { const chart = this.charts.positions; chart.data.datasets[0].data = [ positionSummary.activePositions, positionSummary.totalPositions || 0 ]; chart.update(); } updatePositionsTable(positions) { const tbody = document.getElementById('positionsTableBody'); if (positions.length === 0) { tbody.innerHTML = `