/** * Polling-based Live Updates for ANNOTATE * Replaces WebSocket with simple polling (like clean_dashboard) */ class LiveUpdatesPolling { constructor() { this.pollInterval = null; this.pollDelay = 2000; // Poll every 2 seconds (like clean_dashboard) this.subscriptions = new Set(); this.isPolling = false; // Callbacks this.onChartUpdate = null; this.onPredictionUpdate = null; this.onConnectionChange = null; console.log('LiveUpdatesPolling initialized'); } start() { if (this.isPolling) { console.log('Already polling'); return; } this.isPolling = true; this._startPolling(); if (this.onConnectionChange) { this.onConnectionChange(true); } console.log('Started polling for live updates'); } stop() { if (this.pollInterval) { clearInterval(this.pollInterval); this.pollInterval = null; } this.isPolling = false; if (this.onConnectionChange) { this.onConnectionChange(false); } console.log('Stopped polling for live updates'); } _startPolling() { // Poll immediately, then set interval this._poll(); this.pollInterval = setInterval(() => { this._poll(); }, this.pollDelay); } _poll() { // Poll each subscription this.subscriptions.forEach(sub => { fetch('/api/live-updates', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ symbol: sub.symbol, timeframe: sub.timeframe }) }) .then(response => response.json()) .then(data => { if (data.success) { // Handle chart update if (data.chart_update && this.onChartUpdate) { this.onChartUpdate(data.chart_update); } // Handle prediction update if (data.prediction && this.onPredictionUpdate) { this.onPredictionUpdate(data.prediction); } } }) .catch(error => { console.debug('Polling error:', error); }); }); } subscribe(symbol, timeframe) { const key = `${symbol}_${timeframe}`; this.subscriptions.add({ symbol, timeframe, key }); // Auto-start polling if not already started if (!this.isPolling) { this.start(); } console.log(`Subscribed to live updates: ${symbol} ${timeframe}`); } unsubscribe(symbol, timeframe) { const key = `${symbol}_${timeframe}`; this.subscriptions.forEach(sub => { if (sub.key === key) { this.subscriptions.delete(sub); } }); // Stop polling if no subscriptions if (this.subscriptions.size === 0) { this.stop(); } console.log(`Unsubscribed from live updates: ${symbol} ${timeframe}`); } isConnected() { return this.isPolling; } } // Global instance window.liveUpdatesPolling = null; // Initialize on page load document.addEventListener('DOMContentLoaded', function() { // Initialize polling window.liveUpdatesPolling = new LiveUpdatesPolling(); // Setup callbacks (same interface as WebSocket version) window.liveUpdatesPolling.onConnectionChange = function(connected) { const statusElement = document.getElementById('ws-connection-status'); if (statusElement) { if (connected) { statusElement.innerHTML = 'Live'; } else { statusElement.innerHTML = 'Offline'; } } }; window.liveUpdatesPolling.onChartUpdate = function(data) { // Update chart with new candle if (window.appState && window.appState.chartManager) { window.appState.chartManager.updateLatestCandle(data.symbol, data.timeframe, data.candle); } }; window.liveUpdatesPolling.onPredictionUpdate = function(data) { // Update prediction visualization on charts if (window.appState && window.appState.chartManager) { window.appState.chartManager.updatePredictions(data); } // Update prediction display if (typeof updatePredictionDisplay === 'function') { updatePredictionDisplay(data); } // Add to prediction history if (typeof predictionHistory !== 'undefined') { predictionHistory.unshift(data); if (predictionHistory.length > 5) { predictionHistory = predictionHistory.slice(0, 5); } if (typeof updatePredictionHistory === 'function') { updatePredictionHistory(); } } }; // Function to subscribe to all active timeframes function subscribeToActiveTimeframes() { if (window.appState && window.appState.currentSymbol && window.appState.currentTimeframes) { const symbol = window.appState.currentSymbol; window.appState.currentTimeframes.forEach(timeframe => { window.liveUpdatesPolling.subscribe(symbol, timeframe); }); console.log(`Subscribed to live updates for ${symbol}: ${window.appState.currentTimeframes.join(', ')}`); } } // Auto-start polling console.log('Auto-starting polling for live updates...'); window.liveUpdatesPolling.start(); // Wait for DOM and appState to be ready, then subscribe function initializeSubscriptions() { // Wait a bit for charts to initialize setTimeout(() => { subscribeToActiveTimeframes(); }, 2000); } // Subscribe when DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initializeSubscriptions); } else { initializeSubscriptions(); } // Also monitor for appState changes (fallback) let lastTimeframes = null; setInterval(() => { if (window.appState && window.appState.currentTimeframes && window.appState.chartManager) { const currentTimeframes = window.appState.currentTimeframes.join(','); if (currentTimeframes !== lastTimeframes) { lastTimeframes = currentTimeframes; subscribeToActiveTimeframes(); } } }, 3000); }); // Cleanup on page unload window.addEventListener('beforeunload', function() { if (window.liveUpdatesPolling) { window.liveUpdatesPolling.stop(); } });