Quellcode durchsuchen

Refactor token case normalization across commands and alarm manager - Introduced a new utility function, _normalize_token_case, to standardize token case handling in InfoCommands, ManagementCommands, TradingCommands, AlarmManager, TradingStats, and PriceFormatter. This enhancement improves consistency in token processing, particularly for mixed-case tokens, ensuring accurate command execution and alarm management.

Carles Sentis vor 4 Tagen
Ursprung
Commit
a80a67f780

+ 19 - 4
src/commands/info_commands.py

@@ -4,16 +4,31 @@ Info Commands - Handles information-related Telegram commands.
 """
 
 import logging
-from datetime import datetime
+from datetime import datetime, timezone, timedelta
 from typing import Optional, Dict, Any, List
 from telegram import Update
 from telegram.ext import ContextTypes
+import matplotlib.pyplot as plt
+import matplotlib.dates as mdates
+from io import BytesIO
+import traceback
 
 from src.config.config import Config
 from src.utils.price_formatter import format_price_with_symbol, get_formatter
 
 logger = logging.getLogger(__name__)
 
+def _normalize_token_case(token: str) -> str:
+    """
+    Normalize token case: if any characters are already uppercase, keep as-is.
+    Otherwise, convert to uppercase. This handles mixed-case tokens like kPEPE, kBONK.
+    """
+    # Check if any character is already uppercase
+    if any(c.isupper() for c in token):
+        return token  # Keep original case for mixed-case tokens
+    else:
+        return token.upper()  # Convert to uppercase for all-lowercase input
+
 class InfoCommands:
     """Handles all information-related Telegram commands."""
     
@@ -708,7 +723,7 @@ class InfoCommands:
         
         # Get token from arguments or use default
         if context.args and len(context.args) > 0:
-            token = context.args[0].upper()
+            token = _normalize_token_case(context.args[0])
         else:
             token = Config.DEFAULT_TRADING_TOKEN
         
@@ -774,7 +789,7 @@ class InfoCommands:
         
         # Get token from arguments or use default
         if context.args and len(context.args) > 0:
-            token = context.args[0].upper()
+            token = _normalize_token_case(context.args[0])
         else:
             token = Config.DEFAULT_TRADING_TOKEN
         
@@ -821,7 +836,7 @@ class InfoCommands:
             # Check if specific token is requested
             if context.args and len(context.args) >= 1:
                 # Detailed performance for specific token
-                token = context.args[0].upper()
+                token = _normalize_token_case(context.args[0])
                 await self._show_token_performance(chat_id, token, context)
             else:
                 # Show token performance ranking

+ 13 - 2
src/commands/management_commands.py

@@ -17,6 +17,17 @@ from src.monitoring.alarm_manager import AlarmManager
 
 logger = logging.getLogger(__name__)
 
+def _normalize_token_case(token: str) -> str:
+    """
+    Normalize token case: if any characters are already uppercase, keep as-is.
+    Otherwise, convert to uppercase. This handles mixed-case tokens like kPEPE, kBONK.
+    """
+    # Check if any character is already uppercase
+    if any(c.isupper() for c in token):
+        return token  # Keep original case for mixed-case tokens
+    else:
+        return token.upper()  # Convert to uppercase for all-lowercase input
+
 class ManagementCommands:
     """Handles all management-related Telegram commands."""
     
@@ -137,7 +148,7 @@ class ManagementCommands:
                     return
                 except ValueError:
                     # Not a number, treat as token
-                    token = arg.upper()
+                    token = _normalize_token_case(arg)
                     alarms = self.alarm_manager.get_alarms_by_token(token)
                     message = self.alarm_manager.format_alarm_list(alarms, f"{token} Price Alarms")
                     await context.bot.send_message(chat_id=chat_id, text=message, parse_mode='HTML')
@@ -145,7 +156,7 @@ class ManagementCommands:
             
             elif len(context.args) == 2:
                 # Set new alarm: /alarm TOKEN PRICE
-                token = context.args[0].upper()
+                token = _normalize_token_case(context.args[0])
                 target_price = float(context.args[1])
                 
                 # Get current market price

+ 17 - 6
src/commands/trading_commands.py

@@ -13,6 +13,17 @@ from src.utils.price_formatter import get_formatter
 
 logger = logging.getLogger(__name__)
 
+def _normalize_token_case(token: str) -> str:
+    """
+    Normalize token case: if any characters are already uppercase, keep as-is.
+    Otherwise, convert to uppercase. This handles mixed-case tokens like kPEPE, kBONK.
+    """
+    # Check if any character is already uppercase
+    if any(c.isupper() for c in token):
+        return token  # Keep original case for mixed-case tokens
+    else:
+        return token.upper()  # Convert to uppercase for all-lowercase input
+
 class TradingCommands:
     """Handles all trading-related Telegram commands."""
     
@@ -46,7 +57,7 @@ class TradingCommands:
                 ))
                 return
             
-            token = context.args[0].upper()
+            token = _normalize_token_case(context.args[0])
             usdc_amount = float(context.args[1])
             
             # Parse arguments for price and stop loss
@@ -170,7 +181,7 @@ This will {"place a limit buy order" if limit_price else "execute a market buy o
                 ))
                 return
             
-            token = context.args[0].upper()
+            token = _normalize_token_case(context.args[0])
             usdc_amount = float(context.args[1])
             
             # Parse arguments (similar to long_command)
@@ -288,7 +299,7 @@ This will {"place a limit sell order" if limit_price else "execute a market sell
                 ))
                 return
             
-            token = context.args[0].upper()
+            token = _normalize_token_case(context.args[0])
             
             # Find the position
             position = self.trading_engine.find_position(token)
@@ -365,7 +376,7 @@ This will {"place a limit sell order" if limit_price else "execute a market sell
                 ))
                 return
             
-            token = context.args[0].upper()
+            token = _normalize_token_case(context.args[0])
             stop_price = float(context.args[1])
             
             # Find the position
@@ -469,7 +480,7 @@ This will place a limit {exit_side} order at {formatter.format_price_with_symbol
                 ))
                 return
             
-            token = context.args[0].upper()
+            token = _normalize_token_case(context.args[0])
             tp_price = float(context.args[1])
             
             # Find the position
@@ -573,7 +584,7 @@ This will place a limit {exit_side} order at {formatter.format_price_with_symbol
                 ))
                 return
             
-            token = context.args[0].upper()
+            token = _normalize_token_case(context.args[0])
             
             confirmation_text = f"""
 🚫 <b>Cancel All Orders Confirmation</b>

+ 14 - 3
src/monitoring/alarm_manager.py

@@ -8,11 +8,22 @@ Manages price alarms for tokens with persistence and monitoring integration.
 import json
 import os
 import logging
-from datetime import datetime
+from datetime import datetime, timezone
 from typing import Dict, List, Any, Optional
 
 logger = logging.getLogger(__name__)
 
+def _normalize_token_case(token: str) -> str:
+    """
+    Normalize token case: if any characters are already uppercase, keep as-is.
+    Otherwise, convert to uppercase. This handles mixed-case tokens like kPEPE, kBONK.
+    """
+    # Check if any character is already uppercase
+    if any(c.isupper() for c in token):
+        return token  # Keep original case for mixed-case tokens
+    else:
+        return token.upper()  # Convert to uppercase for all-lowercase input
+
 class AlarmManager:
     """Manages price alarms with persistence and monitoring."""
     
@@ -75,7 +86,7 @@ class AlarmManager:
         
         alarm = {
             'id': self.data['next_id'],
-            'token': token.upper(),
+            'token': _normalize_token_case(token),
             'target_price': target_price,
             'current_price_at_creation': current_price,
             'direction': direction,
@@ -102,7 +113,7 @@ class AlarmManager:
     
     def get_alarms_by_token(self, token: str) -> List[Dict[str, Any]]:
         """Get all active alarms for a specific token."""
-        token = token.upper()
+        token = _normalize_token_case(token)
         return [alarm for alarm in self.data.get('alarms', []) if alarm['token'] == token and alarm['status'] == 'active']
     
     def get_all_active_alarms(self) -> List[Dict[str, Any]]:

+ 13 - 2
src/trading/trading_stats.py

@@ -17,6 +17,17 @@ import uuid
 
 logger = logging.getLogger(__name__)
 
+def _normalize_token_case(token: str) -> str:
+    """
+    Normalize token case: if any characters are already uppercase, keep as-is.
+    Otherwise, convert to uppercase. This handles mixed-case tokens like kPEPE, kBONK.
+    """
+    # Check if any character is already uppercase
+    if any(c.isupper() for c in token):
+        return token  # Keep original case for mixed-case tokens
+    else:
+        return token.upper()  # Convert to uppercase for all-lowercase input
+
 class TradingStats:
     """Comprehensive trading statistics tracker using SQLite."""
 
@@ -713,7 +724,7 @@ class TradingStats:
 
     def get_token_detailed_stats(self, token: str) -> Dict[str, Any]:
         """Get detailed statistics for a specific token using DB queries and cycle calculation."""
-        upper_token = token.upper()
+        upper_token = _normalize_token_case(token)
         
         # Get all trades for this specific token (symbol starts with token + '/')
         # This is simpler than trying to filter cycles by token string directly in SQL for complex symbols
@@ -738,7 +749,7 @@ class TradingStats:
         # We need a version of cycle calculation that can be limited or we filter its output.
         
         all_completed_cycles = self.calculate_completed_trade_cycles()
-        token_cycles = [c for c in all_completed_cycles if c['token'].upper() == upper_token]
+        token_cycles = [c for c in all_completed_cycles if _normalize_token_case(c['token']) == upper_token]
 
         total_individual_orders = len(all_trades_for_token_symbol_prefix)
         buy_orders = len([t for t in all_trades_for_token_symbol_prefix if t['side'] == 'buy'])

+ 11 - 1
src/utils/price_formatter.py

@@ -9,6 +9,16 @@ from functools import lru_cache
 
 logger = logging.getLogger(__name__)
 
+def _normalize_token_case(token: str) -> str:
+    """
+    Normalize token case: if any characters are already uppercase, keep as-is.
+    Otherwise, convert to uppercase. This handles mixed-case tokens like kPEPE, kBONK.
+    """
+    # Check if any character is already uppercase
+    if any(c.isupper() for c in token):
+        return token  # Keep original case for mixed-case tokens
+    else:
+        return token.upper()  # Convert to uppercase for all-lowercase input
 
 class PriceFormatter:
     """Handles price formatting with proper decimal precision from exchange data."""
@@ -101,7 +111,7 @@ class PriceFormatter:
     
     def _get_default_decimals_for_token(self, token: str) -> int:
         """Get smart default decimal places based on token characteristics."""
-        token = token.upper()
+        token = _normalize_token_case(token)
         
         # High-value tokens (usually need fewer decimals)
         if token in ['BTC', 'ETH', 'BNB', 'SOL', 'ADA', 'DOT', 'AVAX', 'MATIC', 'LINK']: