UI and stability
This commit is contained in:
@ -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"""
|
||||
|
@ -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([
|
||||
|
Reference in New Issue
Block a user