254 lines
9.8 KiB
HTML
254 lines
9.8 KiB
HTML
<div class="inference-panel">
|
|
<!-- Inference Controls -->
|
|
<div class="row mb-3">
|
|
<div class="col-md-8">
|
|
<h6>Inference Simulation</h6>
|
|
<p class="text-muted small mb-0">
|
|
Replay annotated periods with model predictions to measure performance
|
|
</p>
|
|
</div>
|
|
<div class="col-md-4 text-end">
|
|
<div class="btn-group" role="group">
|
|
<button class="btn btn-sm btn-outline-primary" id="inference-play-btn">
|
|
<i class="fas fa-play"></i>
|
|
</button>
|
|
<button class="btn btn-sm btn-outline-primary" id="inference-pause-btn" disabled>
|
|
<i class="fas fa-pause"></i>
|
|
</button>
|
|
<button class="btn btn-sm btn-outline-primary" id="inference-stop-btn" disabled>
|
|
<i class="fas fa-stop"></i>
|
|
</button>
|
|
</div>
|
|
<select class="form-select form-select-sm d-inline-block w-auto ms-2" id="inference-speed-select">
|
|
<option value="1">1x</option>
|
|
<option value="2">2x</option>
|
|
<option value="5">5x</option>
|
|
<option value="10">10x</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Inference Chart -->
|
|
<div class="row mb-3">
|
|
<div class="col-12">
|
|
<div id="inference-chart" style="height: 400px;"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Performance Metrics -->
|
|
<div class="row">
|
|
<div class="col-md-3">
|
|
<div class="card bg-dark">
|
|
<div class="card-body text-center py-2">
|
|
<div class="small text-muted">Accuracy</div>
|
|
<div class="h4 mb-0" id="metric-accuracy">--</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card bg-dark">
|
|
<div class="card-body text-center py-2">
|
|
<div class="small text-muted">Precision</div>
|
|
<div class="h4 mb-0" id="metric-precision">--</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card bg-dark">
|
|
<div class="card-body text-center py-2">
|
|
<div class="small text-muted">Recall</div>
|
|
<div class="h4 mb-0" id="metric-recall">--</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card bg-dark">
|
|
<div class="card-body text-center py-2">
|
|
<div class="small text-muted">F1 Score</div>
|
|
<div class="h4 mb-0" id="metric-f1">--</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Prediction Timeline -->
|
|
<div class="row mt-3">
|
|
<div class="col-12">
|
|
<h6>Prediction Timeline</h6>
|
|
<div class="table-responsive" style="max-height: 300px; overflow-y: auto;">
|
|
<table class="table table-sm table-dark table-striped">
|
|
<thead>
|
|
<tr>
|
|
<th>Time</th>
|
|
<th>Prediction</th>
|
|
<th>Confidence</th>
|
|
<th>Actual</th>
|
|
<th>Result</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="prediction-timeline-body">
|
|
<tr>
|
|
<td colspan="5" class="text-center text-muted">
|
|
No predictions yet
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Confusion Matrix -->
|
|
<div class="row mt-3">
|
|
<div class="col-md-6">
|
|
<h6>Confusion Matrix</h6>
|
|
<table class="table table-sm table-dark table-bordered text-center">
|
|
<thead>
|
|
<tr>
|
|
<th></th>
|
|
<th colspan="2">Predicted</th>
|
|
</tr>
|
|
<tr>
|
|
<th>Actual</th>
|
|
<th>BUY</th>
|
|
<th>SELL</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<th>BUY</th>
|
|
<td id="cm-tp-buy">0</td>
|
|
<td id="cm-fn-buy">0</td>
|
|
</tr>
|
|
<tr>
|
|
<th>SELL</th>
|
|
<td id="cm-fp-sell">0</td>
|
|
<td id="cm-tn-sell">0</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<h6>Prediction Distribution</h6>
|
|
<div id="prediction-distribution-chart" style="height: 200px;"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let inferenceState = {
|
|
isPlaying: false,
|
|
currentIndex: 0,
|
|
predictions: [],
|
|
annotations: [],
|
|
speed: 1
|
|
};
|
|
|
|
// Playback controls
|
|
document.getElementById('inference-play-btn').addEventListener('click', function() {
|
|
inferenceState.isPlaying = true;
|
|
this.disabled = true;
|
|
document.getElementById('inference-pause-btn').disabled = false;
|
|
document.getElementById('inference-stop-btn').disabled = false;
|
|
playInference();
|
|
});
|
|
|
|
document.getElementById('inference-pause-btn').addEventListener('click', function() {
|
|
inferenceState.isPlaying = false;
|
|
this.disabled = true;
|
|
document.getElementById('inference-play-btn').disabled = false;
|
|
});
|
|
|
|
document.getElementById('inference-stop-btn').addEventListener('click', function() {
|
|
inferenceState.isPlaying = false;
|
|
inferenceState.currentIndex = 0;
|
|
document.getElementById('inference-play-btn').disabled = false;
|
|
document.getElementById('inference-pause-btn').disabled = true;
|
|
this.disabled = true;
|
|
resetInferenceDisplay();
|
|
});
|
|
|
|
document.getElementById('inference-speed-select').addEventListener('change', function(e) {
|
|
inferenceState.speed = parseFloat(e.target.value);
|
|
});
|
|
|
|
function playInference() {
|
|
if (!inferenceState.isPlaying || inferenceState.currentIndex >= inferenceState.predictions.length) {
|
|
inferenceState.isPlaying = false;
|
|
document.getElementById('inference-play-btn').disabled = false;
|
|
document.getElementById('inference-pause-btn').disabled = true;
|
|
document.getElementById('inference-stop-btn').disabled = true;
|
|
return;
|
|
}
|
|
|
|
const prediction = inferenceState.predictions[inferenceState.currentIndex];
|
|
displayPrediction(prediction);
|
|
|
|
inferenceState.currentIndex++;
|
|
|
|
// Schedule next prediction
|
|
const delay = 1000 / inferenceState.speed;
|
|
setTimeout(playInference, delay);
|
|
}
|
|
|
|
function displayPrediction(prediction) {
|
|
// Add to timeline table
|
|
const tbody = document.getElementById('prediction-timeline-body');
|
|
if (tbody.children[0].colSpan === 5) {
|
|
tbody.innerHTML = ''; // Clear "no predictions" message
|
|
}
|
|
|
|
const row = document.createElement('tr');
|
|
const resultClass = prediction.correct ? 'text-success' : 'text-danger';
|
|
const resultIcon = prediction.correct ? 'fa-check' : 'fa-times';
|
|
|
|
row.innerHTML = `
|
|
<td>${new Date(prediction.timestamp).toLocaleTimeString()}</td>
|
|
<td><span class="badge bg-${prediction.predicted_action === 'BUY' ? 'success' : 'danger'}">${prediction.predicted_action}</span></td>
|
|
<td>${(prediction.confidence * 100).toFixed(1)}%</td>
|
|
<td><span class="badge bg-${prediction.actual_action === 'BUY' ? 'success' : 'danger'}">${prediction.actual_action}</span></td>
|
|
<td class="${resultClass}"><i class="fas ${resultIcon}"></i></td>
|
|
`;
|
|
|
|
tbody.appendChild(row);
|
|
|
|
// Scroll to bottom
|
|
tbody.parentElement.scrollTop = tbody.parentElement.scrollHeight;
|
|
|
|
// Update chart (if implemented)
|
|
updateInferenceChart(prediction);
|
|
}
|
|
|
|
function updateInferenceChart(prediction) {
|
|
// TODO: Update Plotly chart with prediction marker
|
|
}
|
|
|
|
function resetInferenceDisplay() {
|
|
document.getElementById('prediction-timeline-body').innerHTML = `
|
|
<tr>
|
|
<td colspan="5" class="text-center text-muted">
|
|
No predictions yet
|
|
</td>
|
|
</tr>
|
|
`;
|
|
|
|
document.getElementById('metric-accuracy').textContent = '--';
|
|
document.getElementById('metric-precision').textContent = '--';
|
|
document.getElementById('metric-recall').textContent = '--';
|
|
document.getElementById('metric-f1').textContent = '--';
|
|
}
|
|
|
|
function updateMetrics(metrics) {
|
|
document.getElementById('metric-accuracy').textContent = (metrics.accuracy * 100).toFixed(1) + '%';
|
|
document.getElementById('metric-precision').textContent = (metrics.precision * 100).toFixed(1) + '%';
|
|
document.getElementById('metric-recall').textContent = (metrics.recall * 100).toFixed(1) + '%';
|
|
document.getElementById('metric-f1').textContent = (metrics.f1_score * 100).toFixed(1) + '%';
|
|
|
|
// Update confusion matrix
|
|
document.getElementById('cm-tp-buy').textContent = metrics.confusion_matrix.tp_buy;
|
|
document.getElementById('cm-fn-buy').textContent = metrics.confusion_matrix.fn_buy;
|
|
document.getElementById('cm-fp-sell').textContent = metrics.confusion_matrix.fp_sell;
|
|
document.getElementById('cm-tn-sell').textContent = metrics.confusion_matrix.tn_sell;
|
|
}
|
|
</script>
|