253 lines
12 KiB
Python
253 lines
12 KiB
Python
"""
|
|
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") |