Browse Source

Refactor trading stats and performance metrics retrieval for improved accuracy

- Updated the `RiskCommands` to fetch basic trading stats, enhancing clarity in the reporting of performance metrics.
- Modified the `_format_trading_stats` method to ensure proper handling of performance metrics, improving the robustness of the output.
- Enhanced the `StatsCommands` to correctly display profit factors and adjust the sorting of top-performing tokens based on realized P&L, ensuring accurate performance representation.
- Introduced additional calculations for average P&L stats in the `PerformanceCalculator`, providing more detailed insights into trading performance.
Carles Sentis 2 days ago
parent
commit
854bf5f22f
4 changed files with 39 additions and 14 deletions
  1. 6 4
      src/commands/info/risk.py
  2. 11 5
      src/commands/info/stats.py
  3. 21 4
      src/stats/performance_calculator.py
  4. 1 1
      trading_bot.py

+ 6 - 4
src/commands/info/risk.py

@@ -33,7 +33,7 @@ class RiskCommands(InfoCommandsBase):
             message.append(await self._format_portfolio_summary(balance_data, positions))
 
             # 3. Trading Stats
-            stats = self.trading_engine.stats.get_trading_stats()
+            stats = self.trading_engine.stats.get_basic_stats()
             message.append(self._format_trading_stats(stats))
 
             # 4. Risk Metrics
@@ -87,13 +87,15 @@ class RiskCommands(InfoCommandsBase):
 
     def _format_trading_stats(self, stats):
         """Formats the trading statistics section."""
-        if not stats:
+        if not stats or 'performance_metrics' not in stats:
             return "<b>Trading Stats:</b> Not available\n"
         
+        perf_metrics = stats['performance_metrics']
+        
         return (
             "<b>Trading Stats:</b>\n"
-            f"• <b>Win Rate:</b> {stats.get('win_rate', 0.0):.2f}%\n"
-            f"• <b>Profit Factor:</b> {stats.get('profit_factor', 0.0):.2f}\n"
+            f"• <b>Win Rate:</b> {perf_metrics.get('win_rate', 0.0):.2f}%\n"
+            f"• <b>Profit Factor:</b> {perf_metrics.get('profit_factor', 0.0):.2f}\n"
         )
 
     def _format_risk_metrics(self, risk_metrics):

+ 11 - 5
src/commands/info/stats.py

@@ -91,10 +91,16 @@ class StatsCommands(InfoCommandsBase):
         sharpe_ratio = risk.get('sharpe_ratio')
         sharpe_str = f"{sharpe_ratio:.2f}" if sharpe_ratio is not None else "N/A"
 
+        profit_factor_val = perf.get('profit_factor')
+        if isinstance(profit_factor_val, (int, float)):
+            profit_factor_str = f"{profit_factor_val:.2f}"
+        else:
+            profit_factor_str = "N/A"
+
         core_metrics = [
             "\n<b>Core Metrics:</b>",
             f"• <b>Win Rate:</b> {perf.get('win_rate', 0.0):.2f}% ({perf.get('total_wins', 0)}W / {perf.get('total_losses', 0)}L)",
-            f"• <b>Profit Factor:</b> {perf.get('profit_factor', 'N/A')}",
+            f"• <b>Profit Factor:</b> {profit_factor_str}",
             f"• <b>Max Drawdown:</b> {max_drawdown_pct:.2f}%{drawdown_date_str}",
             f"• <b>Sharpe Ratio:</b> {sharpe_str}",
             f"• <b>Total Trades:</b> {perf.get('total_trades', 0)}",
@@ -107,8 +113,8 @@ class StatsCommands(InfoCommandsBase):
             f"• <b>Avg Profit per Trade:</b> {await self.formatter.format_price_with_symbol(perf.get('avg_trade_pnl', 0.0))}",
             f"• <b>Avg Winning Trade:</b> {await self.formatter.format_price_with_symbol(perf.get('avg_win_pnl', 0.0))}",
             f"• <b>Avg Losing Trade:</b> {await self.formatter.format_price_with_symbol(perf.get('avg_loss_pnl', 0.0))}",
-            f"• <b>Largest Win:</b> {await self.formatter.format_price_with_symbol(perf.get('largest_win_pnl', 0.0))}",
-            f"• <b>Largest Loss:</b> {await self.formatter.format_price_with_symbol(perf.get('largest_loss_pnl', 0.0))}"
+            f"• <b>Largest Win:</b> {await self.formatter.format_price_with_symbol(perf.get('largest_win', 0.0))}",
+            f"• <b>Largest Loss:</b> {await self.formatter.format_price_with_symbol(perf.get('largest_loss', 0.0))}"
         ]
         
         # --- ROE Analysis (with fixes) ---
@@ -132,9 +138,9 @@ class StatsCommands(InfoCommandsBase):
         top_movers = ["\n<b>Top Performing Tokens (by P&L):</b>"]
         if top_tokens:
             # Sort by total_pnl descending
-            sorted_tokens = sorted(top_tokens, key=lambda x: x.get('total_pnl', 0.0), reverse=True)
+            sorted_tokens = sorted(top_tokens, key=lambda x: x.get('total_realized_pnl', 0.0), reverse=True)
             for token_stat in sorted_tokens[:5]:
-                token_pnl = token_stat.get('total_pnl', 0.0)
+                token_pnl = token_stat.get('total_realized_pnl', 0.0)
                 token_pnl_emoji = "🟢" if token_pnl >= 0 else "🔴"
                 top_movers.append(
                     f"• {token_stat.get('token')}: {token_pnl_emoji} "

+ 21 - 4
src/stats/performance_calculator.py

@@ -42,6 +42,10 @@ class PerformanceCalculator:
     def get_performance_stats(self) -> Dict[str, Any]:
         """Get performance stats."""
         try:
+            # Get initial balance from metadata
+            initial_balance_str = self.db._get_metadata('initial_balance')
+            initial_balance = float(initial_balance_str) if initial_balance_str else 0.0
+
             # Get all token stats
             token_stats = self.db._fetch_query(
                 "SELECT * FROM token_stats", 
@@ -128,15 +132,23 @@ class PerformanceCalculator:
             sum_losing = abs(sum(token.get('sum_of_losing_pnl', 0) for token in token_stats))
             profit_factor = (sum_winning / sum_losing) if sum_losing > 0 else float('inf') if sum_winning > 0 else 0
             
+            # Calculate average P&L stats
+            avg_win_pnl = sum_winning / total_wins if total_wins > 0 else 0
+            avg_loss_pnl = sum_losing / total_losses if total_losses > 0 else 0
+            avg_trade_pnl = total_pnl / total_trades if total_trades > 0 else 0.0
+            
             # Calculate expectancy
-            avg_win = sum_winning / total_wins if total_wins > 0 else 0
-            avg_loss = sum_losing / total_losses if total_losses > 0 else 0
-            expectancy = (avg_win * (win_rate/100)) - (avg_loss * (1 - win_rate/100))
+            expectancy = (avg_win_pnl * (win_rate/100)) - (avg_loss_pnl * (1 - win_rate/100))
             
             # Get max drawdown
             max_drawdown, max_drawdown_pct, drawdown_start_date = self.get_live_max_drawdown()
             
+            # Best/Worst trades by ROE
+            best_roe_trade = self.db._fetchone_query("SELECT token, best_roe_percentage as percentage FROM token_stats WHERE best_roe_percentage IS NOT NULL ORDER BY best_roe_percentage DESC LIMIT 1")
+            worst_roe_trade = self.db._fetchone_query("SELECT token, worst_roe_percentage as percentage FROM token_stats WHERE worst_roe_percentage IS NOT NULL ORDER BY worst_roe_percentage ASC LIMIT 1")
+            
             return {
+                'initial_balance': initial_balance,
                 'total_trades': total_trades,
                 'total_wins': total_wins,
                 'total_losses': total_losses,
@@ -146,6 +158,9 @@ class PerformanceCalculator:
                 'total_exit_volume': total_exit_volume,
                 'profit_factor': profit_factor,
                 'expectancy': expectancy,
+                'avg_trade_pnl': avg_trade_pnl,
+                'avg_win_pnl': avg_win_pnl,
+                'avg_loss_pnl': avg_loss_pnl,
                 'largest_win': largest_win,
                 'largest_loss': largest_loss,
                 'largest_win_token': largest_win_token,
@@ -163,7 +178,9 @@ class PerformanceCalculator:
                 'max_drawdown': max_drawdown,
                 'max_drawdown_pct': max_drawdown_pct,
                 'drawdown_start_date': drawdown_start_date,
-                'open_positions': len(open_positions)
+                'open_positions': len(open_positions),
+                'best_roe_trade': best_roe_trade,
+                'worst_roe_trade': worst_roe_trade
             }
             
         except Exception as e:

+ 1 - 1
trading_bot.py

@@ -14,7 +14,7 @@ from datetime import datetime
 from pathlib import Path
 
 # Bot version
-BOT_VERSION = "2.4.260"
+BOT_VERSION = "2.4.261"
 
 # Add src directory to Python path
 sys.path.insert(0, str(Path(__file__).parent / "src"))