infinite lowad WIP
This commit is contained in:
@@ -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 }
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user