UI
This commit is contained in:
@@ -517,7 +517,16 @@
|
||||
// Real-time inference controls
|
||||
let currentInferenceId = null;
|
||||
let signalPollInterval = null;
|
||||
let predictionHistory = []; // Store last 5 predictions
|
||||
let predictionHistory = []; // Store last 15 predictions
|
||||
|
||||
// PnL tracking for simulated trading ($100 position size)
|
||||
let pnlTracker = {
|
||||
positions: [], // Open positions: {action, entryPrice, entryTime, size}
|
||||
closedTrades: [], // Completed trades with PnL
|
||||
totalPnL: 0,
|
||||
winRate: 0,
|
||||
positionSize: 100 // $100 per trade
|
||||
};
|
||||
|
||||
// Prediction steps slider handler
|
||||
document.getElementById('prediction-steps-slider').addEventListener('input', function() {
|
||||
@@ -563,9 +572,17 @@
|
||||
document.getElementById('inference-status').style.display = 'block';
|
||||
document.getElementById('inference-controls').style.display = 'block';
|
||||
|
||||
// Clear prediction history
|
||||
// Clear prediction history and reset PnL tracker
|
||||
predictionHistory = [];
|
||||
pnlTracker = {
|
||||
positions: [],
|
||||
closedTrades: [],
|
||||
totalPnL: 0,
|
||||
winRate: 0,
|
||||
positionSize: 100
|
||||
};
|
||||
updatePredictionHistory();
|
||||
updatePnLDisplay();
|
||||
|
||||
// Show live mode banner
|
||||
const banner = document.getElementById('live-mode-banner');
|
||||
@@ -636,9 +653,10 @@
|
||||
// Stop polling
|
||||
stopSignalPolling();
|
||||
|
||||
// Stop chart auto-update
|
||||
// Stop chart auto-update and remove metrics overlay
|
||||
if (window.appState && window.appState.chartManager) {
|
||||
window.appState.chartManager.stopAutoUpdate();
|
||||
window.appState.chartManager.removeLiveMetrics();
|
||||
}
|
||||
|
||||
currentInferenceId = null;
|
||||
@@ -842,6 +860,109 @@
|
||||
}
|
||||
}
|
||||
|
||||
function updatePnLTracking(action, currentPrice, timestamp) {
|
||||
// Simple trading simulation: BUY opens long, SELL opens short, HOLD closes positions
|
||||
if (action === 'BUY' && pnlTracker.positions.length === 0) {
|
||||
// Open long position
|
||||
pnlTracker.positions.push({
|
||||
action: 'BUY',
|
||||
entryPrice: currentPrice,
|
||||
entryTime: timestamp,
|
||||
size: pnlTracker.positionSize
|
||||
});
|
||||
} else if (action === 'SELL' && pnlTracker.positions.length === 0) {
|
||||
// Open short position
|
||||
pnlTracker.positions.push({
|
||||
action: 'SELL',
|
||||
entryPrice: currentPrice,
|
||||
entryTime: timestamp,
|
||||
size: pnlTracker.positionSize
|
||||
});
|
||||
} else if (action === 'HOLD' && pnlTracker.positions.length > 0) {
|
||||
// Close all positions
|
||||
pnlTracker.positions.forEach(pos => {
|
||||
let pnl = 0;
|
||||
if (pos.action === 'BUY') {
|
||||
// Long: profit if price went up
|
||||
pnl = (currentPrice - pos.entryPrice) / pos.entryPrice * pos.size;
|
||||
} else if (pos.action === 'SELL') {
|
||||
// Short: profit if price went down
|
||||
pnl = (pos.entryPrice - currentPrice) / pos.entryPrice * pos.size;
|
||||
}
|
||||
|
||||
pnlTracker.closedTrades.push({
|
||||
entryPrice: pos.entryPrice,
|
||||
exitPrice: currentPrice,
|
||||
pnl: pnl,
|
||||
entryTime: pos.entryTime,
|
||||
exitTime: timestamp
|
||||
});
|
||||
|
||||
pnlTracker.totalPnL += pnl;
|
||||
});
|
||||
|
||||
pnlTracker.positions = [];
|
||||
|
||||
// Calculate win rate
|
||||
const wins = pnlTracker.closedTrades.filter(t => t.pnl > 0).length;
|
||||
pnlTracker.winRate = pnlTracker.closedTrades.length > 0
|
||||
? (wins / pnlTracker.closedTrades.length * 100)
|
||||
: 0;
|
||||
}
|
||||
|
||||
// Update PnL display
|
||||
updatePnLDisplay();
|
||||
}
|
||||
|
||||
function updatePnLDisplay() {
|
||||
const pnlColor = pnlTracker.totalPnL >= 0 ? 'text-success' : 'text-danger';
|
||||
const pnlSign = pnlTracker.totalPnL >= 0 ? '+' : '';
|
||||
|
||||
// Update PnL metric
|
||||
const pnlElement = document.getElementById('metric-pnl');
|
||||
if (pnlElement) {
|
||||
pnlElement.textContent = `${pnlSign}$${pnlTracker.totalPnL.toFixed(2)}`;
|
||||
pnlElement.className = `h4 mb-0 ${pnlColor}`;
|
||||
}
|
||||
|
||||
// Update Win Rate
|
||||
const winrateElement = document.getElementById('metric-winrate');
|
||||
if (winrateElement) {
|
||||
winrateElement.textContent = pnlTracker.closedTrades.length > 0
|
||||
? `${pnlTracker.winRate.toFixed(1)}%`
|
||||
: '--';
|
||||
}
|
||||
|
||||
// Update Total Trades
|
||||
const tradesElement = document.getElementById('metric-trades');
|
||||
if (tradesElement) {
|
||||
tradesElement.textContent = pnlTracker.closedTrades.length;
|
||||
}
|
||||
|
||||
// Update Open Positions
|
||||
const positionsElement = document.getElementById('metric-positions');
|
||||
if (positionsElement) {
|
||||
positionsElement.textContent = pnlTracker.positions.length;
|
||||
}
|
||||
|
||||
// Update in live banner if exists
|
||||
const banner = document.getElementById('inference-status');
|
||||
if (banner) {
|
||||
let pnlDiv = document.getElementById('live-banner-pnl');
|
||||
if (!pnlDiv) {
|
||||
const metricsDiv = document.getElementById('live-banner-metrics');
|
||||
if (metricsDiv) {
|
||||
pnlDiv = document.createElement('span');
|
||||
pnlDiv.id = 'live-banner-pnl';
|
||||
metricsDiv.appendChild(pnlDiv);
|
||||
}
|
||||
}
|
||||
if (pnlDiv) {
|
||||
pnlDiv.innerHTML = `<span class="${pnlColor}">PnL: ${pnlSign}$${pnlTracker.totalPnL.toFixed(2)}</span>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updatePredictionHistory() {
|
||||
const historyDiv = document.getElementById('prediction-history');
|
||||
if (predictionHistory.length === 0) {
|
||||
@@ -932,22 +1053,48 @@
|
||||
timestamp = latest.timestamp;
|
||||
}
|
||||
|
||||
// Get current price from signal (backend uses 'price' field)
|
||||
const currentPrice = latest.price || latest.current_price;
|
||||
|
||||
// Add to prediction history (keep last 15)
|
||||
const newPrediction = {
|
||||
timestamp: timestamp,
|
||||
action: latest.action,
|
||||
confidence: latest.confidence,
|
||||
predicted_price: latest.predicted_price,
|
||||
current_price: currentPrice,
|
||||
timeframe: appState.currentTimeframes ? appState.currentTimeframes[0] : '1m'
|
||||
};
|
||||
|
||||
// Filter out undefined/invalid predictions before adding
|
||||
if (latest.action && !isNaN(latest.confidence)) {
|
||||
// Strengthen filter: only add valid signals
|
||||
const validActions = ['BUY', 'SELL', 'HOLD'];
|
||||
if (latest.action &&
|
||||
validActions.includes(latest.action) &&
|
||||
!isNaN(latest.confidence) &&
|
||||
latest.confidence > 0 &&
|
||||
currentPrice &&
|
||||
!isNaN(currentPrice)) {
|
||||
|
||||
// Update PnL tracking
|
||||
updatePnLTracking(latest.action, currentPrice, timestamp);
|
||||
|
||||
predictionHistory.unshift(newPrediction);
|
||||
if (predictionHistory.length > 15) {
|
||||
predictionHistory = predictionHistory.slice(0, 15);
|
||||
}
|
||||
updatePredictionHistory();
|
||||
} else {
|
||||
console.warn('Signal filtered out:', {
|
||||
action: latest.action,
|
||||
confidence: latest.confidence,
|
||||
price: currentPrice,
|
||||
reason: !latest.action ? 'no action' :
|
||||
!validActions.includes(latest.action) ? 'invalid action' :
|
||||
isNaN(latest.confidence) ? 'NaN confidence' :
|
||||
latest.confidence <= 0 ? 'zero confidence' :
|
||||
!currentPrice ? 'no price' :
|
||||
isNaN(currentPrice) ? 'NaN price' : 'unknown'
|
||||
});
|
||||
}
|
||||
|
||||
// Update chart with signal markers and predictions
|
||||
@@ -960,6 +1107,11 @@
|
||||
predictions[modelKey] = latest;
|
||||
|
||||
window.appState.chartManager.updatePredictions(predictions);
|
||||
|
||||
// Display live metrics on the active chart
|
||||
if (data.metrics) {
|
||||
window.appState.chartManager.updateLiveMetrics(data.metrics);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user