fix pred candles viz

This commit is contained in:
Dobromir Popov
2025-11-22 16:22:13 +02:00
parent 539bd68110
commit 423132dc8f
5 changed files with 140 additions and 25 deletions

View File

@@ -15,6 +15,16 @@ class ChartManager {
this.lastPredictionUpdate = {}; // Track last prediction update per timeframe
this.predictionUpdateThrottle = 500; // Min ms between prediction updates
this.lastPredictionHash = null; // Track if predictions actually changed
this.ghostCandleHistory = {}; // Store ghost candles per timeframe (max 10 each)
this.maxGhostCandles = 10; // Maximum number of ghost candles to keep
// Helper to ensure all timestamps are in UTC
this.normalizeTimestamp = (timestamp) => {
if (!timestamp) return null;
// Parse and convert to UTC ISO string
const date = new Date(timestamp);
return date.toISOString(); // Always returns UTC with Z suffix
};
console.log('ChartManager initialized with timeframes:', timeframes);
}
@@ -2008,12 +2018,37 @@ class ChartManager {
targetTimestamp = new Date(inferenceTime.getTime() + 60000);
}
// 1. Next Candle Prediction (Ghost)
// Show the prediction at its proper timestamp
this._addGhostCandlePrediction(candleData, timeframe, predictionTraces, targetTimestamp);
// 1. Initialize ghost candle history for this timeframe if needed
if (!this.ghostCandleHistory[timeframe]) {
this.ghostCandleHistory[timeframe] = [];
}
// 2. Store as "Last Prediction" for this timeframe
// This allows us to visualize the "Shadow" (prediction vs actual) on the next tick
// 2. Add new ghost candle to history
const year = targetTimestamp.getUTCFullYear();
const month = String(targetTimestamp.getUTCMonth() + 1).padStart(2, '0');
const day = String(targetTimestamp.getUTCDate()).padStart(2, '0');
const hours = String(targetTimestamp.getUTCHours()).padStart(2, '0');
const minutes = String(targetTimestamp.getUTCMinutes()).padStart(2, '0');
const seconds = String(targetTimestamp.getUTCSeconds()).padStart(2, '0');
const formattedTimestamp = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
this.ghostCandleHistory[timeframe].push({
timestamp: formattedTimestamp,
candle: candleData,
targetTime: targetTimestamp
});
// 3. Keep only last 10 ghost candles
if (this.ghostCandleHistory[timeframe].length > this.maxGhostCandles) {
this.ghostCandleHistory[timeframe] = this.ghostCandleHistory[timeframe].slice(-this.maxGhostCandles);
}
// 4. Add all ghost candles from history to traces
for (const ghost of this.ghostCandleHistory[timeframe]) {
this._addGhostCandlePrediction(ghost.candle, timeframe, predictionTraces, ghost.targetTime);
}
// 5. Store as "Last Prediction" for shadow rendering
if (!this.lastPredictions) this.lastPredictions = {};
this.lastPredictions[timeframe] = {
@@ -2022,7 +2057,7 @@ class ChartManager {
inferenceTime: predictionTimestamp
};
console.log(`[${timeframe}] Ghost candle prediction placed at ${targetTimestamp.toISOString()} (inference at ${predictionTimestamp})`);
console.log(`[${timeframe}] Ghost candle added (${this.ghostCandleHistory[timeframe].length}/${this.maxGhostCandles}) at ${targetTimestamp.toISOString()}`);
}
}
@@ -2166,6 +2201,15 @@ class ChartManager {
}
}
// Format timestamp to match real candles: 'YYYY-MM-DD HH:MM:SS'
const year = nextTimestamp.getUTCFullYear();
const month = String(nextTimestamp.getUTCMonth() + 1).padStart(2, '0');
const day = String(nextTimestamp.getUTCDate()).padStart(2, '0');
const hours = String(nextTimestamp.getUTCHours()).padStart(2, '0');
const minutes = String(nextTimestamp.getUTCMinutes()).padStart(2, '0');
const seconds = String(nextTimestamp.getUTCSeconds()).padStart(2, '0');
const formattedTimestamp = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
const open = candleData[0];
const high = candleData[1];
const low = candleData[2];
@@ -2174,9 +2218,10 @@ class ChartManager {
// Determine color
const color = close >= open ? '#10b981' : '#ef4444';
// Create ghost candle trace
// Create ghost candle trace with formatted timestamp string (same as real candles)
// 150% wider than normal candles
const ghostTrace = {
x: [nextTimestamp],
x: [formattedTimestamp],
open: [open],
high: [high],
low: [low],
@@ -2184,26 +2229,39 @@ class ChartManager {
type: 'candlestick',
name: 'Ghost Prediction',
increasing: {
line: { color: color, width: 1 },
line: { color: color, width: 3 }, // 150% wider (normal is 2, so 3)
fillcolor: color
},
decreasing: {
line: { color: color, width: 1 },
line: { color: color, width: 3 }, // 150% wider
fillcolor: color
},
opacity: 0.6, // 60% transparent
hoverinfo: 'x+y+text',
text: ['Predicted Next Candle']
text: ['Predicted Next Candle'],
width: 1.5 // 150% width multiplier
};
traces.push(ghostTrace);
console.log('Added ghost candle prediction:', ghostTrace);
console.log('Added ghost candle prediction at:', formattedTimestamp, ghostTrace);
}
_addShadowCandlePrediction(candleData, timestamp, traces) {
// candleData is [Open, High, Low, Close, Volume]
// timestamp is the time where this shadow should appear (matches current candle)
// Format timestamp to match real candles if it's a Date object
let formattedTimestamp = timestamp;
if (timestamp instanceof Date) {
const year = timestamp.getUTCFullYear();
const month = String(timestamp.getUTCMonth() + 1).padStart(2, '0');
const day = String(timestamp.getUTCDate()).padStart(2, '0');
const hours = String(timestamp.getUTCHours()).padStart(2, '0');
const minutes = String(timestamp.getUTCMinutes()).padStart(2, '0');
const seconds = String(timestamp.getUTCSeconds()).padStart(2, '0');
formattedTimestamp = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
const open = candleData[0];
const high = candleData[1];
const low = candleData[2];
@@ -2212,8 +2270,9 @@ class ChartManager {
// Shadow color (purple to distinguish from ghost)
const color = '#8b5cf6'; // Violet
// Shadow candles also 150% wider
const shadowTrace = {
x: [timestamp],
x: [formattedTimestamp],
open: [open],
high: [high],
low: [low],
@@ -2221,16 +2280,17 @@ class ChartManager {
type: 'candlestick',
name: 'Shadow Prediction',
increasing: {
line: { color: color, width: 1 },
line: { color: color, width: 3 }, // 150% wider
fillcolor: 'rgba(139, 92, 246, 0.0)' // Hollow
},
decreasing: {
line: { color: color, width: 1 },
line: { color: color, width: 3 }, // 150% wider
fillcolor: 'rgba(139, 92, 246, 0.0)' // Hollow
},
opacity: 0.7,
hoverinfo: 'x+y+text',
text: ['Past Prediction']
text: ['Past Prediction'],
width: 1.5 // 150% width multiplier
};
traces.push(shadowTrace);