t perd viz - wip
This commit is contained in:
@@ -111,6 +111,80 @@ class ChartManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update latest candle on chart (for live updates)
|
||||
* Efficiently updates only the last candle or adds a new one
|
||||
*/
|
||||
updateLatestCandle(symbol, timeframe, candle) {
|
||||
try {
|
||||
const plotId = `plot-${timeframe}`;
|
||||
const plotElement = document.getElementById(plotId);
|
||||
|
||||
if (!plotElement) {
|
||||
console.debug(`Chart ${plotId} not found for live update`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get current chart data
|
||||
const chartData = Plotly.Plots.data(plotId);
|
||||
if (!chartData || chartData.length < 2) {
|
||||
console.debug(`Chart ${plotId} not initialized yet`);
|
||||
return;
|
||||
}
|
||||
|
||||
const candlestickTrace = chartData[0];
|
||||
const volumeTrace = chartData[1];
|
||||
|
||||
// Parse timestamp
|
||||
const candleTimestamp = new Date(candle.timestamp);
|
||||
|
||||
// Check if this is updating the last candle or adding a new one
|
||||
const lastTimestamp = candlestickTrace.x[candlestickTrace.x.length - 1];
|
||||
const isNewCandle = !lastTimestamp || new Date(lastTimestamp).getTime() < candleTimestamp.getTime();
|
||||
|
||||
if (isNewCandle) {
|
||||
// Add new candle using extendTraces (most efficient)
|
||||
Plotly.extendTraces(plotId, {
|
||||
x: [[candleTimestamp]],
|
||||
open: [[candle.open]],
|
||||
high: [[candle.high]],
|
||||
low: [[candle.low]],
|
||||
close: [[candle.close]]
|
||||
}, [0]);
|
||||
|
||||
// Update volume color based on price direction
|
||||
const volumeColor = candle.close >= candle.open ? '#10b981' : '#ef4444';
|
||||
Plotly.extendTraces(plotId, {
|
||||
x: [[candleTimestamp]],
|
||||
y: [[candle.volume]],
|
||||
marker: { color: [[volumeColor]] }
|
||||
}, [1]);
|
||||
} else {
|
||||
// Update last candle using restyle
|
||||
const lastIndex = candlestickTrace.x.length - 1;
|
||||
Plotly.restyle(plotId, {
|
||||
'x': [[...candlestickTrace.x.slice(0, lastIndex), candleTimestamp]],
|
||||
'open': [[...candlestickTrace.open.slice(0, lastIndex), candle.open]],
|
||||
'high': [[...candlestickTrace.high.slice(0, lastIndex), candle.high]],
|
||||
'low': [[...candlestickTrace.low.slice(0, lastIndex), candle.low]],
|
||||
'close': [[...candlestickTrace.close.slice(0, lastIndex), candle.close]]
|
||||
}, [0]);
|
||||
|
||||
// Update volume
|
||||
const volumeColor = candle.close >= candle.open ? '#10b981' : '#ef4444';
|
||||
Plotly.restyle(plotId, {
|
||||
'x': [[...volumeTrace.x.slice(0, lastIndex), candleTimestamp]],
|
||||
'y': [[...volumeTrace.y.slice(0, lastIndex), candle.volume]],
|
||||
'marker.color': [[...volumeTrace.marker.color.slice(0, lastIndex), volumeColor]]
|
||||
}, [1]);
|
||||
}
|
||||
|
||||
console.debug(`Updated ${timeframe} chart with new candle at ${candleTimestamp.toISOString()}`);
|
||||
} catch (error) {
|
||||
console.error(`Error updating latest candle for ${timeframe}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize charts for all timeframes with pivot bounds
|
||||
*/
|
||||
@@ -1640,4 +1714,172 @@ class ChartManager {
|
||||
loadingDiv.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update model predictions on charts
|
||||
*/
|
||||
updatePredictions(predictions) {
|
||||
if (!predictions) return;
|
||||
|
||||
try {
|
||||
// Update predictions on 1m chart (primary timeframe for predictions)
|
||||
const timeframe = '1m';
|
||||
const chart = this.charts[timeframe];
|
||||
if (!chart) return;
|
||||
|
||||
const plotId = chart.plotId;
|
||||
const plotElement = document.getElementById(plotId);
|
||||
if (!plotElement) return;
|
||||
|
||||
// Get current chart data
|
||||
const chartData = plotElement.data;
|
||||
if (!chartData || chartData.length < 2) return;
|
||||
|
||||
// Prepare prediction markers
|
||||
const predictionShapes = [];
|
||||
const predictionAnnotations = [];
|
||||
|
||||
// Add DQN predictions (arrows)
|
||||
if (predictions.dqn) {
|
||||
this._addDQNPrediction(predictions.dqn, predictionShapes, predictionAnnotations);
|
||||
}
|
||||
|
||||
// Add CNN predictions (trend lines)
|
||||
if (predictions.cnn) {
|
||||
this._addCNNPrediction(predictions.cnn, predictionShapes, predictionAnnotations);
|
||||
}
|
||||
|
||||
// Add Transformer predictions (star markers with trend lines)
|
||||
if (predictions.transformer) {
|
||||
this._addTransformerPrediction(predictions.transformer, predictionShapes, predictionAnnotations);
|
||||
}
|
||||
|
||||
// Update chart layout with predictions
|
||||
if (predictionShapes.length > 0 || predictionAnnotations.length > 0) {
|
||||
Plotly.relayout(plotId, {
|
||||
shapes: [...(chart.layout.shapes || []), ...predictionShapes],
|
||||
annotations: [...(chart.layout.annotations || []), ...predictionAnnotations]
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.debug('Error updating predictions:', error);
|
||||
}
|
||||
}
|
||||
|
||||
_addDQNPrediction(prediction, shapes, annotations) {
|
||||
const timestamp = new Date(prediction.timestamp || Date.now());
|
||||
const price = prediction.current_price || 0;
|
||||
const action = prediction.action || 'HOLD';
|
||||
const confidence = prediction.confidence || 0;
|
||||
|
||||
if (action === 'HOLD' || confidence < 0.4) return;
|
||||
|
||||
// Add arrow annotation
|
||||
annotations.push({
|
||||
x: timestamp,
|
||||
y: price,
|
||||
text: action === 'BUY' ? '▲' : '▼',
|
||||
showarrow: false,
|
||||
font: {
|
||||
size: 16,
|
||||
color: action === 'BUY' ? '#10b981' : '#ef4444'
|
||||
},
|
||||
opacity: 0.5 + confidence * 0.5
|
||||
});
|
||||
}
|
||||
|
||||
_addCNNPrediction(prediction, shapes, annotations) {
|
||||
const timestamp = new Date(prediction.timestamp || Date.now());
|
||||
const currentPrice = prediction.current_price || 0;
|
||||
const predictedPrice = prediction.predicted_price || currentPrice;
|
||||
const confidence = prediction.confidence || 0;
|
||||
|
||||
if (confidence < 0.4 || currentPrice === 0) return;
|
||||
|
||||
// Calculate end time (5 minutes ahead)
|
||||
const endTime = new Date(timestamp.getTime() + 5 * 60 * 1000);
|
||||
|
||||
// Determine color based on direction
|
||||
const isUp = predictedPrice > currentPrice;
|
||||
const color = isUp ? 'rgba(0, 255, 0, 0.5)' : 'rgba(255, 0, 0, 0.5)';
|
||||
|
||||
// Add trend line
|
||||
shapes.push({
|
||||
type: 'line',
|
||||
x0: timestamp,
|
||||
y0: currentPrice,
|
||||
x1: endTime,
|
||||
y1: predictedPrice,
|
||||
line: {
|
||||
color: color,
|
||||
width: 2,
|
||||
dash: 'dot'
|
||||
}
|
||||
});
|
||||
|
||||
// Add target marker
|
||||
annotations.push({
|
||||
x: endTime,
|
||||
y: predictedPrice,
|
||||
text: '◆',
|
||||
showarrow: false,
|
||||
font: {
|
||||
size: 12,
|
||||
color: isUp ? '#10b981' : '#ef4444'
|
||||
},
|
||||
opacity: 0.5 + confidence * 0.5
|
||||
});
|
||||
}
|
||||
|
||||
_addTransformerPrediction(prediction, shapes, annotations) {
|
||||
const timestamp = new Date(prediction.timestamp || Date.now());
|
||||
const currentPrice = prediction.current_price || 0;
|
||||
const predictedPrice = prediction.predicted_price || currentPrice;
|
||||
const confidence = prediction.confidence || 0;
|
||||
const priceChange = prediction.price_change || 0;
|
||||
const horizonMinutes = prediction.horizon_minutes || 10;
|
||||
|
||||
if (confidence < 0.3 || currentPrice === 0) return;
|
||||
|
||||
// Calculate end time
|
||||
const endTime = new Date(timestamp.getTime() + horizonMinutes * 60 * 1000);
|
||||
|
||||
// Determine color based on price change
|
||||
let color;
|
||||
if (priceChange > 0.5) {
|
||||
color = 'rgba(0, 200, 255, 0.6)'; // Cyan for UP
|
||||
} else if (priceChange < -0.5) {
|
||||
color = 'rgba(255, 100, 0, 0.6)'; // Orange for DOWN
|
||||
} else {
|
||||
color = 'rgba(150, 150, 255, 0.5)'; // Light blue for STABLE
|
||||
}
|
||||
|
||||
// Add trend line
|
||||
shapes.push({
|
||||
type: 'line',
|
||||
x0: timestamp,
|
||||
y0: currentPrice,
|
||||
x1: endTime,
|
||||
y1: predictedPrice,
|
||||
line: {
|
||||
color: color,
|
||||
width: 2 + confidence * 2,
|
||||
dash: 'dashdot'
|
||||
}
|
||||
});
|
||||
|
||||
// Add star marker at target
|
||||
annotations.push({
|
||||
x: endTime,
|
||||
y: predictedPrice,
|
||||
text: '★',
|
||||
showarrow: false,
|
||||
font: {
|
||||
size: 14 + confidence * 6,
|
||||
color: color
|
||||
},
|
||||
opacity: 0.6 + confidence * 0.4
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user