Browse Source

Remove print_ccxt_positions.py and enhance risk metrics reporting in RiskCommands and StatsCommands

- Deleted the unused print_ccxt_positions.py file.
- Updated RiskCommands to include current drawdown percentage and peak drawdown date in the risk metrics output.
- Modified StatsCommands to reflect similar changes, ensuring consistent reporting of drawdown metrics.
- Improved error handling for date parsing in both commands to enhance robustness.
Carles Sentis 2 days ago
parent
commit
bff7b85864

+ 0 - 6
print_ccxt_positions.py

@@ -1,6 +0,0 @@
-from src.clients.hyperliquid_client import HyperliquidClient
-import pprint
-
-client = HyperliquidClient()
-positions = client.get_positions()
-pprint.pprint(positions) 

+ 12 - 8
src/commands/info/risk.py

@@ -104,20 +104,24 @@ class RiskCommands(InfoCommandsBase):
             return "<b>Risk Metrics:</b> Not available\n"
 
         max_drawdown_pct = risk_metrics.get('max_drawdown_percentage', 0.0)
-        drawdown_start_date_iso = risk_metrics.get('drawdown_start_date')
-        drawdown_date_str = ""
-        if drawdown_start_date_iso:
+        current_drawdown_pct = risk_metrics.get('current_drawdown_percentage', 0.0)
+        peak_timestamp = risk_metrics.get('drawdown_peak_timestamp')
+
+        peak_date_str = ""
+        if peak_timestamp:
             try:
-                drawdown_date = datetime.fromisoformat(drawdown_start_date_iso).strftime('%Y-%m-%d')
-                drawdown_date_str = f" (since {drawdown_date})"
-            except (ValueError, TypeError):
-                logger.warning(f"Could not parse drawdown_start_date: {drawdown_start_date_iso}")
+                # The object from the monitor is already a datetime object
+                peak_date = peak_timestamp.strftime('%Y-%m-%d')
+                peak_date_str = f" (peak on {peak_date})"
+            except (ValueError, TypeError, AttributeError):
+                logger.warning(f"Could not parse peak timestamp: {peak_timestamp}")
 
         sharpe_ratio = risk_metrics.get('sharpe_ratio')
         sharpe_str = f"{sharpe_ratio:.2f}" if sharpe_ratio is not None else "N/A"
 
         return (
             "<b>Risk Metrics:</b>\n"
-            f"• <b>Max Drawdown:</b> {max_drawdown_pct:.2f}%{drawdown_date_str}\n"
+            f"• <b>Max Drawdown:</b> {max_drawdown_pct:.2f}%\n"
+            f"• <b>Current Drawdown:</b> {current_drawdown_pct:.2f}%{peak_date_str}\n"
             f"• <b>Sharpe Ratio:</b> {sharpe_str}\n"
         )

+ 12 - 8
src/commands/info/stats.py

@@ -79,14 +79,17 @@ class StatsCommands(InfoCommandsBase):
 
         # --- Core Metrics ---
         max_drawdown_pct = risk.get('max_drawdown_percentage', 0.0)
-        drawdown_start_date_iso = risk.get('drawdown_start_date')
-        drawdown_date_str = ""
-        if drawdown_start_date_iso:
+        current_drawdown_pct = risk.get('current_drawdown_percentage', 0.0)
+        peak_timestamp = risk.get('drawdown_peak_timestamp')
+        
+        peak_date_str = ""
+        if peak_timestamp:
             try:
-                drawdown_date = datetime.fromisoformat(drawdown_start_date_iso).strftime('%Y-%m-%d')
-                drawdown_date_str = f" (since {drawdown_date})"
-            except (ValueError, TypeError):
-                logger.warning(f"Could not parse drawdown start date: {drawdown_start_date_iso}")
+                # The object from the monitor is already a datetime object
+                peak_date = peak_timestamp.strftime('%Y-%m-%d')
+                peak_date_str = f" (peak on {peak_date})"
+            except (ValueError, TypeError, AttributeError):
+                logger.warning(f"Could not parse peak timestamp: {peak_timestamp}")
 
         sharpe_ratio = risk.get('sharpe_ratio')
         sharpe_str = f"{sharpe_ratio:.2f}" if sharpe_ratio is not None else "N/A"
@@ -101,7 +104,8 @@ class StatsCommands(InfoCommandsBase):
             "\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> {profit_factor_str}",
-            f"• <b>Max Drawdown:</b> {max_drawdown_pct:.2f}%{drawdown_date_str}",
+            f"• <b>Max Drawdown:</b> {max_drawdown_pct:.2f}%",
+            f"• <b>Current Drawdown:</b> {current_drawdown_pct:.2f}%{peak_date_str}",
             f"• <b>Sharpe Ratio:</b> {sharpe_str}",
             f"• <b>Total Trades:</b> {perf.get('total_trades', 0)}",
             f"• <b>Total Volume:</b> {await self.formatter.format_price_with_symbol(perf.get('total_entry_volume', 0.0))}"

+ 26 - 5
src/monitoring/drawdown_monitor.py

@@ -1,6 +1,7 @@
 from __future__ import annotations
 import logging
-from typing import TYPE_CHECKING
+from typing import TYPE_CHECKING, Optional
+from datetime import datetime, timezone
 
 if TYPE_CHECKING:
     from src.stats import TradingStats
@@ -22,7 +23,9 @@ class DrawdownMonitor:
         """
         self.stats = stats
         self.peak_balance = 0.0
+        self.peak_balance_timestamp: Optional[datetime] = None
         self.max_drawdown_pct = 0.0
+        self.current_drawdown_pct = 0.0
         self._load_state()
 
     def _load_state(self):
@@ -30,14 +33,19 @@ class DrawdownMonitor:
         try:
             peak_balance_str = self.stats._get_metadata('drawdown_peak_balance')
             max_drawdown_pct_str = self.stats._get_metadata('drawdown_max_drawdown_pct')
+            peak_balance_timestamp_str = self.stats._get_metadata('drawdown_peak_balance_timestamp')
             
             self.peak_balance = float(peak_balance_str) if peak_balance_str else 0.0
             self.max_drawdown_pct = float(max_drawdown_pct_str) if max_drawdown_pct_str else 0.0
+            if peak_balance_timestamp_str:
+                self.peak_balance_timestamp = datetime.fromisoformat(peak_balance_timestamp_str)
             
             # If peak balance is zero, initialize it with the initial account balance.
             if self.peak_balance == 0.0:
                 initial_balance_str = self.stats._get_metadata('initial_balance')
-                self.peak_balance = float(initial_balance_str) if initial_balance_str else 0.0
+                if initial_balance_str:
+                    self.peak_balance = float(initial_balance_str)
+                    self.peak_balance_timestamp = datetime.now(timezone.utc)
             
             logger.info(f"DrawdownMonitor state loaded: Peak Balance=${self.peak_balance:,.2f}, Max Drawdown={self.max_drawdown_pct:.2f}%")
 
@@ -49,6 +57,8 @@ class DrawdownMonitor:
         try:
             self.stats._set_metadata('drawdown_peak_balance', str(self.peak_balance))
             self.stats._set_metadata('drawdown_max_drawdown_pct', str(self.max_drawdown_pct))
+            if self.peak_balance_timestamp:
+                self.stats._set_metadata('drawdown_peak_balance_timestamp', self.peak_balance_timestamp.isoformat())
             logger.debug("DrawdownMonitor state saved.")
         except Exception as e:
             logger.error(f"Error saving DrawdownMonitor state: {e}", exc_info=True)
@@ -64,13 +74,16 @@ class DrawdownMonitor:
         
         if current_balance > self.peak_balance:
             self.peak_balance = current_balance
+            self.peak_balance_timestamp = datetime.now(timezone.utc)
             state_changed = True
         
         if self.peak_balance > 0:
             drawdown = (self.peak_balance - current_balance) / self.peak_balance
+            self.current_drawdown_pct = drawdown * 100
+            
             # Only update if the new drawdown is significantly larger
-            if (drawdown * 100) > self.max_drawdown_pct + 0.01:
-                self.max_drawdown_pct = drawdown * 100
+            if self.current_drawdown_pct > self.max_drawdown_pct + 0.01:
+                self.max_drawdown_pct = self.current_drawdown_pct
                 state_changed = True
         
         if state_changed:
@@ -78,4 +91,12 @@ class DrawdownMonitor:
 
     def get_max_drawdown(self) -> float:
         """Returns the maximum drawdown percentage."""
-        return self.max_drawdown_pct 
+        return self.max_drawdown_pct
+
+    def get_current_drawdown(self) -> float:
+        """Returns the current drawdown percentage since the last peak."""
+        return self.current_drawdown_pct
+
+    def get_peak_balance_timestamp(self) -> Optional[datetime]:
+        """Returns the timestamp of the last peak balance."""
+        return self.peak_balance_timestamp 

+ 14 - 3
src/stats/trading_stats.py

@@ -18,6 +18,7 @@ from .trade_lifecycle_manager import TradeLifecycleManager
 from .aggregation_manager import AggregationManager
 from .performance_calculator import PerformanceCalculator
 from src.utils.token_display_formatter import get_formatter, normalize_token_case
+from src.monitoring.drawdown_monitor import DrawdownMonitor
 
 logger = logging.getLogger(__name__)
 
@@ -34,6 +35,7 @@ class TradingStats:
         self.trade_manager = TradeLifecycleManager(self.db_manager) 
         self.aggregation_manager = AggregationManager(self.db_manager)
         self.performance_calculator = PerformanceCalculator(self.db_manager)
+        self.drawdown_monitor = DrawdownMonitor(self)
         
         logger.info("🚀 TradingStats initialized with modular components")
 
@@ -375,11 +377,20 @@ class TradingStats:
         return self.performance_calculator.calculate_max_consecutive_losses()
     
     def get_risk_metrics(self) -> Dict[str, Any]:
-        """Get risk metrics."""
-        return self.performance_calculator.get_risk_metrics()
+        """Returns a dictionary of key risk metrics."""
+        base_metrics = self.performance_calculator.get_risk_metrics()
+        
+        # Add live drawdown data from the monitor
+        base_metrics['max_drawdown_percentage'] = self.drawdown_monitor.get_max_drawdown()
+        base_metrics['current_drawdown_percentage'] = self.drawdown_monitor.get_current_drawdown()
+        base_metrics['drawdown_peak_timestamp'] = self.drawdown_monitor.get_peak_balance_timestamp()
+        
+        return base_metrics
     
     def get_period_performance(self, start_date: str, end_date: str) -> Dict[str, Any]:
-        """Get period performance."""
+        """
+        Calculates and returns performance metrics for a specific period.
+        """
         return self.performance_calculator.get_period_performance(start_date, end_date)
     
     def get_recent_performance_trend(self, days: int = 7) -> Dict[str, Any]:

+ 1 - 1
trading_bot.py

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