ui fixes
This commit is contained in:
@ -9,15 +9,21 @@
|
|||||||
"training_enabled": true
|
"training_enabled": true
|
||||||
},
|
},
|
||||||
"cob_rl": {
|
"cob_rl": {
|
||||||
"inference_enabled": true,
|
"inference_enabled": false,
|
||||||
"training_enabled": true
|
"training_enabled": true
|
||||||
},
|
},
|
||||||
"decision_fusion": {
|
"decision_fusion": {
|
||||||
"inference_enabled": false,
|
"inference_enabled": false,
|
||||||
"training_enabled": false
|
"training_enabled": true
|
||||||
|
},
|
||||||
|
"transformer": {
|
||||||
|
"inference_enabled": false,
|
||||||
|
"training_enabled": true
|
||||||
|
},
|
||||||
|
"dqn_agent": {
|
||||||
|
"inference_enabled": false,
|
||||||
|
"training_enabled": true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
"timestamp": "2025-07-29T15:55:43.690404"
|
"timestamp": "2025-07-29T18:37:29.759605"
|
||||||
}
|
}
|
@ -327,7 +327,8 @@ class CleanTradingDashboard:
|
|||||||
self.app = Dash(__name__, external_stylesheets=[
|
self.app = Dash(__name__, external_stylesheets=[
|
||||||
'https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css',
|
'https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css',
|
||||||
'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css'
|
'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css'
|
||||||
], suppress_callback_exceptions=True)
|
])
|
||||||
|
#, suppress_callback_exceptions=True)
|
||||||
|
|
||||||
# Suppress Dash development mode logging
|
# Suppress Dash development mode logging
|
||||||
self.app.enable_dev_tools(debug=False, dev_tools_silence_routes_logging=True)
|
self.app.enable_dev_tools(debug=False, dev_tools_silence_routes_logging=True)
|
||||||
@ -1363,43 +1364,72 @@ class CleanTradingDashboard:
|
|||||||
error_msg = html.P(f"COB Error: {str(e)}", className="text-danger small")
|
error_msg = html.P(f"COB Error: {str(e)}", className="text-danger small")
|
||||||
return error_msg, error_msg
|
return error_msg, error_msg
|
||||||
|
|
||||||
|
# Original training metrics callback - temporarily disabled for testing
|
||||||
|
# @self.app.callback(
|
||||||
|
# Output('training-metrics', 'children'),
|
||||||
|
# [Input('slow-interval-component', 'n_intervals'),
|
||||||
|
# Input('fast-interval-component', 'n_intervals'), # Add fast interval for testing
|
||||||
|
# Input('refresh-training-metrics-btn', 'n_clicks')] # Add manual refresh button
|
||||||
|
# )
|
||||||
|
# def update_training_metrics(slow_intervals, fast_intervals, n_clicks):
|
||||||
|
# """Update training metrics"""
|
||||||
|
# logger.info(f"update_training_metrics callback triggered with slow_intervals={slow_intervals}, fast_intervals={fast_intervals}, n_clicks={n_clicks}")
|
||||||
|
# try:
|
||||||
|
# # Get toggle states from orchestrator
|
||||||
|
# toggle_states = {}
|
||||||
|
# if self.orchestrator:
|
||||||
|
# # Get all available models dynamically
|
||||||
|
# available_models = self._get_available_models()
|
||||||
|
# logger.info(f"Available models: {list(available_models.keys())}")
|
||||||
|
# for model_name in available_models.keys():
|
||||||
|
# toggle_states[model_name] = self.orchestrator.get_model_toggle_state(model_name)
|
||||||
|
# else:
|
||||||
|
# # Fallback to dashboard dynamic state
|
||||||
|
# toggle_states = {}
|
||||||
|
# for model_name, state in self.model_toggle_states.items():
|
||||||
|
# toggle_states[model_name] = state
|
||||||
|
# # Now using slow-interval-component (10s) - no batching needed
|
||||||
|
#
|
||||||
|
# logger.info(f"Getting training metrics with toggle_states: {toggle_states}")
|
||||||
|
# metrics_data = self._get_training_metrics(toggle_states)
|
||||||
|
# logger.info(f"update_training_metrics callback: got metrics_data type={type(metrics_data)}")
|
||||||
|
# if metrics_data and isinstance(metrics_data, dict):
|
||||||
|
# logger.info(f"Metrics data keys: {list(metrics_data.keys())}")
|
||||||
|
# if 'loaded_models' in metrics_data:
|
||||||
|
# logger.info(f"Loaded models count: {len(metrics_data['loaded_models'])}")
|
||||||
|
# logger.info(f"Loaded model names: {list(metrics_data['loaded_models'].keys())}")
|
||||||
|
# else:
|
||||||
|
# logger.warning("No 'loaded_models' key in metrics_data!")
|
||||||
|
# else:
|
||||||
|
# logger.warning(f"Invalid metrics_data: {metrics_data}")
|
||||||
|
#
|
||||||
|
# logger.info("Formatting training metrics...")
|
||||||
|
# formatted_metrics = self.component_manager.format_training_metrics(metrics_data)
|
||||||
|
# logger.info(f"Formatted metrics type: {type(formatted_metrics)}, length: {len(formatted_metrics) if isinstance(formatted_metrics, list) else 'N/A'}")
|
||||||
|
# return formatted_metrics
|
||||||
|
# except PreventUpdate:
|
||||||
|
# logger.info("PreventUpdate raised in training metrics callback")
|
||||||
|
# raise
|
||||||
|
# except Exception as e:
|
||||||
|
# logger.error(f"Error updating training metrics: {e}")
|
||||||
|
# import traceback
|
||||||
|
# logger.error(f"Traceback: {traceback.format_exc()}")
|
||||||
|
# return [html.P(f"Error: {str(e)}", className="text-danger")]
|
||||||
|
|
||||||
|
# Test callback for training metrics
|
||||||
@self.app.callback(
|
@self.app.callback(
|
||||||
Output('training-metrics', 'children'),
|
Output('training-metrics', 'children'),
|
||||||
[Input('slow-interval-component', 'n_intervals')] # OPTIMIZED: Move to 10s interval
|
[Input('refresh-training-metrics-btn', 'n_clicks')],
|
||||||
|
prevent_initial_call=False
|
||||||
)
|
)
|
||||||
def update_training_metrics(n):
|
def test_training_metrics_callback(n_clicks):
|
||||||
"""Update training metrics"""
|
"""Test callback for training metrics"""
|
||||||
|
logger.info(f"test_training_metrics_callback triggered with n_clicks={n_clicks}")
|
||||||
try:
|
try:
|
||||||
# Get toggle states from orchestrator
|
# Return a simple test message
|
||||||
toggle_states = {}
|
return [html.P("Training metrics test - callback is working!", className="text-success")]
|
||||||
if self.orchestrator:
|
|
||||||
# Get all available models dynamically
|
|
||||||
available_models = self._get_available_models()
|
|
||||||
for model_name in available_models.keys():
|
|
||||||
toggle_states[model_name] = self.orchestrator.get_model_toggle_state(model_name)
|
|
||||||
else:
|
|
||||||
# Fallback to dashboard dynamic state
|
|
||||||
toggle_states = {}
|
|
||||||
for model_name, state in self.model_toggle_states.items():
|
|
||||||
toggle_states[model_name] = state
|
|
||||||
# Now using slow-interval-component (10s) - no batching needed
|
|
||||||
|
|
||||||
metrics_data = self._get_training_metrics(toggle_states)
|
|
||||||
logger.debug(f"update_training_metrics callback: got metrics_data type={type(metrics_data)}")
|
|
||||||
if metrics_data and isinstance(metrics_data, dict):
|
|
||||||
logger.debug(f"Metrics data keys: {list(metrics_data.keys())}")
|
|
||||||
if 'loaded_models' in metrics_data:
|
|
||||||
logger.debug(f"Loaded models count: {len(metrics_data['loaded_models'])}")
|
|
||||||
logger.debug(f"Loaded model names: {list(metrics_data['loaded_models'].keys())}")
|
|
||||||
else:
|
|
||||||
logger.warning("No 'loaded_models' key in metrics_data!")
|
|
||||||
else:
|
|
||||||
logger.warning(f"Invalid metrics_data: {metrics_data}")
|
|
||||||
return self.component_manager.format_training_metrics(metrics_data)
|
|
||||||
except PreventUpdate:
|
|
||||||
raise
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error updating training metrics: {e}")
|
logger.error(f"Error in test callback: {e}")
|
||||||
return [html.P(f"Error: {str(e)}", className="text-danger")]
|
return [html.P(f"Error: {str(e)}", className="text-danger")]
|
||||||
|
|
||||||
# Manual trading buttons
|
# Manual trading buttons
|
||||||
|
@ -140,7 +140,8 @@ class DashboardComponentManager:
|
|||||||
# Create table headers
|
# Create table headers
|
||||||
headers = html.Thead([
|
headers = html.Thead([
|
||||||
html.Tr([
|
html.Tr([
|
||||||
html.Th("Time", className="small"),
|
html.Th("Entry Time", className="small"),
|
||||||
|
html.Th("Exit Time", className="small"),
|
||||||
html.Th("Side", className="small"),
|
html.Th("Side", className="small"),
|
||||||
html.Th("Size", className="small"),
|
html.Th("Size", className="small"),
|
||||||
html.Th("Entry", className="small"),
|
html.Th("Entry", className="small"),
|
||||||
@ -158,6 +159,7 @@ class DashboardComponentManager:
|
|||||||
if hasattr(trade, 'entry_time'):
|
if hasattr(trade, 'entry_time'):
|
||||||
# This is a trade object
|
# This is a trade object
|
||||||
entry_time = getattr(trade, 'entry_time', 'Unknown')
|
entry_time = getattr(trade, 'entry_time', 'Unknown')
|
||||||
|
exit_time = getattr(trade, 'exit_time', 'Unknown')
|
||||||
side = getattr(trade, 'side', 'UNKNOWN')
|
side = getattr(trade, 'side', 'UNKNOWN')
|
||||||
size = getattr(trade, 'size', 0)
|
size = getattr(trade, 'size', 0)
|
||||||
entry_price = getattr(trade, 'entry_price', 0)
|
entry_price = getattr(trade, 'entry_price', 0)
|
||||||
@ -168,6 +170,7 @@ class DashboardComponentManager:
|
|||||||
else:
|
else:
|
||||||
# This is a dictionary format
|
# This is a dictionary format
|
||||||
entry_time = trade.get('entry_time', 'Unknown')
|
entry_time = trade.get('entry_time', 'Unknown')
|
||||||
|
exit_time = trade.get('exit_time', 'Unknown')
|
||||||
side = trade.get('side', 'UNKNOWN')
|
side = trade.get('side', 'UNKNOWN')
|
||||||
size = trade.get('quantity', trade.get('size', 0)) # Try 'quantity' first, then 'size'
|
size = trade.get('quantity', trade.get('size', 0)) # Try 'quantity' first, then 'size'
|
||||||
entry_price = trade.get('entry_price', 0)
|
entry_price = trade.get('entry_price', 0)
|
||||||
@ -176,11 +179,17 @@ class DashboardComponentManager:
|
|||||||
fees = trade.get('fees', 0)
|
fees = trade.get('fees', 0)
|
||||||
hold_time_seconds = trade.get('hold_time_seconds', 0.0)
|
hold_time_seconds = trade.get('hold_time_seconds', 0.0)
|
||||||
|
|
||||||
# Format time
|
# Format entry time
|
||||||
if isinstance(entry_time, datetime):
|
if isinstance(entry_time, datetime):
|
||||||
time_str = entry_time.strftime('%H:%M:%S')
|
entry_time_str = entry_time.strftime('%H:%M:%S')
|
||||||
else:
|
else:
|
||||||
time_str = str(entry_time)
|
entry_time_str = str(entry_time)
|
||||||
|
|
||||||
|
# Format exit time
|
||||||
|
if isinstance(exit_time, datetime):
|
||||||
|
exit_time_str = exit_time.strftime('%H:%M:%S')
|
||||||
|
else:
|
||||||
|
exit_time_str = str(exit_time)
|
||||||
|
|
||||||
# Determine P&L color
|
# Determine P&L color
|
||||||
pnl_class = "text-success" if pnl >= 0 else "text-danger"
|
pnl_class = "text-success" if pnl >= 0 else "text-danger"
|
||||||
@ -197,7 +206,8 @@ class DashboardComponentManager:
|
|||||||
net_pnl = pnl - fees
|
net_pnl = pnl - fees
|
||||||
|
|
||||||
row = html.Tr([
|
row = html.Tr([
|
||||||
html.Td(time_str, className="small"),
|
html.Td(entry_time_str, className="small"),
|
||||||
|
html.Td(exit_time_str, className="small"),
|
||||||
html.Td(side, className=f"small {side_class}"),
|
html.Td(side, className=f"small {side_class}"),
|
||||||
html.Td(f"${position_size_usd:.2f}", className="small"), # Show size in USD
|
html.Td(f"${position_size_usd:.2f}", className="small"), # Show size in USD
|
||||||
html.Td(f"${entry_price:.2f}", className="small"),
|
html.Td(f"${entry_price:.2f}", className="small"),
|
||||||
@ -714,11 +724,11 @@ class DashboardComponentManager:
|
|||||||
"""Format training metrics for display - Enhanced with loaded models"""
|
"""Format training metrics for display - Enhanced with loaded models"""
|
||||||
try:
|
try:
|
||||||
# DEBUG: Log what we're receiving
|
# DEBUG: Log what we're receiving
|
||||||
logger.debug(f"format_training_metrics received: {type(metrics_data)}")
|
logger.info(f"format_training_metrics received: {type(metrics_data)}")
|
||||||
if metrics_data:
|
if metrics_data:
|
||||||
logger.debug(f"Metrics keys: {list(metrics_data.keys()) if isinstance(metrics_data, dict) else 'Not a dict'}")
|
logger.info(f"Metrics keys: {list(metrics_data.keys()) if isinstance(metrics_data, dict) else 'Not a dict'}")
|
||||||
if isinstance(metrics_data, dict) and 'loaded_models' in metrics_data:
|
if isinstance(metrics_data, dict) and 'loaded_models' in metrics_data:
|
||||||
logger.debug(f"Loaded models: {list(metrics_data['loaded_models'].keys())}")
|
logger.info(f"Loaded models: {list(metrics_data['loaded_models'].keys())}")
|
||||||
|
|
||||||
if not metrics_data or 'error' in metrics_data:
|
if not metrics_data or 'error' in metrics_data:
|
||||||
logger.warning(f"No training data or error in metrics_data: {metrics_data}")
|
logger.warning(f"No training data or error in metrics_data: {metrics_data}")
|
||||||
@ -772,6 +782,7 @@ class DashboardComponentManager:
|
|||||||
checkpoint_status = "LOADED" if model_info.get('checkpoint_loaded', False) else "FRESH"
|
checkpoint_status = "LOADED" if model_info.get('checkpoint_loaded', False) else "FRESH"
|
||||||
|
|
||||||
# Model card
|
# Model card
|
||||||
|
logger.info(f"Creating model card for {model_name} with toggles: inference={model_info.get('inference_enabled', True)}, training={model_info.get('training_enabled', True)}")
|
||||||
model_card = html.Div([
|
model_card = html.Div([
|
||||||
# Header with model name and toggle
|
# Header with model name and toggle
|
||||||
html.Div([
|
html.Div([
|
||||||
@ -1043,10 +1054,15 @@ class DashboardComponentManager:
|
|||||||
html.Span(f"{enhanced_stats['recent_validation_score']:.3f}", className="text-primary small fw-bold")
|
html.Span(f"{enhanced_stats['recent_validation_score']:.3f}", className="text-primary small fw-bold")
|
||||||
], className="mb-1"))
|
], className="mb-1"))
|
||||||
|
|
||||||
|
logger.info(f"format_training_metrics returning {len(content)} components")
|
||||||
|
for i, component in enumerate(content[:3]): # Log first 3 components
|
||||||
|
logger.info(f" Component {i}: {type(component)}")
|
||||||
return content
|
return content
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error formatting training metrics: {e}")
|
logger.error(f"Error formatting training metrics: {e}")
|
||||||
|
import traceback
|
||||||
|
logger.error(f"Traceback: {traceback.format_exc()}")
|
||||||
return [html.P(f"Error: {str(e)}", className="text-danger small")]
|
return [html.P(f"Error: {str(e)}", className="text-danger small")]
|
||||||
|
|
||||||
def _format_cnn_pivot_prediction(self, model_info):
|
def _format_cnn_pivot_prediction(self, model_info):
|
||||||
|
@ -17,11 +17,32 @@ class DashboardLayoutManager:
|
|||||||
|
|
||||||
def create_main_layout(self):
|
def create_main_layout(self):
|
||||||
"""Create the main dashboard layout"""
|
"""Create the main dashboard layout"""
|
||||||
return html.Div([
|
try:
|
||||||
self._create_header(),
|
print("Creating main layout...")
|
||||||
self._create_interval_component(),
|
header = self._create_header()
|
||||||
self._create_main_content()
|
print("Header created")
|
||||||
], className="container-fluid")
|
interval_component = self._create_interval_component()
|
||||||
|
print("Interval component created")
|
||||||
|
main_content = self._create_main_content()
|
||||||
|
print("Main content created")
|
||||||
|
|
||||||
|
layout = html.Div([
|
||||||
|
header,
|
||||||
|
interval_component,
|
||||||
|
main_content
|
||||||
|
], className="container-fluid")
|
||||||
|
|
||||||
|
print("Main layout created successfully")
|
||||||
|
return layout
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error creating main layout: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
# Return a simple error layout
|
||||||
|
return html.Div([
|
||||||
|
html.H1("Dashboard Error", className="text-danger"),
|
||||||
|
html.P(f"Error creating layout: {str(e)}", className="text-danger")
|
||||||
|
])
|
||||||
|
|
||||||
def _create_header(self):
|
def _create_header(self):
|
||||||
"""Create the dashboard header"""
|
"""Create the dashboard header"""
|
||||||
@ -52,7 +73,15 @@ class DashboardLayoutManager:
|
|||||||
dcc.Interval(
|
dcc.Interval(
|
||||||
id='slow-interval-component',
|
id='slow-interval-component',
|
||||||
interval=10000, # Update every 10 seconds (0.1 Hz) - OPTIMIZED
|
interval=10000, # Update every 10 seconds (0.1 Hz) - OPTIMIZED
|
||||||
n_intervals=0
|
n_intervals=0,
|
||||||
|
disabled=False
|
||||||
|
),
|
||||||
|
# Fast interval for testing (5 seconds)
|
||||||
|
dcc.Interval(
|
||||||
|
id='fast-interval-component',
|
||||||
|
interval=5000, # Update every 5 seconds for testing
|
||||||
|
n_intervals=0,
|
||||||
|
disabled=False
|
||||||
),
|
),
|
||||||
# WebSocket-based updates for high-frequency data (no interval needed)
|
# WebSocket-based updates for high-frequency data (no interval needed)
|
||||||
html.Div(id='websocket-updates-container', style={'display': 'none'})
|
html.Div(id='websocket-updates-container', style={'display': 'none'})
|
||||||
@ -357,10 +386,16 @@ class DashboardLayoutManager:
|
|||||||
html.Div([
|
html.Div([
|
||||||
html.Div([
|
html.Div([
|
||||||
html.Div([
|
html.Div([
|
||||||
html.H6([
|
html.Div([
|
||||||
html.I(className="fas fa-brain me-2"),
|
html.H6([
|
||||||
"Models & Training Progress",
|
html.I(className="fas fa-brain me-2"),
|
||||||
], className="card-title mb-2"),
|
"Models & Training Progress",
|
||||||
|
], className="card-title mb-2"),
|
||||||
|
html.Button([
|
||||||
|
html.I(className="fas fa-sync-alt me-1"),
|
||||||
|
"Refresh"
|
||||||
|
], id="refresh-training-metrics-btn", className="btn btn-sm btn-outline-primary")
|
||||||
|
], className="d-flex justify-content-between align-items-center mb-2"),
|
||||||
html.Div(
|
html.Div(
|
||||||
id="training-metrics",
|
id="training-metrics",
|
||||||
style={"height": "300px", "overflowY": "auto"},
|
style={"height": "300px", "overflowY": "auto"},
|
||||||
|
Reference in New Issue
Block a user