infinite lowad WIP

This commit is contained in:
Dobromir Popov
2025-10-24 23:04:29 +03:00
parent 07b82f0a1f
commit 2233a88d3e
5 changed files with 522 additions and 73 deletions

View File

@@ -298,7 +298,7 @@ class ChartManager {
return; // Don't process as regular chart click
}
}
// Regular chart click for annotation marking
this.handleChartClick(timeframe, eventData);
});
@@ -308,10 +308,10 @@ class ChartManager {
console.log('=== plotly_clickannotation event fired ===');
console.log('Event data:', eventData);
console.log('Annotation:', eventData.annotation);
const annotationName = eventData.annotation.name;
console.log('Annotation name:', annotationName);
if (annotationName) {
const parts = annotationName.split('_');
const action = parts[0]; // 'entry', 'exit', or 'delete'
@@ -675,22 +675,22 @@ class ChartManager {
// Get existing pivot annotations (they have specific names like L1H, L2L)
const existingLayout = chart.element.layout || {};
const existingAnnotations = existingLayout.annotations || [];
const pivotAnnotations = existingAnnotations.filter(ann =>
const pivotAnnotations = existingAnnotations.filter(ann =>
ann.text && ann.text.match(/^L\d+[HL]$/)
);
// Merge pivot annotations with trade annotations
const allAnnotations = [...pivotAnnotations, ...plotlyAnnotations];
// Get existing pivot shapes
const existingShapes = existingLayout.shapes || [];
const pivotShapes = existingShapes.filter(shape =>
const pivotShapes = existingShapes.filter(shape =>
shape.layer === 'below' && shape.line && shape.line.dash === 'dash'
);
// Merge pivot shapes with trade annotation shapes
const allShapes = [...pivotShapes, ...plotlyShapes];
// Update chart layout with merged annotations and shapes
Plotly.relayout(chart.plotId, {
annotations: allAnnotations,
@@ -921,15 +921,15 @@ class ChartManager {
*/
clearAllAnnotations() {
console.log('Clearing all annotations from charts');
// Clear from memory
this.annotations = {};
// Update all charts
Object.keys(this.charts).forEach(timeframe => {
this.updateChartAnnotations(timeframe);
});
console.log('All annotations cleared from charts');
}
@@ -940,23 +940,23 @@ class ChartManager {
const chartContainer = document.getElementById('chart-container');
const visibleCharts = document.querySelectorAll('.timeframe-chart:not(.minimized)');
const minimizedCharts = document.querySelectorAll('.timeframe-chart.minimized');
// Remove scroll if all charts are visible or if some are minimized
if (minimizedCharts.length > 0) {
chartContainer.classList.add('no-scroll');
// Calculate available height for visible charts
const containerHeight = chartContainer.clientHeight;
const headerHeight = 50; // Approximate header height
const availableHeight = containerHeight - (visibleCharts.length * headerHeight);
const chartHeight = Math.max(200, availableHeight / visibleCharts.length);
// Update visible chart heights
visibleCharts.forEach(chart => {
const plotElement = chart.querySelector('.chart-plot');
if (plotElement) {
plotElement.style.height = `${chartHeight}px`;
// Trigger Plotly resize
const plotId = plotElement.id;
if (plotId) {
@@ -966,13 +966,13 @@ class ChartManager {
});
} else {
chartContainer.classList.remove('no-scroll');
// Reset to default heights
visibleCharts.forEach(chart => {
const plotElement = chart.querySelector('.chart-plot');
if (plotElement) {
plotElement.style.height = '300px';
// Trigger Plotly resize
const plotId = plotElement.id;
if (plotId) {
@@ -981,7 +981,7 @@ class ChartManager {
}
});
}
console.log(`Updated chart layout: ${visibleCharts.length} visible, ${minimizedCharts.length} minimized`);
}
@@ -1122,7 +1122,7 @@ class ChartManager {
// Check if we're near the left edge (need older data)
const nearLeftEdge = (visibleStart - dataStart) < threshold;
// Check if we're near the right edge (need newer data)
const nearRightEdge = (dataEnd - visibleEnd) < threshold;
@@ -1153,23 +1153,28 @@ class ChartManager {
this.showLoadingIndicator(timeframe, direction);
try {
// Calculate time range to fetch
const limit = 500; // Fetch 500 more candles
// Fetch more candles - no limit, get as much as available
let startTime, endTime;
if (direction === 'before') {
// Load older data
endTime = referenceTime.toISOString();
startTime = null; // Let backend calculate based on limit
// Load older data: get candles BEFORE the first candle we have
// Use the actual first timestamp from our data
const firstTimestamp = chart.data.timestamps[0];
endTime = new Date(firstTimestamp).toISOString();
startTime = null;
console.log(`Loading older data before ${endTime} for ${timeframe}`);
} else {
// Load newer data
startTime = referenceTime.toISOString();
// Load newer data: get candles AFTER the last candle we have
// Use the actual last timestamp from our data
const lastTimestamp = chart.data.timestamps[chart.data.timestamps.length - 1];
startTime = new Date(lastTimestamp).toISOString();
endTime = null;
console.log(`Loading newer data after ${startTime} for ${timeframe}`);
}
console.log(`Loading ${limit} more candles ${direction} ${referenceTime.toISOString()} for ${timeframe}`);
// Fetch more data from backend
// Fetch more data from backend (no limit - get all available)
const response = await fetch('/api/chart-data', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@@ -1178,7 +1183,7 @@ class ChartManager {
timeframes: [timeframe],
start_time: startTime,
end_time: endTime,
limit: limit,
limit: 1000, // Request 1000 candles at a time
direction: direction
})
});
@@ -1187,10 +1192,17 @@ class ChartManager {
if (result.success && result.chart_data && result.chart_data[timeframe]) {
const newData = result.chart_data[timeframe];
// Check if we got any new data
if (newData.timestamps.length === 0) {
console.warn(`No more data available for ${timeframe} ${direction}`);
window.showWarning('No more historical data available');
return;
}
// Merge with existing data
this.mergeChartData(timeframe, newData, direction);
console.log(`Loaded ${newData.timestamps.length} new candles for ${timeframe}`);
window.showSuccess(`Loaded ${newData.timestamps.length} more candles`);
} else {
@@ -1208,36 +1220,67 @@ class ChartManager {
}
/**
* Merge new data with existing chart data
* Merge new data with existing chart data (with deduplication)
*/
mergeChartData(timeframe, newData, direction) {
const chart = this.charts[timeframe];
if (!chart || !chart.data) return;
const existingData = chart.data;
// Create a set of existing timestamps for deduplication
const existingTimestamps = new Set(existingData.timestamps);
// Filter out duplicate timestamps from new data
const uniqueIndices = [];
newData.timestamps.forEach((ts, idx) => {
if (!existingTimestamps.has(ts)) {
uniqueIndices.push(idx);
}
});
// If no unique data, nothing to merge
if (uniqueIndices.length === 0) {
console.log(`No unique data to merge for ${timeframe}`);
return;
}
// Extract only unique data points
const uniqueNewData = {
timestamps: uniqueIndices.map(i => newData.timestamps[i]),
open: uniqueIndices.map(i => newData.open[i]),
high: uniqueIndices.map(i => newData.high[i]),
low: uniqueIndices.map(i => newData.low[i]),
close: uniqueIndices.map(i => newData.close[i]),
volume: uniqueIndices.map(i => newData.volume[i]),
pivot_markers: newData.pivot_markers || {}
};
console.log(`Merging ${uniqueIndices.length} unique candles (filtered ${newData.timestamps.length - uniqueIndices.length} duplicates)`);
let mergedData;
if (direction === 'before') {
// Prepend older data
mergedData = {
timestamps: [...newData.timestamps, ...existingData.timestamps],
open: [...newData.open, ...existingData.open],
high: [...newData.high, ...existingData.high],
low: [...newData.low, ...existingData.low],
close: [...newData.close, ...existingData.close],
volume: [...newData.volume, ...existingData.volume],
pivot_markers: { ...newData.pivot_markers, ...existingData.pivot_markers }
timestamps: [...uniqueNewData.timestamps, ...existingData.timestamps],
open: [...uniqueNewData.open, ...existingData.open],
high: [...uniqueNewData.high, ...existingData.high],
low: [...uniqueNewData.low, ...existingData.low],
close: [...uniqueNewData.close, ...existingData.close],
volume: [...uniqueNewData.volume, ...existingData.volume],
pivot_markers: { ...uniqueNewData.pivot_markers, ...existingData.pivot_markers }
};
} else {
// Append newer data
mergedData = {
timestamps: [...existingData.timestamps, ...newData.timestamps],
open: [...existingData.open, ...newData.open],
high: [...existingData.high, ...newData.high],
low: [...existingData.low, ...newData.low],
close: [...existingData.close, ...newData.close],
volume: [...existingData.volume, ...newData.volume],
pivot_markers: { ...existingData.pivot_markers, ...newData.pivot_markers }
timestamps: [...existingData.timestamps, ...uniqueNewData.timestamps],
open: [...existingData.open, ...uniqueNewData.open],
high: [...existingData.high, ...uniqueNewData.high],
low: [...existingData.low, ...uniqueNewData.low],
close: [...existingData.close, ...uniqueNewData.close],
volume: [...existingData.volume, ...uniqueNewData.volume],
pivot_markers: { ...existingData.pivot_markers, ...uniqueNewData.pivot_markers }
};
}