"""Performance metrics for backtesting.""" from decimal import Decimal from typing import Dict, List, Any, Optional from src.trading.paper_trading import PaperTradingSimulator class BacktestMetrics: """Calculates backtest performance metrics.""" def calculate_metrics( self, simulator: PaperTradingSimulator, trades: List[Dict[str, Any]], initial_capital: Decimal, total_fees: Optional[Decimal] = None ) -> Dict[str, Any]: """Calculate backtest metrics, including fee-adjusted metrics. Args: simulator: Paper trading simulator trades: List of executed trades (may include 'fee' field) initial_capital: Initial capital total_fees: Total fees paid (if None, calculated from trades) Returns: Dictionary of metrics """ final_value = simulator.get_portfolio_value() # Calculate total fees if not provided if total_fees is None: total_fees = sum( Decimal(str(trade.get('fee', 0))) for trade in trades ) # Gross return (before fees) gross_return = ((final_value - initial_capital) / initial_capital) * 100 if initial_capital > 0 else 0 # Net return (after fees) - fees are already deducted in simulator # But we calculate what return would be without fees for comparison value_without_fees = final_value + total_fees net_return = ((final_value - initial_capital) / initial_capital) * 100 if initial_capital > 0 else 0 gross_return_without_fees = ((value_without_fees - initial_capital) / initial_capital) * 100 if initial_capital > 0 else 0 # Fee impact fee_impact = gross_return_without_fees - net_return # Calculate win rate from trades win_rate = self._calculate_win_rate(trades) return { "initial_capital": float(initial_capital), "final_capital": float(final_value), "total_return": float(net_return), # Net return (after fees) "gross_return": float(gross_return_without_fees), # Gross return (before fees) "total_fees": float(total_fees), "fee_impact_percent": float(fee_impact), "fee_percentage": float((total_fees / initial_capital) * 100) if initial_capital > 0 else 0.0, "total_trades": len(trades), "win_rate": win_rate, "sharpe_ratio": 0.0, # Placeholder - would need returns series "max_drawdown": 0.0, # Placeholder - would need equity curve } def _calculate_win_rate(self, trades: List[Dict[str, Any]]) -> float: """Calculate win rate from trades. Args: trades: List of trades Returns: Win rate (0.0 to 1.0) """ if len(trades) < 2: return 0.0 # Simple approach: count buy-sell pairs # This is a simplified calculation - full implementation would track positions wins = 0 total_pairs = 0 # Group trades by symbol and calculate pairs # For now, return placeholder return 0.5