uni data storage
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
<option value="">Loading models...</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Training Controls -->
|
||||
<div class="mb-3">
|
||||
<button class="btn btn-primary btn-sm w-100" id="train-model-btn">
|
||||
@@ -21,7 +21,7 @@
|
||||
Train Model
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Training Status -->
|
||||
<div id="training-status" style="display: none;">
|
||||
<div class="alert alert-info py-2 px-2 mb-2">
|
||||
@@ -32,10 +32,8 @@
|
||||
<strong class="small">Training in progress</strong>
|
||||
</div>
|
||||
<div class="progress mb-1" style="height: 10px;">
|
||||
<div class="progress-bar progress-bar-striped progress-bar-animated"
|
||||
id="training-progress-bar"
|
||||
role="progressbar"
|
||||
style="width: 0%"></div>
|
||||
<div class="progress-bar progress-bar-striped progress-bar-animated" id="training-progress-bar"
|
||||
role="progressbar" style="width: 0%"></div>
|
||||
</div>
|
||||
<div class="small">
|
||||
<div>Epoch: <span id="training-epoch">0</span>/<span id="training-total-epochs">0</span></div>
|
||||
@@ -43,7 +41,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Training Results -->
|
||||
<div id="training-results" style="display: none;">
|
||||
<div class="alert alert-success py-2 px-2 mb-2">
|
||||
@@ -58,7 +56,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Real-Time Inference -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label small">Real-Time Inference</label>
|
||||
@@ -71,7 +69,7 @@
|
||||
Stop Inference
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Inference Status -->
|
||||
<div id="inference-status" style="display: none;">
|
||||
<div class="alert alert-success py-2 px-2 mb-2">
|
||||
@@ -88,7 +86,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Test Case Stats -->
|
||||
<div class="small text-muted">
|
||||
<div class="d-flex justify-content-between">
|
||||
@@ -111,7 +109,7 @@
|
||||
.then(data => {
|
||||
const modelSelect = document.getElementById('model-select');
|
||||
modelSelect.innerHTML = '';
|
||||
|
||||
|
||||
if (data.success && data.models.length > 0) {
|
||||
data.models.forEach(model => {
|
||||
const option = document.createElement('option');
|
||||
@@ -132,202 +130,214 @@
|
||||
modelSelect.innerHTML = '<option value="">Error loading models</option>';
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Load models when page loads
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', loadAvailableModels);
|
||||
} else {
|
||||
loadAvailableModels();
|
||||
}
|
||||
|
||||
|
||||
// Train model button
|
||||
document.getElementById('train-model-btn').addEventListener('click', function() {
|
||||
document.getElementById('train-model-btn').addEventListener('click', function () {
|
||||
const modelName = document.getElementById('model-select').value;
|
||||
|
||||
|
||||
if (appState.annotations.length === 0) {
|
||||
showError('No annotations available for training');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Get annotation IDs
|
||||
const annotationIds = appState.annotations.map(a => a.annotation_id);
|
||||
|
||||
|
||||
// Start training
|
||||
startTraining(modelName, annotationIds);
|
||||
});
|
||||
|
||||
|
||||
function startTraining(modelName, annotationIds) {
|
||||
// Show training status
|
||||
document.getElementById('training-status').style.display = 'block';
|
||||
document.getElementById('training-results').style.display = 'none';
|
||||
document.getElementById('train-model-btn').disabled = true;
|
||||
|
||||
|
||||
// Reset progress
|
||||
document.getElementById('training-progress-bar').style.width = '0%';
|
||||
document.getElementById('training-epoch').textContent = '0';
|
||||
document.getElementById('training-loss').textContent = '--';
|
||||
|
||||
|
||||
// Start training request
|
||||
fetch('/api/train-model', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
model_name: modelName,
|
||||
annotation_ids: annotationIds
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
// Start polling for training progress
|
||||
pollTrainingProgress(data.training_id);
|
||||
} else {
|
||||
showError('Failed to start training: ' + data.error.message);
|
||||
document.getElementById('training-status').style.display = 'none';
|
||||
document.getElementById('train-model-btn').disabled = false;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showError('Network error: ' + error.message);
|
||||
document.getElementById('training-status').style.display = 'none';
|
||||
document.getElementById('train-model-btn').disabled = false;
|
||||
});
|
||||
}
|
||||
|
||||
function pollTrainingProgress(trainingId) {
|
||||
const pollInterval = setInterval(function() {
|
||||
fetch('/api/training-progress', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({training_id: trainingId})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
const progress = data.progress;
|
||||
|
||||
// Update progress bar
|
||||
const percentage = (progress.current_epoch / progress.total_epochs) * 100;
|
||||
document.getElementById('training-progress-bar').style.width = percentage + '%';
|
||||
document.getElementById('training-epoch').textContent = progress.current_epoch;
|
||||
document.getElementById('training-total-epochs').textContent = progress.total_epochs;
|
||||
document.getElementById('training-loss').textContent = progress.current_loss.toFixed(4);
|
||||
|
||||
// Check if complete
|
||||
if (progress.status === 'completed') {
|
||||
clearInterval(pollInterval);
|
||||
showTrainingResults(progress);
|
||||
} else if (progress.status === 'failed') {
|
||||
clearInterval(pollInterval);
|
||||
showError('Training failed: ' + progress.error);
|
||||
document.getElementById('training-status').style.display = 'none';
|
||||
document.getElementById('train-model-btn').disabled = false;
|
||||
}
|
||||
// Start polling for training progress
|
||||
pollTrainingProgress(data.training_id);
|
||||
} else {
|
||||
showError('Failed to start training: ' + data.error.message);
|
||||
document.getElementById('training-status').style.display = 'none';
|
||||
document.getElementById('train-model-btn').disabled = false;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
clearInterval(pollInterval);
|
||||
showError('Failed to get training progress: ' + error.message);
|
||||
showError('Network error: ' + error.message);
|
||||
document.getElementById('training-status').style.display = 'none';
|
||||
document.getElementById('train-model-btn').disabled = false;
|
||||
});
|
||||
}
|
||||
|
||||
function pollTrainingProgress(trainingId) {
|
||||
const pollInterval = setInterval(function () {
|
||||
fetch('/api/training-progress', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ training_id: trainingId })
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
const progress = data.progress;
|
||||
|
||||
// Update progress bar
|
||||
const percentage = (progress.current_epoch / progress.total_epochs) * 100;
|
||||
document.getElementById('training-progress-bar').style.width = percentage + '%';
|
||||
document.getElementById('training-epoch').textContent = progress.current_epoch;
|
||||
document.getElementById('training-total-epochs').textContent = progress.total_epochs;
|
||||
document.getElementById('training-loss').textContent = progress.current_loss.toFixed(4);
|
||||
|
||||
// Check if complete
|
||||
if (progress.status === 'completed') {
|
||||
clearInterval(pollInterval);
|
||||
showTrainingResults(progress);
|
||||
} else if (progress.status === 'failed') {
|
||||
clearInterval(pollInterval);
|
||||
showError('Training failed: ' + progress.error);
|
||||
document.getElementById('training-status').style.display = 'none';
|
||||
document.getElementById('train-model-btn').disabled = false;
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
clearInterval(pollInterval);
|
||||
showError('Failed to get training progress: ' + error.message);
|
||||
document.getElementById('training-status').style.display = 'none';
|
||||
document.getElementById('train-model-btn').disabled = false;
|
||||
});
|
||||
}, 1000); // Poll every second
|
||||
}
|
||||
|
||||
|
||||
function showTrainingResults(results) {
|
||||
// Hide training status
|
||||
document.getElementById('training-status').style.display = 'none';
|
||||
|
||||
|
||||
// Show results
|
||||
document.getElementById('training-results').style.display = 'block';
|
||||
document.getElementById('result-loss').textContent = results.final_loss.toFixed(4);
|
||||
document.getElementById('result-accuracy').textContent = (results.accuracy * 100).toFixed(2) + '%';
|
||||
document.getElementById('result-duration').textContent = results.duration_seconds.toFixed(1) + 's';
|
||||
|
||||
|
||||
// Update last training time
|
||||
document.getElementById('last-training-time').textContent = new Date().toLocaleTimeString();
|
||||
|
||||
|
||||
// Re-enable train button
|
||||
document.getElementById('train-model-btn').disabled = false;
|
||||
|
||||
|
||||
showSuccess('Training completed successfully');
|
||||
}
|
||||
|
||||
|
||||
// Real-time inference controls
|
||||
let currentInferenceId = null;
|
||||
let signalPollInterval = null;
|
||||
|
||||
document.getElementById('start-inference-btn').addEventListener('click', function() {
|
||||
|
||||
document.getElementById('start-inference-btn').addEventListener('click', function () {
|
||||
const modelName = document.getElementById('model-select').value;
|
||||
|
||||
|
||||
if (!modelName) {
|
||||
showError('Please select a model first');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Start real-time inference
|
||||
fetch('/api/realtime-inference/start', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
model_name: modelName,
|
||||
symbol: appState.currentSymbol
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
currentInferenceId = data.inference_id;
|
||||
|
||||
// Update UI
|
||||
document.getElementById('start-inference-btn').style.display = 'none';
|
||||
document.getElementById('stop-inference-btn').style.display = 'block';
|
||||
document.getElementById('inference-status').style.display = 'block';
|
||||
|
||||
// Start polling for signals
|
||||
startSignalPolling();
|
||||
|
||||
showSuccess('Real-time inference started');
|
||||
} else {
|
||||
showError('Failed to start inference: ' + data.error.message);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showError('Network error: ' + error.message);
|
||||
});
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
currentInferenceId = data.inference_id;
|
||||
|
||||
// Update UI
|
||||
document.getElementById('start-inference-btn').style.display = 'none';
|
||||
document.getElementById('stop-inference-btn').style.display = 'block';
|
||||
document.getElementById('inference-status').style.display = 'block';
|
||||
|
||||
// Show live mode banner
|
||||
const banner = document.getElementById('live-mode-banner');
|
||||
if (banner) {
|
||||
banner.style.display = 'block';
|
||||
}
|
||||
|
||||
// Start polling for signals
|
||||
startSignalPolling();
|
||||
|
||||
showSuccess('Real-time inference started - Charts now updating live');
|
||||
} else {
|
||||
showError('Failed to start inference: ' + data.error.message);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showError('Network error: ' + error.message);
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById('stop-inference-btn').addEventListener('click', function() {
|
||||
|
||||
document.getElementById('stop-inference-btn').addEventListener('click', function () {
|
||||
if (!currentInferenceId) return;
|
||||
|
||||
|
||||
// Stop real-time inference
|
||||
fetch('/api/realtime-inference/stop', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({inference_id: currentInferenceId})
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ inference_id: currentInferenceId })
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
// Update UI
|
||||
document.getElementById('start-inference-btn').style.display = 'block';
|
||||
document.getElementById('stop-inference-btn').style.display = 'none';
|
||||
document.getElementById('inference-status').style.display = 'none';
|
||||
|
||||
// Stop polling
|
||||
stopSignalPolling();
|
||||
|
||||
currentInferenceId = null;
|
||||
showSuccess('Real-time inference stopped');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showError('Network error: ' + error.message);
|
||||
});
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
// Update UI
|
||||
document.getElementById('start-inference-btn').style.display = 'block';
|
||||
document.getElementById('stop-inference-btn').style.display = 'none';
|
||||
document.getElementById('inference-status').style.display = 'none';
|
||||
|
||||
// Hide live mode banner
|
||||
const banner = document.getElementById('live-mode-banner');
|
||||
if (banner) {
|
||||
banner.style.display = 'none';
|
||||
}
|
||||
|
||||
// Stop polling
|
||||
stopSignalPolling();
|
||||
|
||||
currentInferenceId = null;
|
||||
showSuccess('Real-time inference stopped');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showError('Network error: ' + error.message);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function startSignalPolling() {
|
||||
signalPollInterval = setInterval(function() {
|
||||
signalPollInterval = setInterval(function () {
|
||||
// Poll for signals
|
||||
fetch('/api/realtime-inference/signals')
|
||||
.then(response => response.json())
|
||||
@@ -335,9 +345,9 @@
|
||||
if (data.success && data.signals.length > 0) {
|
||||
const latest = data.signals[0];
|
||||
document.getElementById('latest-signal').textContent = latest.action;
|
||||
document.getElementById('latest-confidence').textContent =
|
||||
document.getElementById('latest-confidence').textContent =
|
||||
(latest.confidence * 100).toFixed(1) + '%';
|
||||
|
||||
|
||||
// Update chart with signal markers
|
||||
if (appState.chartManager) {
|
||||
displaySignalOnChart(latest);
|
||||
@@ -347,17 +357,17 @@
|
||||
.catch(error => {
|
||||
console.error('Error polling signals:', error);
|
||||
});
|
||||
|
||||
|
||||
// Update charts with latest data
|
||||
updateChartsWithLiveData();
|
||||
}, 1000); // Poll every second
|
||||
}
|
||||
|
||||
|
||||
function updateChartsWithLiveData() {
|
||||
// Fetch latest chart data
|
||||
fetch('/api/chart-data', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
symbol: appState.currentSymbol,
|
||||
timeframes: appState.currentTimeframes,
|
||||
@@ -365,72 +375,85 @@
|
||||
end_time: null
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success && appState.chartManager) {
|
||||
// Update each chart with new data
|
||||
Object.keys(data.chart_data).forEach(timeframe => {
|
||||
const chartData = data.chart_data[timeframe];
|
||||
if (appState.chartManager.charts[timeframe]) {
|
||||
updateSingleChart(timeframe, chartData);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error updating charts:', error);
|
||||
});
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success && appState.chartManager) {
|
||||
// Update each chart with new data
|
||||
Object.keys(data.chart_data).forEach(timeframe => {
|
||||
const chartData = data.chart_data[timeframe];
|
||||
if (appState.chartManager.charts[timeframe]) {
|
||||
updateSingleChart(timeframe, chartData);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error updating charts:', error);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
let liveUpdateCount = 0;
|
||||
|
||||
function updateSingleChart(timeframe, newData) {
|
||||
const chart = appState.chartManager.charts[timeframe];
|
||||
if (!chart) return;
|
||||
|
||||
// Update candlestick data
|
||||
Plotly.update(chart.plotId, {
|
||||
x: [newData.timestamps],
|
||||
open: [newData.open],
|
||||
high: [newData.high],
|
||||
low: [newData.low],
|
||||
close: [newData.close]
|
||||
}, {}, [0]);
|
||||
|
||||
// Update volume data
|
||||
const volumeColors = newData.close.map((close, i) => {
|
||||
if (i === 0) return '#3b82f6';
|
||||
return close >= newData.open[i] ? '#10b981' : '#ef4444';
|
||||
});
|
||||
|
||||
Plotly.update(chart.plotId, {
|
||||
x: [newData.timestamps],
|
||||
y: [newData.volume],
|
||||
'marker.color': [volumeColors]
|
||||
}, {}, [1]);
|
||||
|
||||
try {
|
||||
// Update candlestick data
|
||||
Plotly.update(chart.plotId, {
|
||||
x: [newData.timestamps],
|
||||
open: [newData.open],
|
||||
high: [newData.high],
|
||||
low: [newData.low],
|
||||
close: [newData.close]
|
||||
}, {}, [0]);
|
||||
|
||||
// Update volume data
|
||||
const volumeColors = newData.close.map((close, i) => {
|
||||
if (i === 0) return '#3b82f6';
|
||||
return close >= newData.open[i] ? '#10b981' : '#ef4444';
|
||||
});
|
||||
|
||||
Plotly.update(chart.plotId, {
|
||||
x: [newData.timestamps],
|
||||
y: [newData.volume],
|
||||
'marker.color': [volumeColors]
|
||||
}, {}, [1]);
|
||||
|
||||
// Update counter
|
||||
liveUpdateCount++;
|
||||
const counterEl = document.getElementById('live-update-count');
|
||||
if (counterEl) {
|
||||
counterEl.textContent = liveUpdateCount + ' updates';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error updating chart:', timeframe, error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function stopSignalPolling() {
|
||||
if (signalPollInterval) {
|
||||
clearInterval(signalPollInterval);
|
||||
signalPollInterval = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function displaySignalOnChart(signal) {
|
||||
// Add signal marker to chart
|
||||
if (!appState.chartManager || !appState.chartManager.charts) return;
|
||||
|
||||
|
||||
// Add marker to all timeframe charts
|
||||
Object.keys(appState.chartManager.charts).forEach(timeframe => {
|
||||
const chart = appState.chartManager.charts[timeframe];
|
||||
if (!chart) return;
|
||||
|
||||
|
||||
// Get current annotations
|
||||
const currentAnnotations = chart.element.layout.annotations || [];
|
||||
|
||||
|
||||
// Determine marker based on signal
|
||||
let markerText = '';
|
||||
let markerColor = '#9ca3af';
|
||||
|
||||
|
||||
if (signal.action === 'BUY') {
|
||||
markerText = '🔵 BUY';
|
||||
markerColor = '#10b981';
|
||||
@@ -440,7 +463,7 @@
|
||||
} else {
|
||||
return; // Don't show HOLD signals
|
||||
}
|
||||
|
||||
|
||||
// Add new signal marker
|
||||
const newAnnotation = {
|
||||
x: signal.timestamp,
|
||||
@@ -460,25 +483,25 @@
|
||||
borderpad: 4,
|
||||
opacity: 0.8
|
||||
};
|
||||
|
||||
|
||||
// Keep only last 10 signal markers
|
||||
const signalAnnotations = currentAnnotations.filter(ann =>
|
||||
const signalAnnotations = currentAnnotations.filter(ann =>
|
||||
ann.text && (ann.text.includes('BUY') || ann.text.includes('SELL'))
|
||||
).slice(-9);
|
||||
|
||||
|
||||
// Combine with existing non-signal annotations
|
||||
const otherAnnotations = currentAnnotations.filter(ann =>
|
||||
const otherAnnotations = currentAnnotations.filter(ann =>
|
||||
!ann.text || (!ann.text.includes('BUY') && !ann.text.includes('SELL'))
|
||||
);
|
||||
|
||||
|
||||
const allAnnotations = [...otherAnnotations, ...signalAnnotations, newAnnotation];
|
||||
|
||||
|
||||
// Update chart
|
||||
Plotly.relayout(chart.plotId, {
|
||||
annotations: allAnnotations
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
console.log('Signal displayed:', signal.action, '@', signal.price);
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
Reference in New Issue
Block a user