anotate ui phase 1

This commit is contained in:
Dobromir Popov
2025-10-18 16:37:13 +03:00
parent d136f9d79c
commit bc7095308a
26 changed files with 3616 additions and 80 deletions

View File

@@ -0,0 +1,255 @@
/**
* 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);
}
}