""" 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")