/** * ChartManager - Manages Plotly charts for multi-timeframe visualization */ class ChartManager { constructor(containerId, timeframes) { this.containerId = containerId; this.timeframes = timeframes; this.charts = {}; this.annotations = {}; this.syncedTime = null; console.log('ChartManager initialized with timeframes:', timeframes); } /** * Initialize charts for all timeframes */ initializeCharts(chartData) { console.log('Initializing charts with data:', chartData); this.timeframes.forEach(timeframe => { if (chartData[timeframe]) { this.createChart(timeframe, chartData[timeframe]); } }); // Enable crosshair this.enableCrosshair(); } /** * Create a single chart for a timeframe */ createChart(timeframe, data) { const plotId = `plot-${timeframe}`; const plotElement = document.getElementById(plotId); if (!plotElement) { console.error(`Plot element not found: ${plotId}`); return; } // Create candlestick trace const candlestickTrace = { x: data.timestamps, open: data.open, high: data.high, low: data.low, close: data.close, type: 'candlestick', name: timeframe, increasing: {line: {color: '#10b981'}}, decreasing: {line: {color: '#ef4444'}} }; // Create volume trace const volumeTrace = { x: data.timestamps, y: data.volume, type: 'bar', name: 'Volume', yaxis: 'y2', marker: {color: '#3b82f6', opacity: 0.3} }; const layout = { title: '', xaxis: { rangeslider: {visible: false}, gridcolor: '#374151', color: '#9ca3af' }, yaxis: { title: 'Price', gridcolor: '#374151', color: '#9ca3af' }, yaxis2: { title: 'Volume', overlaying: 'y', side: 'right', showgrid: false, color: '#9ca3af' }, plot_bgcolor: '#1f2937', paper_bgcolor: '#1f2937', font: {color: '#f8f9fa'}, margin: {l: 50, r: 50, t: 20, b: 40}, hovermode: 'x unified' }; const config = { responsive: true, displayModeBar: true, modeBarButtonsToRemove: ['lasso2d', 'select2d'], displaylogo: false }; Plotly.newPlot(plotId, [candlestickTrace, volumeTrace], layout, config); // Store chart reference this.charts[timeframe] = { plotId: plotId, data: data, element: plotElement }; // Add click handler for annotations plotElement.on('plotly_click', (eventData) => { this.handleChartClick(timeframe, eventData); }); console.log(`Chart created for ${timeframe}`); } /** * Handle chart click for annotation */ handleChartClick(timeframe, eventData) { if (!eventData.points || eventData.points.length === 0) return; const point = eventData.points[0]; const clickData = { timeframe: timeframe, timestamp: point.x, price: point.close || point.y, index: point.pointIndex }; console.log('Chart clicked:', clickData); // Trigger annotation manager if (window.appState && window.appState.annotationManager) { window.appState.annotationManager.handleChartClick(clickData); } } /** * Update charts with new data */ updateCharts(newData) { Object.keys(newData).forEach(timeframe => { if (this.charts[timeframe]) { const plotId = this.charts[timeframe].plotId; Plotly.react(plotId, [ { x: newData[timeframe].timestamps, open: newData[timeframe].open, high: newData[timeframe].high, low: newData[timeframe].low, close: newData[timeframe].close, type: 'candlestick' }, { x: newData[timeframe].timestamps, y: newData[timeframe].volume, type: 'bar', yaxis: 'y2' } ]); } }); } /** * Add annotation to charts */ addAnnotation(annotation) { console.log('Adding annotation to charts:', annotation); // Store annotation this.annotations[annotation.annotation_id] = annotation; // Add markers to relevant timeframe chart const timeframe = annotation.timeframe; if (this.charts[timeframe]) { // TODO: Add visual markers using Plotly annotations this.updateChartAnnotations(timeframe); } } /** * Remove annotation from charts */ removeAnnotation(annotationId) { if (this.annotations[annotationId]) { const annotation = this.annotations[annotationId]; delete this.annotations[annotationId]; // Update chart if (this.charts[annotation.timeframe]) { this.updateChartAnnotations(annotation.timeframe); } } } /** * Update chart annotations */ updateChartAnnotations(timeframe) { // TODO: Implement annotation rendering on charts console.log(`Updating annotations for ${timeframe}`); } /** * Highlight annotation */ highlightAnnotation(annotationId) { console.log('Highlighting annotation:', annotationId); // TODO: Implement highlight effect } /** * Enable crosshair cursor */ enableCrosshair() { // Crosshair is enabled via hovermode in layout console.log('Crosshair enabled'); } /** * Handle zoom */ handleZoom(zoomFactor) { Object.values(this.charts).forEach(chart => { Plotly.relayout(chart.plotId, { 'xaxis.range[0]': null, 'xaxis.range[1]': null }); }); } /** * Reset zoom */ resetZoom() { Object.values(this.charts).forEach(chart => { Plotly.relayout(chart.plotId, { 'xaxis.autorange': true, 'yaxis.autorange': true }); }); } /** * Synchronize time navigation across charts */ syncTimeNavigation(timestamp) { this.syncedTime = timestamp; // TODO: Implement time synchronization console.log('Syncing charts to timestamp:', timestamp); } }