dane z plików - nie chodzi

This commit is contained in:
2025-07-17 00:12:33 +02:00
parent 2a556781da
commit f9d76c85bd
5 changed files with 596 additions and 109 deletions

View File

@ -130,6 +130,15 @@
<div id="progress-container" class="progress-bar-container">
<div class="progress-bar"></div>
</div>
<!-- CSV File Selection Dropdown -->
<div style="margin-top: 15px; width: 100%;">
<label for="csv-file-select" style="display: block; margin-bottom: 5px; font-size: 12px; color: var(--text-secondary);">Data Source:</label>
<select id="csv-file-select" style="width: 100%; background-color: var(--button-bg); border: 1px solid var(--border-color); color: var(--text-primary); padding: 6px; border-radius: 4px; font-size: 12px; cursor: pointer;">
<option value="">Loading...</option>
</select>
<div id="csv-info" style="font-size: 10px; color: var(--text-secondary); margin-top: 3px; text-align: center;"></div>
</div>
</div>
<div class="control-cell" id="indicator-cell-1"></div>
<div class="control-cell" id="indicator-cell-2"></div>
@ -192,6 +201,8 @@
const modalInput = document.getElementById('timeframe-input');
const modalPreviewText = document.getElementById('timeframe-preview-text');
const modalConfirmBtn = document.getElementById('timeframe-confirm-btn');
const csvFileSelect = document.getElementById('csv-file-select');
const csvInfoDiv = document.getElementById('csv-info');
function openModal(initialValue = '') {
modalOverlay.style.display = 'flex';
@ -246,19 +257,88 @@
manager.populateDropdowns();
const socket = io();
socket.on('connect', () => console.log('Socket.IO connected.'));
socket.on('connect', () => {
console.log('Socket.IO connected.');
// Request available CSV files
socket.emit('get_csv_files');
});
socket.on('history_progress', (data) => {
if (data && data.progress) progressBar.style.width = `${data.progress}%`;
});
socket.on('csv_files_list', (data) => {
console.log('Received CSV files list:', data);
populateCsvDropdown(data.files, data.selected);
});
function populateCsvDropdown(files, selectedFile) {
csvFileSelect.innerHTML = '';
if (files.length === 0) {
const option = document.createElement('option');
option.value = '';
option.textContent = 'No CSV files available';
csvFileSelect.appendChild(option);
csvInfoDiv.textContent = '';
return;
}
files.forEach(file => {
const option = document.createElement('option');
option.value = file.filename;
option.textContent = file.display_name;
if (file.filename === selectedFile) {
option.selected = true;
// Show info about selected file
const sizeInMB = (file.size / (1024 * 1024)).toFixed(1);
csvInfoDiv.textContent = `${sizeInMB} MB - ${file.filename}`;
}
csvFileSelect.appendChild(option);
});
}
csvFileSelect.addEventListener('change', (e) => {
const selectedFile = e.target.value;
if (selectedFile) {
console.log('User selected CSV file:', selectedFile);
// Update info display
const selectedOption = e.target.selectedOptions[0];
const files = Array.from(e.target.options).map(option => ({
filename: option.value,
display_name: option.textContent,
size: 0 // Will be updated by server response
}));
socket.emit('select_csv_file', { filename: selectedFile });
// Show loading state
progressContainer.style.display = 'block';
progressBar.style.width = '0%';
csvInfoDiv.textContent = 'Loading...';
}
});
socket.on('history_finished', (data) => {
if (!data || !data.klines_1m) return;
progressBar.style.width = '100%';
baseCandleData1m = data.klines_1m.map(k => ({
time: k[0] / 1000, open: parseFloat(k[1]), high: parseFloat(k[2]),
low: parseFloat(k[3]), close: parseFloat(k[4])
}));
baseCandleData1m = data.klines_1m
.map(k => ({
time: k[0] / 1000,
open: parseFloat(k[1]),
high: parseFloat(k[2]),
low: parseFloat(k[3]),
close: parseFloat(k[4])
}))
.filter(candle => {
// Filter out invalid candles with null, undefined, or NaN values
return candle.time &&
!isNaN(candle.open) && !isNaN(candle.high) &&
!isNaN(candle.low) && !isNaN(candle.close) &&
candle.open > 0 && candle.high > 0 &&
candle.low > 0 && candle.close > 0;
});
updateChartForTimeframe(true);
setTimeout(() => { progressContainer.style.display = 'none'; }, 500);
});
@ -266,6 +346,16 @@
// --- MODIFICATION START: Rewritten candle update and creation logic ---
function handleLiveUpdate(update) {
if (baseCandleData1m.length === 0 || displayedCandleData.length === 0) return;
// Validate the update data
if (!update || !update.time ||
isNaN(update.open) || isNaN(update.high) ||
isNaN(update.low) || isNaN(update.close) ||
update.open <= 0 || update.high <= 0 ||
update.low <= 0 || update.close <= 0) {
console.warn('Invalid update data received:', update);
return;
}
// First, ensure the base 1m data is up-to-date.
const lastBaseCandle = baseCandleData1m[baseCandleData1m.length - 1];
@ -278,20 +368,21 @@
const candleDurationSeconds = currentTimeframeMinutes * 60;
let lastDisplayedCandle = displayedCandleData[displayedCandleData.length - 1];
// Calculate which bucket this update belongs to using simple division
const updateBucketTime = Math.floor(update.time / candleDurationSeconds) * candleDurationSeconds;
// Check if the update belongs to the currently forming displayed candle
if (update.time >= lastDisplayedCandle.time && update.time < lastDisplayedCandle.time + candleDurationSeconds) {
if (updateBucketTime === lastDisplayedCandle.time) {
// It does, so just update the High, Low, and Close prices
lastDisplayedCandle.high = Math.max(lastDisplayedCandle.high, update.high);
lastDisplayedCandle.low = Math.min(lastDisplayedCandle.low, update.low);
lastDisplayedCandle.close = update.close;
candlestickSeries.update(lastDisplayedCandle);
} else if (update.time >= lastDisplayedCandle.time + candleDurationSeconds) {
} else if (updateBucketTime > lastDisplayedCandle.time) {
// This update is for a NEW candle.
const newCandleTime = Math.floor(update.time / candleDurationSeconds) * candleDurationSeconds;
// Create the new candle. Its O,H,L,C are all from this first tick.
const newCandle = {
time: newCandleTime,
time: updateBucketTime,
open: update.open,
high: update.high,
low: update.low,
@ -383,19 +474,37 @@
function updateChartForTimeframe(isFullReset = false) {
if (baseCandleData1m.length === 0) return;
const visibleTimeRange = isFullReset ? null : chart.timeScale().getVisibleTimeRange();
const newCandleData = aggregateCandles(baseCandleData1m, currentTimeframeMinutes);
if (newCandleData.length > 0) {
displayedCandleData = newCandleData;
candlestickSeries.setData(displayedCandleData);
chartTitle.textContent = `{{ symbol }} Chart (${currentTimeframeMinutes}m)`;
manager.recalculateAllAfterHistory(baseCandleData1m, displayedCandleData);
if (visibleTimeRange) {
chart.timeScale().setVisibleRange(visibleTimeRange);
try {
const visibleTimeRange = isFullReset ? null : chart.timeScale().getVisibleTimeRange();
const newCandleData = aggregateCandles(baseCandleData1m, currentTimeframeMinutes);
// Validate the aggregated data
const validCandleData = newCandleData.filter(candle => {
return candle && candle.time &&
!isNaN(candle.open) && !isNaN(candle.high) &&
!isNaN(candle.low) && !isNaN(candle.close) &&
candle.open > 0 && candle.high > 0 &&
candle.low > 0 && candle.close > 0;
});
if (validCandleData.length > 0) {
displayedCandleData = validCandleData;
candlestickSeries.setData(displayedCandleData);
chartTitle.textContent = `{{ symbol }} Chart (${currentTimeframeMinutes}m)`;
manager.recalculateAllAfterHistory(baseCandleData1m, displayedCandleData);
if (visibleTimeRange) {
chart.timeScale().setVisibleRange(visibleTimeRange);
} else {
chart.timeScale().fitContent();
}
} else {
chart.timeScale().fitContent();
console.warn('No valid candle data available for timeframe:', currentTimeframeMinutes);
}
} catch (error) {
console.error('Error updating chart for timeframe:', error);
console.error('Current timeframe:', currentTimeframeMinutes);
console.error('Base data length:', baseCandleData1m.length);
}
}