dane z plików - nie chodzi
This commit is contained in:
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user