Browse Source

Refactor datetime handling in Telegram bot state management - Introduced a helper function to safely convert datetime objects to ISO strings, improving error handling for invalid datetime values. Updated state saving and trade processing logic to maintain datetime objects for comparisons, enhancing reliability in trade tracking.

Carles Sentis 5 days ago
parent
commit
22125440b9
1 changed files with 29 additions and 11 deletions
  1. 29 11
      src/telegram_bot.py

+ 29 - 11
src/telegram_bot.py

@@ -113,12 +113,29 @@ class TelegramTradingBot:
     def _save_bot_state(self):
     def _save_bot_state(self):
         """Save bot state to disk."""
         """Save bot state to disk."""
         try:
         try:
+            # Helper function to safely convert datetime to ISO string
+            def safe_datetime_to_iso(dt):
+                if dt is None:
+                    return None
+                try:
+                    # Handle both datetime objects and strings
+                    if isinstance(dt, str):
+                        # If it's already a string, validate it's a valid ISO format
+                        datetime.fromisoformat(dt.replace('Z', '+00:00'))
+                        return dt
+                    else:
+                        # Convert datetime object to ISO string
+                        return dt.isoformat()
+                except (ValueError, AttributeError) as e:
+                    logger.warning(f"⚠️ Invalid datetime value, using None: {dt} - {e}")
+                    return None
+            
             state_data = {
             state_data = {
                 'pending_stop_losses': self.pending_stop_losses,
                 'pending_stop_losses': self.pending_stop_losses,
                 'last_known_orders': list(self.last_known_orders),  # Convert set to list for JSON
                 'last_known_orders': list(self.last_known_orders),  # Convert set to list for JSON
                 'last_known_positions': self.last_known_positions,
                 'last_known_positions': self.last_known_positions,
-                'last_processed_trade_time': self.last_processed_trade_time.isoformat() if self.last_processed_trade_time else None,
-                'last_deposit_withdrawal_check': self.last_deposit_withdrawal_check.isoformat() if self.last_deposit_withdrawal_check else None,
+                'last_processed_trade_time': safe_datetime_to_iso(self.last_processed_trade_time),
+                'last_deposit_withdrawal_check': safe_datetime_to_iso(self.last_deposit_withdrawal_check),
                 'last_updated': datetime.now().isoformat(),
                 'last_updated': datetime.now().isoformat(),
                 'version': self.version
                 'version': self.version
             }
             }
@@ -2627,7 +2644,7 @@ This will place a limit {exit_side} order at ${profit_price:,.2f} to capture pro
             # Initialize last processed time if first run
             # Initialize last processed time if first run
             if self.last_processed_trade_time is None:
             if self.last_processed_trade_time is None:
                 # Set to current time minus 1 hour to catch recent activity
                 # Set to current time minus 1 hour to catch recent activity
-                self.last_processed_trade_time = (datetime.now() - timedelta(hours=1)).isoformat()
+                self.last_processed_trade_time = datetime.now() - timedelta(hours=1)
             
             
             # Filter for new trades since last check
             # Filter for new trades since last check
             new_trades = []
             new_trades = []
@@ -2638,18 +2655,19 @@ This will place a limit {exit_side} order at ${profit_price:,.2f} to capture pro
                 if fill_time:
                 if fill_time:
                     # Convert timestamps to comparable format
                     # Convert timestamps to comparable format
                     try:
                     try:
-                        # Convert fill_time to string if it's not already
+                        # Convert fill_time to datetime object for comparison
                         if isinstance(fill_time, (int, float)):
                         if isinstance(fill_time, (int, float)):
                             # Assume it's a unix timestamp
                             # Assume it's a unix timestamp
-                            fill_time_str = datetime.fromtimestamp(fill_time / 1000 if fill_time > 1e10 else fill_time).isoformat()
+                            fill_datetime = datetime.fromtimestamp(fill_time / 1000 if fill_time > 1e10 else fill_time)
                         else:
                         else:
-                            fill_time_str = str(fill_time)
+                            # Try to parse as ISO string
+                            fill_datetime = datetime.fromisoformat(str(fill_time).replace('Z', '+00:00'))
                         
                         
-                        # Compare as strings
-                        if fill_time_str > self.last_processed_trade_time:
+                        # Compare datetime objects
+                        if fill_datetime > self.last_processed_trade_time:
                             new_trades.append(fill)
                             new_trades.append(fill)
-                            if fill_time_str > latest_trade_time:
-                                latest_trade_time = fill_time_str
+                            if fill_datetime > latest_trade_time:
+                                latest_trade_time = fill_datetime
                     except Exception as timestamp_error:
                     except Exception as timestamp_error:
                         logger.warning(f"⚠️ Error processing timestamp {fill_time}: {timestamp_error}")
                         logger.warning(f"⚠️ Error processing timestamp {fill_time}: {timestamp_error}")
                         continue
                         continue
@@ -2661,7 +2679,7 @@ This will place a limit {exit_side} order at ${profit_price:,.2f} to capture pro
             for trade in new_trades:
             for trade in new_trades:
                 await self._process_external_trade(trade)
                 await self._process_external_trade(trade)
             
             
-            # Update last processed time
+            # Update last processed time (keep as datetime object)
             self.last_processed_trade_time = latest_trade_time
             self.last_processed_trade_time = latest_trade_time
             
             
             # Save state after updating last processed time
             # Save state after updating last processed time