UI and stability

This commit is contained in:
Dobromir Popov
2025-07-28 14:05:37 +03:00
parent 25b2d3840a
commit 44821b2a89
7 changed files with 934 additions and 135 deletions

View File

@ -62,7 +62,12 @@ logging.getLogger('dash').setLevel(logging.WARNING)
logging.getLogger('dash.dash').setLevel(logging.WARNING)
# Import core components
from core.config import get_config
try:
from core.config import get_config
except ImportError:
# Fallback if config module is not available
def get_config():
return {}
from core.data_provider import DataProvider
from core.standardized_data_provider import StandardizedDataProvider
from core.orchestrator import TradingOrchestrator
@ -117,12 +122,17 @@ class CleanTradingDashboard:
"""Clean, modular trading dashboard implementation"""
def __init__(self, data_provider=None, orchestrator: Optional[Any] = None, trading_executor: Optional[TradingExecutor] = None):
self.config = get_config()
# Load configuration safely
try:
self.config = get_config()
except Exception as e:
logger.warning(f"Error loading config, using empty config: {e}")
self.config = {}
# Removed batch counter - now using proper interval separation for performance
# Initialize components
self.data_provider = data_provider or DataProvider()
self.data_provider = data_provider or StandardizedDataProvider()
self.trading_executor = trading_executor or TradingExecutor()
# Initialize unified orchestrator with full ML capabilities
@ -174,10 +184,35 @@ class CleanTradingDashboard:
self.standardized_cnn = None
self._initialize_standardized_cnn()
# Initialize trading mode and cold start settings from config
self.trading_mode_live = False # Default to simulation mode
self.cold_start_enabled = True # Default to cold start enabled
# Load config values if available
try:
if hasattr(self, 'config') and self.config:
# Check if trading mode is live based on config
exchanges = self.config.get('exchanges', {})
if exchanges:
for exchange_name, exchange_config in exchanges.items():
if exchange_config.get('enabled', False):
trading_mode = exchange_config.get('trading_mode', 'simulation')
if trading_mode == 'live':
self.trading_mode_live = True
break
# Check cold start setting
cold_start_config = self.config.get('cold_start', {})
self.cold_start_enabled = cold_start_config.get('enabled', True)
except Exception as e:
logger.warning(f"Error loading config settings, using defaults: {e}")
# Keep default values
# Initialize layout and component managers
self.layout_manager = DashboardLayoutManager(
starting_balance=self._get_initial_balance(),
trading_executor=self.trading_executor
trading_executor=self.trading_executor,
dashboard=self
)
self.component_manager = DashboardComponentManager()
@ -206,6 +241,19 @@ class CleanTradingDashboard:
# ENHANCED: Model control toggles - separate inference and training
self.dqn_inference_enabled = True # Default: enabled
self.dqn_training_enabled = True # Default: enabled
# Trading mode and cold start settings from config
from core.config import get_config
config = get_config()
# Initialize trading mode from config (default to simulation)
default_trading_mode = config.get('exchanges', {}).get('bybit', {}).get('trading_mode', 'simulation')
self.trading_mode_live = (default_trading_mode == 'live')
# Initialize cold start from config (default to enabled)
self.cold_start_enabled = config.get('cold_start', {}).get('enabled', True)
logger.info(f"Dashboard initialized - Trading Mode: {'LIVE' if self.trading_mode_live else 'SIM'}, Cold Start: {'ON' if self.cold_start_enabled else 'OFF'}")
self.cnn_inference_enabled = True
self.cnn_training_enabled = True
@ -611,13 +659,51 @@ class CleanTradingDashboard:
logger.error(f"Error getting model status: {e}")
return {'loaded_models': {}, 'total_models': 0, 'system_status': 'ERROR'}
def _convert_utc_to_local(self, utc_timestamp):
"""Convert UTC timestamp to local timezone for display"""
try:
if utc_timestamp is None:
return datetime.now()
# Handle different input types
if isinstance(utc_timestamp, str):
try:
utc_timestamp = pd.to_datetime(utc_timestamp)
except:
return datetime.now()
# If it's already a datetime object
if isinstance(utc_timestamp, datetime):
# If it has timezone info and is UTC, convert to local
if utc_timestamp.tzinfo is not None:
if str(utc_timestamp.tzinfo) == 'UTC':
# Convert UTC to local timezone
local_timestamp = utc_timestamp.replace(tzinfo=timezone.utc).astimezone()
return local_timestamp.replace(tzinfo=None) # Remove timezone info for display
else:
# Already has timezone, convert to local
return utc_timestamp.astimezone().replace(tzinfo=None)
else:
# No timezone info, assume it's already local
return utc_timestamp
# Fallback
return datetime.now()
except Exception as e:
logger.debug(f"Error converting UTC to local time: {e}")
return datetime.now()
def _safe_strftime(self, timestamp_val, format_str='%H:%M:%S'):
"""Safely format timestamp, handling both string and datetime objects"""
try:
if isinstance(timestamp_val, str):
return timestamp_val
elif hasattr(timestamp_val, 'strftime'):
return timestamp_val.strftime(format_str)
# Convert to local time first
local_timestamp = self._convert_utc_to_local(timestamp_val)
if isinstance(local_timestamp, str):
return local_timestamp
elif hasattr(local_timestamp, 'strftime'):
return local_timestamp.strftime(format_str)
else:
return datetime.now().strftime(format_str)
except Exception as e:
@ -1158,6 +1244,93 @@ class CleanTradingDashboard:
else:
return [html.I(className="fas fa-exclamation-triangle me-1"), "Store Failed"]
return [html.I(className="fas fa-save me-1"), "Store All Models"]
# Trading Mode Toggle
@self.app.callback(
Output('trading-mode-display', 'children'),
Output('trading-mode-display', 'className'),
[Input('trading-mode-switch', 'value')]
)
def update_trading_mode(switch_value):
"""Update trading mode display and apply changes"""
try:
is_live = 'live' in (switch_value or [])
self.trading_mode_live = is_live
# Update trading executor mode if available
if hasattr(self, 'trading_executor') and self.trading_executor:
if hasattr(self.trading_executor, 'set_trading_mode'):
# Use the new set_trading_mode method
success = self.trading_executor.set_trading_mode('live' if is_live else 'simulation')
if success:
logger.info(f"TRADING MODE: {'LIVE' if is_live else 'SIMULATION'} - Mode updated successfully")
else:
logger.error(f"Failed to update trading mode to {'LIVE' if is_live else 'SIMULATION'}")
else:
# Fallback to direct property setting
if is_live:
self.trading_executor.trading_mode = 'live'
self.trading_executor.simulation_mode = False
logger.info("TRADING MODE: LIVE - Real orders will be executed!")
else:
self.trading_executor.trading_mode = 'simulation'
self.trading_executor.simulation_mode = True
logger.info("TRADING MODE: SIMULATION - Orders are simulated")
# Return display text and styling
if is_live:
return "LIVE", "fw-bold text-danger"
else:
return "SIM", "fw-bold text-warning"
except Exception as e:
logger.error(f"Error updating trading mode: {e}")
return "ERROR", "fw-bold text-danger"
# Cold Start Toggle
@self.app.callback(
Output('cold-start-display', 'children'),
Output('cold-start-display', 'className'),
[Input('cold-start-switch', 'value')]
)
def update_cold_start(switch_value):
"""Update cold start training mode"""
try:
is_enabled = 'enabled' in (switch_value or [])
self.cold_start_enabled = is_enabled
# Update orchestrator cold start mode if available
if hasattr(self, 'orchestrator') and self.orchestrator:
if hasattr(self.orchestrator, 'set_cold_start_training_enabled'):
# Use the new set_cold_start_training_enabled method
success = self.orchestrator.set_cold_start_training_enabled(is_enabled)
if success:
logger.info(f"COLD START: {'ON' if is_enabled else 'OFF'} - Training mode updated successfully")
else:
logger.error(f"Failed to update cold start training to {'ON' if is_enabled else 'OFF'}")
else:
# Fallback to direct property setting
if hasattr(self.orchestrator, 'cold_start_enabled'):
self.orchestrator.cold_start_enabled = is_enabled
# Update training frequency based on cold start mode
if hasattr(self.orchestrator, 'training_frequency'):
if is_enabled:
self.orchestrator.training_frequency = 'high' # Train on every signal
logger.info("COLD START: ON - Excessive training enabled")
else:
self.orchestrator.training_frequency = 'normal' # Normal training
logger.info("COLD START: OFF - Normal training frequency")
# Return display text and styling
if is_enabled:
return "ON", "fw-bold text-success"
else:
return "OFF", "fw-bold text-secondary"
except Exception as e:
logger.error(f"Error updating cold start mode: {e}")
return "ERROR", "fw-bold text-danger"
def _get_current_price(self, symbol: str) -> Optional[float]:
"""Get current price for symbol - ONLY using our data providers"""

View File

@ -10,9 +10,10 @@ from datetime import datetime
class DashboardLayoutManager:
"""Manages dashboard layout and structure"""
def __init__(self, starting_balance: float = 100.0, trading_executor=None):
def __init__(self, starting_balance: float = 100.0, trading_executor=None, dashboard=None):
self.starting_balance = starting_balance
self.trading_executor = trading_executor
self.dashboard = dashboard
def create_main_layout(self):
"""Create the main dashboard layout"""
@ -153,6 +154,48 @@ class DashboardLayoutManager:
"Session Controls"
], className="card-title mb-2"),
# Trading Agent Mode Toggle
html.Div([
html.Label([
html.I(className="fas fa-robot me-1"),
"Trading Agent: ",
html.Span(
id="trading-mode-display",
children="LIVE" if getattr(self.dashboard, 'trading_mode_live', False) else "SIM",
className="fw-bold text-danger" if getattr(self.dashboard, 'trading_mode_live', False) else "fw-bold text-warning"
)
], className="form-label small mb-1"),
dcc.Checklist(
id='trading-mode-switch',
options=[{'label': '', 'value': 'live'}],
value=['live'] if getattr(self.dashboard, 'trading_mode_live', False) else [],
className="form-check-input"
),
html.Small("SIM = Simulation Mode, LIVE = Real Trading", className="text-muted d-block")
], className="mb-2"),
# Cold Start Training Toggle
html.Div([
html.Label([
html.I(className="fas fa-fire me-1"),
"Cold Start Training: ",
html.Span(
id="cold-start-display",
children="ON" if getattr(self.dashboard, 'cold_start_enabled', True) else "OFF",
className="fw-bold text-success" if getattr(self.dashboard, 'cold_start_enabled', True) else "fw-bold text-secondary"
)
], className="form-label small mb-1"),
dcc.Checklist(
id='cold-start-switch',
options=[{'label': '', 'value': 'enabled'}],
value=['enabled'] if getattr(self.dashboard, 'cold_start_enabled', True) else [],
className="form-check-input"
),
html.Small("Excessive training during cold start", className="text-muted d-block")
], className="mb-2"),
html.Hr(className="my-2"),
# Leverage Control
html.Div([
html.Label([