This commit is contained in:
Dobromir Popov
2025-11-22 15:25:57 +02:00
parent bccac9614d
commit 2f28fcc89a
7 changed files with 174 additions and 35 deletions

View File

@@ -116,6 +116,8 @@ class TrainingSession:
error: Optional[str] = None error: Optional[str] = None
gpu_utilization: Optional[float] = None # GPU utilization percentage gpu_utilization: Optional[float] = None # GPU utilization percentage
cpu_utilization: Optional[float] = None # CPU utilization percentage cpu_utilization: Optional[float] = None # CPU utilization percentage
annotation_count: Optional[int] = None # Number of annotations used
timeframe: Optional[str] = None # Primary timeframe (e.g., '1m', '5m')
class RealTrainingAdapter: class RealTrainingAdapter:
@@ -208,13 +210,17 @@ class RealTrainingAdapter:
logger.info(f"Available models for training: {available}") logger.info(f"Available models for training: {available}")
return available return available
def start_training(self, model_name: str, test_cases: List[Dict]) -> str: def start_training(self, model_name: str, test_cases: List[Dict],
annotation_count: Optional[int] = None,
timeframe: Optional[str] = None) -> str:
""" """
Start REAL training session with test cases Start REAL training session with test cases
Args: Args:
model_name: Name of model to train (CNN, DQN, Transformer, COB, Extrema) model_name: Name of model to train (CNN, DQN, Transformer, COB, Extrema)
test_cases: List of test cases from annotations test_cases: List of test cases from annotations
annotation_count: Number of annotations used (optional)
timeframe: Primary timeframe for training (optional, e.g., '1m', '5m')
Returns: Returns:
training_id: Unique ID for this training session training_id: Unique ID for this training session
@@ -224,6 +230,10 @@ class RealTrainingAdapter:
training_id = str(uuid.uuid4()) training_id = str(uuid.uuid4())
# Use annotation_count if provided, otherwise use test_cases count
if annotation_count is None:
annotation_count = len(test_cases)
# Create training session # Create training session
session = TrainingSession( session = TrainingSession(
training_id=training_id, training_id=training_id,
@@ -233,7 +243,9 @@ class RealTrainingAdapter:
current_epoch=0, current_epoch=0,
total_epochs=10, # Reasonable for annotation-based training total_epochs=10, # Reasonable for annotation-based training
current_loss=0.0, current_loss=0.0,
start_time=time.time() start_time=time.time(),
annotation_count=annotation_count,
timeframe=timeframe
) )
self.training_sessions[training_id] = session self.training_sessions[training_id] = session
@@ -2358,7 +2370,9 @@ class RealTrainingAdapter:
'current_epoch': session.current_epoch, 'current_epoch': session.current_epoch,
'total_epochs': session.total_epochs, 'total_epochs': session.total_epochs,
'current_loss': session.current_loss, 'current_loss': session.current_loss,
'start_time': session.start_time 'start_time': session.start_time,
'annotation_count': session.annotation_count,
'timeframe': session.timeframe
} }
return None return None

View File

@@ -46,29 +46,6 @@
"exit_state": {} "exit_state": {}
} }
}, },
{
"annotation_id": "91847a37-6315-4546-b5a0-573118311322",
"symbol": "ETH/USDT",
"timeframe": "1s",
"entry": {
"timestamp": "2025-10-25 13:08:04",
"price": 3940.24,
"index": 25
},
"exit": {
"timestamp": "2025-10-25 13:15:12",
"price": 3942.59,
"index": 57
},
"direction": "LONG",
"profit_loss_pct": 0.05964103709419639,
"notes": "",
"created_at": "2025-10-25T16:17:02.931920",
"market_context": {
"entry_state": {},
"exit_state": {}
}
},
{ {
"annotation_id": "479eb310-c963-4837-b712-70e5a42afb53", "annotation_id": "479eb310-c963-4837-b712-70e5a42afb53",
"symbol": "ETH/USDT", "symbol": "ETH/USDT",
@@ -137,10 +114,33 @@
"entry_state": {}, "entry_state": {},
"exit_state": {} "exit_state": {}
} }
},
{
"annotation_id": "46cc0e20-0bfb-498c-9358-71b52a003d0f",
"symbol": "ETH/USDT",
"timeframe": "1s",
"entry": {
"timestamp": "2025-11-22 12:50",
"price": 2712.11,
"index": 26
},
"exit": {
"timestamp": "2025-11-22 12:53:06",
"price": 2721.44,
"index": 45
},
"direction": "LONG",
"profit_loss_pct": 0.3440125953593301,
"notes": "",
"created_at": "2025-11-22T15:19:00.480166",
"market_context": {
"entry_state": {},
"exit_state": {}
}
} }
], ],
"metadata": { "metadata": {
"total_annotations": 6, "total_annotations": 6,
"last_updated": "2025-11-12T13:11:31.267456" "last_updated": "2025-11-22T15:19:15.521679"
} }
} }

View File

@@ -626,8 +626,7 @@ class AnnotationDashboard:
if not self.orchestrator: if not self.orchestrator:
logger.info("Initializing TradingOrchestrator...") logger.info("Initializing TradingOrchestrator...")
self.orchestrator = TradingOrchestrator( self.orchestrator = TradingOrchestrator(
data_provider=self.data_provider, data_provider=self.data_provider
config=self.config
) )
self.training_adapter.orchestrator = self.orchestrator self.training_adapter.orchestrator = self.orchestrator
logger.info("TradingOrchestrator initialized") logger.info("TradingOrchestrator initialized")
@@ -1709,6 +1708,9 @@ class AnnotationDashboard:
# CRITICAL: Get current symbol to filter annotations # CRITICAL: Get current symbol to filter annotations
current_symbol = data.get('symbol', 'ETH/USDT') current_symbol = data.get('symbol', 'ETH/USDT')
# Get primary timeframe for display (optional)
timeframe = data.get('timeframe', '1m')
# If no specific annotations provided, use all for current symbol # If no specific annotations provided, use all for current symbol
if not annotation_ids: if not annotation_ids:
annotations = self.annotation_manager.get_annotations(symbol=current_symbol) annotations = self.annotation_manager.get_annotations(symbol=current_symbol)
@@ -1737,12 +1739,14 @@ class AnnotationDashboard:
} }
}) })
logger.info(f"Starting REAL training with {len(test_cases)} test cases for model {model_name}") logger.info(f"Starting REAL training with {len(test_cases)} test cases ({len(annotation_ids)} annotations) for model {model_name} on {timeframe}")
# Start REAL training (NO SIMULATION!) # Start REAL training (NO SIMULATION!)
training_id = self.training_adapter.start_training( training_id = self.training_adapter.start_training(
model_name=model_name, model_name=model_name,
test_cases=test_cases test_cases=test_cases,
annotation_count=len(annotation_ids),
timeframe=timeframe
) )
return jsonify({ return jsonify({

View File

@@ -10,6 +10,7 @@
/* Chart Panel */ /* Chart Panel */
.chart-panel { .chart-panel {
height: calc(100vh - 150px); height: calc(100vh - 150px);
transition: all 0.3s ease;
} }
.chart-panel .card-body { .chart-panel .card-body {
@@ -17,6 +18,29 @@
overflow: hidden; overflow: hidden;
} }
/* Maximized Chart View */
.chart-maximized {
width: 100% !important;
max-width: 100% !important;
flex: 0 0 100% !important;
transition: all 0.3s ease;
}
.chart-panel-maximized {
height: calc(100vh - 80px) !important;
position: fixed;
top: 60px;
left: 0;
right: 0;
z-index: 1040;
margin: 0 !important;
border-radius: 0 !important;
}
.chart-panel-maximized .card-body {
height: calc(100% - 60px);
}
#chart-container { #chart-container {
height: 100%; height: 100%;
overflow-y: auto; overflow-y: auto;
@@ -236,11 +260,32 @@
padding: 1rem; padding: 1rem;
} }
/* Maximized View - Larger Charts */
.chart-panel-maximized .chart-plot {
height: 400px;
}
@media (min-width: 1400px) {
.chart-panel-maximized .chart-plot {
height: 450px;
}
}
@media (min-width: 1920px) {
.chart-panel-maximized .chart-plot {
height: 500px;
}
}
/* Responsive Adjustments */ /* Responsive Adjustments */
@media (max-width: 1200px) { @media (max-width: 1200px) {
.chart-plot { .chart-plot {
height: 250px; height: 250px;
} }
.chart-panel-maximized .chart-plot {
height: 350px;
}
} }
@media (max-width: 768px) { @media (max-width: 768px) {

View File

@@ -101,6 +101,23 @@
if (typeof checkActiveTraining === 'function') { if (typeof checkActiveTraining === 'function') {
checkActiveTraining(); checkActiveTraining();
} }
// Keyboard shortcuts for chart maximization
document.addEventListener('keydown', function(e) {
// ESC key to exit maximized mode
if (e.key === 'Escape') {
const chartArea = document.querySelector('.chart-maximized');
if (chartArea) {
document.getElementById('maximize-btn').click();
}
}
// F key to toggle maximize (when not typing in input)
if (e.key === 'f' && !e.ctrlKey && !e.metaKey &&
!['INPUT', 'TEXTAREA', 'SELECT'].includes(document.activeElement.tagName)) {
document.getElementById('maximize-btn').click();
}
});
// Setup keyboard shortcuts // Setup keyboard shortcuts
setupKeyboardShortcuts(); setupKeyboardShortcuts();

View File

@@ -14,6 +14,9 @@
<button type="button" class="btn btn-outline-light" id="reset-zoom-btn" title="Reset Zoom"> <button type="button" class="btn btn-outline-light" id="reset-zoom-btn" title="Reset Zoom">
<i class="fas fa-expand"></i> <i class="fas fa-expand"></i>
</button> </button>
<button type="button" class="btn btn-outline-light" id="maximize-btn" title="Maximize Chart Area">
<i class="fas fa-arrows-alt"></i>
</button>
<button type="button" class="btn btn-outline-light" id="fullscreen-btn" title="Fullscreen"> <button type="button" class="btn btn-outline-light" id="fullscreen-btn" title="Fullscreen">
<i class="fas fa-expand-arrows-alt"></i> <i class="fas fa-expand-arrows-alt"></i>
</button> </button>
@@ -110,6 +113,41 @@
} }
}); });
document.getElementById('maximize-btn').addEventListener('click', function () {
const mainRow = document.querySelector('.row.mt-3');
const leftSidebar = mainRow.querySelector('.col-md-2:first-child');
const chartArea = mainRow.querySelector('.col-md-8');
const rightSidebar = mainRow.querySelector('.col-md-2:last-child');
const chartPanel = document.querySelector('.chart-panel');
const maximizeIcon = this.querySelector('i');
// Toggle maximize state
if (chartArea.classList.contains('chart-maximized')) {
// Restore normal view
leftSidebar.style.display = '';
rightSidebar.style.display = '';
chartArea.classList.remove('chart-maximized');
chartPanel.classList.remove('chart-panel-maximized');
maximizeIcon.className = 'fas fa-arrows-alt';
this.title = 'Maximize Chart Area';
} else {
// Maximize chart area
leftSidebar.style.display = 'none';
rightSidebar.style.display = 'none';
chartArea.classList.add('chart-maximized');
chartPanel.classList.add('chart-panel-maximized');
maximizeIcon.className = 'fas fa-compress-arrows-alt';
this.title = 'Restore Normal View';
}
// Update chart layouts after transition
setTimeout(() => {
if (window.appState && window.appState.chartManager) {
window.appState.chartManager.updateChartLayout();
}
}, 350);
});
document.getElementById('fullscreen-btn').addEventListener('click', function () { document.getElementById('fullscreen-btn').addEventListener('click', function () {
const chartContainer = document.getElementById('chart-container'); const chartContainer = document.getElementById('chart-container');
if (chartContainer.requestFullscreen) { if (chartContainer.requestFullscreen) {

View File

@@ -40,9 +40,13 @@
role="progressbar" style="width: 0%"></div> role="progressbar" style="width: 0%"></div>
</div> </div>
<div class="small"> <div class="small">
<div>Epoch: <span id="training-epoch">0</span>/<span id="training-total-epochs">0</span></div> <div>Annotations: <span id="training-annotation-count" class="fw-bold text-primary">--</span></div>
<div>Loss: <span id="training-loss">--</span></div> <div>Timeframe: <span id="training-timeframe" class="fw-bold text-info">--</span></div>
<div>GPU: <span id="training-gpu-util">--</span>% | CPU: <span id="training-cpu-util">--</span>%</div> <div class="mt-1 pt-1 border-top">
<div>Epoch: <span id="training-epoch">0</span>/<span id="training-total-epochs">0</span></div>
<div>Loss: <span id="training-loss">--</span></div>
<div>GPU: <span id="training-gpu-util">--</span>% | CPU: <span id="training-cpu-util">--</span>%</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -193,6 +197,15 @@
// Resume tracking // Resume tracking
activeTrainingId = data.session.training_id; activeTrainingId = data.session.training_id;
showTrainingStatus(); showTrainingStatus();
// Populate annotation count and timeframe if available
if (data.session.annotation_count) {
document.getElementById('training-annotation-count').textContent = data.session.annotation_count;
}
if (data.session.timeframe) {
document.getElementById('training-timeframe').textContent = data.session.timeframe.toUpperCase();
}
pollTrainingProgress(activeTrainingId); pollTrainingProgress(activeTrainingId);
} else { } else {
console.log('No active training session'); console.log('No active training session');
@@ -408,10 +421,17 @@
// Show training status // Show training status
showTrainingStatus(); showTrainingStatus();
// Get primary timeframe for training
const primaryTimeframe = document.getElementById('primary-timeframe-select').value;
// Reset progress // Reset progress
document.getElementById('training-progress-bar').style.width = '0%'; document.getElementById('training-progress-bar').style.width = '0%';
document.getElementById('training-epoch').textContent = '0'; document.getElementById('training-epoch').textContent = '0';
document.getElementById('training-loss').textContent = '--'; document.getElementById('training-loss').textContent = '--';
// Set annotation count and timeframe
document.getElementById('training-annotation-count').textContent = annotationIds.length;
document.getElementById('training-timeframe').textContent = primaryTimeframe.toUpperCase();
// Start training request // Start training request
fetch('/api/train-model', { fetch('/api/train-model', {
@@ -420,7 +440,8 @@
body: JSON.stringify({ body: JSON.stringify({
model_name: modelName, model_name: modelName,
annotation_ids: annotationIds, annotation_ids: annotationIds,
symbol: appState.currentSymbol // CRITICAL: Filter by current symbol symbol: appState.currentSymbol, // CRITICAL: Filter by current symbol
timeframe: primaryTimeframe // Primary timeframe for display
}) })
}) })
.then(response => response.json()) .then(response => response.json())