predictions display
This commit is contained in:
@@ -2525,7 +2525,17 @@ class AnnotationDashboard:
|
|||||||
if transformer_preds:
|
if transformer_preds:
|
||||||
# Convert any remaining tensors to Python types before JSON serialization
|
# Convert any remaining tensors to Python types before JSON serialization
|
||||||
transformer_pred = transformer_preds[-1].copy()
|
transformer_pred = transformer_preds[-1].copy()
|
||||||
|
|
||||||
|
# CRITICAL: Log prediction structure to debug missing predicted_candle
|
||||||
|
logger.debug(f"Transformer prediction keys: {list(transformer_pred.keys())}")
|
||||||
|
if 'predicted_candle' in transformer_pred:
|
||||||
|
logger.debug(f"predicted_candle timeframes: {list(transformer_pred['predicted_candle'].keys()) if isinstance(transformer_pred['predicted_candle'], dict) else 'not a dict'}")
|
||||||
|
|
||||||
predictions['transformer'] = self._serialize_prediction(transformer_pred)
|
predictions['transformer'] = self._serialize_prediction(transformer_pred)
|
||||||
|
|
||||||
|
# Verify predicted_candle is preserved after serialization
|
||||||
|
if 'predicted_candle' not in predictions['transformer'] and 'predicted_candle' in transformer_pred:
|
||||||
|
logger.warning("predicted_candle was lost during serialization!")
|
||||||
|
|
||||||
if predictions:
|
if predictions:
|
||||||
response['prediction'] = predictions
|
response['prediction'] = predictions
|
||||||
|
|||||||
@@ -1021,7 +1021,8 @@ class ChartManager {
|
|||||||
return close >= data.open[i] ? '#10b981' : '#ef4444';
|
return close >= data.open[i] ? '#10b981' : '#ef4444';
|
||||||
});
|
});
|
||||||
|
|
||||||
// Prepare chart data
|
// CRITICAL: Prepare chart data with correct yaxis assignments
|
||||||
|
// Candlestick uses 'y' (price axis on top), Volume uses 'y2' (volume axis at bottom)
|
||||||
const chartData = [
|
const chartData = [
|
||||||
{
|
{
|
||||||
x: data.timestamps,
|
x: data.timestamps,
|
||||||
@@ -1031,6 +1032,7 @@ class ChartManager {
|
|||||||
close: data.close,
|
close: data.close,
|
||||||
type: 'candlestick',
|
type: 'candlestick',
|
||||||
name: 'Price',
|
name: 'Price',
|
||||||
|
yaxis: 'y', // Explicitly set to price axis (top)
|
||||||
increasing: {
|
increasing: {
|
||||||
line: { color: '#10b981', width: 1 },
|
line: { color: '#10b981', width: 1 },
|
||||||
fillcolor: '#10b981'
|
fillcolor: '#10b981'
|
||||||
@@ -1044,7 +1046,7 @@ class ChartManager {
|
|||||||
x: data.timestamps,
|
x: data.timestamps,
|
||||||
y: data.volume,
|
y: data.volume,
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
yaxis: 'y2',
|
yaxis: 'y2', // Explicitly set to volume axis (bottom)
|
||||||
name: 'Volume',
|
name: 'Volume',
|
||||||
marker: {
|
marker: {
|
||||||
color: volumeColors,
|
color: volumeColors,
|
||||||
@@ -1183,16 +1185,109 @@ class ChartManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use Plotly.react for efficient updates
|
// CRITICAL FIX: Preserve existing layout (theme, yaxis domains, etc.) when updating
|
||||||
const update = {
|
const chart = this.charts[timeframe];
|
||||||
|
if (!chart) return;
|
||||||
|
|
||||||
|
const plotElement = document.getElementById(plotId);
|
||||||
|
if (!plotElement || !plotElement._fullLayout) {
|
||||||
|
// Chart not initialized yet, skip
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current layout to preserve theme and settings
|
||||||
|
const currentLayout = plotElement._fullLayout;
|
||||||
|
const currentConfig = plotElement._fullConfig || {};
|
||||||
|
|
||||||
|
// Preserve critical layout settings
|
||||||
|
const layoutUpdate = {
|
||||||
shapes: shapes,
|
shapes: shapes,
|
||||||
annotations: annotations
|
annotations: annotations,
|
||||||
|
// Preserve theme colors
|
||||||
|
plot_bgcolor: currentLayout.plot_bgcolor || '#1f2937',
|
||||||
|
paper_bgcolor: currentLayout.paper_bgcolor || '#1f2937',
|
||||||
|
font: currentLayout.font || { color: '#f8f9fa', size: 11 },
|
||||||
|
// CRITICAL: Preserve yaxis domains (price on top [0.3, 1], volume at bottom [0, 0.25])
|
||||||
|
// This ensures charts don't get swapped
|
||||||
|
yaxis: {
|
||||||
|
domain: currentLayout.yaxis?.domain || [0.3, 1], // Price chart on top
|
||||||
|
title: currentLayout.yaxis?.title || { text: 'Price', font: { size: 10 } },
|
||||||
|
gridcolor: currentLayout.yaxis?.gridcolor || '#374151',
|
||||||
|
color: currentLayout.yaxis?.color || '#9ca3af',
|
||||||
|
side: currentLayout.yaxis?.side || 'left'
|
||||||
|
},
|
||||||
|
yaxis2: {
|
||||||
|
domain: currentLayout.yaxis2?.domain || [0, 0.25], // Volume chart at bottom
|
||||||
|
title: currentLayout.yaxis2?.title || { text: 'Volume', font: { size: 10 } },
|
||||||
|
gridcolor: currentLayout.yaxis2?.gridcolor || '#374151',
|
||||||
|
color: currentLayout.yaxis2?.color || '#9ca3af',
|
||||||
|
showgrid: currentLayout.yaxis2?.showgrid !== undefined ? currentLayout.yaxis2.showgrid : false,
|
||||||
|
side: currentLayout.yaxis2?.side || 'right'
|
||||||
|
},
|
||||||
|
// Preserve xaxis settings
|
||||||
|
xaxis: {
|
||||||
|
gridcolor: currentLayout.xaxis?.gridcolor || '#374151',
|
||||||
|
color: currentLayout.xaxis?.color || '#9ca3af'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Plotly.react(plotId, chartData, update);
|
|
||||||
|
// Use Plotly.react with full layout to preserve theme and structure
|
||||||
|
Plotly.react(plotId, chartData, layoutUpdate, currentConfig).then(() => {
|
||||||
|
// Restore predictions and signals after chart update
|
||||||
|
if (this.predictions && this.predictions[timeframe]) {
|
||||||
|
this.updatePredictions({ [timeframe]: this.predictions[timeframe] });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore ghost candles if they exist
|
||||||
|
if (this.ghostCandleHistory && this.ghostCandleHistory[timeframe]) {
|
||||||
|
this.ghostCandleHistory[timeframe].forEach(ghost => {
|
||||||
|
this._addGhostCandle(timeframe, ghost);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// CRITICAL: Fetch latest predictions from API after refresh
|
||||||
|
// This ensures predictions and signals are displayed even after refresh
|
||||||
|
this._fetchAndRestorePredictions(timeframe);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch and restore predictions after chart refresh
|
||||||
|
*/
|
||||||
|
_fetchAndRestorePredictions(timeframe) {
|
||||||
|
try {
|
||||||
|
// Fetch latest signals which include predictions
|
||||||
|
fetch('/api/realtime-inference/signals')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success && data.signals && data.signals.length > 0) {
|
||||||
|
const latest = data.signals[0];
|
||||||
|
|
||||||
|
// Update predictions if transformer prediction exists
|
||||||
|
if (latest.predicted_candle && Object.keys(latest.predicted_candle).length > 0) {
|
||||||
|
const predictions = {};
|
||||||
|
predictions['transformer'] = latest;
|
||||||
|
this.updatePredictions(predictions);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update signals on chart
|
||||||
|
if (latest.action && ['BUY', 'SELL', 'HOLD'].includes(latest.action)) {
|
||||||
|
if (typeof displaySignalOnChart === 'function') {
|
||||||
|
displaySignalOnChart(latest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.debug('Could not fetch predictions after refresh:', error);
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.debug('Error fetching predictions:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add annotation to charts
|
* Add annotation to charts
|
||||||
*/
|
*/
|
||||||
@@ -2643,6 +2738,13 @@ class ChartManager {
|
|||||||
|
|
||||||
// Add Transformer predictions (star markers with trend lines + ghost candles)
|
// Add Transformer predictions (star markers with trend lines + ghost candles)
|
||||||
if (predictions.transformer) {
|
if (predictions.transformer) {
|
||||||
|
console.log(`[updatePredictions] Processing transformer prediction:`, {
|
||||||
|
action: predictions.transformer.action,
|
||||||
|
confidence: predictions.transformer.confidence,
|
||||||
|
has_predicted_candle: !!predictions.transformer.predicted_candle,
|
||||||
|
predicted_candle_keys: predictions.transformer.predicted_candle ? Object.keys(predictions.transformer.predicted_candle) : []
|
||||||
|
});
|
||||||
|
|
||||||
this._addTransformerPrediction(predictions.transformer, predictionShapes, predictionAnnotations);
|
this._addTransformerPrediction(predictions.transformer, predictionShapes, predictionAnnotations);
|
||||||
|
|
||||||
// Add trend vector visualization (shorter projection to avoid zoom issues)
|
// Add trend vector visualization (shorter projection to avoid zoom issues)
|
||||||
@@ -2650,7 +2752,7 @@ class ChartManager {
|
|||||||
this._addTrendPrediction(predictions.transformer.trend_vector, predictionShapes, predictionAnnotations);
|
this._addTrendPrediction(predictions.transformer.trend_vector, predictionShapes, predictionAnnotations);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle Predicted Candles
|
// Handle Predicted Candles (ghost candles)
|
||||||
if (predictions.transformer.predicted_candle) {
|
if (predictions.transformer.predicted_candle) {
|
||||||
console.log(`[updatePredictions] predicted_candle data:`, predictions.transformer.predicted_candle);
|
console.log(`[updatePredictions] predicted_candle data:`, predictions.transformer.predicted_candle);
|
||||||
const candleData = predictions.transformer.predicted_candle[timeframe];
|
const candleData = predictions.transformer.predicted_candle[timeframe];
|
||||||
@@ -2665,7 +2767,11 @@ class ChartManager {
|
|||||||
let targetTimestamp;
|
let targetTimestamp;
|
||||||
|
|
||||||
// Get the last real candle timestamp to ensure we predict the NEXT one
|
// Get the last real candle timestamp to ensure we predict the NEXT one
|
||||||
const lastRealCandle = chart.data.timestamps[chart.data.timestamps.length - 1];
|
// CRITICAL FIX: Use Plotly data structure (chartData[0].x for timestamps)
|
||||||
|
const candlestickTrace = chartData[0]; // First trace is candlestick
|
||||||
|
const lastRealCandle = candlestickTrace && candlestickTrace.x && candlestickTrace.x.length > 0
|
||||||
|
? candlestickTrace.x[candlestickTrace.x.length - 1]
|
||||||
|
: null;
|
||||||
if (lastRealCandle) {
|
if (lastRealCandle) {
|
||||||
const lastCandleTime = new Date(lastRealCandle);
|
const lastCandleTime = new Date(lastRealCandle);
|
||||||
// Predict for the next candle period
|
// Predict for the next candle period
|
||||||
@@ -2750,21 +2856,40 @@ class ChartManager {
|
|||||||
// If we have a stored prediction that matches the CURRENT candle time, show it
|
// If we have a stored prediction that matches the CURRENT candle time, show it
|
||||||
if (this.lastPredictions && this.lastPredictions[timeframe]) {
|
if (this.lastPredictions && this.lastPredictions[timeframe]) {
|
||||||
const lastPred = this.lastPredictions[timeframe];
|
const lastPred = this.lastPredictions[timeframe];
|
||||||
const currentTimestamp = chart.data.timestamps[chart.data.timestamps.length - 1];
|
// CRITICAL FIX: Use Plotly data structure for timestamps
|
||||||
|
const candlestickTrace = chartData[0];
|
||||||
|
const currentTimestamp = candlestickTrace && candlestickTrace.x && candlestickTrace.x.length > 0
|
||||||
|
? candlestickTrace.x[candlestickTrace.x.length - 1]
|
||||||
|
: null;
|
||||||
|
|
||||||
// Compare timestamps (allow small diff for jitter)
|
if (currentTimestamp) {
|
||||||
if (Math.abs(new Date(lastPred.timestamp).getTime() - new Date(currentTimestamp).getTime()) < 1000) {
|
// Compare timestamps (allow small diff for jitter)
|
||||||
this._addShadowCandlePrediction(lastPred.candle, currentTimestamp, predictionTraces);
|
if (Math.abs(new Date(lastPred.timestamp).getTime() - new Date(currentTimestamp).getTime()) < 1000) {
|
||||||
|
this._addShadowCandlePrediction(lastPred.candle, currentTimestamp, predictionTraces);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update chart layout with predictions
|
// Update chart layout with predictions
|
||||||
if (predictionShapes.length > 0 || predictionAnnotations.length > 0) {
|
if (predictionShapes.length > 0 || predictionAnnotations.length > 0) {
|
||||||
|
// CRITICAL FIX: Get layout from Plotly element, not chart object
|
||||||
|
const currentLayout = plotElement._fullLayout || {};
|
||||||
|
const existingShapes = currentLayout.shapes || [];
|
||||||
|
const existingAnnotations = currentLayout.annotations || [];
|
||||||
|
|
||||||
|
// Merge new predictions with existing ones (avoid duplicates)
|
||||||
|
const allShapes = [...existingShapes, ...predictionShapes];
|
||||||
|
const allAnnotations = [...existingAnnotations, ...predictionAnnotations];
|
||||||
|
|
||||||
Plotly.relayout(plotId, {
|
Plotly.relayout(plotId, {
|
||||||
shapes: [...(chart.layout.shapes || []), ...predictionShapes],
|
shapes: allShapes,
|
||||||
annotations: [...(chart.layout.annotations || []), ...predictionAnnotations]
|
annotations: allAnnotations
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log(`[updatePredictions] Added ${predictionShapes.length} shapes and ${predictionAnnotations.length} annotations to ${timeframe} chart`);
|
||||||
|
} else {
|
||||||
|
console.debug(`[updatePredictions] No prediction shapes/annotations to add for ${timeframe}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add prediction traces (ghost candles)
|
// Add prediction traces (ghost candles)
|
||||||
@@ -3105,54 +3230,133 @@ class ChartManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_addTransformerPrediction(prediction, shapes, annotations) {
|
_addTransformerPrediction(prediction, shapes, annotations) {
|
||||||
const timestamp = new Date(prediction.timestamp || Date.now());
|
// CRITICAL FIX: Get actual price from chart instead of using normalized prediction prices
|
||||||
const currentPrice = prediction.current_price || 0;
|
const timeframe = window.appState?.currentTimeframes?.[0] || '1m';
|
||||||
const predictedPrice = prediction.predicted_price || currentPrice;
|
const chart = this.charts[timeframe];
|
||||||
|
if (!chart) {
|
||||||
|
console.warn(`[Transformer Prediction] Chart not found for timeframe: ${timeframe}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get actual current price from the last candle on the chart
|
||||||
|
let actualCurrentPrice = 0;
|
||||||
|
const plotElement = document.getElementById(chart.plotId);
|
||||||
|
if (plotElement && plotElement.data && plotElement.data.length > 0) {
|
||||||
|
const candlestickTrace = plotElement.data[0]; // First trace is candlestick
|
||||||
|
if (candlestickTrace && candlestickTrace.close && candlestickTrace.close.length > 0) {
|
||||||
|
actualCurrentPrice = candlestickTrace.close[candlestickTrace.close.length - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to prediction price if chart price not available (but check if it's normalized)
|
||||||
|
if (actualCurrentPrice === 0 || actualCurrentPrice < 1) {
|
||||||
|
// Price might be normalized, try to use prediction price
|
||||||
|
actualCurrentPrice = prediction.current_price || 0;
|
||||||
|
// If still looks normalized (< 1), we can't display it properly
|
||||||
|
if (actualCurrentPrice < 1) {
|
||||||
|
console.warn('[Transformer Prediction] Price appears normalized, cannot display on chart. Chart price:', actualCurrentPrice);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CRITICAL FIX: Parse timestamp correctly (handle both ISO strings and Date objects)
|
||||||
|
let timestamp;
|
||||||
|
if (prediction.timestamp) {
|
||||||
|
if (typeof prediction.timestamp === 'string') {
|
||||||
|
// Handle various timestamp formats
|
||||||
|
timestamp = new Date(prediction.timestamp);
|
||||||
|
if (isNaN(timestamp.getTime())) {
|
||||||
|
// Try parsing as GMT format
|
||||||
|
timestamp = new Date(prediction.timestamp.replace('GMT', 'UTC'));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
timestamp = new Date(prediction.timestamp);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
timestamp = new Date();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use current time if timestamp parsing failed
|
||||||
|
if (isNaN(timestamp.getTime())) {
|
||||||
|
timestamp = new Date();
|
||||||
|
}
|
||||||
const confidence = prediction.confidence || 0;
|
const confidence = prediction.confidence || 0;
|
||||||
const priceChange = prediction.price_change || 0;
|
const priceChange = prediction.price_change || 0;
|
||||||
const horizonMinutes = prediction.horizon_minutes || 10;
|
const horizonMinutes = prediction.horizon_minutes || 10;
|
||||||
|
|
||||||
if (confidence < 0.3 || currentPrice === 0) return;
|
if (confidence < 0.3 || actualCurrentPrice === 0) return;
|
||||||
|
|
||||||
|
// CRITICAL: Calculate predicted price from actual current price and price change
|
||||||
|
// priceChange is typically a percentage or ratio
|
||||||
|
let actualPredictedPrice;
|
||||||
|
if (prediction.predicted_price && prediction.predicted_price > 1) {
|
||||||
|
// Use predicted_price if it looks like actual price (not normalized)
|
||||||
|
actualPredictedPrice = prediction.predicted_price;
|
||||||
|
} else if (typeof priceChange === 'number') {
|
||||||
|
// Calculate from price change (could be percentage or ratio)
|
||||||
|
if (Math.abs(priceChange) > 10) {
|
||||||
|
// Looks like percentage (e.g., 1.0 = 1%)
|
||||||
|
actualPredictedPrice = actualCurrentPrice * (1 + priceChange / 100);
|
||||||
|
} else {
|
||||||
|
// Looks like ratio (e.g., 0.01 = 1%)
|
||||||
|
actualPredictedPrice = actualCurrentPrice * (1 + priceChange);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fallback: use action to determine direction
|
||||||
|
if (prediction.action === 'BUY') {
|
||||||
|
actualPredictedPrice = actualCurrentPrice * 1.01; // +1%
|
||||||
|
} else if (prediction.action === 'SELL') {
|
||||||
|
actualPredictedPrice = actualCurrentPrice * 0.99; // -1%
|
||||||
|
} else {
|
||||||
|
actualPredictedPrice = actualCurrentPrice; // HOLD
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate end time
|
// Calculate end time
|
||||||
const endTime = new Date(timestamp.getTime() + horizonMinutes * 60 * 1000);
|
const endTime = new Date(timestamp.getTime() + horizonMinutes * 60 * 1000);
|
||||||
|
|
||||||
// Determine color based on price change
|
// Determine color based on action or price change
|
||||||
let color;
|
let color;
|
||||||
if (priceChange > 0.5) {
|
if (prediction.action === 'BUY' || (priceChange > 0 && priceChange > 0.5)) {
|
||||||
color = 'rgba(0, 200, 255, 0.6)'; // Cyan for UP
|
color = 'rgba(0, 200, 255, 0.6)'; // Cyan for UP/BUY
|
||||||
} else if (priceChange < -0.5) {
|
} else if (prediction.action === 'SELL' || (priceChange < 0 && priceChange < -0.5)) {
|
||||||
color = 'rgba(255, 100, 0, 0.6)'; // Orange for DOWN
|
color = 'rgba(255, 100, 0, 0.6)'; // Orange for DOWN/SELL
|
||||||
} else {
|
} else {
|
||||||
color = 'rgba(150, 150, 255, 0.5)'; // Light blue for STABLE
|
color = 'rgba(150, 150, 255, 0.5)'; // Light blue for STABLE/HOLD
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add trend line
|
// Add trend line from current price to predicted price
|
||||||
shapes.push({
|
shapes.push({
|
||||||
type: 'line',
|
type: 'line',
|
||||||
x0: timestamp,
|
x0: timestamp,
|
||||||
y0: currentPrice,
|
y0: actualCurrentPrice,
|
||||||
x1: endTime,
|
x1: endTime,
|
||||||
y1: predictedPrice,
|
y1: actualPredictedPrice,
|
||||||
line: {
|
line: {
|
||||||
color: color,
|
color: color,
|
||||||
width: 2 + confidence * 2,
|
width: 2 + confidence * 2,
|
||||||
dash: 'dashdot'
|
dash: 'dashdot'
|
||||||
}
|
},
|
||||||
|
layer: 'above'
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add star marker at target
|
// Add star marker at target with action label
|
||||||
|
const actionText = prediction.action === 'BUY' ? '▲' : prediction.action === 'SELL' ? '▼' : '★';
|
||||||
annotations.push({
|
annotations.push({
|
||||||
x: endTime,
|
x: endTime,
|
||||||
y: predictedPrice,
|
y: actualPredictedPrice,
|
||||||
text: '★',
|
text: `${actionText} ${(confidence * 100).toFixed(0)}%`,
|
||||||
showarrow: false,
|
showarrow: false,
|
||||||
font: {
|
font: {
|
||||||
size: 14 + confidence * 6,
|
size: 12 + confidence * 4,
|
||||||
color: color
|
color: color
|
||||||
},
|
},
|
||||||
opacity: 0.6 + confidence * 0.4
|
bgcolor: 'rgba(31, 41, 55, 0.8)',
|
||||||
|
borderpad: 3,
|
||||||
|
opacity: 0.8 + confidence * 0.2
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log(`[Transformer Prediction] Added prediction marker: ${prediction.action} @ ${actualCurrentPrice.toFixed(2)} -> ${actualPredictedPrice.toFixed(2)} (${(confidence * 100).toFixed(1)}% confidence)`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -70,15 +70,31 @@ class LiveUpdatesPolling {
|
|||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
// Handle chart update
|
// Handle chart update (even if null, predictions should still be processed)
|
||||||
if (data.chart_update && this.onChartUpdate) {
|
if (data.chart_update && this.onChartUpdate) {
|
||||||
this.onChartUpdate(data.chart_update);
|
this.onChartUpdate(data.chart_update);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle prediction update
|
// CRITICAL FIX: Handle prediction update properly
|
||||||
|
// data.prediction is already in format { transformer: {...}, dqn: {...}, cnn: {...} }
|
||||||
if (data.prediction && this.onPredictionUpdate) {
|
if (data.prediction && this.onPredictionUpdate) {
|
||||||
|
// Log prediction data for debugging
|
||||||
|
console.log('[Live Updates] Received prediction data:', {
|
||||||
|
has_transformer: !!data.prediction.transformer,
|
||||||
|
has_dqn: !!data.prediction.dqn,
|
||||||
|
has_cnn: !!data.prediction.cnn,
|
||||||
|
transformer_action: data.prediction.transformer?.action,
|
||||||
|
transformer_confidence: data.prediction.transformer?.confidence,
|
||||||
|
has_predicted_candle: !!data.prediction.transformer?.predicted_candle
|
||||||
|
});
|
||||||
|
|
||||||
|
// Pass the prediction object directly (it's already in the correct format)
|
||||||
this.onPredictionUpdate(data.prediction);
|
this.onPredictionUpdate(data.prediction);
|
||||||
|
} else if (!data.prediction) {
|
||||||
|
console.debug('[Live Updates] No prediction data in response');
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
console.debug('[Live Updates] Response not successful:', data);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
@@ -148,24 +164,51 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
window.liveUpdatesPolling.onPredictionUpdate = function(data) {
|
window.liveUpdatesPolling.onPredictionUpdate = function(data) {
|
||||||
|
// CRITICAL FIX: data is already in format { transformer: {...}, dqn: {...}, cnn: {...} }
|
||||||
|
console.log('[Live Updates] Prediction received:', data);
|
||||||
|
|
||||||
// Update prediction visualization on charts
|
// Update prediction visualization on charts
|
||||||
if (window.appState && window.appState.chartManager) {
|
if (window.appState && window.appState.chartManager) {
|
||||||
|
// Store predictions for later use
|
||||||
|
if (!window.appState.chartManager.predictions) {
|
||||||
|
window.appState.chartManager.predictions = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update stored predictions
|
||||||
|
if (data.transformer) {
|
||||||
|
window.appState.chartManager.predictions['transformer'] = data.transformer;
|
||||||
|
}
|
||||||
|
if (data.dqn) {
|
||||||
|
window.appState.chartManager.predictions['dqn'] = data.dqn;
|
||||||
|
}
|
||||||
|
if (data.cnn) {
|
||||||
|
window.appState.chartManager.predictions['cnn'] = data.cnn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update charts with predictions
|
||||||
window.appState.chartManager.updatePredictions(data);
|
window.appState.chartManager.updatePredictions(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update prediction display
|
// Update prediction display in UI
|
||||||
if (typeof updatePredictionDisplay === 'function') {
|
if (typeof updatePredictionDisplay === 'function') {
|
||||||
updatePredictionDisplay(data);
|
// updatePredictionDisplay expects a single prediction object, not the full data structure
|
||||||
|
// Pass the transformer prediction if available
|
||||||
|
if (data.transformer) {
|
||||||
|
updatePredictionDisplay(data.transformer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to prediction history
|
// Add to prediction history (use transformer prediction if available)
|
||||||
if (typeof predictionHistory !== 'undefined') {
|
if (typeof predictionHistory !== 'undefined') {
|
||||||
predictionHistory.unshift(data);
|
const predictionToAdd = data.transformer || data.dqn || data.cnn || data;
|
||||||
if (predictionHistory.length > 5) {
|
if (predictionToAdd) {
|
||||||
predictionHistory = predictionHistory.slice(0, 5);
|
predictionHistory.unshift(predictionToAdd);
|
||||||
}
|
if (predictionHistory.length > 5) {
|
||||||
if (typeof updatePredictionHistory === 'function') {
|
predictionHistory = predictionHistory.slice(0, 5);
|
||||||
updatePredictionHistory();
|
}
|
||||||
|
if (typeof updatePredictionHistory === 'function') {
|
||||||
|
updatePredictionHistory();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user