logging channels; training steps storage

This commit is contained in:
Dobromir Popov
2025-11-22 12:47:43 +02:00
parent 7a219b5ebc
commit e404658dc7
9 changed files with 938 additions and 37 deletions

View File

@@ -12,6 +12,9 @@ class ChartManager {
this.updateTimers = {}; // Track auto-update timers
this.autoUpdateEnabled = false; // Auto-update state
this.liveMetricsOverlay = null; // Live metrics display overlay
this.lastPredictionUpdate = {}; // Track last prediction update per timeframe
this.predictionUpdateThrottle = 500; // Min ms between prediction updates
this.lastPredictionHash = null; // Track if predictions actually changed
console.log('ChartManager initialized with timeframes:', timeframes);
}
@@ -172,6 +175,14 @@ class ChartManager {
});
});
// Merge pivot markers
if (newData.pivot_markers) {
if (!chart.data.pivot_markers) {
chart.data.pivot_markers = {};
}
Object.assign(chart.data.pivot_markers, newData.pivot_markers);
}
// 2. Update existing candles in place if they exist in new data
// Iterate backwards to optimize for recent updates
let updatesCount = 0;
@@ -212,7 +223,12 @@ class ChartManager {
if (updatesCount > 0 || remainingTimestamps.length > 0) {
console.log(`[${timeframe}] Chart update: ${updatesCount} updated, ${remainingTimestamps.length} new candles`);
this.recalculatePivots(timeframe, chart.data);
// Only recalculate pivots if we have NEW candles (not just updates to existing ones)
// This prevents unnecessary pivot recalculation on every live candle update
if (remainingTimestamps.length > 0) {
this.recalculatePivots(timeframe, chart.data);
}
this.updateSingleChart(timeframe, chart.data);
window.liveUpdateCount = (window.liveUpdateCount || 0) + 1;
@@ -1774,25 +1790,30 @@ class ChartManager {
});
}
// Update chart layout with new pivots
Plotly.relayout(chart.plotId, {
// Batch update: Use Plotly.update to combine layout and trace updates
// This reduces flickering by doing both operations in one call
const layoutUpdate = {
shapes: shapes,
annotations: annotations
});
};
const traceUpdate = pivotDots.x.length > 0 ? {
x: [pivotDots.x],
y: [pivotDots.y],
text: [pivotDots.text],
'marker.color': [pivotDots.marker.color],
'marker.size': [pivotDots.marker.size],
'marker.symbol': [pivotDots.marker.symbol]
} : {};
// Update pivot dots trace
// Use Plotly.update to batch both operations
if (pivotDots.x.length > 0) {
Plotly.restyle(chart.plotId, {
x: [pivotDots.x],
y: [pivotDots.y],
text: [pivotDots.text],
'marker.color': [pivotDots.marker.color],
'marker.size': [pivotDots.marker.size],
'marker.symbol': [pivotDots.marker.symbol]
}, [2]); // Trace index 2 is pivot dots
Plotly.update(chart.plotId, traceUpdate, layoutUpdate, [2]); // Trace index 2 is pivot dots
} else {
Plotly.relayout(chart.plotId, layoutUpdate);
}
console.log(`🎨 Redrawn ${timeframe} chart with updated pivots`);
console.log(`Redrawn ${timeframe} chart with updated pivots`);
}
/**
@@ -1803,6 +1824,8 @@ class ChartManager {
if (!chart) return;
const plotId = chart.plotId;
const plotElement = document.getElementById(plotId);
if (!plotElement) return;
// Create volume colors
const volumeColors = data.close.map((close, i) => {
@@ -1810,18 +1833,34 @@ class ChartManager {
return close >= data.open[i] ? '#10b981' : '#ef4444';
});
// Update traces
const update = {
x: [data.timestamps, data.timestamps],
open: [data.open],
high: [data.high],
low: [data.low],
close: [data.close],
y: [undefined, data.volume],
'marker.color': [undefined, volumeColors]
// Use Plotly.react for smoother, non-flickering updates
// It only updates what changed, unlike restyle which can cause flicker
const currentData = plotElement.data;
// Update only the first two traces (candlestick and volume)
// Keep other traces (pivots, predictions) intact
const updatedTraces = [...currentData];
// Update candlestick trace (trace 0)
updatedTraces[0] = {
...updatedTraces[0],
x: data.timestamps,
open: data.open,
high: data.high,
low: data.low,
close: data.close
};
// Update volume trace (trace 1)
updatedTraces[1] = {
...updatedTraces[1],
x: data.timestamps,
y: data.volume,
marker: { ...updatedTraces[1].marker, color: volumeColors }
};
Plotly.restyle(plotId, update, [0, 1]);
// Use react instead of restyle - it's smarter about what to update
Plotly.react(plotId, updatedTraces, plotElement.layout, plotElement.config);
console.log(`Updated ${timeframe} chart with ${data.timestamps.length} candles`);
}
@@ -1882,7 +1921,36 @@ class ChartManager {
// This ensures predictions appear on the chart the user is watching (e.g., '1s')
const timeframe = window.appState?.currentTimeframes?.[0] || '1m';
const chart = this.charts[timeframe];
if (!chart) return;
if (!chart) {
console.warn(`[updatePredictions] Chart not found for timeframe: ${timeframe}`);
return;
}
// Throttle prediction updates to avoid flickering
const now = Date.now();
const lastUpdate = this.lastPredictionUpdate[timeframe] || 0;
// Create a simple hash of prediction data to detect actual changes
const predictionHash = JSON.stringify({
action: predictions.transformer?.action,
confidence: predictions.transformer?.confidence,
predicted_price: predictions.transformer?.predicted_price,
timestamp: predictions.transformer?.timestamp
});
// Skip update if:
// 1. Too soon since last update (throttle)
// 2. Predictions haven't actually changed
if (now - lastUpdate < this.predictionUpdateThrottle && predictionHash === this.lastPredictionHash) {
console.debug(`[updatePredictions] Skipping update (throttled or unchanged)`);
return;
}
this.lastPredictionUpdate[timeframe] = now;
this.lastPredictionHash = predictionHash;
console.log(`[updatePredictions] Timeframe: ${timeframe}, Predictions:`, predictions);
const plotId = chart.plotId;
const plotElement = document.getElementById(plotId);
@@ -1918,7 +1986,9 @@ class ChartManager {
// Handle Predicted Candles
if (predictions.transformer.predicted_candle) {
console.log(`[updatePredictions] predicted_candle data:`, predictions.transformer.predicted_candle);
const candleData = predictions.transformer.predicted_candle[timeframe];
console.log(`[updatePredictions] candleData for ${timeframe}:`, candleData);
if (candleData) {
// Get the prediction timestamp from the model (when inference was made)
const predictionTimestamp = predictions.transformer.timestamp || new Date().toISOString();
@@ -2005,8 +2075,8 @@ class ChartManager {
// trendVector contains: angle_degrees, steepness, direction, price_delta
// We visualize this as a ray from current price
// Need current candle close and timestamp
const timeframe = '1m'; // Default to 1m for now
// Use the active timeframe from app state
const timeframe = window.appState?.currentTimeframes?.[0] || '1m';
const chart = this.charts[timeframe];
if (!chart || !chart.data) return;