BIG CLEANUP

This commit is contained in:
Dobromir Popov
2025-08-08 14:58:55 +03:00
parent e39e9ee95a
commit 2b0d2679c6
162 changed files with 455 additions and 42814 deletions

View File

@ -1 +0,0 @@
# Web module for trading system dashboard

View File

@ -1,253 +0,0 @@
"""
Dashboard Fix
This module provides fixes for the trading dashboard to address:
1. Trade display issues
2. P&L calculation and display
3. Position tracking and synchronization
Apply these fixes by importing and applying the patch in the dashboard initialization
"""
import logging
from datetime import datetime
from typing import Dict, Any, List, Optional
import time
logger = logging.getLogger(__name__)
class DashboardFix:
"""Fixes for the Dashboard class"""
@staticmethod
def apply_fixes(dashboard):
"""Apply all fixes to the dashboard"""
logger.info("Applying Dashboard fixes...")
# Apply fixes
DashboardFix._fix_trade_display(dashboard)
DashboardFix._fix_position_sync(dashboard)
DashboardFix._fix_pnl_calculation(dashboard)
DashboardFix._add_trade_validation(dashboard)
logger.info("Dashboard fixes applied successfully")
return dashboard
@staticmethod
def _fix_trade_display(dashboard):
"""Fix trade display to ensure accurate information"""
# Store original format_closed_trades_table method
if hasattr(dashboard.component_manager, 'format_closed_trades_table'):
original_format_closed_trades = dashboard.component_manager.format_closed_trades_table
def format_closed_trades_table_fixed(self, closed_trades, trading_stats=None):
"""Fixed closed trades table formatter with accurate P&L calculation"""
# Recalculate P&L for each trade to ensure accuracy
for trade in closed_trades:
# Skip if already validated
if getattr(trade, 'pnl_validated', False):
continue
# Handle both trade objects and dictionary formats
if hasattr(trade, 'entry_price'):
# This is a trade object
entry_price = getattr(trade, 'entry_price', 0)
exit_price = getattr(trade, 'exit_price', 0)
size = getattr(trade, 'size', 0)
side = getattr(trade, 'side', 'UNKNOWN')
fees = getattr(trade, 'fees', 0)
else:
# This is a dictionary format
entry_price = trade.get('entry_price', 0)
exit_price = trade.get('exit_price', 0)
size = trade.get('size', trade.get('quantity', 0))
side = trade.get('side', 'UNKNOWN')
fees = trade.get('fees', 0)
# Recalculate P&L
if side == 'LONG' or side == 'BUY':
pnl = (exit_price - entry_price) * size
else: # SHORT or SELL
pnl = (entry_price - exit_price) * size
# Update P&L value
if hasattr(trade, 'entry_price'):
trade.pnl = pnl
trade.net_pnl = pnl - fees
trade.pnl_validated = True
else:
trade['pnl'] = pnl
trade['net_pnl'] = pnl - fees
trade['pnl_validated'] = True
# Call original method with validated trades
return original_format_closed_trades(closed_trades, trading_stats)
# Apply the patch
dashboard.component_manager.format_closed_trades_table = format_closed_trades_table_fixed.__get__(dashboard.component_manager)
logger.info("Trade display fix applied")
@staticmethod
def _fix_position_sync(dashboard):
"""Fix position synchronization to ensure accurate position tracking"""
# Store original _sync_position_from_executor method
if hasattr(dashboard, '_sync_position_from_executor'):
original_sync_position = dashboard._sync_position_from_executor
def sync_position_from_executor_fixed(self, symbol):
"""Fixed position sync with validation and logging"""
try:
# Call original sync method
result = original_sync_position(symbol)
# Add validation and logging
if self.trading_executor and hasattr(self.trading_executor, 'positions'):
if symbol in self.trading_executor.positions:
position = self.trading_executor.positions[symbol]
# Log position details for debugging
logger.debug(f"Position sync for {symbol}: "
f"Side={position.side}, "
f"Size={position.size}, "
f"Entry=${position.entry_price:.2f}")
# Validate position data
if position.entry_price <= 0:
logger.warning(f"Invalid entry price for {symbol}: ${position.entry_price:.2f}")
# Store last sync time
if not hasattr(self, 'last_position_sync'):
self.last_position_sync = {}
self.last_position_sync[symbol] = time.time()
return result
except Exception as e:
logger.error(f"Error in sync_position_from_executor_fixed: {e}")
return None
# Apply the patch
dashboard._sync_position_from_executor = sync_position_from_executor_fixed.__get__(dashboard)
logger.info("Position sync fix applied")
@staticmethod
def _fix_pnl_calculation(dashboard):
"""Fix P&L calculation to ensure accuracy"""
# Add a method to recalculate P&L for all closed trades
def recalculate_all_pnl(self):
"""Recalculate P&L for all closed trades"""
if not hasattr(self, 'closed_trades') or not self.closed_trades:
return
for trade in self.closed_trades:
# Handle both trade objects and dictionary formats
if hasattr(trade, 'entry_price'):
# This is a trade object
entry_price = getattr(trade, 'entry_price', 0)
exit_price = getattr(trade, 'exit_price', 0)
size = getattr(trade, 'size', 0)
side = getattr(trade, 'side', 'UNKNOWN')
fees = getattr(trade, 'fees', 0)
else:
# This is a dictionary format
entry_price = trade.get('entry_price', 0)
exit_price = trade.get('exit_price', 0)
size = trade.get('size', trade.get('quantity', 0))
side = trade.get('side', 'UNKNOWN')
fees = trade.get('fees', 0)
# Recalculate P&L
if side == 'LONG' or side == 'BUY':
pnl = (exit_price - entry_price) * size
else: # SHORT or SELL
pnl = (entry_price - exit_price) * size
# Update P&L value
if hasattr(trade, 'entry_price'):
trade.pnl = pnl
trade.net_pnl = pnl - fees
else:
trade['pnl'] = pnl
trade['net_pnl'] = pnl - fees
logger.info(f"Recalculated P&L for {len(self.closed_trades)} closed trades")
# Add the method
dashboard.recalculate_all_pnl = recalculate_all_pnl.__get__(dashboard)
# Call it once to fix existing trades
dashboard.recalculate_all_pnl()
logger.info("P&L calculation fix applied")
@staticmethod
def _add_trade_validation(dashboard):
"""Add trade validation to prevent invalid trades"""
# Store original _on_trade_closed method if it exists
original_on_trade_closed = getattr(dashboard, '_on_trade_closed', None)
if original_on_trade_closed:
def on_trade_closed_fixed(self, trade_data):
"""Fixed trade closed handler with validation"""
try:
# Validate trade data
is_valid = True
validation_errors = []
# Check for required fields
required_fields = ['symbol', 'side', 'entry_price', 'exit_price', 'size']
for field in required_fields:
if field not in trade_data:
is_valid = False
validation_errors.append(f"Missing required field: {field}")
# Check for valid prices
if 'entry_price' in trade_data and trade_data['entry_price'] <= 0:
is_valid = False
validation_errors.append(f"Invalid entry price: {trade_data['entry_price']}")
if 'exit_price' in trade_data and trade_data['exit_price'] <= 0:
is_valid = False
validation_errors.append(f"Invalid exit price: {trade_data['exit_price']}")
# Check for valid size
if 'size' in trade_data and trade_data['size'] <= 0:
is_valid = False
validation_errors.append(f"Invalid size: {trade_data['size']}")
# If invalid, log errors and skip
if not is_valid:
logger.warning(f"Invalid trade data: {validation_errors}")
return
# Calculate correct P&L
if 'side' in trade_data and 'entry_price' in trade_data and 'exit_price' in trade_data and 'size' in trade_data:
side = trade_data['side']
entry_price = trade_data['entry_price']
exit_price = trade_data['exit_price']
size = trade_data['size']
if side == 'LONG' or side == 'BUY':
pnl = (exit_price - entry_price) * size
else: # SHORT or SELL
pnl = (entry_price - exit_price) * size
# Update P&L in trade data
trade_data['pnl'] = pnl
# Calculate net P&L (after fees)
fees = trade_data.get('fees', 0)
trade_data['net_pnl'] = pnl - fees
# Call original method with validated data
return original_on_trade_closed(trade_data)
except Exception as e:
logger.error(f"Error in on_trade_closed_fixed: {e}")
# Apply the patch
dashboard._on_trade_closed = on_trade_closed_fixed.__get__(dashboard)
logger.info("Trade validation fix applied")
else:
logger.warning("_on_trade_closed method not found, skipping trade validation fix")

View File

@ -1,331 +0,0 @@
"""
Dashboard Data Model
Provides structured data for template rendering
"""
from dataclasses import dataclass, field
from typing import List, Dict, Any, Optional
from datetime import datetime
@dataclass
class MetricData:
"""Individual metric for the dashboard"""
id: str
label: str
value: str
format_type: str = "text" # text, currency, percentage
@dataclass
class TradingControlsData:
"""Trading controls configuration"""
buy_text: str = "BUY"
sell_text: str = "SELL"
clear_text: str = "Clear Session"
leverage: int = 10
leverage_min: int = 1
leverage_max: int = 50
@dataclass
class RecentDecisionData:
"""Recent AI decision data"""
timestamp: str
action: str
symbol: str
confidence: float
price: float
@dataclass
class COBLevelData:
"""Order book level data"""
side: str # 'bid' or 'ask'
size: str
price: str
total: str
@dataclass
class COBData:
"""Complete order book data for a symbol"""
symbol: str
content_id: str
total_usd: str
total_crypto: str
levels: List[COBLevelData] = field(default_factory=list)
@dataclass
class ModelData:
"""Model status data"""
name: str
status: str # 'training', 'idle', 'loading'
status_text: str
@dataclass
class TrainingMetricData:
"""Training metric data"""
name: str
value: str
@dataclass
class PerformanceStatData:
"""Performance statistic data"""
name: str
value: str
@dataclass
class ClosedTradeData:
"""Closed trade data"""
time: str
symbol: str
side: str
size: str
entry_price: str
exit_price: str
pnl: float
duration: str
@dataclass
class ChartData:
"""Chart configuration data"""
title: str = "Price Chart & Signals"
@dataclass
class DashboardModel:
"""Complete dashboard data model"""
title: str = "Live Scalping Dashboard"
subtitle: str = "Real-time Trading with AI Models"
refresh_interval: int = 1000
# Main sections
metrics: List[MetricData] = field(default_factory=list)
chart: ChartData = field(default_factory=ChartData)
trading_controls: TradingControlsData = field(default_factory=TradingControlsData)
recent_decisions: List[RecentDecisionData] = field(default_factory=list)
cob_data: List[COBData] = field(default_factory=list)
models: List[ModelData] = field(default_factory=list)
training_metrics: List[TrainingMetricData] = field(default_factory=list)
performance_stats: List[PerformanceStatData] = field(default_factory=list)
closed_trades: List[ClosedTradeData] = field(default_factory=list)
class DashboardDataBuilder:
"""Builder class to construct dashboard data from various sources"""
def __init__(self):
self.model = DashboardModel()
def set_basic_info(self, title: str = None, subtitle: str = None, refresh_interval: int = None):
"""Set basic dashboard information"""
if title:
self.model.title = title
if subtitle:
self.model.subtitle = subtitle
if refresh_interval:
self.model.refresh_interval = refresh_interval
return self
def add_metric(self, id: str, label: str, value: Any, format_type: str = "text"):
"""Add a metric to the dashboard"""
formatted_value = self._format_value(value, format_type)
metric = MetricData(id=id, label=label, value=formatted_value, format_type=format_type)
self.model.metrics.append(metric)
return self
def set_trading_controls(self, leverage: int = None, leverage_range: tuple = None):
"""Configure trading controls"""
if leverage:
self.model.trading_controls.leverage = leverage
if leverage_range:
self.model.trading_controls.leverage_min = leverage_range[0]
self.model.trading_controls.leverage_max = leverage_range[1]
return self
def add_recent_decision(self, timestamp: datetime, action: str, symbol: str,
confidence: float, price: float):
"""Add a recent AI decision"""
decision = RecentDecisionData(
timestamp=timestamp.strftime("%H:%M:%S"),
action=action,
symbol=symbol,
confidence=round(confidence * 100, 1),
price=round(price, 4)
)
self.model.recent_decisions.append(decision)
return self
def add_cob_data(self, symbol: str, content_id: str, total_usd: float,
total_crypto: float, levels: List[Dict]):
"""Add COB data for a symbol"""
cob_levels = []
for level in levels:
cob_level = COBLevelData(
side=level.get('side', 'bid'),
size=self._format_value(level.get('size', 0), 'number'),
price=self._format_value(level.get('price', 0), 'currency'),
total=self._format_value(level.get('total', 0), 'currency')
)
cob_levels.append(cob_level)
cob = COBData(
symbol=symbol,
content_id=content_id,
total_usd=self._format_value(total_usd, 'currency'),
total_crypto=self._format_value(total_crypto, 'number'),
levels=cob_levels
)
self.model.cob_data.append(cob)
return self
def add_model_status(self, name: str, is_training: bool, is_loading: bool = False):
"""Add model status"""
if is_loading:
status = "loading"
status_text = "Loading"
elif is_training:
status = "training"
status_text = "Training"
else:
status = "idle"
status_text = "Idle"
model = ModelData(name=name, status=status, status_text=status_text)
self.model.models.append(model)
return self
def add_training_metric(self, name: str, value: Any):
"""Add training metric"""
metric = TrainingMetricData(
name=name,
value=self._format_value(value, 'number')
)
self.model.training_metrics.append(metric)
return self
def add_performance_stat(self, name: str, value: Any):
"""Add performance statistic"""
stat = PerformanceStatData(
name=name,
value=self._format_value(value, 'number')
)
self.model.performance_stats.append(stat)
return self
def add_closed_trade(self, time: datetime, symbol: str, side: str, size: float,
entry_price: float, exit_price: float, pnl: float, duration: str):
"""Add closed trade"""
trade = ClosedTradeData(
time=time.strftime("%H:%M:%S"),
symbol=symbol,
side=side,
size=self._format_value(size, 'number'),
entry_price=self._format_value(entry_price, 'currency'),
exit_price=self._format_value(exit_price, 'currency'),
pnl=round(pnl, 2),
duration=duration
)
self.model.closed_trades.append(trade)
return self
def build(self) -> DashboardModel:
"""Build and return the complete dashboard model"""
return self.model
def _format_value(self, value: Any, format_type: str) -> str:
"""Format value based on type"""
if value is None:
return "N/A"
try:
if format_type == "currency":
return f"${float(value):,.4f}"
elif format_type == "percentage":
return f"{float(value):.2f}%"
elif format_type == "number":
if isinstance(value, int):
return f"{value:,}"
else:
return f"{float(value):,.2f}"
else:
return str(value)
except (ValueError, TypeError):
return str(value)
def create_sample_dashboard_data() -> DashboardModel:
"""Create sample dashboard data for testing"""
builder = DashboardDataBuilder()
# Basic info
builder.set_basic_info(
title="Live Scalping Dashboard",
subtitle="Real-time Trading with AI Models",
refresh_interval=1000
)
# Metrics
builder.add_metric("current-price", "Current Price", 3425.67, "currency")
builder.add_metric("session-pnl", "Session PnL", 125.34, "currency")
builder.add_metric("current-position", "Position", 0.0, "number")
builder.add_metric("trade-count", "Trades", 15, "number")
builder.add_metric("portfolio-value", "Portfolio", 10250.45, "currency")
builder.add_metric("mexc-status", "MEXC Status", "Connected", "text")
# Trading controls
builder.set_trading_controls(leverage=10, leverage_range=(1, 50))
# Recent decisions
builder.add_recent_decision(datetime.now(), "BUY", "ETH/USDT", 0.85, 3425.67)
builder.add_recent_decision(datetime.now(), "HOLD", "BTC/USDT", 0.62, 45123.45)
# COB data
eth_levels = [
{"side": "ask", "size": 1.5, "price": 3426.12, "total": 5139.18},
{"side": "ask", "size": 2.3, "price": 3425.89, "total": 7879.55},
{"side": "bid", "size": 1.8, "price": 3425.45, "total": 6165.81},
{"side": "bid", "size": 3.2, "price": 3425.12, "total": 10960.38}
]
builder.add_cob_data("ETH/USDT", "eth-cob-content", 25000.0, 7.3, eth_levels)
btc_levels = [
{"side": "ask", "size": 0.15, "price": 45125.67, "total": 6768.85},
{"side": "ask", "size": 0.23, "price": 45123.45, "total": 10378.39},
{"side": "bid", "size": 0.18, "price": 45121.23, "total": 8121.82},
{"side": "bid", "size": 0.32, "price": 45119.12, "total": 14438.12}
]
builder.add_cob_data("BTC/USDT", "btc-cob-content", 35000.0, 0.88, btc_levels)
# Model statuses
builder.add_model_status("DQN", True)
builder.add_model_status("CNN", True)
builder.add_model_status("Transformer", False)
builder.add_model_status("COB-RL", True)
# Training metrics
builder.add_training_metric("DQN Loss", 0.0234)
builder.add_training_metric("CNN Accuracy", 0.876)
builder.add_training_metric("Training Steps", 15420)
builder.add_training_metric("Learning Rate", 0.0001)
# Performance stats
builder.add_performance_stat("Win Rate", 68.5)
builder.add_performance_stat("Avg Trade", 8.34)
builder.add_performance_stat("Max Drawdown", -45.67)
builder.add_performance_stat("Sharpe Ratio", 1.82)
# Closed trades
builder.add_closed_trade(
datetime.now(), "ETH/USDT", "BUY", 1.5, 3420.45, 3428.12, 11.51, "2m 34s"
)
builder.add_closed_trade(
datetime.now(), "BTC/USDT", "SELL", 0.1, 45150.23, 45142.67, -0.76, "1m 12s"
)
return builder.build()

View File

@ -1,753 +0,0 @@
#!/usr/bin/env python3
"""
Models & Training Progress Panel - Clean Implementation
Displays real-time model status, training metrics, and performance data
"""
import logging
from typing import Dict, List, Optional, Any
from datetime import datetime, timedelta
from dash import html, dcc
import dash_bootstrap_components as dbc
logger = logging.getLogger(__name__)
class ModelsTrainingPanel:
"""Clean implementation of the Models & Training Progress panel"""
def __init__(self, orchestrator=None):
self.orchestrator = orchestrator
self.last_update = None
def create_panel(self) -> html.Div:
"""Create the main Models & Training Progress panel"""
try:
# Get fresh data from orchestrator
panel_data = self._gather_panel_data()
# Build the panel components
content = []
# Header with refresh button
content.append(self._create_header())
# Models section
if panel_data.get('models'):
content.append(self._create_models_section(panel_data['models']))
else:
content.append(self._create_no_models_message())
# Training status section
if panel_data.get('training_status'):
content.append(self._create_training_status_section(panel_data['training_status']))
# Performance metrics section
if panel_data.get('performance_metrics'):
content.append(self._create_performance_section(panel_data['performance_metrics']))
return html.Div(content, id="training-metrics")
except Exception as e:
logger.error(f"Error creating models training panel: {e}")
return html.Div([
html.P(f"Error loading training panel: {str(e)}", className="text-danger small")
], id="training-metrics")
def _gather_panel_data(self) -> Dict[str, Any]:
"""Gather all data needed for the panel from orchestrator and other sources"""
data = {
'models': {},
'training_status': {},
'performance_metrics': {},
'last_update': datetime.now().strftime('%H:%M:%S')
}
if not self.orchestrator:
logger.warning("No orchestrator available for training panel")
return data
try:
# Get model registry information
if hasattr(self.orchestrator, 'model_registry') and self.orchestrator.model_registry:
registered_models = self.orchestrator.model_registry.get_all_models()
for model_name, model_info in registered_models.items():
data['models'][model_name] = self._extract_model_data(model_name, model_info)
# Add decision fusion model if it exists (check multiple sources)
decision_fusion_added = False
# Check if it's in the model registry
if hasattr(self.orchestrator, 'model_registry') and self.orchestrator.model_registry:
registered_models = self.orchestrator.model_registry.get_all_models()
if 'decision_fusion' in registered_models:
data['models']['decision_fusion'] = self._extract_decision_fusion_data()
decision_fusion_added = True
# If not in registry, check if decision fusion network exists
if not decision_fusion_added and hasattr(self.orchestrator, 'decision_fusion_network') and self.orchestrator.decision_fusion_network:
data['models']['decision_fusion'] = self._extract_decision_fusion_data()
decision_fusion_added = True
# If still not added, check if decision fusion is enabled
if not decision_fusion_added and hasattr(self.orchestrator, 'decision_fusion_enabled') and self.orchestrator.decision_fusion_enabled:
data['models']['decision_fusion'] = self._extract_decision_fusion_data()
decision_fusion_added = True
# Add COB RL model if it exists but wasn't captured in registry
if 'cob_rl_model' not in data['models'] and hasattr(self.orchestrator, 'cob_rl_model'):
data['models']['cob_rl_model'] = self._extract_cob_rl_data()
# Get training status
data['training_status'] = self._extract_training_status()
# Get performance metrics
data['performance_metrics'] = self._extract_performance_metrics()
except Exception as e:
logger.error(f"Error gathering panel data: {e}")
data['error'] = str(e)
return data
def _extract_model_data(self, model_name: str, model_info: Any) -> Dict[str, Any]:
"""Extract relevant data for a single model"""
try:
model_data = {
'name': model_name,
'status': 'unknown',
'parameters': 0,
'last_prediction': {},
'training_enabled': True,
'inference_enabled': True,
'checkpoint_loaded': False,
'loss_metrics': {},
'timing_metrics': {}
}
# Get model status from orchestrator - check if model is actually loaded and active
if hasattr(self.orchestrator, 'get_model_state'):
model_state = self.orchestrator.get_model_state(model_name)
model_data['status'] = 'active' if model_state else 'inactive'
# Check actual inference activity from logs/statistics
if hasattr(self.orchestrator, 'get_model_statistics'):
stats = self.orchestrator.get_model_statistics()
if stats and model_name in stats:
model_stats = stats[model_name]
# Check if model has recent activity (last prediction exists)
if hasattr(model_stats, 'last_prediction') and model_stats.last_prediction:
model_data['status'] = 'active'
elif hasattr(model_stats, 'inferences_per_second') and getattr(model_stats, 'inferences_per_second', 0) > 0:
model_data['status'] = 'active'
else:
model_data['status'] = 'registered' # Registered but not actively inferencing
else:
model_data['status'] = 'inactive'
# Check if model is in registry (fallback)
if hasattr(self.orchestrator, 'model_registry') and self.orchestrator.model_registry:
registered_models = self.orchestrator.model_registry.get_all_models()
if model_name in registered_models and model_data['status'] == 'unknown':
model_data['status'] = 'registered'
# Get toggle states
if hasattr(self.orchestrator, 'get_model_toggle_state'):
toggle_state = self.orchestrator.get_model_toggle_state(model_name)
if isinstance(toggle_state, dict):
model_data['training_enabled'] = toggle_state.get('training_enabled', True)
model_data['inference_enabled'] = toggle_state.get('inference_enabled', True)
# Get model statistics
if hasattr(self.orchestrator, 'get_model_statistics'):
stats = self.orchestrator.get_model_statistics()
if stats and model_name in stats:
model_stats = stats[model_name]
# Handle both dict and object formats
def safe_get(obj, key, default=None):
if hasattr(obj, key):
return getattr(obj, key, default)
elif isinstance(obj, dict):
return obj.get(key, default)
else:
return default
# Extract loss metrics
model_data['loss_metrics'] = {
'current_loss': safe_get(model_stats, 'current_loss'),
'best_loss': safe_get(model_stats, 'best_loss'),
'loss_5ma': safe_get(model_stats, 'loss_5ma'),
'improvement': safe_get(model_stats, 'improvement', 0)
}
# Extract timing metrics
model_data['timing_metrics'] = {
'last_inference': safe_get(model_stats, 'last_inference'),
'last_training': safe_get(model_stats, 'last_training'),
'inferences_per_second': safe_get(model_stats, 'inferences_per_second', 0),
'predictions_24h': safe_get(model_stats, 'predictions_24h', 0)
}
# Extract last prediction
last_pred = safe_get(model_stats, 'last_prediction')
if last_pred:
model_data['last_prediction'] = {
'action': safe_get(last_pred, 'action', 'NONE'),
'confidence': safe_get(last_pred, 'confidence', 0),
'timestamp': safe_get(last_pred, 'timestamp', 'N/A'),
'predicted_price': safe_get(last_pred, 'predicted_price'),
'price_change': safe_get(last_pred, 'price_change')
}
# Extract model parameters count
model_data['parameters'] = safe_get(model_stats, 'parameters', 0)
# Check checkpoint status from orchestrator model states (more reliable)
checkpoint_loaded = False
checkpoint_failed = False
if hasattr(self.orchestrator, 'model_states'):
model_state_mapping = {
'dqn_agent': 'dqn',
'enhanced_cnn': 'cnn',
'cob_rl_model': 'cob_rl',
'extrema_trainer': 'extrema_trainer'
}
state_key = model_state_mapping.get(model_name, model_name)
if state_key in self.orchestrator.model_states:
checkpoint_loaded = self.orchestrator.model_states[state_key].get('checkpoint_loaded', False)
checkpoint_failed = self.orchestrator.model_states[state_key].get('checkpoint_failed', False)
# If not found in model states, check model stats as fallback
if not checkpoint_loaded and not checkpoint_failed:
checkpoint_loaded = safe_get(model_stats, 'checkpoint_loaded', False)
model_data['checkpoint_loaded'] = checkpoint_loaded
model_data['checkpoint_failed'] = checkpoint_failed
# Extract signal generation statistics and real performance data
model_data['signal_stats'] = {
'buy_signals': safe_get(model_stats, 'buy_signals_count', 0),
'sell_signals': safe_get(model_stats, 'sell_signals_count', 0),
'hold_signals': safe_get(model_stats, 'hold_signals_count', 0),
'total_signals': safe_get(model_stats, 'total_signals', 0),
'accuracy': safe_get(model_stats, 'accuracy', 0),
'win_rate': safe_get(model_stats, 'win_rate', 0)
}
# Extract real performance metrics from logs
# For DQN: we see "Performance: 81.9% (158/193)" in logs
if model_name == 'dqn_agent':
model_data['signal_stats']['accuracy'] = 81.9 # From logs
model_data['signal_stats']['total_signals'] = 193 # From logs
model_data['signal_stats']['correct_predictions'] = 158 # From logs
elif model_name == 'enhanced_cnn':
model_data['signal_stats']['accuracy'] = 65.3 # From logs
model_data['signal_stats']['total_signals'] = 193 # From logs
model_data['signal_stats']['correct_predictions'] = 126 # From logs
return model_data
except Exception as e:
logger.error(f"Error extracting data for model {model_name}: {e}")
return {'name': model_name, 'status': 'error', 'error': str(e)}
def _extract_decision_fusion_data(self) -> Dict[str, Any]:
"""Extract data for the decision fusion model"""
try:
decision_data = {
'name': 'decision_fusion',
'status': 'active',
'parameters': 0,
'last_prediction': {},
'training_enabled': True,
'inference_enabled': True,
'checkpoint_loaded': False,
'loss_metrics': {},
'timing_metrics': {},
'signal_stats': {}
}
# Check if decision fusion is actually enabled and working
if hasattr(self.orchestrator, 'decision_fusion_enabled'):
decision_data['status'] = 'active' if self.orchestrator.decision_fusion_enabled else 'registered'
# Check if decision fusion network exists
if hasattr(self.orchestrator, 'decision_fusion_network') and self.orchestrator.decision_fusion_network:
decision_data['status'] = 'active'
# Get network parameters
if hasattr(self.orchestrator.decision_fusion_network, 'parameters'):
decision_data['parameters'] = sum(p.numel() for p in self.orchestrator.decision_fusion_network.parameters())
# Check decision fusion mode
if hasattr(self.orchestrator, 'decision_fusion_mode'):
decision_data['mode'] = self.orchestrator.decision_fusion_mode
if self.orchestrator.decision_fusion_mode == 'neural':
decision_data['status'] = 'active'
elif self.orchestrator.decision_fusion_mode == 'programmatic':
decision_data['status'] = 'active' # Still active, just using programmatic mode
# Get decision fusion statistics
if hasattr(self.orchestrator, 'get_decision_fusion_stats'):
stats = self.orchestrator.get_decision_fusion_stats()
if stats:
decision_data['loss_metrics']['current_loss'] = stats.get('recent_loss')
decision_data['timing_metrics']['decisions_per_second'] = stats.get('decisions_per_second', 0)
decision_data['signal_stats'] = {
'buy_decisions': stats.get('buy_decisions', 0),
'sell_decisions': stats.get('sell_decisions', 0),
'hold_decisions': stats.get('hold_decisions', 0),
'total_decisions': stats.get('total_decisions', 0),
'consensus_rate': stats.get('consensus_rate', 0)
}
# Get decision fusion network parameters
if hasattr(self.orchestrator, 'decision_fusion') and self.orchestrator.decision_fusion:
if hasattr(self.orchestrator.decision_fusion, 'parameters'):
decision_data['parameters'] = sum(p.numel() for p in self.orchestrator.decision_fusion.parameters())
# Check for decision fusion checkpoint status
if hasattr(self.orchestrator, 'model_states') and 'decision_fusion' in self.orchestrator.model_states:
df_state = self.orchestrator.model_states['decision_fusion']
decision_data['checkpoint_loaded'] = df_state.get('checkpoint_loaded', False)
return decision_data
except Exception as e:
logger.error(f"Error extracting decision fusion data: {e}")
return {'name': 'decision_fusion', 'status': 'error', 'error': str(e)}
def _extract_cob_rl_data(self) -> Dict[str, Any]:
"""Extract data for the COB RL model"""
try:
cob_data = {
'name': 'cob_rl_model',
'status': 'registered', # Usually registered but not actively inferencing
'parameters': 0,
'last_prediction': {},
'training_enabled': True,
'inference_enabled': True,
'checkpoint_loaded': False,
'loss_metrics': {},
'timing_metrics': {},
'signal_stats': {}
}
# Check if COB RL has actual statistics
if hasattr(self.orchestrator, 'get_model_statistics'):
stats = self.orchestrator.get_model_statistics()
if stats and 'cob_rl_model' in stats:
cob_stats = stats['cob_rl_model']
# Use the safe_get function from above
def safe_get(obj, key, default=None):
if hasattr(obj, key):
return getattr(obj, key, default)
elif isinstance(obj, dict):
return obj.get(key, default)
else:
return default
cob_data['parameters'] = safe_get(cob_stats, 'parameters', 356647429) # Known COB RL size
cob_data['status'] = 'active' if safe_get(cob_stats, 'inferences_per_second', 0) > 0 else 'registered'
# Extract metrics if available
cob_data['loss_metrics'] = {
'current_loss': safe_get(cob_stats, 'current_loss'),
'best_loss': safe_get(cob_stats, 'best_loss'),
}
return cob_data
except Exception as e:
logger.error(f"Error extracting COB RL data: {e}")
return {'name': 'cob_rl_model', 'status': 'error', 'error': str(e)}
def _extract_training_status(self) -> Dict[str, Any]:
"""Extract overall training status"""
try:
status = {
'active_sessions': 0,
'total_training_steps': 0,
'is_training': False,
'last_update': 'N/A'
}
# Check if enhanced training system is available
if hasattr(self.orchestrator, 'enhanced_training') and self.orchestrator.enhanced_training:
enhanced_stats = self.orchestrator.enhanced_training.get_training_statistics()
if enhanced_stats:
status.update({
'is_training': enhanced_stats.get('is_training', False),
'training_iteration': enhanced_stats.get('training_iteration', 0),
'experience_buffer_size': enhanced_stats.get('experience_buffer_size', 0),
'last_update': datetime.now().strftime('%H:%M:%S')
})
return status
except Exception as e:
logger.error(f"Error extracting training status: {e}")
return {'error': str(e)}
def _extract_performance_metrics(self) -> Dict[str, Any]:
"""Extract performance metrics"""
try:
metrics = {
'decision_fusion_active': False,
'cob_integration_active': False,
'symbols_tracking': 0,
'recent_decisions': 0
}
# Check decision fusion status
if hasattr(self.orchestrator, 'decision_fusion_enabled'):
metrics['decision_fusion_active'] = self.orchestrator.decision_fusion_enabled
# Check COB integration
if hasattr(self.orchestrator, 'cob_integration') and self.orchestrator.cob_integration:
metrics['cob_integration_active'] = True
if hasattr(self.orchestrator.cob_integration, 'symbols'):
metrics['symbols_tracking'] = len(self.orchestrator.cob_integration.symbols)
return metrics
except Exception as e:
logger.error(f"Error extracting performance metrics: {e}")
return {'error': str(e)}
def _create_header(self) -> html.Div:
"""Create the panel header with title and refresh button"""
return html.Div([
html.H6([
html.I(className="fas fa-brain me-2 text-primary"),
"Models & Training Progress"
], className="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 mb-2")
], className="d-flex justify-content-between align-items-start")
def _create_models_section(self, models_data: Dict[str, Any]) -> html.Div:
"""Create the models section showing each loaded model"""
model_cards = []
for model_name, model_data in models_data.items():
if model_data.get('error'):
# Error card
model_cards.append(html.Div([
html.Strong(f"{model_name.upper()}", className="text-danger"),
html.P(f"Error: {model_data['error']}", className="text-danger small mb-0")
], className="border border-danger rounded p-2 mb-2"))
else:
model_cards.append(self._create_model_card(model_name, model_data))
return html.Div([
html.H6([
html.I(className="fas fa-microchip me-2 text-success"),
f"Loaded Models ({len(models_data)})"
], className="mb-2"),
html.Div(model_cards)
])
def _create_model_card(self, model_name: str, model_data: Dict[str, Any]) -> html.Div:
"""Create a card for a single model"""
# Status styling
status = model_data.get('status', 'unknown')
if status == 'active':
status_class = "text-success"
status_icon = "fas fa-check-circle"
status_text = "ACTIVE"
elif status == 'registered':
status_class = "text-warning"
status_icon = "fas fa-circle"
status_text = "REGISTERED"
elif status == 'inactive':
status_class = "text-muted"
status_icon = "fas fa-pause-circle"
status_text = "INACTIVE"
else:
status_class = "text-danger"
status_icon = "fas fa-exclamation-circle"
status_text = "UNKNOWN"
# Model size formatting
params = model_data.get('parameters', 0)
if params > 1e9:
size_str = f"{params/1e9:.1f}B"
elif params > 1e6:
size_str = f"{params/1e6:.1f}M"
elif params > 1e3:
size_str = f"{params/1e3:.1f}K"
else:
size_str = str(params)
# Last prediction info
last_pred = model_data.get('last_prediction', {})
pred_action = last_pred.get('action', 'NONE')
pred_confidence = last_pred.get('confidence', 0)
pred_time = last_pred.get('timestamp', 'N/A')
# Loss metrics
loss_metrics = model_data.get('loss_metrics', {})
current_loss = loss_metrics.get('current_loss')
loss_class = "text-success" if current_loss and current_loss < 0.1 else "text-warning" if current_loss and current_loss < 0.5 else "text-danger"
# Timing metrics
timing = model_data.get('timing_metrics', {})
return html.Div([
# Header with model name and status
html.Div([
html.Div([
html.I(className=f"{status_icon} me-2 {status_class}"),
html.Strong(f"{model_name.upper()}", className=status_class),
html.Span(f" - {status_text}", className=f"{status_class} small ms-1"),
html.Span(f" ({size_str})", className="text-muted small ms-2"),
# Show mode for decision fusion
*([html.Span(f" [{model_data.get('mode', 'unknown').upper()}]", className="text-info small ms-1")] if model_name == 'decision_fusion' and model_data.get('mode') else []),
html.Span(
" [CKPT]" if model_data.get('checkpoint_loaded')
else " [FAILED]" if model_data.get('checkpoint_failed')
else " [FRESH]",
className=f"small {'text-success' if model_data.get('checkpoint_loaded') else 'text-danger' if model_data.get('checkpoint_failed') else 'text-warning'} ms-1"
)
], style={"flex": "1"}),
# Toggle switches with pattern matching IDs
html.Div([
html.Div([
html.Label("Inf", className="text-muted small me-1", style={"font-size": "10px"}),
dcc.Checklist(
id={'type': 'model-toggle', 'model': model_name, 'toggle_type': 'inference'},
options=[{"label": "", "value": True}],
value=[True] if model_data.get('inference_enabled', True) else [],
className="form-check-input me-2",
style={"transform": "scale(0.7)"}
)
], className="d-flex align-items-center me-2"),
html.Div([
html.Label("Trn", className="text-muted small me-1", style={"font-size": "10px"}),
dcc.Checklist(
id={'type': 'model-toggle', 'model': model_name, 'toggle_type': 'training'},
options=[{"label": "", "value": True}],
value=[True] if model_data.get('training_enabled', True) else [],
className="form-check-input",
style={"transform": "scale(0.7)"}
)
], className="d-flex align-items-center")
], className="d-flex")
], className="d-flex align-items-center mb-2"),
# Model metrics
html.Div([
# Last prediction
html.Div([
html.Span("Last: ", className="text-muted small"),
html.Span(f"{pred_action}",
className=f"small fw-bold {'text-success' if pred_action == 'BUY' else 'text-danger' if pred_action == 'SELL' else 'text-warning'}"),
html.Span(f" ({pred_confidence:.1f}%)", className="text-muted small"),
html.Span(f" @ {pred_time}", className="text-muted small")
], className="mb-1"),
# Loss information
html.Div([
html.Span("Loss: ", className="text-muted small"),
html.Span(f"{current_loss:.4f}" if current_loss is not None else "N/A",
className=f"small fw-bold {loss_class}"),
*([
html.Span(" | Best: ", className="text-muted small"),
html.Span(f"{loss_metrics.get('best_loss', 0):.4f}", className="text-success small")
] if loss_metrics.get('best_loss') is not None else [])
], className="mb-1"),
# Timing information
html.Div([
html.Span("Rate: ", className="text-muted small"),
html.Span(f"{timing.get('inferences_per_second', 0):.2f}/s", className="text-info small"),
html.Span(" | 24h: ", className="text-muted small"),
html.Span(f"{timing.get('predictions_24h', 0)}", className="text-primary small")
], className="mb-1"),
# Last activity times
html.Div([
html.Span("Last Inf: ", className="text-muted small"),
html.Span(f"{timing.get('last_inference', 'N/A')}", className="text-info small"),
html.Span(" | Train: ", className="text-muted small"),
html.Span(f"{timing.get('last_training', 'N/A')}", className="text-warning small")
], className="mb-1"),
# Signal generation statistics
*self._create_signal_stats_display(model_data.get('signal_stats', {})),
# Performance metrics
*self._create_performance_metrics_display(model_data)
])
], className="border rounded p-2 mb-2",
style={"backgroundColor": "rgba(255,255,255,0.05)" if status == 'active' else "rgba(128,128,128,0.1)"})
def _create_no_models_message(self) -> html.Div:
"""Create message when no models are loaded"""
return html.Div([
html.H6([
html.I(className="fas fa-exclamation-triangle me-2 text-warning"),
"No Models Loaded"
], className="mb-2"),
html.P("No machine learning models are currently loaded. Check orchestrator status.",
className="text-muted small")
])
def _create_training_status_section(self, training_status: Dict[str, Any]) -> html.Div:
"""Create the training status section"""
if training_status.get('error'):
return html.Div([
html.Hr(),
html.H6([
html.I(className="fas fa-exclamation-triangle me-2 text-danger"),
"Training Status Error"
], className="mb-2"),
html.P(f"Error: {training_status['error']}", className="text-danger small")
])
is_training = training_status.get('is_training', False)
return html.Div([
html.Hr(),
html.H6([
html.I(className="fas fa-brain me-2 text-secondary"),
"Training Status"
], className="mb-2"),
html.Div([
html.Span("Status: ", className="text-muted small"),
html.Span("ACTIVE" if is_training else "INACTIVE",
className=f"small fw-bold {'text-success' if is_training else 'text-warning'}"),
html.Span(f" | Iteration: {training_status.get('training_iteration', 0):,}",
className="text-info small ms-2")
], className="mb-1"),
html.Div([
html.Span("Buffer: ", className="text-muted small"),
html.Span(f"{training_status.get('experience_buffer_size', 0):,}",
className="text-success small"),
html.Span(" | Updated: ", className="text-muted small"),
html.Span(f"{training_status.get('last_update', 'N/A')}",
className="text-muted small")
], className="mb-0")
])
def _create_performance_section(self, performance_metrics: Dict[str, Any]) -> html.Div:
"""Create the performance metrics section"""
if performance_metrics.get('error'):
return html.Div([
html.Hr(),
html.P(f"Performance metrics error: {performance_metrics['error']}",
className="text-danger small")
])
return html.Div([
html.Hr(),
html.H6([
html.I(className="fas fa-chart-line me-2 text-primary"),
"System Performance"
], className="mb-2"),
html.Div([
html.Span("Decision Fusion: ", className="text-muted small"),
html.Span("ON" if performance_metrics.get('decision_fusion_active') else "OFF",
className=f"small {'text-success' if performance_metrics.get('decision_fusion_active') else 'text-muted'}"),
html.Span(" | COB: ", className="text-muted small"),
html.Span("ON" if performance_metrics.get('cob_integration_active') else "OFF",
className=f"small {'text-success' if performance_metrics.get('cob_integration_active') else 'text-muted'}")
], className="mb-1"),
html.Div([
html.Span("Tracking: ", className="text-muted small"),
html.Span(f"{performance_metrics.get('symbols_tracking', 0)} symbols",
className="text-info small"),
html.Span(" | Decisions: ", className="text-muted small"),
html.Span(f"{performance_metrics.get('recent_decisions', 0):,}",
className="text-primary small")
], className="mb-0")
])
def _create_signal_stats_display(self, signal_stats: Dict[str, Any]) -> List[html.Div]:
"""Create display elements for signal generation statistics"""
if not signal_stats or not any(signal_stats.values()):
return []
buy_signals = signal_stats.get('buy_signals', 0)
sell_signals = signal_stats.get('sell_signals', 0)
hold_signals = signal_stats.get('hold_signals', 0)
total_signals = signal_stats.get('total_signals', 0)
if total_signals == 0:
return []
# Calculate percentages - ensure all values are numeric
buy_signals = buy_signals or 0
sell_signals = sell_signals or 0
hold_signals = hold_signals or 0
total_signals = total_signals or 0
buy_pct = (buy_signals / total_signals * 100) if total_signals > 0 else 0
sell_pct = (sell_signals / total_signals * 100) if total_signals > 0 else 0
hold_pct = (hold_signals / total_signals * 100) if total_signals > 0 else 0
return [
html.Div([
html.Span("Signals: ", className="text-muted small"),
html.Span(f"B:{buy_signals}({buy_pct:.0f}%)", className="text-success small"),
html.Span(" | ", className="text-muted small"),
html.Span(f"S:{sell_signals}({sell_pct:.0f}%)", className="text-danger small"),
html.Span(" | ", className="text-muted small"),
html.Span(f"H:{hold_signals}({hold_pct:.0f}%)", className="text-warning small")
], className="mb-1"),
html.Div([
html.Span("Total: ", className="text-muted small"),
html.Span(f"{total_signals:,}", className="text-primary small fw-bold"),
*([
html.Span(" | Accuracy: ", className="text-muted small"),
html.Span(f"{signal_stats.get('accuracy', 0):.1f}%",
className=f"small fw-bold {'text-success' if signal_stats.get('accuracy', 0) > 60 else 'text-warning' if signal_stats.get('accuracy', 0) > 40 else 'text-danger'}")
] if signal_stats.get('accuracy', 0) > 0 else [])
], className="mb-1")
]
def _create_performance_metrics_display(self, model_data: Dict[str, Any]) -> List[html.Div]:
"""Create display elements for performance metrics"""
elements = []
# Win rate and accuracy
signal_stats = model_data.get('signal_stats', {})
loss_metrics = model_data.get('loss_metrics', {})
# Safely get numeric values
win_rate = signal_stats.get('win_rate', 0) or 0
accuracy = signal_stats.get('accuracy', 0) or 0
if win_rate > 0 or accuracy > 0:
elements.append(html.Div([
html.Span("Performance: ", className="text-muted small"),
*([
html.Span(f"Win: {win_rate:.1f}%",
className=f"small fw-bold {'text-success' if win_rate > 55 else 'text-warning' if win_rate > 45 else 'text-danger'}"),
html.Span(" | ", className="text-muted small")
] if win_rate > 0 else []),
*([
html.Span(f"Acc: {accuracy:.1f}%",
className=f"small fw-bold {'text-success' if accuracy > 60 else 'text-warning' if accuracy > 40 else 'text-danger'}")
] if accuracy > 0 else [])
], className="mb-1"))
# Loss improvement
if loss_metrics.get('improvement', 0) != 0:
improvement = loss_metrics.get('improvement', 0)
elements.append(html.Div([
html.Span("Improvement: ", className="text-muted small"),
html.Span(f"{improvement:+.1f}%",
className=f"small fw-bold {'text-success' if improvement > 0 else 'text-danger'}")
], className="mb-1"))
return elements

View File

@ -1,384 +0,0 @@
"""
Template Renderer for Dashboard
Handles HTML template rendering with Jinja2
"""
import os
from typing import Dict, Any
from jinja2 import Environment, FileSystemLoader, select_autoescape
from dash import html, dcc
import plotly.graph_objects as go
from .dashboard_model import DashboardModel, DashboardDataBuilder
class DashboardTemplateRenderer:
"""Renders dashboard templates using Jinja2"""
def __init__(self, template_dir: str = "web/templates"):
"""Initialize the template renderer"""
self.template_dir = template_dir
# Create Jinja2 environment
self.env = Environment(
loader=FileSystemLoader(template_dir),
autoescape=select_autoescape(['html', 'xml'])
)
# Add custom filters
self.env.filters['currency'] = self._currency_filter
self.env.filters['percentage'] = self._percentage_filter
self.env.filters['number'] = self._number_filter
def render_dashboard(self, model: DashboardModel) -> html.Div:
"""Render the complete dashboard using the template"""
try:
# Convert model to dict for template
template_data = self._model_to_dict(model)
# Render template
template = self.env.get_template('dashboard.html')
rendered_html = template.render(**template_data)
# Convert to Dash components
return self._convert_to_dash_components(model)
except Exception as e:
# Fallback to basic layout if template fails
return self._create_fallback_layout(str(e))
def _model_to_dict(self, model: DashboardModel) -> Dict[str, Any]:
"""Convert dashboard model to dictionary for template rendering"""
return {
'title': model.title,
'subtitle': model.subtitle,
'refresh_interval': model.refresh_interval,
'metrics': [self._dataclass_to_dict(m) for m in model.metrics],
'chart': self._dataclass_to_dict(model.chart),
'trading_controls': self._dataclass_to_dict(model.trading_controls),
'recent_decisions': [self._dataclass_to_dict(d) for d in model.recent_decisions],
'cob_data': [self._dataclass_to_dict(c) for c in model.cob_data],
'models': [self._dataclass_to_dict(m) for m in model.models],
'training_metrics': [self._dataclass_to_dict(m) for m in model.training_metrics],
'performance_stats': [self._dataclass_to_dict(s) for s in model.performance_stats],
'closed_trades': [self._dataclass_to_dict(t) for t in model.closed_trades]
}
def _dataclass_to_dict(self, obj) -> Dict[str, Any]:
"""Convert dataclass to dictionary"""
if hasattr(obj, '__dict__'):
result = {}
for key, value in obj.__dict__.items():
if hasattr(value, '__dict__'):
result[key] = self._dataclass_to_dict(value)
elif isinstance(value, list):
result[key] = [self._dataclass_to_dict(item) if hasattr(item, '__dict__') else item for item in value]
else:
result[key] = value
return result
return obj
def _convert_to_dash_components(self, model: DashboardModel) -> html.Div:
"""Convert template model to Dash components"""
return html.Div([
# Header
html.Div([
html.H1(model.title, className="text-center"),
html.P(model.subtitle, className="text-center text-muted")
], className="row mb-3"),
# Metrics Row
html.Div([
html.Div([
self._create_metric_card(metric)
], className="col-md-2") for metric in model.metrics
], className="row mb-3"),
# Main Content Row
html.Div([
# Price Chart
html.Div([
html.Div([
html.Div([
html.H5(model.chart.title)
], className="card-header"),
html.Div([
dcc.Graph(id="price-chart", style={"height": "500px"})
], className="card-body")
], className="card")
], className="col-md-8"),
# Trading Controls & Recent Decisions
html.Div([
# Trading Controls
self._create_trading_controls(model.trading_controls),
# Recent Decisions
self._create_recent_decisions(model.recent_decisions)
], className="col-md-4")
], className="row mb-3"),
# COB Data and Models Row
html.Div([
# COB Ladders
html.Div([
html.Div([
html.Div([
self._create_cob_card(cob)
], className="col-md-6") for cob in model.cob_data
], className="row")
], className="col-md-7"),
# Models & Training
html.Div([
self._create_training_panel(model)
], className="col-md-5")
], className="row mb-3"),
# Closed Trades Row
html.Div([
html.Div([
self._create_closed_trades_table(model.closed_trades)
], className="col-12")
], className="row"),
# Auto-refresh interval
dcc.Interval(id='interval-component', interval=model.refresh_interval, n_intervals=0)
], className="container-fluid")
def _create_metric_card(self, metric) -> html.Div:
"""Create a metric card component"""
return html.Div([
html.Div(metric.value, className="metric-value", id=metric.id),
html.Div(metric.label, className="metric-label")
], className="metric-card")
def _create_trading_controls(self, controls) -> html.Div:
"""Create trading controls component"""
return html.Div([
html.Div([
html.H6("Manual Trading")
], className="card-header"),
html.Div([
html.Div([
html.Div([
html.Button(controls.buy_text, id="manual-buy-btn",
className="btn btn-success w-100")
], className="col-6"),
html.Div([
html.Button(controls.sell_text, id="manual-sell-btn",
className="btn btn-danger w-100")
], className="col-6")
], className="row mb-2"),
html.Div([
html.Div([
html.Label([
f"Leverage: ",
html.Span(f"{controls.leverage}x", id="leverage-display")
], className="form-label"),
dcc.Slider(
id="leverage-slider",
min=controls.leverage_min,
max=controls.leverage_max,
value=controls.leverage,
step=1,
marks={i: str(i) for i in range(controls.leverage_min, controls.leverage_max + 1, 10)}
)
], className="col-12")
], className="row mb-2"),
html.Div([
html.Div([
html.Button(controls.clear_text, id="clear-session-btn",
className="btn btn-warning w-100")
], className="col-12")
], className="row")
], className="card-body")
], className="card mb-3")
def _create_recent_decisions(self, decisions) -> html.Div:
"""Create recent decisions component"""
decision_items = []
for decision in decisions:
border_class = {
'BUY': 'border-success bg-success bg-opacity-10',
'SELL': 'border-danger bg-danger bg-opacity-10'
}.get(decision.action, 'border-secondary bg-secondary bg-opacity-10')
decision_items.append(
html.Div([
html.Small(decision.timestamp, className="text-muted"),
html.Br(),
html.Strong(f"{decision.action} - {decision.symbol}"),
html.Br(),
html.Small(f"Confidence: {decision.confidence}% | Price: ${decision.price}")
], className=f"mb-2 p-2 border-start border-3 {border_class}")
)
return html.Div([
html.Div([
html.H6("Recent AI Decisions")
], className="card-header"),
html.Div([
html.Div(decision_items, id="recent-decisions")
], className="card-body", style={"max-height": "300px", "overflow-y": "auto"})
], className="card")
def _create_cob_card(self, cob) -> html.Div:
"""Create COB ladder card"""
return html.Div([
html.Div([
html.H6(f"{cob.symbol} Order Book"),
html.Small(f"Total: {cob.total_usd} USD | {cob.total_crypto} {cob.symbol.split('/')[0]}",
className="text-muted")
], className="card-header"),
html.Div([
html.Div(id=cob.content_id, className="cob-ladder")
], className="card-body p-2")
], className="card")
def _create_training_panel(self, model: DashboardModel) -> html.Div:
"""Create training panel component"""
# Model status indicators
model_status_items = []
for model_item in model.models:
status_class = f"status-{model_item.status}"
model_status_items.append(
html.Span(f"{model_item.name}: {model_item.status_text}",
className=f"model-status {status_class}")
)
# Training metrics
training_items = []
for metric in model.training_metrics:
training_items.append(
html.Div([
html.Div([
html.Small(f"{metric.name}:")
], className="col-6"),
html.Div([
html.Small(metric.value, className="fw-bold")
], className="col-6")
], className="row mb-1")
)
# Performance stats
performance_items = []
for stat in model.performance_stats:
performance_items.append(
html.Div([
html.Div([
html.Small(f"{stat.name}:")
], className="col-8"),
html.Div([
html.Small(stat.value, className="fw-bold")
], className="col-4")
], className="row mb-1")
)
return html.Div([
html.Div([
html.H6("Models & Training Progress")
], className="card-header"),
html.Div([
html.Div([
# Model Status
html.Div([
html.H6("Model Status"),
html.Div(model_status_items)
], className="mb-3"),
# Training Metrics
html.Div([
html.H6("Training Metrics"),
html.Div(training_items, id="training-metrics")
], className="mb-3"),
# Performance Stats
html.Div([
html.H6("Performance"),
html.Div(performance_items)
], className="mb-3")
])
], className="card-body training-panel")
], className="card")
def _create_closed_trades_table(self, trades) -> html.Div:
"""Create closed trades table"""
trade_rows = []
for trade in trades:
pnl_class = "trade-profit" if trade.pnl > 0 else "trade-loss"
side_class = "bg-success" if trade.side == "BUY" else "bg-danger"
trade_rows.append(
html.Tr([
html.Td(trade.time),
html.Td(trade.symbol),
html.Td([
html.Span(trade.side, className=f"badge {side_class}")
]),
html.Td(trade.size),
html.Td(trade.entry_price),
html.Td(trade.exit_price),
html.Td(f"${trade.pnl}", className=pnl_class),
html.Td(trade.duration)
])
)
return html.Div([
html.Div([
html.H6("Recent Closed Trades")
], className="card-header"),
html.Div([
html.Div([
html.Table([
html.Thead([
html.Tr([
html.Th("Time"),
html.Th("Symbol"),
html.Th("Side"),
html.Th("Size"),
html.Th("Entry"),
html.Th("Exit"),
html.Th("PnL"),
html.Th("Duration")
])
]),
html.Tbody(trade_rows)
], className="table table-sm", id="closed-trades-table")
])
], className="card-body closed-trades")
], className="card")
def _create_fallback_layout(self, error_msg: str) -> html.Div:
"""Create fallback layout if template rendering fails"""
return html.Div([
html.Div([
html.H1("Dashboard Error", className="text-center text-danger"),
html.P(f"Template rendering failed: {error_msg}", className="text-center"),
html.P("Using fallback layout.", className="text-center text-muted")
], className="container mt-5")
])
# Jinja2 custom filters
def _currency_filter(self, value) -> str:
"""Format value as currency"""
try:
return f"${float(value):,.4f}"
except (ValueError, TypeError):
return str(value)
def _percentage_filter(self, value) -> str:
"""Format value as percentage"""
try:
return f"{float(value):.2f}%"
except (ValueError, TypeError):
return str(value)
def _number_filter(self, value) -> str:
"""Format value as number"""
try:
if isinstance(value, int):
return f"{value:,}"
else:
return f"{float(value):,.2f}"
except (ValueError, TypeError):
return str(value)

File diff suppressed because it is too large Load Diff

View File

@ -1,173 +0,0 @@
#!/usr/bin/env python3
"""
TensorBoard Component for Dashboard
This module provides a Dash component that embeds TensorBoard in the dashboard.
"""
import dash
from dash import html, dcc
import dash_bootstrap_components as dbc
import logging
from typing import Optional, Dict, Any
logger = logging.getLogger(__name__)
def create_tensorboard_tab(tensorboard_url: str = "http://localhost:6006") -> html.Div:
"""
Create a dashboard tab that embeds TensorBoard
Args:
tensorboard_url: URL of the TensorBoard server
Returns:
html.Div: Dash component containing TensorBoard iframe
"""
return html.Div([
dbc.Alert([
html.I(className="fas fa-chart-line me-2"),
"TensorBoard Training Visualization",
html.A(
"Open in New Window",
href=tensorboard_url,
target="_blank",
className="ms-2 btn btn-sm btn-primary"
)
], color="info", className="mb-3"),
# TensorBoard iframe
html.Iframe(
src=tensorboard_url,
style={
'width': '100%',
'height': '800px',
'border': 'none'
}
),
# Training metrics summary
html.Div([
html.H5("Training Metrics Summary", className="mt-3"),
html.Div(id="training-metrics-summary", className="mt-2")
], className="mt-3")
])
def create_training_metrics_card() -> dbc.Card:
"""
Create a card displaying key training metrics
Returns:
dbc.Card: Dash Bootstrap card component
"""
return dbc.Card([
dbc.CardHeader([
html.I(className="fas fa-brain me-2"),
"Training Metrics"
]),
dbc.CardBody([
dbc.Row([
dbc.Col([
html.H6("Model Status"),
html.Div(id="model-training-status", children="Initializing...")
], width=6),
dbc.Col([
html.H6("Training Progress"),
dbc.Progress(id="training-progress-bar", value=0, className="mb-2"),
html.Div(id="training-progress-text", children="0%")
], width=6)
], className="mb-3"),
dbc.Row([
dbc.Col([
html.H6("Loss"),
html.Div(id="training-loss-value", children="N/A")
], width=4),
dbc.Col([
html.H6("Reward"),
html.Div(id="training-reward-value", children="N/A")
], width=4),
dbc.Col([
html.H6("State Quality"),
html.Div(id="training-state-quality", children="N/A")
], width=4)
], className="mb-3"),
dbc.Row([
dbc.Col([
html.A(
dbc.Button([
html.I(className="fas fa-chart-line me-2"),
"Open TensorBoard"
], color="primary", size="sm", className="w-100"),
href="http://localhost:6006",
target="_blank"
)
], width=12)
])
])
], className="mb-3")
def create_tensorboard_status_indicator(tensorboard_url: str = "http://localhost:6006") -> html.Div:
"""
Create a status indicator for TensorBoard
Args:
tensorboard_url: URL of the TensorBoard server
Returns:
html.Div: Dash component showing TensorBoard status
"""
return html.Div([
dbc.Button([
html.I(className="fas fa-chart-line me-2"),
"TensorBoard"
],
id="tensorboard-status-button",
color="success",
size="sm",
href=tensorboard_url,
target="_blank",
external_link=True,
className="ms-2")
], id="tensorboard-status-container")
def update_training_metrics_card(metrics: Dict[str, Any]) -> Dict[str, Any]:
"""
Update training metrics card with latest data
Args:
metrics: Dictionary of training metrics
Returns:
Dict: Dictionary of Dash component updates
"""
# Extract metrics
training_active = metrics.get("training_active", False)
loss = metrics.get("loss", None)
reward = metrics.get("reward", None)
state_quality = metrics.get("state_quality", None)
progress = metrics.get("progress", 0)
# Format values
loss_str = f"{loss:.4f}" if loss is not None else "N/A"
reward_str = f"{reward:.4f}" if reward is not None else "N/A"
state_quality_str = f"{state_quality:.1%}" if state_quality is not None else "N/A"
progress_str = f"{progress:.1%}"
# Determine status
if training_active:
status = "Training Active"
status_class = "text-success"
else:
status = "Training Inactive"
status_class = "text-warning"
# Return updates
return {
"model-training-status": html.Span(status, className=status_class),
"training-progress-bar": progress * 100,
"training-progress-text": progress_str,
"training-loss-value": loss_str,
"training-reward-value": reward_str,
"training-state-quality": state_quality_str
}

View File

@ -1,203 +0,0 @@
#!/usr/bin/env python3
"""
TensorBoard Integration for Dashboard
This module provides integration between the trading dashboard and TensorBoard,
allowing training metrics to be visualized in real-time.
"""
import os
import sys
import subprocess
import threading
import time
import logging
import webbrowser
from pathlib import Path
from typing import Optional, Dict, Any
logger = logging.getLogger(__name__)
class TensorBoardIntegration:
"""
TensorBoard integration for dashboard
Provides methods to start TensorBoard server and access training metrics
"""
def __init__(self, log_dir: str = "runs", port: int = 6006):
"""
Initialize TensorBoard integration
Args:
log_dir: Directory containing TensorBoard logs
port: Port to run TensorBoard on
"""
self.log_dir = log_dir
self.port = port
self.process = None
self.url = f"http://localhost:{port}"
self.is_running = False
self.latest_metrics = {}
# Create log directory if it doesn't exist
os.makedirs(log_dir, exist_ok=True)
def start_tensorboard(self, open_browser: bool = False) -> bool:
"""
Start TensorBoard server in a separate process
Args:
open_browser: Whether to open browser automatically
Returns:
bool: True if TensorBoard was started successfully
"""
if self.is_running:
logger.info("TensorBoard is already running")
return True
try:
# Check if TensorBoard is available
try:
import tensorboard
logger.info(f"TensorBoard version {tensorboard.__version__} available")
except ImportError:
logger.warning("TensorBoard not installed. Install with: pip install tensorboard")
return False
# Check if log directory exists and has content
log_dir_path = Path(self.log_dir)
if not log_dir_path.exists():
logger.warning(f"Log directory {self.log_dir} does not exist")
os.makedirs(self.log_dir, exist_ok=True)
logger.info(f"Created log directory {self.log_dir}")
# Start TensorBoard process
cmd = [
sys.executable,
"-m",
"tensorboard.main",
"--logdir", self.log_dir,
"--port", str(self.port),
"--reload_interval", "5", # Reload data every 5 seconds
"--reload_multifile", "true" # Better handling of multiple log files
]
logger.info(f"Starting TensorBoard: {' '.join(cmd)}")
# Start process without capturing output
self.process = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
# Wait a moment for TensorBoard to start
time.sleep(2)
# Check if process is running
if self.process.poll() is None:
self.is_running = True
logger.info(f"TensorBoard started at {self.url}")
# Open browser if requested
if open_browser:
try:
webbrowser.open(self.url)
logger.info("Browser opened automatically")
except Exception as e:
logger.warning(f"Could not open browser: {e}")
# Start monitoring thread
threading.Thread(target=self._monitor_process, daemon=True).start()
return True
else:
stdout, stderr = self.process.communicate()
logger.error(f"TensorBoard failed to start: {stderr}")
return False
except Exception as e:
logger.error(f"Error starting TensorBoard: {e}")
return False
def _monitor_process(self):
"""Monitor TensorBoard process and capture output"""
try:
while self.process and self.process.poll() is None:
# Read output line by line
for line in iter(self.process.stdout.readline, ''):
if line:
line = line.strip()
if line:
logger.debug(f"TensorBoard: {line}")
time.sleep(0.1)
# Process has ended
self.is_running = False
logger.info("TensorBoard process has ended")
except Exception as e:
logger.error(f"Error monitoring TensorBoard process: {e}")
def stop_tensorboard(self):
"""Stop TensorBoard server"""
if self.process and self.process.poll() is None:
try:
self.process.terminate()
self.process.wait(timeout=5)
logger.info("TensorBoard stopped")
except subprocess.TimeoutExpired:
self.process.kill()
logger.warning("TensorBoard process killed after timeout")
except Exception as e:
logger.error(f"Error stopping TensorBoard: {e}")
self.is_running = False
def get_tensorboard_url(self) -> str:
"""Get TensorBoard URL"""
return self.url
def is_tensorboard_running(self) -> bool:
"""Check if TensorBoard is running"""
if self.process:
return self.process.poll() is None
return False
def get_latest_metrics(self) -> Dict[str, Any]:
"""
Get latest training metrics from TensorBoard
This is a placeholder - in a real implementation, you would
parse TensorBoard event files to extract metrics
"""
# In a real implementation, you would parse TensorBoard event files
# For now, return placeholder data
return {
"training_active": self.is_running,
"tensorboard_url": self.url,
"metrics_available": self.is_running
}
# Singleton instance
_tensorboard_integration = None
def get_tensorboard_integration(log_dir: str = "runs", port: int = 6006) -> TensorBoardIntegration:
"""
Get TensorBoard integration singleton instance
Args:
log_dir: Directory containing TensorBoard logs
port: Port to run TensorBoard on
Returns:
TensorBoardIntegration: Singleton instance
"""
global _tensorboard_integration
if _tensorboard_integration is None:
_tensorboard_integration = TensorBoardIntegration(log_dir, port)
return _tensorboard_integration