Эх сурвалжийг харах

Update bot version and remove deprecated files for improved project structure.

- Deleted the telegram_bot.py and trading_stats_original_backup.py files, which are no longer needed, streamlining the project structure.
- Enhanced overall maintainability by removing obsolete scripts and ensuring only relevant files are retained.
Carles Sentis 1 долоо хоног өмнө
parent
commit
bc6ff7b2cd

+ 0 - 4637
src/backup/telegram_bot.py

@@ -1,4637 +0,0 @@
-#!/usr/bin/env python3
-"""
-Telegram Bot for Hyperliquid Trading
-
-This module provides a Telegram interface for manual Hyperliquid trading
-with comprehensive statistics tracking and phone-friendly controls.
-"""
-
-import asyncio
-import json
-import os
-from datetime import datetime, timedelta
-from typing import Optional, Dict, Any
-from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup, ReplyKeyboardMarkup, KeyboardButton
-from telegram.ext import Application, CommandHandler, CallbackQueryHandler, ContextTypes, MessageHandler, filters
-from hyperliquid_client import HyperliquidClient
-from src.stats import TradingStats
-from config import Config
-from alarm_manager import AlarmManager
-from logging_config import setup_logging, cleanup_logs, format_log_stats
-
-# Set up logging using the new configuration system
-logger = setup_logging().getChild(__name__)
-
-class TelegramTradingBot:
-    """Telegram trading bot for manual trading operations."""
-    
-    def __init__(self):
-        """Initialize the Telegram trading bot."""
-        self.client = HyperliquidClient()
-        self.application = None
-        self.order_monitoring_task = None
-        self.last_filled_orders = set()
-        self.alarms = []  # List to store price alarms
-        self.bot_heartbeat_seconds = getattr(Config, 'BOT_HEARTBEAT_SECONDS', 10)
-        self.external_trade_timestamps = set()  # Track external trade timestamps to avoid duplicates
-        self.last_position_check = {}  # Track last position state for comparison
-        self.position_tracker = {}  # For enhanced position tracking
-        self.stats = None
-        self.version = "Unknown"  # Will be set by launcher
-        
-        # Bot state persistence file
-        self.bot_state_file = "bot_state.json"
-        
-        # Order monitoring attributes
-        self.monitoring_active = False
-        self.last_known_orders = set()  # Track order IDs we've seen
-        self.last_known_positions = {}  # Track position sizes for P&L calculation
-        
-        # External trade monitoring
-        self.last_processed_trade_time = None  # Track last processed external trade
-        
-        # Deposit/Withdrawal monitoring
-        self.last_deposit_withdrawal_check = None  # Track last deposit/withdrawal check
-        self.deposit_withdrawal_check_interval = 3600  # Check every hour (3600 seconds)
-        
-        # Alarm management
-        self.alarm_manager = AlarmManager()
-        
-        # Pending stop loss storage
-        self.pending_stop_losses = {}  # Format: {order_id: {'token': str, 'stop_price': float, 'side': str, 'amount': float, 'order_type': str}}
-        
-        # Track bot-generated trades to avoid double processing
-        self.bot_trade_ids = set()  # Track trade IDs generated by bot commands
-        
-        # Load bot state first, then initialize stats
-        self._load_bot_state()
-        self._initialize_stats()
-    
-    def _load_bot_state(self):
-        """Load bot state from disk."""
-        try:
-            if os.path.exists(self.bot_state_file):
-                with open(self.bot_state_file, 'r') as f:
-                    state_data = json.load(f)
-                
-                # Restore critical state
-                self.pending_stop_losses = state_data.get('pending_stop_losses', {})
-                self.last_known_orders = set(state_data.get('last_known_orders', []))
-                self.last_known_positions = state_data.get('last_known_positions', {})
-                
-                # Restore bot trade IDs (prevent double processing)
-                self.bot_trade_ids = set(state_data.get('bot_trade_ids', []))
-                
-                # Restore timestamps (convert from ISO string if present)
-                last_trade_time = state_data.get('last_processed_trade_time')
-                if last_trade_time:
-                    try:
-                        self.last_processed_trade_time = datetime.fromisoformat(last_trade_time)
-                    except (ValueError, TypeError):
-                        self.last_processed_trade_time = None
-                
-                last_deposit_check = state_data.get('last_deposit_withdrawal_check')
-                if last_deposit_check:
-                    try:
-                        self.last_deposit_withdrawal_check = datetime.fromisoformat(last_deposit_check)
-                    except (ValueError, TypeError):
-                        self.last_deposit_withdrawal_check = None
-                
-                logger.info(f"🔄 Restored bot state: {len(self.pending_stop_losses)} pending stop losses, {len(self.last_known_orders)} tracked orders")
-                
-                # Log details about restored pending stop losses
-                if self.pending_stop_losses:
-                    for order_id, stop_loss_info in self.pending_stop_losses.items():
-                        token = stop_loss_info.get('token', 'Unknown')
-                        stop_price = stop_loss_info.get('stop_price', 0)
-                        order_type = stop_loss_info.get('order_type', 'Unknown')
-                        logger.info(f"📋 Restored pending stop loss: {order_id} -> {token} {order_type} @ ${stop_price}")
-                        
-        except Exception as e:
-            logger.error(f"❌ Error loading bot state: {e}")
-            # Initialize with defaults
-            self.pending_stop_losses = {}
-            self.last_known_orders = set()
-            self.last_known_positions = {}
-            self.bot_trade_ids = set()
-            self.last_processed_trade_time = None
-            self.last_deposit_withdrawal_check = None
-    
-    def _save_bot_state(self):
-        """Save bot state to disk."""
-        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 = {
-                'pending_stop_losses': self.pending_stop_losses,
-                'last_known_orders': list(self.last_known_orders),  # Convert set to list for JSON
-                'last_known_positions': self.last_known_positions,
-                'bot_trade_ids': list(self.bot_trade_ids),  # Track bot-generated trades
-                '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(),
-                'version': self.version
-            }
-            
-            with open(self.bot_state_file, 'w') as f:
-                json.dump(state_data, f, indent=2, default=str)
-                
-            logger.debug(f"💾 Saved bot state: {len(self.pending_stop_losses)} pending stop losses")
-            
-        except Exception as e:
-            logger.error(f"❌ Error saving bot state: {e}")
-
-    def _initialize_stats(self):
-        """Initialize stats with current balance."""
-        try:
-            # Initialize TradingStats object first
-            self.stats = TradingStats()
-            
-            # Get current balance and set it as initial balance
-            balance = self.client.get_balance()
-            if balance and balance.get('total'):
-                # Get USDC balance as the main balance
-                usdc_balance = float(balance['total'].get('USDC', 0))
-                self.stats.set_initial_balance(usdc_balance)
-        except Exception as e:
-            logger.error(f"Could not initialize stats: {e}")
-    
-    def is_authorized(self, chat_id: str) -> bool:
-        """Check if the chat ID is authorized to use the bot."""
-        return str(chat_id) == str(Config.TELEGRAM_CHAT_ID)
-    
-    async def send_message(self, text: str, parse_mode: str = 'HTML') -> None:
-        """Send a message to the authorized chat."""
-        if self.application and Config.TELEGRAM_CHAT_ID:
-            try:
-                await self.application.bot.send_message(
-                    chat_id=Config.TELEGRAM_CHAT_ID,
-                    text=text,
-                    parse_mode=parse_mode
-                )
-            except Exception as e:
-                logger.error(f"Failed to send message: {e}")
-    
-    def _create_custom_keyboard(self) -> Optional[ReplyKeyboardMarkup]:
-        """Create a custom keyboard from configuration."""
-        if not Config.TELEGRAM_CUSTOM_KEYBOARD_ENABLED:
-            return None
-        
-        try:
-            layout = Config.TELEGRAM_CUSTOM_KEYBOARD_LAYOUT
-            # Parse the layout: "cmd1,cmd2,cmd3|cmd4,cmd5|cmd6,cmd7,cmd8,cmd9"
-            rows = layout.split('|')
-            keyboard = []
-            
-            for row in rows:
-                commands = [cmd.strip() for cmd in row.split(',') if cmd.strip()]
-                if commands:
-                    keyboard.append([KeyboardButton(cmd.lstrip('/').capitalize()) for cmd in commands])
-            
-            if keyboard:
-                return ReplyKeyboardMarkup(
-                    keyboard,
-                    resize_keyboard=True,  # Resize to fit screen
-                    one_time_keyboard=False,  # Keep keyboard persistent
-                    selective=True  # Show only to authorized users
-                )
-        except Exception as e:
-            logger.error(f"Failed to create custom keyboard: {e}")
-        
-        return None
-    
-    async def start_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle the /start command."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        welcome_text = """
-🤖 <b>Welcome to Hyperliquid Trading Bot</b>
-
-📱 <b>Quick Actions:</b>
-• Trading: /long BTC 100 or /short ETH 50
-• Exit: /exit BTC (closes position)
-• Info: /balance, /positions, /orders
-
-📊 <b>Market Data:</b>
-• /market - Detailed market overview
-• /price - Quick price check
-
-<b>⚡ Quick Commands:</b>
-• /balance - Account balance  
-• /positions - Open positions
-• /orders - Active orders
-• /market - Market data & prices
-
-<b>🚀 Trading:</b>
-• /long BTC 100 - Long position
-• /long BTC 100 45000 - Limit order
-• /long BTC 100 sl:44000 - With stop loss
-• /short ETH 50 - Short position  
-• /short ETH 50 3500 sl:3600 - With stop loss
-• /exit BTC - Close position
-• /coo BTC - Cancel open orders
-
-<b>🛡️ Risk Management:</b>
-• Enabled: {risk_enabled}
-• Auto Stop Loss: {stop_loss}%
-• Order Stop Loss: Use sl:price parameter
-• /sl BTC 44000 - Manual stop loss
-• /tp BTC 50000 - Take profit order
-
-<b>📈 Performance & Analytics:</b>
-• /stats - Complete trading statistics
-• /performance - Token performance ranking & detailed stats
-• /daily - Daily performance (last 10 days)
-• /weekly - Weekly performance (last 10 weeks)
-• /monthly - Monthly performance (last 10 months)
-• /risk - Sharpe ratio, drawdown, VaR
-• /version - Bot version & system information
-• /trades - Recent trade history
-
-<b>🔔 Price Alerts:</b>
-• /alarm - List all active alarms
-• /alarm BTC 50000 - Set alarm for BTC at $50,000
-• /alarm BTC - Show all BTC alarms
-• /alarm 3 - Remove alarm ID 3
-
-<b>🔄 Automatic Monitoring:</b>
-• Real-time order fill alerts
-• Position opened/closed notifications  
-• P&L calculations on trade closure
-• Price alarm triggers
-• External trade detection & sync
-• Auto stats synchronization
-• Automatic stop loss placement
-• {heartbeat}-second monitoring interval
-
-<b>📊 Universal Trade Tracking:</b>
-• Bot trades: Full logging & notifications
-• Platform trades: Auto-detected & synced
-• Mobile app trades: Monitored & recorded
-• API trades: Tracked & included in stats
-
-Type /help for detailed command information.
-
-<b>🔄 Order Monitoring:</b>
-• /monitoring - View monitoring status
-• /logs - View log file statistics and cleanup
-
-<b>⚙️ Configuration:</b>
-• Symbol: {symbol}
-• Default Token: {symbol}
-• Network: {network}
-
-<b>🛡️ Safety Features:</b>
-• All trades logged automatically
-• Comprehensive performance tracking
-• Real-time balance monitoring
-• Risk metrics calculation
-• Automatic stop loss protection
-
-<b>📱 Mobile Optimized:</b>
-• Quick action buttons
-• Instant notifications
-• Clean, readable layout
-• One-tap commands
-
-<b>💡 Quick Access:</b>
-• /commands or /c - One-tap button menu for all commands
-• Buttons below for instant access to key functions
-
-For support, contact your bot administrator.
-        """.format(
-            symbol=Config.DEFAULT_TRADING_TOKEN,
-            network="Testnet" if Config.HYPERLIQUID_TESTNET else "Mainnet",
-            risk_enabled=Config.RISK_MANAGEMENT_ENABLED,
-            stop_loss=Config.STOP_LOSS_PERCENTAGE,
-            heartbeat=Config.BOT_HEARTBEAT_SECONDS
-        )
-        
-        keyboard = [
-            [
-                InlineKeyboardButton("💰 Balance", callback_data="balance"),
-                InlineKeyboardButton("📊 Stats", callback_data="stats")
-            ],
-            [
-                InlineKeyboardButton("📈 Positions", callback_data="positions"),
-                InlineKeyboardButton("📋 Orders", callback_data="orders")
-            ],
-            [
-                InlineKeyboardButton("💵 Price", callback_data="price"),
-                InlineKeyboardButton("📊 Market", callback_data="market")
-            ],
-            [
-                InlineKeyboardButton("🔄 Recent Trades", callback_data="trades"),
-                InlineKeyboardButton("⚙️ Help", callback_data="help")
-            ]
-        ]
-        reply_markup = InlineKeyboardMarkup(keyboard)
-        
-        # Create custom keyboard for persistent buttons
-        custom_keyboard = self._create_custom_keyboard()
-        
-        # Send message with inline keyboard
-        await update.message.reply_text(
-            welcome_text, 
-            parse_mode='HTML', 
-            reply_markup=reply_markup
-        )
-        
-        # If custom keyboard is enabled, send a follow-up message to set the custom keyboard
-        if custom_keyboard:
-            await update.message.reply_text(
-                "⌨️ <b>Custom keyboard enabled!</b>\n\nUse the buttons below for quick access to commands:",
-                parse_mode='HTML',
-                reply_markup=custom_keyboard
-            )
-    
-    async def help_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle the /help command."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        help_text = """
-🔧 <b>Hyperliquid Trading Bot - Complete Guide</b>
-
-<b>💼 Account Management:</b>
-• /balance - Show account balance
-• /positions - Show open positions  
-• /orders - Show open orders
-• /balance_adjustments - View deposit/withdrawal history
-
-<b>📊 Market Data:</b>
-• /market - Detailed market data (default token)
-• /market BTC - Market data for specific token
-• /price - Quick price check (default token)
-• /price SOL - Price for specific token
-
-<b>🚀 Perps Trading:</b>
-• /long BTC 100 - Long BTC with $100 USDC (Market Order)
-• /long BTC 100 45000 - Long BTC with $100 USDC at $45,000 (Limit Order)
-• /long BTC 100 sl:44000 - Market order with automatic stop loss
-• /long BTC 100 45000 sl:44000 - Limit order with automatic stop loss
-• /short ETH 50 - Short ETH with $50 USDC (Market Order)
-• /short ETH 50 3500 - Short ETH with $50 USDC at $3,500 (Limit Order)
-• /short ETH 50 sl:3600 - Market order with automatic stop loss
-• /short ETH 50 3500 sl:3600 - Limit order with automatic stop loss
-• /exit BTC - Close BTC position with Market Order
-
-<b>🛡️ Risk Management:</b>
-• /sl BTC 44000 - Set stop loss for BTC at $44,000
-• /tp BTC 50000 - Set take profit for BTC at $50,000
-
-<b>🚨 Automatic Stop Loss:</b>
-• Enabled: {risk_enabled}
-• Stop Loss: {stop_loss}% (automatic execution)
-• Monitoring: Every {heartbeat} seconds
-• Order-based: Use sl:price parameter for automatic placement
-
-<b>📋 Order Management:</b>
-• /orders - Show all open orders
-• /orders BTC - Show open orders for BTC only
-• /coo BTC - Cancel all open orders for BTC
-
-<b>📈 Statistics & Analytics:</b>
-• /stats - Complete trading statistics
-• /performance - Win rate, profit factor, etc.
-• /risk - Sharpe ratio, drawdown, VaR
-• /version - Bot version & system information
-• /trades - Recent trade history
-
-<b>🔔 Price Alerts:</b>
-• /alarm - List all active alarms
-• /alarm BTC 50000 - Set alarm for BTC at $50,000
-• /alarm BTC - Show all BTC alarms
-• /alarm 3 - Remove alarm ID 3
-
-<b>🔄 Order Monitoring:</b>
-• /monitoring - View monitoring status
-• /logs - View log file statistics and cleanup
-
-<b>⚙️ Configuration:</b>
-• Symbol: {symbol}
-• Default Token: {symbol}
-• Network: {network}
-
-<b>🛡️ Safety Features:</b>
-• All trades logged automatically
-• Comprehensive performance tracking
-• Real-time balance monitoring
-• Deposit/withdrawal tracking (hourly)
-• Risk metrics calculation
-• Automatic stop loss placement
-
-<b>📱 Mobile Optimized:</b>
-• Quick action buttons
-• Instant notifications
-• Clean, readable layout
-• One-tap commands
-
-For support, contact your bot administrator.
-        """.format(
-            symbol=Config.DEFAULT_TRADING_TOKEN,
-            network="Testnet" if Config.HYPERLIQUID_TESTNET else "Mainnet",
-            risk_enabled=Config.RISK_MANAGEMENT_ENABLED,
-            stop_loss=Config.STOP_LOSS_PERCENTAGE,
-            heartbeat=Config.BOT_HEARTBEAT_SECONDS
-        )
-        
-        await update.message.reply_text(help_text, parse_mode='HTML')
-
-    async def commands_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle the /commands and /c command with quick action buttons."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        commands_text = """
-📱 <b>Quick Commands</b>
-
-Tap any button below for instant access to bot functions:
-
-💡 <b>Pro Tip:</b> These buttons work the same as typing the commands manually, but faster!
-        """
-        
-        keyboard = [
-            [
-                InlineKeyboardButton("💰 Balance", callback_data="balance"),
-                InlineKeyboardButton("📈 Positions", callback_data="positions")
-            ],
-            [
-                InlineKeyboardButton("📋 Orders", callback_data="orders"),
-                InlineKeyboardButton("📊 Stats", callback_data="stats")
-            ],
-            [
-                InlineKeyboardButton("💵 Price", callback_data="price"),
-                InlineKeyboardButton("📊 Market", callback_data="market")
-            ],
-            [
-                InlineKeyboardButton("🏆 Performance", callback_data="performance"),
-                InlineKeyboardButton("🔔 Alarms", callback_data="alarm")
-            ],
-            [
-                InlineKeyboardButton("📅 Daily", callback_data="daily"),
-                InlineKeyboardButton("📊 Weekly", callback_data="weekly")
-            ],
-            [
-                InlineKeyboardButton("📆 Monthly", callback_data="monthly"),
-                InlineKeyboardButton("🔄 Trades", callback_data="trades")
-            ],
-            [
-                InlineKeyboardButton("🔄 Monitoring", callback_data="monitoring"),
-                InlineKeyboardButton("📝 Logs", callback_data="logs")
-            ],
-            [
-                InlineKeyboardButton("⚙️ Help", callback_data="help")
-            ]
-        ]
-        reply_markup = InlineKeyboardMarkup(keyboard)
-        
-        await update.message.reply_text(commands_text, parse_mode='HTML', reply_markup=reply_markup)
-    
-    async def stats_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle the /stats command."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        # Get current balance for stats
-        balance = self.client.get_balance()
-        current_balance = 0
-        if balance and balance.get('total'):
-            current_balance = float(balance['total'].get('USDC', 0))
-        
-        stats_message = self.stats.format_stats_message(current_balance)
-        await update.message.reply_text(stats_message, parse_mode='HTML')
-    
-    async def trades_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle the /trades command."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        recent_trades = self.stats.get_recent_trades(10)
-        
-        if not recent_trades:
-            await update.message.reply_text("📝 No trades recorded yet.")
-            return
-        
-        trades_text = "🔄 <b>Recent Trades</b>\n\n"
-        
-        for trade in reversed(recent_trades[-5:]):  # Show last 5 trades
-            timestamp = datetime.fromisoformat(trade['timestamp']).strftime('%m/%d %H:%M')
-            side_emoji = "🟢" if trade['side'] == 'buy' else "🔴"
-            
-            trades_text += f"{side_emoji} <b>{trade['side'].upper()}</b> {trade['amount']} {trade['symbol']}\n"
-            trades_text += f"   💰 ${trade['price']:,.2f} | 💵 ${trade['value']:,.2f}\n"
-            trades_text += f"   📅 {timestamp}\n\n"
-        
-        await update.message.reply_text(trades_text, parse_mode='HTML')
-    
-    async def balance_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle the /balance command."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        balance = self.client.get_balance()
-        if balance:
-            balance_text = "💰 <b>Account Balance</b>\n\n"
-            
-            # Debug: Show raw balance structure (can be removed after debugging)
-            logger.debug(f"Raw balance data: {balance}")
-            
-            # CCXT balance structure includes 'free', 'used', and 'total'
-            total_balance = balance.get('total', {})
-            free_balance = balance.get('free', {})
-            used_balance = balance.get('used', {})
-            
-            if total_balance:
-                total_value = 0
-                available_value = 0
-                
-                # Display individual assets
-                for asset, amount in total_balance.items():
-                    if float(amount) > 0:
-                        free_amount = float(free_balance.get(asset, 0))
-                        used_amount = float(used_balance.get(asset, 0))
-                        
-                        balance_text += f"💵 <b>{asset}:</b>\n"
-                        balance_text += f"   📊 Total: {amount}\n"
-                        balance_text += f"   ✅ Available: {free_amount}\n"
-                        
-                        if used_amount > 0:
-                            balance_text += f"   🔒 In Use: {used_amount}\n"
-                        
-                        balance_text += "\n"
-                        
-                        # Calculate totals (convert all to USDC equivalent for summary)
-                        if asset == 'USDC':
-                            total_value += float(amount)
-                            available_value += free_amount
-                        else:
-                            # For non-USDC assets, add to totals (assuming 1:1 for now, could be enhanced with price conversion)
-                            total_value += float(amount)
-                            available_value += free_amount
-                
-                # Summary section
-                balance_text += f"💼 <b>Portfolio Summary:</b>\n"
-                balance_text += f"   💰 Total Value: ${total_value:,.2f}\n"
-                balance_text += f"   🚀 Available for Trading: ${available_value:,.2f}\n"
-                
-                if total_value - available_value > 0:
-                    balance_text += f"   🔒 In Active Use: ${total_value - available_value:,.2f}\n"
-                
-                # Add P&L summary
-                basic_stats = self.stats.get_basic_stats()
-                if basic_stats['initial_balance'] > 0:
-                    # Use USDC balance for P&L calculation
-                    usdc_total = float(total_balance.get('USDC', 0))
-                    pnl = usdc_total - basic_stats['initial_balance']
-                    pnl_percent = (pnl / basic_stats['initial_balance']) * 100
-                    
-                    balance_text += f"\n📊 <b>Performance (USDC):</b>\n"
-                    balance_text += f"   💵 P&L: ${pnl:,.2f} ({pnl_percent:+.2f}%)\n"
-                    balance_text += f"   📈 Initial: ${basic_stats['initial_balance']:,.2f}"
-            else:
-                balance_text += "📭 No balance data available"
-        else:
-            balance_text = "❌ Could not fetch balance data"
-        
-        await update.message.reply_text(balance_text, parse_mode='HTML')
-    
-    async def positions_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle the /positions command."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        positions = self.client.get_positions()
-        
-        if positions is not None:  # Successfully fetched (could be empty list)
-            # Filter for actual open positions
-            open_positions = [p for p in positions if float(p.get('contracts', 0)) != 0]
-            
-            # Add position count to header
-            position_count = len(open_positions)
-            positions_text = f"📈 <b>Open Positions ({position_count})</b>\n\n"
-            
-            if open_positions:
-                total_unrealized = 0
-                total_position_value = 0
-                
-                for position in open_positions:
-                    symbol = position.get('symbol', 'Unknown')
-                    contracts = float(position.get('contracts', 0))
-                    unrealized_pnl = float(position.get('unrealizedPnl', 0))
-                    
-                    # Use the correct field name for entry price
-                    entry_price = float(position.get('entryPrice', 0))  # Changed from 'entryPx' to 'entryPrice'
-                    
-                    # Try to get position value from notional first, then calculate if not available
-                    notional = position.get('notional')
-                    if notional is not None:
-                        position_value = float(notional)
-                    else:
-                        # Fallback calculation
-                        position_value = abs(contracts) * entry_price
-                    
-                    # Calculate P&L percentage based on position value
-                    pnl_percentage = (unrealized_pnl / position_value * 100) if position_value > 0 else 0
-                    
-                    pnl_emoji = "🟢" if unrealized_pnl >= 0 else "🔴"
-                    
-                    # Extract token name for cleaner display
-                    token = symbol.split('/')[0] if '/' in symbol else symbol
-                    
-                    # Use CCXT's side field if available, otherwise fall back to contracts sign
-                    side_field = position.get('side', '').lower()
-                    if side_field in ['long', 'short']:
-                        position_type = side_field.upper()
-                    else:
-                        # Fallback for exchanges that don't provide side field
-                        position_type = "LONG" if contracts > 0 else "SHORT"
-                    
-                    positions_text += f"📊 <b>{token}</b> ({position_type})\n"
-                    positions_text += f"   📏 Size: {abs(contracts):.6f} {token}\n"
-                    positions_text += f"   💰 Entry: ${entry_price:,.2f}\n"
-                    positions_text += f"   💵 Value: ${position_value:,.2f}\n"
-                    positions_text += f"   {pnl_emoji} P&L: ${unrealized_pnl:,.2f} ({pnl_percentage:+.2f}%)\n\n"
-                    
-                    total_unrealized += unrealized_pnl
-                    total_position_value += position_value
-                
-                # Calculate overall P&L percentage
-                total_pnl_percentage = (total_unrealized / total_position_value * 100) if total_position_value > 0 else 0
-                total_pnl_emoji = "🟢" if total_unrealized >= 0 else "🔴"
-                
-                positions_text += f"💼 <b>Total Portfolio:</b>\n"
-                positions_text += f"   💵 Total Value: ${total_position_value:,.2f}\n"
-                positions_text += f"   {total_pnl_emoji} Total P&L: ${total_unrealized:,.2f} ({total_pnl_percentage:+.2f}%)"
-            else:
-                positions_text += "📭 <b>No open positions currently</b>\n\n"
-                positions_text += "🚀 Ready to start trading!\n"
-                positions_text += "Use /long or /short commands to open positions."
-        else:
-            # Actual API error
-            positions_text = "❌ <b>Could not fetch positions data</b>\n\n"
-            positions_text += "🔄 Please try again in a moment.\n"
-            positions_text += "If the issue persists, check your connection."
-        
-        await update.message.reply_text(positions_text, parse_mode='HTML')
-    
-    async def orders_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle the /orders command with optional token filter."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        # Check if token filter is provided
-        token_filter = None
-        if context.args and len(context.args) >= 1:
-            token_filter = context.args[0].upper()
-        
-        orders = self.client.get_open_orders()
-        
-        # Debug: Log what we got from orders
-        logger.debug(f"Raw orders data: {orders}")
-        logger.debug(f"Orders type: {type(orders)}, Length: {len(orders) if orders else 'None'}")
-        
-        if orders is not None:  # Successfully fetched (could be empty list)
-            if token_filter:
-                # Filter orders for specific token
-                target_symbol = f"{token_filter}/USDC:USDC"
-                filtered_orders = [order for order in orders if order.get('symbol') == target_symbol]
-                orders_text = f"📋 <b>Open Orders - {token_filter} ({len(filtered_orders)})</b>\n\n"
-            else:
-                filtered_orders = orders
-                orders_text = f"📋 <b>All Open Orders ({len(orders)})</b>\n\n"
-            
-            if filtered_orders and len(filtered_orders) > 0:
-                for order in filtered_orders:
-                    symbol = order.get('symbol', 'Unknown')
-                    side = order.get('side', 'Unknown')
-                    amount = order.get('amount', 0)
-                    price = order.get('price', 0)
-                    order_id = order.get('id', 'Unknown')
-                    
-                    # Extract token from symbol for display
-                    token = symbol.split('/')[0] if '/' in symbol else symbol
-                    
-                    side_emoji = "🟢" if side.lower() == 'buy' else "🔴"
-                    
-                    orders_text += f"{side_emoji} <b>{token}</b>\n"
-                    orders_text += f"   📊 {side.upper()} {amount} @ ${price:,.2f}\n"
-                    orders_text += f"   💵 Value: ${float(amount) * float(price):,.2f}\n"
-                    orders_text += f"   🔑 ID: <code>{order_id}</code>\n\n"
-                
-                # Add helpful commands
-                if token_filter:
-                    orders_text += f"💡 <b>Quick Actions:</b>\n"
-                    orders_text += f"• <code>/coo {token_filter}</code> - Cancel all {token_filter} orders\n"
-                    orders_text += f"• <code>/orders</code> - View all orders"
-                else:
-                    orders_text += f"💡 <b>Filter by token:</b> <code>/orders BTC</code>, <code>/orders ETH</code>"
-            else:
-                if token_filter:
-                    orders_text += f"📭 <b>No open orders for {token_filter}</b>\n\n"
-                    orders_text += f"💡 No pending {token_filter} orders found.\n"
-                    orders_text += f"Use <code>/long {token_filter} 100</code> or <code>/short {token_filter} 100</code> to create new orders."
-                else:
-                    orders_text += "📭 <b>No open orders currently</b>\n\n"
-                    orders_text += "💡 All clear! No pending orders.\n"
-                    orders_text += "Use /long or /short commands to place new orders."
-        else:
-            # Actual API error
-            orders_text = "❌ <b>Could not fetch orders data</b>\n\n"
-            orders_text += "🔄 Please try again in a moment.\n"
-            orders_text += "If the issue persists, check your connection."
-        
-        await update.message.reply_text(orders_text, parse_mode='HTML')
-    
-    async def market_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle the /market command."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        # Check if token is provided as argument
-        if context.args and len(context.args) >= 1:
-            token = context.args[0].upper()
-        else:
-            token = Config.DEFAULT_TRADING_TOKEN
-        
-        # Convert token to full symbol format for API
-        symbol = f"{token}/USDC:USDC"
-        market_data = self.client.get_market_data(symbol)
-        
-        if market_data and market_data.get('ticker'):
-            try:
-                ticker = market_data['ticker']
-                orderbook = market_data.get('orderbook', {})
-                
-                # Safely extract ticker data with fallbacks
-                current_price = float(ticker.get('last') or 0)
-                high_24h = float(ticker.get('high') or 0)
-                low_24h = float(ticker.get('low') or 0)
-                volume_24h = ticker.get('baseVolume') or ticker.get('volume') or 'N/A'
-                
-                market_text = f"📊 <b>Market Data - {token}</b>\n\n"
-                
-                if current_price > 0:
-                    market_text += f"💵 <b>Current Price:</b> ${current_price:,.2f}\n"
-                else:
-                    market_text += f"💵 <b>Current Price:</b> N/A\n"
-                
-                if high_24h > 0:
-                    market_text += f"📈 <b>24h High:</b> ${high_24h:,.2f}\n"
-                else:
-                    market_text += f"📈 <b>24h High:</b> N/A\n"
-                
-                if low_24h > 0:
-                    market_text += f"📉 <b>24h Low:</b> ${low_24h:,.2f}\n"
-                else:
-                    market_text += f"📉 <b>24h Low:</b> N/A\n"
-                
-                market_text += f"📊 <b>24h Volume:</b> {volume_24h}\n\n"
-                
-                # Handle orderbook data safely
-                if orderbook and orderbook.get('bids') and orderbook.get('asks'):
-                    try:
-                        bids = orderbook.get('bids', [])
-                        asks = orderbook.get('asks', [])
-                        
-                        if bids and asks and len(bids) > 0 and len(asks) > 0:
-                            best_bid = float(bids[0][0]) if bids[0][0] else 0
-                            best_ask = float(asks[0][0]) if asks[0][0] else 0
-                            
-                            if best_bid > 0 and best_ask > 0:
-                                spread = best_ask - best_bid
-                                spread_percent = (spread / best_ask * 100) if best_ask > 0 else 0
-                                
-                                market_text += f"🟢 <b>Best Bid:</b> ${best_bid:,.2f}\n"
-                                market_text += f"🔴 <b>Best Ask:</b> ${best_ask:,.2f}\n"
-                                market_text += f"📏 <b>Spread:</b> ${spread:.2f} ({spread_percent:.3f}%)\n"
-                            else:
-                                market_text += f"📋 <b>Orderbook:</b> Data unavailable\n"
-                        else:
-                            market_text += f"📋 <b>Orderbook:</b> No orders available\n"
-                    except (IndexError, ValueError, TypeError) as e:
-                        market_text += f"📋 <b>Orderbook:</b> Error parsing data\n"
-                else:
-                    market_text += f"📋 <b>Orderbook:</b> Not available\n"
-                
-                # Add usage hint
-                market_text += f"\n💡 <b>Usage:</b> <code>/market {token}</code> or <code>/market</code> for default"
-                
-            except (ValueError, TypeError) as e:
-                market_text = f"❌ <b>Error parsing market data</b>\n\n"
-                market_text += f"🔧 Raw data received but couldn't parse values.\n"
-                market_text += f"📞 Please try again or contact support if this persists."
-        else:
-            market_text = f"❌ <b>Could not fetch market data for {token}</b>\n\n"
-            market_text += f"🔄 Please try again in a moment.\n"
-            market_text += f"🌐 Check your network connection.\n"
-            market_text += f"📡 API may be temporarily unavailable.\n\n"
-            market_text += f"💡 <b>Usage:</b> <code>/market BTC</code>, <code>/market ETH</code>, <code>/market SOL</code>, etc."
-        
-        await update.message.reply_text(market_text, parse_mode='HTML')
-    
-    async def price_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle the /price command."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        # Check if token is provided as argument
-        if context.args and len(context.args) >= 1:
-            token = context.args[0].upper()
-        else:
-            token = Config.DEFAULT_TRADING_TOKEN
-        
-        # Convert token to full symbol format for API
-        symbol = f"{token}/USDC:USDC"
-        market_data = self.client.get_market_data(symbol)
-        
-        if market_data and market_data.get('ticker'):
-            try:
-                ticker = market_data['ticker']
-                price_value = ticker.get('last')
-                
-                if price_value is not None:
-                    price = float(price_value)
-                    price_text = f"💵 <b>{token}</b>: ${price:,.2f}"
-                    
-                    # Add timestamp
-                    timestamp = datetime.now().strftime('%H:%M:%S')
-                    price_text += f"\n⏰ <i>Updated: {timestamp}</i>"
-                    
-                    # Add usage hint
-                    price_text += f"\n💡 <i>Usage: </i><code>/price {symbol}</code><i> or </i><code>/price</code><i> for default</i>"
-                else:
-                    price_text = f"💵 <b>{symbol}</b>: Price not available\n⚠️ <i>Data temporarily unavailable</i>"
-                    
-            except (ValueError, TypeError) as e:
-                price_text = f"❌ <b>Error parsing price for {symbol}</b>\n🔧 <i>Please try again</i>"
-        else:
-            price_text = f"❌ <b>Could not fetch price for {symbol}</b>\n🔄 <i>Please try again in a moment</i>\n\n"
-            price_text += f"💡 <b>Usage:</b> <code>/price BTC</code>, <code>/price ETH</code>, <code>/price SOL</code>, etc."
-        
-        await update.message.reply_text(price_text, parse_mode='HTML')
-    
-    async def button_callback(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle inline keyboard button presses."""
-        query = update.callback_query
-        await query.answer()
-        
-        if not self.is_authorized(query.message.chat_id):
-            await query.edit_message_text("❌ Unauthorized access.")
-            return
-        
-        callback_data = query.data
-        
-        # Handle trading confirmations
-        if callback_data.startswith('confirm_long_'):
-            parts = callback_data.split('_')
-            token = parts[2]
-            usdc_amount = float(parts[3])
-            try:
-                price = float(parts[4])
-            except (ValueError, TypeError):
-                price = None  # Will be handled in execute_long_order
-            is_limit = len(parts) > 5 and parts[5] == 'limit'
-            
-            # Parse stop loss if present
-            stop_loss_price = None
-            if len(parts) > 6 and parts[6] == 'sl':
-                try:
-                    stop_loss_price = float(parts[7])
-                except (ValueError, TypeError):
-                    stop_loss_price = None
-            elif len(parts) > 5 and parts[5] == 'sl':
-                try:
-                    stop_loss_price = float(parts[6])
-                except (ValueError, TypeError):
-                    stop_loss_price = None
-            
-            await self._execute_long_order(query, token, usdc_amount, price, is_limit, stop_loss_price)
-            return
-            
-        elif callback_data.startswith('confirm_short_'):
-            parts = callback_data.split('_')
-            token = parts[2]
-            usdc_amount = float(parts[3])
-            try:
-                price = float(parts[4])
-            except (ValueError, TypeError):
-                price = None  # Will be handled in execute_short_order
-            is_limit = len(parts) > 5 and parts[5] == 'limit'
-            
-            # Parse stop loss if present
-            stop_loss_price = None
-            if len(parts) > 6 and parts[6] == 'sl':
-                try:
-                    stop_loss_price = float(parts[7])
-                except (ValueError, TypeError):
-                    stop_loss_price = None
-            elif len(parts) > 5 and parts[5] == 'sl':
-                try:
-                    stop_loss_price = float(parts[6])
-                except (ValueError, TypeError):
-                    stop_loss_price = None
-            
-            await self._execute_short_order(query, token, usdc_amount, price, is_limit, stop_loss_price)
-            return
-            
-        elif callback_data.startswith('confirm_exit_'):
-            parts = callback_data.split('_')
-            token = parts[2]
-            exit_side = parts[3]
-            contracts = float(parts[4])
-            price = float(parts[5])
-            await self._execute_exit_order(query, token, exit_side, contracts, price)
-            return
-            
-        elif callback_data.startswith('confirm_coo_'):
-            parts = callback_data.split('_')
-            token = parts[2]
-            await self._execute_coo(query, token)
-            return
-            
-        elif callback_data.startswith('confirm_sl_'):
-            parts = callback_data.split('_')
-            token = parts[2]
-            exit_side = parts[3]
-            contracts = float(parts[4])
-            price = float(parts[5])
-            await self._execute_sl_order(query, token, exit_side, contracts, price)
-            return
-            
-        elif callback_data.startswith('confirm_tp_'):
-            parts = callback_data.split('_')
-            token = parts[2]
-            exit_side = parts[3]
-            contracts = float(parts[4])
-            price = float(parts[5])
-            await self._execute_tp_order(query, token, exit_side, contracts, price)
-            return
-            
-        elif callback_data == 'cancel_order':
-            await query.edit_message_text("❌ Order cancelled.")
-            return
-        
-        # Create a fake update object for reusing command handlers
-        fake_update = Update(
-            update_id=update.update_id,
-            message=query.message,
-            callback_query=query
-        )
-        
-        # Handle regular button callbacks
-        if callback_data == "balance":
-            await self.balance_command(fake_update, context)
-        elif callback_data == "stats":
-            await self.stats_command(fake_update, context)
-        elif callback_data == "positions":
-            await self.positions_command(fake_update, context)
-        elif callback_data == "orders":
-            await self.orders_command(fake_update, context)
-        elif callback_data == "market":
-            await self.market_command(fake_update, context)
-        elif callback_data == "price":
-            await self.price_command(fake_update, context)
-        elif callback_data == "trades":
-            await self.trades_command(fake_update, context)
-        elif callback_data == "help":
-            await self.help_command(fake_update, context)
-        elif callback_data == "performance":
-            await self.performance_command(fake_update, context)
-        elif callback_data == "alarm":
-            await self.alarm_command(fake_update, context)
-        elif callback_data == "daily":
-            await self.daily_command(fake_update, context)
-        elif callback_data == "weekly":
-            await self.weekly_command(fake_update, context)
-        elif callback_data == "monthly":
-            await self.monthly_command(fake_update, context)
-        elif callback_data == "monitoring":
-            await self.monitoring_command(fake_update, context)
-        elif callback_data == "logs":
-            await self.logs_command(fake_update, context)
-    
-    async def _execute_long_order(self, query, token: str, usdc_amount: float, price: float, is_limit: bool, stop_loss_price: float = None):
-        """Execute a long order."""
-        symbol = f"{token}/USDC:USDC"
-        
-        try:
-            await query.edit_message_text("⏳ Opening long position...")
-            
-            # Validate price
-            if price is None or price <= 0:
-                # Try to get current market price
-                market_data = self.client.get_market_data(symbol)
-                if market_data and market_data.get('ticker'):
-                    price = float(market_data['ticker'].get('last', 0))
-                    if price <= 0:
-                        await query.edit_message_text("❌ Unable to get valid market price. Please try again.")
-                        return
-                else:
-                    await query.edit_message_text("❌ Unable to fetch market price. Please try again.")
-                    return
-            
-            # Calculate token amount based on USDC value and price
-            token_amount = usdc_amount / price
-            
-            # Place order (limit or market)
-            if is_limit:
-                order = self.client.place_limit_order(symbol, 'buy', token_amount, price)
-            else:
-                order = self.client.place_market_order(symbol, 'buy', token_amount)
-            
-            if order:
-                # Record the trade in stats
-                order_id = order.get('id', 'N/A')
-                actual_price = order.get('average', price)  # Use actual fill price if available
-                if actual_price is None or actual_price <= 0:
-                    # This should not happen due to our price validation above, but extra safety
-                    actual_price = price
-                action_type = self.stats.record_trade_with_enhanced_tracking(symbol, 'buy', token_amount, actual_price, order_id, "bot")
-                
-                # Track this as a bot-generated trade to prevent double processing
-                if order_id and order_id != 'N/A':
-                    self.bot_trade_ids.add(order_id)
-                    self._save_bot_state()  # Save state to persist bot trade tracking
-                    logger.info(f"🤖 Tracked bot trade ID: {order_id}")
-                
-                # Save pending stop loss if provided
-                if stop_loss_price is not None:
-                    self.pending_stop_losses[order_id] = {
-                        'token': token,
-                        'symbol': symbol,
-                        'stop_price': stop_loss_price,
-                        'side': 'sell',  # For long position, stop loss is a sell order
-                        'amount': token_amount,
-                        'order_type': 'long',
-                        'original_order_id': order_id,
-                        'is_limit': is_limit
-                    }
-                    self._save_bot_state()  # Save state after adding pending stop loss
-                    logger.info(f"💾 Saved pending stop loss for order {order_id}: sell {token_amount:.6f} {token} @ ${stop_loss_price}")
-                
-                success_message = f"""
-✅ <b>Long Position {'Placed' if is_limit else 'Opened'} Successfully!</b>
-
-📊 <b>Order Details:</b>
-• Token: {token}
-• Direction: LONG (Buy)
-• Amount: {token_amount:.6f} {token}
-• Price: ${price:,.2f}
-• USDC Value: ~${usdc_amount:,.2f}
-• Order Type: {'Limit' if is_limit else 'Market'} Order
-• Order ID: <code>{order_id}</code>
-"""
-                
-                # Add stop loss confirmation if provided
-                if stop_loss_price is not None:
-                    success_message += f"""
-🛑 <b>Stop Loss Saved:</b>
-• Stop Price: ${stop_loss_price:,.2f}
-• Will be placed automatically when order fills
-• Status: PENDING ⏳
-"""
-                
-                success_message += f"\n🚀 Your {'limit order has been placed' if is_limit else 'long position is now active'}!"
-                
-                await query.edit_message_text(success_message, parse_mode='HTML')
-                logger.info(f"Long {'limit order placed' if is_limit else 'position opened'}: {token_amount:.6f} {token} @ ${price} ({'Limit' if is_limit else 'Market'})")
-            else:
-                await query.edit_message_text(f"❌ Failed to {'place limit order' if is_limit else 'open long position'}. Please try again.")
-                
-        except Exception as e:
-            error_message = f"❌ Error {'placing limit order' if is_limit else 'opening long position'}: {str(e)}"
-            await query.edit_message_text(error_message)
-            logger.error(f"Error in long order: {e}")
-    
-    async def _execute_short_order(self, query, token: str, usdc_amount: float, price: float, is_limit: bool, stop_loss_price: float = None):
-        """Execute a short order."""
-        symbol = f"{token}/USDC:USDC"
-        
-        try:
-            await query.edit_message_text("⏳ Opening short position...")
-            
-            # Validate price
-            if price is None or price <= 0:
-                # Try to get current market price
-                market_data = self.client.get_market_data(symbol)
-                if market_data and market_data.get('ticker'):
-                    price = float(market_data['ticker'].get('last', 0))
-                    if price <= 0:
-                        await query.edit_message_text("❌ Unable to get valid market price. Please try again.")
-                        return
-                else:
-                    await query.edit_message_text("❌ Unable to fetch market price. Please try again.")
-                    return
-            
-            # Calculate token amount based on USDC value and price
-            token_amount = usdc_amount / price
-            
-            # Place order (limit or market)
-            if is_limit:
-                order = self.client.place_limit_order(symbol, 'sell', token_amount, price)
-            else:
-                order = self.client.place_market_order(symbol, 'sell', token_amount)
-            
-            if order:
-                # Record the trade in stats
-                order_id = order.get('id', 'N/A')
-                actual_price = order.get('average', price)  # Use actual fill price if available
-                if actual_price is None or actual_price <= 0:
-                    # This should not happen due to our price validation above, but extra safety
-                    actual_price = price
-                action_type = self.stats.record_trade_with_enhanced_tracking(symbol, 'sell', token_amount, actual_price, order_id, "bot")
-                
-                # Track this as a bot-generated trade to prevent double processing
-                if order_id and order_id != 'N/A':
-                    self.bot_trade_ids.add(order_id)
-                    self._save_bot_state()  # Save state to persist bot trade tracking
-                    logger.info(f"🤖 Tracked bot trade ID: {order_id}")
-                
-                # Save pending stop loss if provided
-                if stop_loss_price is not None:
-                    self.pending_stop_losses[order_id] = {
-                        'token': token,
-                        'symbol': symbol,
-                        'stop_price': stop_loss_price,
-                        'side': 'buy',  # For short position, stop loss is a buy order
-                        'amount': token_amount,
-                        'order_type': 'short',
-                        'original_order_id': order_id,
-                        'is_limit': is_limit
-                    }
-                    self._save_bot_state()  # Save state after adding pending stop loss
-                    logger.info(f"💾 Saved pending stop loss for order {order_id}: buy {token_amount:.6f} {token} @ ${stop_loss_price}")
-                
-                success_message = f"""
-✅ <b>Short Position {'Placed' if is_limit else 'Opened'} Successfully!</b>
-
-📊 <b>Order Details:</b>
-• Token: {token}
-• Direction: SHORT (Sell)
-• Amount: {token_amount:.6f} {token}
-• Price: ${price:,.2f}
-• USDC Value: ~${usdc_amount:,.2f}
-• Order Type: {'Limit' if is_limit else 'Market'} Order
-• Order ID: <code>{order_id}</code>
-"""
-                
-                # Add stop loss confirmation if provided
-                if stop_loss_price is not None:
-                    success_message += f"""
-🛑 <b>Stop Loss Saved:</b>
-• Stop Price: ${stop_loss_price:,.2f}
-• Will be placed automatically when order fills
-• Status: PENDING ⏳
-"""
-                
-                success_message += f"\n📉 Your {'limit order has been placed' if is_limit else 'short position is now active'}!"
-                
-                await query.edit_message_text(success_message, parse_mode='HTML')
-                logger.info(f"Short {'limit order placed' if is_limit else 'position opened'}: {token_amount:.6f} {token} @ ${price} ({'Limit' if is_limit else 'Market'})")
-            else:
-                await query.edit_message_text(f"❌ Failed to {'place limit order' if is_limit else 'open short position'}. Please try again.")
-                
-        except Exception as e:
-            error_message = f"❌ Error {'placing limit order' if is_limit else 'opening short position'}: {str(e)}"
-            await query.edit_message_text(error_message)
-            logger.error(f"Error in short order: {e}")
-    
-    async def _execute_exit_order(self, query, token: str, exit_side: str, contracts: float, price: float):
-        """Execute an exit order."""
-        symbol = f"{token}/USDC:USDC"
-        
-        try:
-            await query.edit_message_text("⏳ Closing position...")
-            
-            # Place market order to close position
-            order = self.client.place_market_order(symbol, exit_side, contracts)
-            
-            if order:
-                # Record the trade in stats
-                order_id = order.get('id', 'N/A')
-                actual_price = order.get('average', price)  # Use actual fill price if available
-                if actual_price is None or actual_price <= 0:
-                    # Fallback to ensure we have a valid price
-                    actual_price = price
-                action_type = self.stats.record_trade_with_enhanced_tracking(symbol, exit_side, contracts, actual_price, order_id, "bot")
-                
-                # Track this as a bot-generated trade to prevent double processing
-                if order_id and order_id != 'N/A':
-                    self.bot_trade_ids.add(order_id)
-                    self._save_bot_state()  # Save state to persist bot trade tracking
-                    logger.info(f"🤖 Tracked bot trade ID: {order_id}")
-                
-                position_type = "LONG" if exit_side == "sell" else "SHORT"
-                
-                success_message = f"""
-✅ <b>Position Closed Successfully!</b>
-
-📊 <b>Exit Details:</b>
-• Token: {token}
-• Position Closed: {position_type}
-• Exit Side: {exit_side.upper()}
-• Amount: {contracts} {token}
-• Est. Price: ~${price:,.2f}
-• Order Type: Market Order
-• Order ID: <code>{order_id}</code>
-
-🎯 <b>Position Summary:</b>
-• Status: CLOSED
-• Exit Value: ~${contracts * price:,.2f}
-
-📊 Use /stats to see updated performance metrics.
-                """
-                
-                await query.edit_message_text(success_message, parse_mode='HTML')
-                logger.info(f"Position closed: {exit_side} {contracts} {token} @ ~${price}")
-            else:
-                await query.edit_message_text("❌ Failed to close position. Please try again.")
-                
-        except Exception as e:
-            error_message = f"❌ Error closing position: {str(e)}"
-            await query.edit_message_text(error_message)
-            logger.error(f"Error closing position: {e}")
-    
-    async def _execute_coo(self, query, token: str):
-        """Execute cancel open orders for a specific token."""
-        symbol = f"{token}/USDC:USDC"
-        
-        try:
-            await query.edit_message_text("⏳ Cancelling all orders...")
-            
-            # Get current orders for this token
-            all_orders = self.client.get_open_orders()
-            if all_orders is None:
-                await query.edit_message_text(f"❌ Could not fetch orders to cancel {token} orders")
-                return
-            
-            # Filter orders for the specific token
-            token_orders = [order for order in all_orders if order.get('symbol') == symbol]
-            
-            if not token_orders:
-                await query.edit_message_text(f"📭 No open orders found for {token}")
-                return
-            
-            # Cancel each order
-            cancelled_orders = []
-            failed_orders = []
-            
-            for order in token_orders:
-                order_id = order.get('id')
-                if order_id:
-                    try:
-                        success = self.client.cancel_order(order_id, symbol)
-                        if success:
-                            cancelled_orders.append(order)
-                        else:
-                            failed_orders.append(order)
-                    except Exception as e:
-                        logger.error(f"Failed to cancel order {order_id}: {e}")
-                        failed_orders.append(order)
-            
-            # Create result message
-            result_message = f"""
-✅ <b>Cancel Orders Results</b>
-
-📊 <b>Summary:</b>
-• Token: {token}
-• Cancelled: {len(cancelled_orders)} orders
-• Failed: {len(failed_orders)} orders
-• Total Attempted: {len(token_orders)} orders
-"""
-            
-            if cancelled_orders:
-                result_message += f"\n🗑️ <b>Successfully Cancelled:</b>\n"
-                for order in cancelled_orders:
-                    side = order.get('side', 'Unknown')
-                    amount = order.get('amount', 0)
-                    price = order.get('price', 0)
-                    side_emoji = "🟢" if side.lower() == 'buy' else "🔴"
-                    result_message += f"{side_emoji} {side.upper()} {amount} @ ${price:,.2f}\n"
-            
-            if failed_orders:
-                result_message += f"\n❌ <b>Failed to Cancel:</b>\n"
-                for order in failed_orders:
-                    side = order.get('side', 'Unknown')
-                    amount = order.get('amount', 0)
-                    price = order.get('price', 0)
-                    order_id = order.get('id', 'Unknown')
-                    side_emoji = "🟢" if side.lower() == 'buy' else "🔴"
-                    result_message += f"{side_emoji} {side.upper()} {amount} @ ${price:,.2f} (ID: {order_id})\n"
-            
-            if len(cancelled_orders) == len(token_orders):
-                result_message += f"\n🎉 All {token} orders successfully cancelled!"
-            elif len(cancelled_orders) > 0:
-                result_message += f"\n⚠️ Some orders cancelled. Check failed orders above."
-            else:
-                result_message += f"\n❌ Could not cancel any {token} orders."
-            
-            await query.edit_message_text(result_message, parse_mode='HTML')
-            logger.info(f"COO executed for {token}: {len(cancelled_orders)}/{len(token_orders)} orders cancelled")
-            
-        except Exception as e:
-            error_message = f"❌ Error cancelling {token} orders: {str(e)}"
-            await query.edit_message_text(error_message)
-            logger.error(f"Error in COO execution: {e}")
-    
-    async def _execute_sl_order(self, query, token: str, exit_side: str, contracts: float, price: float):
-        """Execute a stop loss order."""
-        symbol = f"{token}/USDC:USDC"
-        
-        try:
-            await query.edit_message_text("⏳ Setting stop loss...")
-            
-            # Place stop loss order
-            order = self.client.place_stop_loss_order(symbol, exit_side, contracts, price)
-            
-            if order:
-                # Record the trade in stats
-                order_id = order.get('id', 'N/A')
-                actual_price = order.get('average', price)  # Use actual fill price if available
-                action_type = self.stats.record_trade_with_enhanced_tracking(symbol, exit_side, contracts, actual_price, order_id, "bot")
-                
-                position_type = "LONG" if exit_side == "sell" else "SHORT"
-                
-                success_message = f"""
-✅ <b>Stop Loss Order Set Successfully!</b>
-
-📊 <b>Stop Loss Details:</b>
-• Token: {token}
-• Position: {position_type}
-• Size: {contracts} contracts
-• Stop Price: ${price:,.2f}
-• Action: {exit_side.upper()} (Close {position_type})
-• Amount: {contracts} {token}
-• Order Type: Limit Order
-• Order ID: <code>{order_id}</code>
-
-🎯 <b>Stop Loss Execution:</b>
-• Status: SET
-• Exit Value: ~${contracts * price:,.2f}
-
-📊 Use /stats to see updated performance metrics.
-                """
-                
-                await query.edit_message_text(success_message, parse_mode='HTML')
-                logger.info(f"Stop loss set: {exit_side} {contracts} {token} @ ${price}")
-            else:
-                await query.edit_message_text("❌ Failed to set stop loss. Please try again.")
-                
-        except Exception as e:
-            error_message = f"❌ Error setting stop loss: {str(e)}"
-            await query.edit_message_text(error_message)
-            logger.error(f"Error setting stop loss: {e}")
-    
-    async def _execute_tp_order(self, query, token: str, exit_side: str, contracts: float, price: float):
-        """Execute a take profit order."""
-        symbol = f"{token}/USDC:USDC"
-        
-        try:
-            await query.edit_message_text("⏳ Setting take profit...")
-            
-            # Place take profit order
-            order = self.client.place_take_profit_order(symbol, exit_side, contracts, price)
-            
-            if order:
-                # Record the trade in stats
-                order_id = order.get('id', 'N/A')
-                actual_price = order.get('average', price)  # Use actual fill price if available
-                if actual_price is None or actual_price <= 0:
-                    # Fallback to ensure we have a valid price
-                    actual_price = price
-                action_type = self.stats.record_trade_with_enhanced_tracking(symbol, exit_side, contracts, actual_price, order_id, "bot")
-                
-                position_type = "LONG" if exit_side == "sell" else "SHORT"
-                
-                success_message = f"""
-✅ <b>Take Profit Order Set Successfully!</b>
-
-📊 <b>Take Profit Details:</b>
-• Token: {token}
-• Position: {position_type}
-• Size: {contracts} contracts
-• Target Price: ${price:,.2f}
-• Action: {exit_side.upper()} (Close {position_type})
-• Amount: {contracts} {token}
-• Order Type: Limit Order
-• Order ID: <code>{order_id}</code>
-
-🎯 <b>Take Profit Execution:</b>
-• Status: SET
-• Exit Value: ~${contracts * price:,.2f}
-
-📊 Use /stats to see updated performance metrics.
-                """
-                
-                await query.edit_message_text(success_message, parse_mode='HTML')
-                logger.info(f"Take profit set: {exit_side} {contracts} {token} @ ${price}")
-            else:
-                await query.edit_message_text("❌ Failed to set take profit. Please try again.")
-                
-        except Exception as e:
-            error_message = f"❌ Error setting take profit: {str(e)}"
-            await query.edit_message_text(error_message)
-            logger.error(f"Error setting take profit: {e}")
-    
-    async def handle_keyboard_message(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle messages from custom keyboard buttons."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        message_text = update.message.text.lower()
-        
-        # Map keyboard button text to commands
-        command_map = {
-            'balance': '/balance',
-            'positions': '/positions',
-            'orders': '/orders',
-            'stats': '/stats',
-            'trades': '/trades',
-            'market': '/market',
-            'price': '/price',
-            'help': '/help',
-            'commands': '/commands',
-            'monitoring': '/monitoring',
-            'logs': '/logs',
-            'performance': '/performance',
-            'daily': '/daily',
-            'weekly': '/weekly',
-            'monthly': '/monthly',
-            'risk': '/risk',
-            'alarm': '/alarm',
-            'keyboard': '/keyboard'
-        }
-        
-        # Check if the message matches any keyboard command
-        if message_text in command_map:
-            # Create a fake update object with the corresponding command
-            update.message.text = command_map[message_text]
-            # Get the handler for this command and call it
-            handlers = self.application.handlers[0]  # Get default group handlers
-            for handler in handlers:
-                if hasattr(handler, 'callback') and hasattr(handler, 'filters'):
-                    if await handler.check_update(update):
-                        await handler.callback(update, context)
-                        return
-        
-        # If no keyboard command matched, show a help message
-        await update.message.reply_text("❓ Unknown command. Use /help to see available commands.")
-
-    async def unknown_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle unknown commands."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        await update.message.reply_text(
-            "❓ Unknown command. Use /help to see available commands or tap the buttons in /start."
-        )
-    
-    def setup_handlers(self):
-        """Set up command handlers for the bot."""
-        if not self.application:
-            return
-        
-        # Command handlers
-        self.application.add_handler(CommandHandler("start", self.start_command))
-        self.application.add_handler(CommandHandler("help", self.help_command))
-        self.application.add_handler(CommandHandler("commands", self.commands_command))
-        self.application.add_handler(CommandHandler("c", self.commands_command))
-        self.application.add_handler(CommandHandler("balance", self.balance_command))
-        self.application.add_handler(CommandHandler("positions", self.positions_command))
-        self.application.add_handler(CommandHandler("orders", self.orders_command))
-        self.application.add_handler(CommandHandler("market", self.market_command))
-        self.application.add_handler(CommandHandler("price", self.price_command))
-        self.application.add_handler(CommandHandler("stats", self.stats_command))
-        self.application.add_handler(CommandHandler("trades", self.trades_command))
-        self.application.add_handler(CommandHandler("long", self.long_command))
-        self.application.add_handler(CommandHandler("short", self.short_command))
-        self.application.add_handler(CommandHandler("exit", self.exit_command))
-        self.application.add_handler(CommandHandler("coo", self.coo_command))
-        self.application.add_handler(CommandHandler("sl", self.sl_command))
-        self.application.add_handler(CommandHandler("tp", self.tp_command))
-        self.application.add_handler(CommandHandler("monitoring", self.monitoring_command))
-        self.application.add_handler(CommandHandler("alarm", self.alarm_command))
-        self.application.add_handler(CommandHandler("logs", self.logs_command))
-        self.application.add_handler(CommandHandler("performance", self.performance_command))
-        self.application.add_handler(CommandHandler("daily", self.daily_command))
-        self.application.add_handler(CommandHandler("weekly", self.weekly_command))
-        self.application.add_handler(CommandHandler("monthly", self.monthly_command))
-        self.application.add_handler(CommandHandler("risk", self.risk_command))
-        self.application.add_handler(CommandHandler("version", self.version_command))
-        self.application.add_handler(CommandHandler("balance_adjustments", self.balance_adjustments_command))
-        self.application.add_handler(CommandHandler("keyboard", self.keyboard_command))
-        self.application.add_handler(CommandHandler("debug", self.debug_command))
-        
-        # Callback query handler for inline keyboards
-        self.application.add_handler(CallbackQueryHandler(self.button_callback))
-        
-        # Handle clean keyboard button messages (without /)
-        self.application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, self.handle_keyboard_message))
-        
-        # Handle unknown commands
-        self.application.add_handler(MessageHandler(filters.COMMAND, self.unknown_command))
-    
-    async def run(self):
-        """Run the Telegram bot."""
-        if not Config.TELEGRAM_BOT_TOKEN:
-            logger.error("❌ TELEGRAM_BOT_TOKEN not configured")
-            return
-        
-        if not Config.TELEGRAM_CHAT_ID:
-            logger.error("❌ TELEGRAM_CHAT_ID not configured")
-            return
-        
-        try:
-            # Create application
-            self.application = Application.builder().token(Config.TELEGRAM_BOT_TOKEN).build()
-            
-            # Set up handlers
-            self.setup_handlers()
-            
-            logger.info("🚀 Starting Telegram trading bot...")
-            
-            # Initialize the application
-            await self.application.initialize()
-            
-            # Send startup notification
-            await self.send_message(
-                f"🤖 <b>Manual Trading Bot v{self.version} Started</b>\n\n"
-                f"✅ Connected to Hyperliquid {'Testnet' if Config.HYPERLIQUID_TESTNET else 'Mainnet'}\n"
-                f"📊 Default Symbol: {Config.DEFAULT_TRADING_TOKEN}\n"
-                f"📱 Manual trading ready!\n"
-                f"🔄 Order monitoring: Active ({Config.BOT_HEARTBEAT_SECONDS}s interval)\n"
-                f"🔄 External trade monitoring: Active\n"
-                f"🔔 Price alarms: Active\n"
-                f"📊 Auto stats sync: Enabled\n"
-                f"📝 Logging: {'File + Console' if Config.LOG_TO_FILE else 'Console only'}\n"
-                f"⏰ Started at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n"
-                "Use /start for quick actions or /help for all commands."
-            )
-            
-            # Perform initial log cleanup
-            try:
-                cleanup_logs(days_to_keep=30)
-                logger.info("🧹 Initial log cleanup completed")
-            except Exception as e:
-                logger.warning(f"⚠️ Initial log cleanup failed: {e}")
-            
-            # Start the application
-            await self.application.start()
-            
-            # Start order monitoring
-            await self.start_order_monitoring()
-            
-            # Start polling for updates manually
-            logger.info("🔄 Starting update polling...")
-            
-            # Get updates in a loop
-            last_update_id = 0
-            while True:
-                try:
-                    # Get updates from Telegram
-                    updates = await self.application.bot.get_updates(
-                        offset=last_update_id + 1,
-                        timeout=30,
-                        allowed_updates=None
-                    )
-                    
-                    # Process each update
-                    for update in updates:
-                        last_update_id = update.update_id
-                        
-                        # Process the update through the application
-                        await self.application.process_update(update)
-                        
-                except Exception as e:
-                    logger.error(f"Error processing updates: {e}")
-                    await asyncio.sleep(5)  # Wait before retrying
-                    
-        except asyncio.CancelledError:
-            logger.info("🛑 Bot polling cancelled")
-            raise
-            
-        except Exception as e:
-            logger.error(f"❌ Error in telegram bot: {e}")
-            raise
-            
-        finally:
-            # Clean shutdown
-            try:
-                await self.stop_order_monitoring()
-                if self.application:
-                    await self.application.stop()
-                    await self.application.shutdown()
-            except Exception as e:
-                logger.error(f"Error during shutdown: {e}")
-
-    async def long_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle the /long command for opening long positions."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        try:
-            if not context.args or len(context.args) < 2:
-                await update.message.reply_text(
-                    "❌ Usage: /long [token] [USDC amount] [price (optional)] [sl:price (optional)]\n"
-                    "Examples:\n"
-                    "• /long BTC 100 - Market order\n"
-                    "• /long BTC 100 45000 - Limit order at $45,000\n"
-                    "• /long BTC 100 sl:44000 - Market order with stop loss at $44,000\n"
-                    "• /long BTC 100 45000 sl:44000 - Limit order at $45,000 with stop loss at $44,000"
-                )
-                return
-            
-            token = context.args[0].upper()
-            usdc_amount = float(context.args[1])
-            
-            # Parse arguments for price and stop loss
-            limit_price = None
-            stop_loss_price = None
-            
-            # Parse remaining arguments
-            for i, arg in enumerate(context.args[2:], 2):
-                if arg.startswith('sl:'):
-                    # Stop loss parameter
-                    try:
-                        stop_loss_price = float(arg[3:])  # Remove 'sl:' prefix
-                    except ValueError:
-                        await update.message.reply_text("❌ Invalid stop loss price format. Use sl:price (e.g., sl:44000)")
-                        return
-                elif limit_price is None:
-                    # First non-sl parameter is the limit price
-                    try:
-                        limit_price = float(arg)
-                    except ValueError:
-                        await update.message.reply_text("❌ Invalid limit price format. Please use numbers only.")
-                        return
-            
-            # Determine order type
-            if limit_price:
-                order_type = "Limit"
-                order_description = f"at ${limit_price:,.2f}"
-            else:
-                order_type = "Market"
-                order_description = "at current market price"
-            
-            # Convert token to full symbol format for Hyperliquid
-            symbol = f"{token}/USDC:USDC"
-            
-            # Get current market price to calculate amount and for display
-            market_data = self.client.get_market_data(symbol)
-            if not market_data:
-                await update.message.reply_text(f"❌ Could not fetch price for {token}")
-                return
-            
-            current_price = float(market_data['ticker'].get('last', 0))
-            if current_price <= 0:
-                await update.message.reply_text(f"❌ Invalid price for {token}")
-                return
-            
-            # Validate stop loss price for long positions
-            if stop_loss_price is not None:
-                entry_price = limit_price if limit_price else current_price
-                if stop_loss_price >= entry_price:
-                    await update.message.reply_text(
-                        f"❌ Stop loss price should be BELOW entry price for long positions\n\n"
-                        f"📊 Entry Price: ${entry_price:,.2f}\n"
-                        f"🛑 Stop Loss: ${stop_loss_price:,.2f} ❌\n\n"
-                        f"💡 Try a lower price like: /long {token} {usdc_amount} {f'{limit_price} ' if limit_price else ''}sl:{entry_price * 0.95:.0f}"
-                    )
-                    return
-            
-            # Calculate token amount based on price (market or limit)
-            calculation_price = limit_price if limit_price else current_price
-            token_amount = usdc_amount / calculation_price
-            
-            # Create confirmation message
-            confirmation_text = f"""
-🟢 <b>Long Position Confirmation</b>
-
-📊 <b>Order Details:</b>
-• Token: {token}
-• Direction: LONG (Buy)
-• USDC Value: ${usdc_amount:,.2f}
-• Current Price: ${current_price:,.2f}
-• Order Type: {order_type} Order
-• Token Amount: {token_amount:.6f} {token}
-
-🎯 <b>Execution:</b>
-• Will buy {token_amount:.6f} {token} {order_description}
-• Est. Value: ${token_amount * calculation_price:,.2f}
-"""
-            
-            # Add stop loss information if provided
-            if stop_loss_price is not None:
-                confirmation_text += f"""
-🛑 <b>Stop Loss:</b>
-• Stop Price: ${stop_loss_price:,.2f}
-• Will be placed automatically after order fills
-• Protection Level: {((calculation_price - stop_loss_price) / calculation_price * 100):.1f}% below entry
-"""
-            
-            confirmation_text += "\n⚠️ <b>Are you sure you want to open this long position?</b>"
-            
-            # Use limit_price for callback if provided, otherwise current_price
-            callback_price = limit_price if limit_price else current_price
-            callback_data = f"confirm_long_{token}_{usdc_amount}_{callback_price}"
-            if limit_price:
-                callback_data += "_limit"
-            if stop_loss_price is not None:
-                callback_data += f"_sl_{stop_loss_price}"
-            
-            keyboard = [
-                [
-                    InlineKeyboardButton("✅ Confirm Long", callback_data=callback_data),
-                    InlineKeyboardButton("❌ Cancel", callback_data="cancel_order")
-                ]
-            ]
-            reply_markup = InlineKeyboardMarkup(keyboard)
-            
-            await update.message.reply_text(confirmation_text, parse_mode='HTML', reply_markup=reply_markup)
-            
-        except ValueError:
-            await update.message.reply_text("❌ Invalid USDC amount or price. Please use numbers only.")
-        except Exception as e:
-            await update.message.reply_text(f"❌ Error processing long command: {e}")
-    
-    async def short_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle the /short command for opening short positions."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        try:
-            if not context.args or len(context.args) < 2:
-                await update.message.reply_text(
-                    "❌ Usage: /short [token] [USDC amount] [price (optional)] [sl:price (optional)]\n"
-                    "Examples:\n"
-                    "• /short BTC 100 - Market order\n"
-                    "• /short BTC 100 46000 - Limit order at $46,000\n"
-                    "• /short BTC 100 sl:47000 - Market order with stop loss at $47,000\n"
-                    "• /short BTC 100 46000 sl:47000 - Limit order at $46,000 with stop loss at $47,000"
-                )
-                return
-            
-            token = context.args[0].upper()
-            usdc_amount = float(context.args[1])
-            
-            # Parse arguments for price and stop loss
-            limit_price = None
-            stop_loss_price = None
-            
-            # Parse remaining arguments
-            for i, arg in enumerate(context.args[2:], 2):
-                if arg.startswith('sl:'):
-                    # Stop loss parameter
-                    try:
-                        stop_loss_price = float(arg[3:])  # Remove 'sl:' prefix
-                    except ValueError:
-                        await update.message.reply_text("❌ Invalid stop loss price format. Use sl:price (e.g., sl:47000)")
-                        return
-                elif limit_price is None:
-                    # First non-sl parameter is the limit price
-                    try:
-                        limit_price = float(arg)
-                    except ValueError:
-                        await update.message.reply_text("❌ Invalid limit price format. Please use numbers only.")
-                        return
-            
-            # Determine order type
-            if limit_price:
-                order_type = "Limit"
-                order_description = f"at ${limit_price:,.2f}"
-            else:
-                order_type = "Market"
-                order_description = "at current market price"
-            
-            # Convert token to full symbol format for Hyperliquid
-            symbol = f"{token}/USDC:USDC"
-            
-            # Get current market price to calculate amount and for display
-            market_data = self.client.get_market_data(symbol)
-            if not market_data:
-                await update.message.reply_text(f"❌ Could not fetch price for {token}")
-                return
-            
-            current_price = float(market_data['ticker'].get('last', 0))
-            if current_price <= 0:
-                await update.message.reply_text(f"❌ Invalid price for {token}")
-                return
-            
-            # Validate stop loss price for short positions
-            if stop_loss_price is not None:
-                entry_price = limit_price if limit_price else current_price
-                if stop_loss_price <= entry_price:
-                    await update.message.reply_text(
-                        f"❌ Stop loss price should be ABOVE entry price for short positions\n\n"
-                        f"📊 Entry Price: ${entry_price:,.2f}\n"
-                        f"🛑 Stop Loss: ${stop_loss_price:,.2f} ❌\n\n"
-                        f"💡 Try a higher price like: /short {token} {usdc_amount} {f'{limit_price} ' if limit_price else ''}sl:{entry_price * 1.05:.0f}"
-                    )
-                    return
-            
-            # Calculate token amount based on price (market or limit)
-            calculation_price = limit_price if limit_price else current_price
-            token_amount = usdc_amount / calculation_price
-            
-            # Create confirmation message
-            confirmation_text = f"""
-🔴 <b>Short Position Confirmation</b>
-
-📊 <b>Order Details:</b>
-• Token: {token}
-• Direction: SHORT (Sell)
-• USDC Value: ${usdc_amount:,.2f}
-• Current Price: ${current_price:,.2f}
-• Order Type: {order_type} Order
-• Token Amount: {token_amount:.6f} {token}
-
-🎯 <b>Execution:</b>
-• Will sell {token_amount:.6f} {token} {order_description}
-• Est. Value: ${token_amount * calculation_price:,.2f}
-"""
-            
-            # Add stop loss information if provided
-            if stop_loss_price is not None:
-                confirmation_text += f"""
-🛑 <b>Stop Loss:</b>
-• Stop Price: ${stop_loss_price:,.2f}
-• Will be placed automatically after order fills
-• Protection Level: {((stop_loss_price - calculation_price) / calculation_price * 100):.1f}% above entry
-"""
-            
-            confirmation_text += "\n⚠️ <b>Are you sure you want to open this short position?</b>"
-            
-            # Use limit_price for callback if provided, otherwise current_price
-            callback_price = limit_price if limit_price else current_price
-            callback_data = f"confirm_short_{token}_{usdc_amount}_{callback_price}"
-            if limit_price:
-                callback_data += "_limit"
-            if stop_loss_price is not None:
-                callback_data += f"_sl_{stop_loss_price}"
-            
-            keyboard = [
-                [
-                    InlineKeyboardButton("✅ Confirm Short", callback_data=callback_data),
-                    InlineKeyboardButton("❌ Cancel", callback_data="cancel_order")
-                ]
-            ]
-            reply_markup = InlineKeyboardMarkup(keyboard)
-            
-            await update.message.reply_text(confirmation_text, parse_mode='HTML', reply_markup=reply_markup)
-            
-        except ValueError:
-            await update.message.reply_text("❌ Invalid USDC amount or price. Please use numbers only.")
-        except Exception as e:
-            await update.message.reply_text(f"❌ Error processing short command: {e}")
-    
-    async def exit_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle the /exit command for closing positions."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        try:
-            if not context.args or len(context.args) < 1:
-                await update.message.reply_text(
-                    "❌ Usage: /exit [token]\n"
-                    "Example: /exit BTC"
-                )
-                return
-            
-            token = context.args[0].upper()
-            symbol = f"{token}/USDC:USDC"
-            
-            # Get current positions to find the position for this token
-            positions = self.client.get_positions()
-            if positions is None:
-                await update.message.reply_text(f"❌ Could not fetch positions to check {token} position")
-                return
-            
-            # Find the position for this token
-            current_position = None
-            for position in positions:
-                if position.get('symbol') == symbol and float(position.get('contracts', 0)) != 0:
-                    current_position = position
-                    break
-            
-            if not current_position:
-                await update.message.reply_text(f"📭 No open position found for {token}")
-                return
-            
-            # Extract position details
-            contracts = float(current_position.get('contracts', 0))
-            entry_price = float(current_position.get('entryPx', 0))
-            unrealized_pnl = float(current_position.get('unrealizedPnl', 0))
-            
-            # Determine position direction and exit details
-            if contracts > 0:
-                position_type = "LONG"
-                exit_side = "sell"
-                exit_emoji = "🔴"
-            else:
-                position_type = "SHORT"
-                exit_side = "buy"
-                exit_emoji = "🟢"
-                contracts = abs(contracts)  # Make positive for display
-            
-            # Get current market price
-            market_data = self.client.get_market_data(symbol)
-            if not market_data:
-                await update.message.reply_text(f"❌ Could not fetch current price for {token}")
-                return
-            
-            current_price = float(market_data['ticker'].get('last', 0))
-            if current_price <= 0:
-                await update.message.reply_text(f"❌ Invalid current price for {token}")
-                return
-            
-            # Calculate estimated exit value
-            exit_value = contracts * current_price
-            
-            # Create confirmation message
-            pnl_emoji = "🟢" if unrealized_pnl >= 0 else "🔴"
-            
-            confirmation_text = f"""
-{exit_emoji} <b>Exit Position Confirmation</b>
-
-📊 <b>Position Details:</b>
-• Token: {token}
-• Position: {position_type}
-• Size: {contracts} contracts
-• Entry Price: ${entry_price:,.2f}
-• Current Price: ${current_price:,.2f}
-• {pnl_emoji} Unrealized P&L: ${unrealized_pnl:,.2f}
-
-🎯 <b>Exit Order:</b>
-• Action: {exit_side.upper()} (Close {position_type})
-• Amount: {contracts} {token}
-• Est. Value: ~${exit_value:,.2f}
-• Order Type: Market Order
-
-⚠️ <b>Are you sure you want to close this {position_type} position?</b>
-
-This will place a market {exit_side} order to close your entire {token} position.
-            """
-            
-            keyboard = [
-                [
-                    InlineKeyboardButton(f"✅ Close {position_type}", callback_data=f"confirm_exit_{token}_{exit_side}_{contracts}_{current_price}"),
-                    InlineKeyboardButton("❌ Cancel", callback_data="cancel_order")
-                ]
-            ]
-            reply_markup = InlineKeyboardMarkup(keyboard)
-            
-            await update.message.reply_text(confirmation_text, parse_mode='HTML', reply_markup=reply_markup)
-            
-        except ValueError:
-            await update.message.reply_text("❌ Invalid token format. Please use token symbols like BTC, ETH, etc.")
-        except Exception as e:
-            await update.message.reply_text(f"❌ Error processing exit command: {e}")
-
-    async def coo_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle the /coo (cancel open orders) command for a specific token."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        try:
-            if not context.args or len(context.args) < 1:
-                await update.message.reply_text(
-                    "❌ Usage: /coo [token]\n"
-                    "Example: /coo BTC\n\n"
-                    "This command cancels ALL open orders for the specified token."
-                )
-                return
-            
-            token = context.args[0].upper()
-            symbol = f"{token}/USDC:USDC"
-            
-            # Get current orders for this token
-            all_orders = self.client.get_open_orders()
-            if all_orders is None:
-                await update.message.reply_text(f"❌ Could not fetch orders to cancel {token} orders")
-                return
-            
-            # Filter orders for the specific token
-            token_orders = [order for order in all_orders if order.get('symbol') == symbol]
-            
-            if not token_orders:
-                await update.message.reply_text(f"📭 No open orders found for {token}")
-                return
-            
-            # Create confirmation message with order details
-            confirmation_text = f"""
-⚠️ <b>Cancel All {token} Orders</b>
-
-📋 <b>Orders to Cancel:</b>
-"""
-            
-            total_value = 0
-            for order in token_orders:
-                side = order.get('side', 'Unknown')
-                amount = order.get('amount', 0)
-                price = order.get('price', 0)
-                order_id = order.get('id', 'Unknown')
-                
-                side_emoji = "🟢" if side.lower() == 'buy' else "🔴"
-                order_value = float(amount) * float(price)
-                total_value += order_value
-                
-                confirmation_text += f"{side_emoji} {side.upper()} {amount} @ ${price:,.2f} (${order_value:,.2f})\n"
-            
-            confirmation_text += f"""
-💰 <b>Total Value:</b> ${total_value:,.2f}
-🔢 <b>Orders Count:</b> {len(token_orders)}
-
-⚠️ <b>Are you sure you want to cancel ALL {token} orders?</b>
-
-This action cannot be undone.
-            """
-            
-            keyboard = [
-                [
-                    InlineKeyboardButton(f"✅ Cancel All {token}", callback_data=f"confirm_coo_{token}"),
-                    InlineKeyboardButton("❌ Keep Orders", callback_data="cancel_order")
-                ]
-            ]
-            reply_markup = InlineKeyboardMarkup(keyboard)
-            
-            await update.message.reply_text(confirmation_text, parse_mode='HTML', reply_markup=reply_markup)
-            
-        except ValueError:
-            await update.message.reply_text("❌ Invalid token format. Please use token symbols like BTC, ETH, etc.")
-        except Exception as e:
-            await update.message.reply_text(f"❌ Error processing cancel orders command: {e}")
-
-    async def sl_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle the /sl (stop loss) command for setting stop loss orders."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        try:
-            if not context.args or len(context.args) < 2:
-                await update.message.reply_text(
-                    "❌ Usage: /sl [token] [price]\n"
-                    "Example: /sl BTC 44000\n\n"
-                    "This creates a stop loss order at the specified price."
-                )
-                return
-            
-            token = context.args[0].upper()
-            stop_price = float(context.args[1])
-            symbol = f"{token}/USDC:USDC"
-            
-            # Get current positions to find the position for this token
-            positions = self.client.get_positions()
-            if positions is None:
-                await update.message.reply_text(f"❌ Could not fetch positions to check {token} position")
-                return
-            
-            # Find the position for this token
-            current_position = None
-            for position in positions:
-                if position.get('symbol') == symbol and float(position.get('contracts', 0)) != 0:
-                    current_position = position
-                    break
-            
-            if not current_position:
-                await update.message.reply_text(f"📭 No open position found for {token}\n\nYou need an open position to set a stop loss.")
-                return
-            
-            # Extract position details
-            contracts = float(current_position.get('contracts', 0))
-            entry_price = float(current_position.get('entryPx', 0))
-            unrealized_pnl = float(current_position.get('unrealizedPnl', 0))
-            
-            # Use CCXT's side field to determine position direction
-            side_field = current_position.get('side', '').lower()
-            
-            if side_field == 'long':
-                # Long position - stop loss should be below entry price
-                position_type = "LONG"
-                exit_side = "sell"
-                exit_emoji = "🔴"
-                contracts_abs = contracts
-                
-                if stop_price >= entry_price:
-                    await update.message.reply_text(
-                        f"⚠️ Stop loss price should be BELOW entry price for long positions\n\n"
-                        f"📊 Your {token} LONG position:\n"
-                        f"• Entry Price: ${entry_price:,.2f}\n"
-                        f"• Stop Price: ${stop_price:,.2f} ❌\n\n"
-                        f"💡 Try a lower price like: /sl {token} {entry_price * 0.95:.0f}"
-                    )
-                    return
-            elif side_field == 'short':
-                # Short position - stop loss should be above entry price
-                position_type = "SHORT"
-                exit_side = "buy"
-                exit_emoji = "🟢"
-                contracts_abs = contracts  # Already positive from CCXT
-                
-                # Debug logging for short position validation
-                logger.info(f"🛑 Stop loss validation for SHORT: entry_price=${entry_price}, stop_price=${stop_price}, contracts={contracts}")
-                
-                if stop_price <= entry_price:
-                    await update.message.reply_text(
-                        f"⚠️ Stop loss price should be ABOVE entry price for short positions\n\n"
-                        f"📊 Your {token} SHORT position:\n"
-                        f"• Entry Price: ${entry_price:,.2f}\n"
-                        f"• Stop Price: ${stop_price:,.2f} ❌\n"
-                        f"• Contracts: {contracts} (side: {side_field})\n\n"
-                        f"💡 Try a higher price like: /sl {token} {entry_price * 1.05:.0f}\n\n"
-                        f"🔧 Debug: stop_price ({stop_price}) <= entry_price ({entry_price}) = {stop_price <= entry_price}"
-                    )
-                    return
-                else:
-                    logger.info(f"✅ Stop loss validation PASSED for SHORT: ${stop_price} > ${entry_price}")
-            else:
-                await update.message.reply_text(f"❌ Could not determine position direction for {token}. Side field: '{side_field}'")
-                return
-            
-            # Get current market price for reference
-            market_data = self.client.get_market_data(symbol)
-            current_price = 0
-            if market_data:
-                current_price = float(market_data['ticker'].get('last', 0))
-            
-            # Calculate estimated P&L at stop loss
-            if side_field == 'long':
-                pnl_at_stop = (stop_price - entry_price) * contracts_abs
-            else:  # short
-                pnl_at_stop = (entry_price - stop_price) * contracts_abs
-            
-            # Create confirmation message
-            pnl_emoji = "🟢" if pnl_at_stop >= 0 else "🔴"
-            
-            confirmation_text = f"""
-🛑 <b>Stop Loss Order Confirmation</b>
-
-📊 <b>Position Details:</b>
-• Token: {token}
-• Position: {position_type}
-• Size: {contracts_abs} contracts
-• Entry Price: ${entry_price:,.2f}
-• Current Price: ${current_price:,.2f}
-
-🎯 <b>Stop Loss Order:</b>
-• Stop Price: ${stop_price:,.2f}
-• Action: {exit_side.upper()} (Close {position_type})
-• Amount: {contracts_abs} {token}
-• Order Type: Limit Order
-• {pnl_emoji} Est. P&L: ${pnl_at_stop:,.2f}
-
-⚠️ <b>Are you sure you want to set this stop loss?</b>
-
-This will place a limit {exit_side} order at ${stop_price:,.2f} to protect your {position_type} position.
-            """
-            
-            keyboard = [
-                [
-                    InlineKeyboardButton(f"✅ Set Stop Loss", callback_data=f"confirm_sl_{token}_{exit_side}_{contracts_abs}_{stop_price}"),
-                    InlineKeyboardButton("❌ Cancel", callback_data="cancel_order")
-                ]
-            ]
-            reply_markup = InlineKeyboardMarkup(keyboard)
-            
-            await update.message.reply_text(confirmation_text, parse_mode='HTML', reply_markup=reply_markup)
-            
-        except ValueError:
-            await update.message.reply_text("❌ Invalid price format. Please use numbers only.")
-        except Exception as e:
-            await update.message.reply_text(f"❌ Error processing stop loss command: {e}")
-
-    async def tp_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle the /tp (take profit) command for setting take profit orders."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        try:
-            if not context.args or len(context.args) < 2:
-                await update.message.reply_text(
-                    "❌ Usage: /tp [token] [price]\n"
-                    "Example: /tp BTC 50000\n\n"
-                    "This creates a take profit order at the specified price."
-                )
-                return
-            
-            token = context.args[0].upper()
-            profit_price = float(context.args[1])
-            symbol = f"{token}/USDC:USDC"
-            
-            # Get current positions to find the position for this token
-            positions = self.client.get_positions()
-            if positions is None:
-                await update.message.reply_text(f"❌ Could not fetch positions to check {token} position")
-                return
-            
-            # Find the position for this token
-            current_position = None
-            for position in positions:
-                if position.get('symbol') == symbol and float(position.get('contracts', 0)) != 0:
-                    current_position = position
-                    break
-            
-            if not current_position:
-                await update.message.reply_text(f"📭 No open position found for {token}\n\nYou need an open position to set a take profit.")
-                return
-            
-            # Extract position details
-            contracts = float(current_position.get('contracts', 0))
-            entry_price = float(current_position.get('entryPx', 0))
-            unrealized_pnl = float(current_position.get('unrealizedPnl', 0))
-            
-            # Use CCXT's side field to determine position direction
-            side_field = current_position.get('side', '').lower()
-            
-            if side_field == 'long':
-                # Long position - take profit should be above entry price
-                position_type = "LONG"
-                exit_side = "sell"
-                exit_emoji = "🔴"
-                contracts_abs = contracts
-                
-                if profit_price <= entry_price:
-                    await update.message.reply_text(
-                        f"⚠️ Take profit price should be ABOVE entry price for long positions\n\n"
-                        f"📊 Your {token} LONG position:\n"
-                        f"• Entry Price: ${entry_price:,.2f}\n"
-                        f"• Take Profit: ${profit_price:,.2f} ❌\n\n"
-                        f"💡 Try a higher price like: /tp {token} {entry_price * 1.05:.0f}"
-                    )
-                    return
-            elif side_field == 'short':
-                # Short position - take profit should be below entry price
-                position_type = "SHORT"
-                exit_side = "buy"
-                exit_emoji = "🟢"
-                contracts_abs = contracts  # Already positive from CCXT
-                
-                if profit_price >= entry_price:
-                    await update.message.reply_text(
-                        f"⚠️ Take profit price should be BELOW entry price for short positions\n\n"
-                        f"📊 Your {token} SHORT position:\n"
-                        f"• Entry Price: ${entry_price:,.2f}\n"
-                        f"• Take Profit: ${profit_price:,.2f} ❌\n\n"
-                        f"💡 Try a lower price like: /tp {token} {entry_price * 0.95:.0f}"
-                    )
-                    return
-            else:
-                await update.message.reply_text(f"❌ Could not determine position direction for {token}. Side field: '{side_field}'")
-                return
-            
-            # Get current market price for reference
-            market_data = self.client.get_market_data(symbol)
-            current_price = 0
-            if market_data:
-                current_price = float(market_data['ticker'].get('last', 0))
-            
-            # Calculate estimated P&L at take profit
-            if side_field == 'long':
-                pnl_at_tp = (profit_price - entry_price) * contracts_abs
-            else:  # short
-                pnl_at_tp = (entry_price - profit_price) * contracts_abs
-            
-            # Create confirmation message
-            pnl_emoji = "🟢" if pnl_at_tp >= 0 else "🔴"
-            
-            confirmation_text = f"""
-🎯 <b>Take Profit Order Confirmation</b>
-
-📊 <b>Position Details:</b>
-• Token: {token}
-• Position: {position_type}
-• Size: {contracts_abs} contracts
-• Entry Price: ${entry_price:,.2f}
-• Current Price: ${current_price:,.2f}
-
-💰 <b>Take Profit Order:</b>
-• Target Price: ${profit_price:,.2f}
-• Action: {exit_side.upper()} (Close {position_type})
-• Amount: {contracts_abs} {token}
-• Order Type: Limit Order
-• {pnl_emoji} Est. P&L: ${pnl_at_tp:,.2f}
-
-⚠️ <b>Are you sure you want to set this take profit?</b>
-
-This will place a limit {exit_side} order at ${profit_price:,.2f} to capture profits from your {position_type} position.
-            """
-            
-            keyboard = [
-                [
-                    InlineKeyboardButton(f"✅ Set Take Profit", callback_data=f"confirm_tp_{token}_{exit_side}_{contracts_abs}_{profit_price}"),
-                    InlineKeyboardButton("❌ Cancel", callback_data="cancel_order")
-                ]
-            ]
-            reply_markup = InlineKeyboardMarkup(keyboard)
-            
-            await update.message.reply_text(confirmation_text, parse_mode='HTML', reply_markup=reply_markup)
-            
-        except ValueError:
-            await update.message.reply_text("❌ Invalid price format. Please use numbers only.")
-        except Exception as e:
-            await update.message.reply_text(f"❌ Error processing take profit command: {e}")
-
-    async def start_order_monitoring(self):
-        """Start the order monitoring background task."""
-        # Safety check in case this is called before initialization is complete
-        if not hasattr(self, 'monitoring_active'):
-            self.monitoring_active = False
-            
-        if self.monitoring_active:
-            return
-        
-        self.monitoring_active = True
-        logger.info("🔄 Starting order monitoring...")
-        
-        # Initialize tracking data
-        await self._initialize_order_tracking()
-        
-        # Start monitoring loop
-        asyncio.create_task(self._order_monitoring_loop())
-    
-    async def stop_order_monitoring(self):
-        """Stop the order monitoring background task."""
-        # Safety check in case this is called before initialization is complete
-        if hasattr(self, 'monitoring_active'):
-            self.monitoring_active = False
-        logger.info("⏹️ Stopping order monitoring...")
-    
-    async def _initialize_order_tracking(self):
-        """Initialize order and position tracking."""
-        try:
-            # Get current open orders to initialize tracking
-            orders = self.client.get_open_orders()
-            if orders:
-                self.last_known_orders = {order.get('id') for order in orders if order.get('id')}
-                logger.info(f"📋 Initialized tracking with {len(self.last_known_orders)} open orders")
-            
-            # Get current positions for P&L tracking
-            positions = self.client.get_positions()
-            if positions:
-                for position in positions:
-                    symbol = position.get('symbol')
-                    contracts = float(position.get('contracts', 0))
-                    entry_price = float(position.get('entryPx', 0))
-                    
-                    if symbol and contracts != 0:
-                        self.last_known_positions[symbol] = {
-                            'contracts': contracts,
-                            'entry_price': entry_price
-                        }
-                logger.info(f"📊 Initialized tracking with {len(self.last_known_positions)} positions")
-                
-        except Exception as e:
-            logger.error(f"❌ Error initializing order tracking: {e}")
-    
-    async def _order_monitoring_loop(self):
-        """Main monitoring loop that runs every Config.BOT_HEARTBEAT_SECONDS seconds."""
-        while getattr(self, 'monitoring_active', False):
-            try:
-                await self._check_order_fills()
-                await asyncio.sleep(Config.BOT_HEARTBEAT_SECONDS)  # Use configurable interval
-            except asyncio.CancelledError:
-                logger.info("🛑 Order monitoring cancelled")
-                break
-            except Exception as e:
-                logger.error(f"❌ Error in order monitoring loop: {e}")
-                await asyncio.sleep(Config.BOT_HEARTBEAT_SECONDS)  # Continue monitoring even if there's an error
-    
-    async def _check_order_fills(self):
-        """Check for filled orders and send notifications."""
-        try:
-            # Get current orders and positions
-            current_orders = self.client.get_open_orders() or []
-            current_positions = self.client.get_positions() or []
-            
-            # Get current order IDs
-            current_order_ids = {order.get('id') for order in current_orders if order.get('id')}
-            
-            # Find filled orders (orders that were in last_known_orders but not in current_orders)
-            filled_order_ids = self.last_known_orders - current_order_ids
-            
-            # Add some debugging for filled order detection
-            if filled_order_ids:
-                logger.info(f"🎯 Detected {len(filled_order_ids)} filled orders: {list(filled_order_ids)}")
-                
-                # Log pending stop losses before processing
-                if self.pending_stop_losses:
-                    logger.info(f"📋 Current pending stop losses: {list(self.pending_stop_losses.keys())}")
-                    for order_id in filled_order_ids:
-                        if order_id in self.pending_stop_losses:
-                            stop_loss_info = self.pending_stop_losses[order_id]
-                            logger.info(f"🛑 Will process stop loss for filled order {order_id}: {stop_loss_info['token']} @ ${stop_loss_info['stop_price']}")
-                
-                await self._process_filled_orders(filled_order_ids, current_positions)
-                
-                # Process pending stop losses for filled orders
-                await self._process_pending_stop_losses(filled_order_ids)
-            else:
-                # Log if we have pending stop losses but no filled orders
-                if self.pending_stop_losses:
-                    logger.debug(f"📋 No filled orders detected, but {len(self.pending_stop_losses)} pending stop losses remain")
-            
-            # Update tracking data
-            self.last_known_orders = current_order_ids
-            await self._update_position_tracking(current_positions)
-            
-            # Save state after updating tracking data
-            self._save_bot_state()
-            
-            # Check price alarms
-            await self._check_price_alarms()
-            
-            # Check external trades (trades made outside the bot)
-            await self._check_external_trades()
-            
-            # Check stop losses (if risk management is enabled)
-            if Config.RISK_MANAGEMENT_ENABLED:
-                await self._check_stop_losses(current_positions)
-            
-            # Check deposits/withdrawals (hourly)
-            await self._check_deposits_withdrawals()
-            
-            # Clean up cancelled orders from pending stop losses
-            await self._cleanup_cancelled_stop_losses(current_order_ids)
-            
-        except Exception as e:
-            logger.error(f"❌ Error checking order fills: {e}")
-
-    async def _process_pending_stop_losses(self, filled_order_ids: set):
-        """Process pending stop losses for filled orders."""
-        try:
-            processed_any = False
-            for order_id in filled_order_ids:
-                if order_id in self.pending_stop_losses:
-                    stop_loss_info = self.pending_stop_losses[order_id]
-                    
-                    # Place the stop loss order
-                    await self._place_pending_stop_loss(order_id, stop_loss_info)
-                    
-                    # Remove from pending after processing
-                    del self.pending_stop_losses[order_id]
-                    processed_any = True
-                    logger.info(f"🗑️ Removed processed stop loss for order {order_id}")
-            
-            # Save state if any stop losses were processed
-            if processed_any:
-                self._save_bot_state()
-                    
-        except Exception as e:
-            logger.error(f"❌ Error processing pending stop losses: {e}")
-
-    async def _place_pending_stop_loss(self, original_order_id: str, stop_loss_info: Dict[str, Any]):
-        """Place a pending stop loss order."""
-        try:
-            token = stop_loss_info['token']
-            symbol = stop_loss_info['symbol']
-            stop_price = stop_loss_info['stop_price']
-            side = stop_loss_info['side']
-            amount = stop_loss_info['amount']
-            order_type = stop_loss_info['order_type']
-            
-            logger.info(f"🛑 Placing automatic stop loss: {side} {amount:.6f} {token} @ ${stop_price}")
-            
-            # Place the stop loss order as a limit order
-            order = self.client.place_limit_order(symbol, side, amount, stop_price)
-            
-            if order:
-                order_id = order.get('id', 'N/A')
-                
-                # Send notification
-                await self._send_stop_loss_placed_notification(token, order_type, stop_price, amount, order_id, original_order_id)
-                
-                logger.info(f"✅ Successfully placed automatic stop loss for {token}: Order ID {order_id}")
-            else:
-                # Send failure notification
-                await self._send_stop_loss_failed_notification(token, order_type, stop_price, amount, original_order_id)
-                logger.error(f"❌ Failed to place automatic stop loss for {token}")
-                
-        except Exception as e:
-            logger.error(f"❌ Error placing pending stop loss: {e}")
-            await self._send_stop_loss_failed_notification(
-                stop_loss_info.get('token', 'Unknown'), 
-                stop_loss_info.get('order_type', 'Unknown'), 
-                stop_loss_info.get('stop_price', 0), 
-                stop_loss_info.get('amount', 0), 
-                original_order_id, 
-                str(e)
-            )
-
-    async def _cleanup_cancelled_stop_losses(self, current_order_ids: set):
-        """Remove pending stop losses for cancelled orders."""
-        try:
-            # Find orders that are no longer active but were not filled
-            orders_to_remove = []
-            
-            for order_id, stop_loss_info in self.pending_stop_losses.items():
-                if order_id not in current_order_ids:
-                    # Order is no longer in open orders, check if it was cancelled (not filled)
-                    # We assume if it's not in current_order_ids and we haven't processed it as filled,
-                    # then it was likely cancelled
-                    orders_to_remove.append(order_id)
-            
-            # Remove cancelled orders from pending stop losses
-            for order_id in orders_to_remove:
-                stop_loss_info = self.pending_stop_losses[order_id]
-                token = stop_loss_info['token']
-                
-                # Send notification about cancelled stop loss
-                await self._send_stop_loss_cancelled_notification(token, stop_loss_info, order_id)
-                
-                # Remove from pending
-                del self.pending_stop_losses[order_id]
-                logger.info(f"🗑️ Removed pending stop loss for cancelled order {order_id}")
-            
-            # Save state if any stop losses were removed
-            if orders_to_remove:
-                self._save_bot_state()
-                
-        except Exception as e:
-            logger.error(f"❌ Error cleaning up cancelled stop losses: {e}")
-
-    async def _send_stop_loss_placed_notification(self, token: str, order_type: str, stop_price: float, amount: float, stop_order_id: str, original_order_id: str):
-        """Send notification when stop loss is successfully placed."""
-        try:
-            position_type = order_type.upper()
-            
-            message = f"""
-🛑 <b>Stop Loss Placed Automatically</b>
-
-✅ <b>Stop Loss Active</b>
-
-📊 <b>Details:</b>
-• Token: {token}
-• Position: {position_type}
-• Stop Price: ${stop_price:,.2f}
-• Amount: {amount:.6f} {token}
-• Stop Loss Order ID: <code>{stop_order_id}</code>
-• Original Order ID: <code>{original_order_id}</code>
-
-🎯 <b>Protection:</b>
-• Status: ACTIVE ✅
-• Will execute if price reaches ${stop_price:,.2f}
-• Order Type: Limit Order
-
-💡 Your {position_type} position is now protected with automatic stop loss!
-            """
-            
-            await self.send_message(message.strip())
-            logger.info(f"📢 Sent stop loss placed notification: {token} @ ${stop_price}")
-            
-        except Exception as e:
-            logger.error(f"❌ Error sending stop loss placed notification: {e}")
-
-    async def _send_stop_loss_failed_notification(self, token: str, order_type: str, stop_price: float, amount: float, original_order_id: str, error: str = None):
-        """Send notification when stop loss placement fails."""
-        try:
-            position_type = order_type.upper()
-            
-            message = f"""
-⚠️ <b>Stop Loss Placement Failed</b>
-
-❌ <b>Automatic Stop Loss Failed</b>
-
-📊 <b>Details:</b>
-• Token: {token}
-• Position: {position_type}
-• Intended Stop Price: ${stop_price:,.2f}
-• Amount: {amount:.6f} {token}
-• Original Order ID: <code>{original_order_id}</code>
-
-🚨 <b>Action Required:</b>
-• Your position is NOT protected
-• Consider manually setting stop loss: <code>/sl {token} {stop_price:.0f}</code>
-• Monitor your position closely
-
-{f'🔧 Error: {error}' if error else ''}
-
-💡 Use /sl command to manually set stop loss protection.
-            """
-            
-            await self.send_message(message.strip())
-            logger.info(f"📢 Sent stop loss failed notification: {token}")
-            
-        except Exception as e:
-            logger.error(f"❌ Error sending stop loss failed notification: {e}")
-
-    async def _send_stop_loss_cancelled_notification(self, token: str, stop_loss_info: Dict[str, Any], order_id: str):
-        """Send notification when stop loss is cancelled due to order cancellation."""
-        try:
-            position_type = stop_loss_info['order_type'].upper()
-            stop_price = stop_loss_info['stop_price']
-            
-            message = f"""
-🚫 <b>Stop Loss Cancelled</b>
-
-📊 <b>Original Order Cancelled</b>
-
-• Token: {token}
-• Position: {position_type}
-• Cancelled Stop Price: ${stop_price:,.2f}
-• Original Order ID: <code>{order_id}</code>
-
-💡 <b>Status:</b>
-• Pending stop loss automatically cancelled
-• No position protection was placed
-• Order was cancelled before execution
-
-🔄 If you still want to trade {token}, place a new order with stop loss protection.
-            """
-            
-            await self.send_message(message.strip())
-            logger.info(f"📢 Sent stop loss cancelled notification: {token}")
-            
-        except Exception as e:
-            logger.error(f"❌ Error sending stop loss cancelled notification: {e}")
-
-    async def _check_price_alarms(self):
-        """Check all active price alarms."""
-        try:
-            # Get all active alarms
-            active_alarms = self.alarm_manager.get_all_active_alarms()
-            if not active_alarms:
-                return
-            
-            # Get unique tokens from alarms
-            tokens_to_check = list(set(alarm['token'] for alarm in active_alarms))
-            
-            # Fetch current prices for all tokens
-            price_data = {}
-            for token in tokens_to_check:
-                symbol = f"{token}/USDC:USDC"
-                market_data = self.client.get_market_data(symbol)
-                
-                if market_data and market_data.get('ticker'):
-                    current_price = market_data['ticker'].get('last')
-                    if current_price is not None:
-                        price_data[token] = float(current_price)
-            
-            # Check alarms against current prices
-            triggered_alarms = self.alarm_manager.check_alarms(price_data)
-            
-            # Send notifications for triggered alarms
-            for alarm in triggered_alarms:
-                await self._send_alarm_notification(alarm)
-                
-        except Exception as e:
-            logger.error(f"❌ Error checking price alarms: {e}")
-
-    async def _send_alarm_notification(self, alarm: Dict[str, Any]):
-        """Send notification for triggered alarm."""
-        try:
-            message = self.alarm_manager.format_triggered_alarm(alarm)
-            await self.send_message(message)
-            logger.info(f"📢 Sent alarm notification: {alarm['token']} ID {alarm['id']} @ ${alarm['triggered_price']}")
-        except Exception as e:
-            logger.error(f"❌ Error sending alarm notification: {e}")
-
-    async def _check_external_trades(self):
-        """Check for trades made outside the Telegram bot and update stats."""
-        try:
-            # Get recent fills from Hyperliquid
-            recent_fills = self.client.get_recent_fills()
-            
-            if not recent_fills:
-                return
-            
-            # Initialize last processed time if first run
-            if self.last_processed_trade_time is None:
-                # Set to current time minus 1 hour to catch recent activity
-                self.last_processed_trade_time = datetime.now() - timedelta(hours=1)
-            
-            # Filter for new trades since last check
-            new_trades = []
-            latest_trade_time = self.last_processed_trade_time
-            
-            for fill in recent_fills:
-                fill_time = fill.get('timestamp')
-                if fill_time:
-                    # Convert timestamps to comparable format
-                    try:
-                        # Convert fill_time to datetime object for comparison
-                        if isinstance(fill_time, (int, float)):
-                            # Assume it's a unix timestamp
-                            fill_datetime = datetime.fromtimestamp(fill_time / 1000 if fill_time > 1e10 else fill_time)
-                        else:
-                            # Try to parse as ISO string
-                            fill_datetime = datetime.fromisoformat(str(fill_time).replace('Z', '+00:00'))
-                        
-                        # Compare datetime objects
-                        if fill_datetime > self.last_processed_trade_time:
-                            new_trades.append(fill)
-                            if fill_datetime > latest_trade_time:
-                                latest_trade_time = fill_datetime
-                    except Exception as timestamp_error:
-                        logger.warning(f"⚠️ Error processing timestamp {fill_time}: {timestamp_error}")
-                        continue
-            
-            if not new_trades:
-                return
-            
-            # Process new trades
-            for trade in new_trades:
-                # Log trade processing for debugging
-                trade_id = trade.get('id', 'external')
-                symbol = trade.get('symbol', 'Unknown')
-                side = trade.get('side', 'Unknown')
-                amount = trade.get('amount', 0)
-                price = trade.get('price', 0)
-                
-                logger.info(f"🔍 Processing trade: {trade_id} - {side} {amount} {symbol} @ ${price}")
-                
-                await self._process_external_trade(trade)
-            
-            # Update last processed time (keep as datetime object)
-            self.last_processed_trade_time = latest_trade_time
-            
-            # Save state after updating last processed time
-            self._save_bot_state()
-            
-            if new_trades:
-                logger.info(f"📊 Processed {len(new_trades)} external trades")
-                
-        except Exception as e:
-            logger.error(f"❌ Error checking external trades: {e}")
-
-    async def _check_deposits_withdrawals(self):
-        """Check for deposits and withdrawals to maintain accurate P&L tracking."""
-        try:
-            # Check if it's time to run (hourly check)
-            current_time = datetime.now()
-            
-            if self.last_deposit_withdrawal_check is not None:
-                time_since_last_check = (current_time - self.last_deposit_withdrawal_check).total_seconds()
-                if time_since_last_check < self.deposit_withdrawal_check_interval:
-                    return  # Not time to check yet
-            
-            logger.info("🔍 Checking for deposits and withdrawals...")
-            
-            # Initialize last check time if first run
-            if self.last_deposit_withdrawal_check is None:
-                # Set to 24 hours ago to catch recent activity
-                self.last_deposit_withdrawal_check = current_time - timedelta(hours=24)
-            
-            # Calculate timestamp for API calls (last check time)
-            since_timestamp = int(self.last_deposit_withdrawal_check.timestamp() * 1000)  # Hyperliquid expects milliseconds
-            
-            # Track new deposits/withdrawals
-            new_deposits = 0
-            new_withdrawals = 0
-            
-            # Check if sync_client is available
-            if not hasattr(self.client, 'sync_client') or not self.client.sync_client:
-                logger.warning("⚠️ CCXT sync_client not available for deposit/withdrawal checking")
-                self.last_deposit_withdrawal_check = current_time
-                return
-            
-            # Set up user parameter for Hyperliquid API calls
-            params = {}
-            if Config.HYPERLIQUID_WALLET_ADDRESS:
-                wallet_address = Config.HYPERLIQUID_WALLET_ADDRESS
-                params['user'] = f"0x{wallet_address}" if not wallet_address.startswith('0x') else wallet_address
-            else:
-                logger.warning("⚠️ No wallet address configured for deposit/withdrawal checking")
-                self.last_deposit_withdrawal_check = current_time
-                return
-            
-            # Check for deposits
-            try:
-                deposits = self.client.sync_client.fetch_deposits(code='USDC', since=since_timestamp, params=params)
-                if deposits:
-                    for deposit in deposits:
-                        amount = float(deposit.get('amount', 0))
-                        timestamp = deposit.get('datetime', datetime.now().isoformat())
-                        deposit_id = deposit.get('id', 'unknown')
-                        
-                        # Record in stats to adjust P&L calculations
-                        self.stats.record_deposit(amount, timestamp, deposit_id)
-                        new_deposits += 1
-                        
-                        # Send notification
-                        await self._send_deposit_notification(amount, timestamp)
-                        
-            except Exception as e:
-                logger.warning(f"⚠️ Error fetching deposits: {e}")
-            
-            # Check for withdrawals
-            try:
-                withdrawals = self.client.sync_client.fetch_withdrawals(code='USDC', since=since_timestamp, params=params)
-                if withdrawals:
-                    for withdrawal in withdrawals:
-                        amount = float(withdrawal.get('amount', 0))
-                        timestamp = withdrawal.get('datetime', datetime.now().isoformat())
-                        withdrawal_id = withdrawal.get('id', 'unknown')
-                        
-                        # Record in stats to adjust P&L calculations
-                        self.stats.record_withdrawal(amount, timestamp, withdrawal_id)
-                        new_withdrawals += 1
-                        
-                        # Send notification
-                        await self._send_withdrawal_notification(amount, timestamp)
-                        
-            except Exception as e:
-                logger.warning(f"⚠️ Error fetching withdrawals: {e}")
-            
-            # Update last check time
-            self.last_deposit_withdrawal_check = current_time
-            
-            # Save state after updating last check time
-            self._save_bot_state()
-            
-            if new_deposits > 0 or new_withdrawals > 0:
-                logger.info(f"💰 Processed {new_deposits} deposits and {new_withdrawals} withdrawals")
-                
-                # Get updated balance adjustments summary
-                adjustments = self.stats.get_balance_adjustments_summary()
-                logger.info(f"📊 Total adjustments: ${adjustments['net_adjustment']:,.2f} net ({adjustments['adjustment_count']} total)")
-            
-        except Exception as e:
-            logger.error(f"❌ Error checking deposits/withdrawals: {e}")
-
-    async def _send_deposit_notification(self, amount: float, timestamp: str):
-        """Send notification for detected deposit."""
-        try:
-            time_str = datetime.fromisoformat(timestamp.replace('Z', '+00:00')).strftime('%H:%M:%S')
-            
-            message = f"""
-💰 <b>Deposit Detected</b>
-
-💵 <b>Amount:</b> ${amount:,.2f} USDC
-⏰ <b>Time:</b> {time_str}
-
-📊 <b>P&L Impact:</b>
-• Initial balance adjusted to maintain accurate P&L
-• Trading statistics unaffected by balance change
-• This deposit will not show as trading profit
-
-✅ <b>Balance tracking updated automatically</b>
-            """
-            
-            await self.send_message(message.strip())
-            logger.info(f"📱 Sent deposit notification: ${amount:,.2f}")
-            
-        except Exception as e:
-            logger.error(f"❌ Error sending deposit notification: {e}")
-
-    async def _send_withdrawal_notification(self, amount: float, timestamp: str):
-        """Send notification for detected withdrawal."""
-        try:
-            time_str = datetime.fromisoformat(timestamp.replace('Z', '+00:00')).strftime('%H:%M:%S')
-            
-            message = f"""
-💸 <b>Withdrawal Detected</b>
-
-💵 <b>Amount:</b> ${amount:,.2f} USDC
-⏰ <b>Time:</b> {time_str}
-
-📊 <b>P&L Impact:</b>
-• Initial balance adjusted to maintain accurate P&L
-• Trading statistics unaffected by balance change
-• This withdrawal will not show as trading loss
-
-✅ <b>Balance tracking updated automatically</b>
-            """
-            
-            await self.send_message(message.strip())
-            logger.info(f"📱 Sent withdrawal notification: ${amount:,.2f}")
-            
-        except Exception as e:
-            logger.error(f"❌ Error sending withdrawal notification: {e}")
-
-    async def _process_external_trade(self, trade: Dict[str, Any]):
-        """Process an individual external trade and determine if it's opening or closing a position."""
-        try:
-            # Extract trade information
-            symbol = trade.get('symbol', '')
-            side = trade.get('side', '')
-            amount = float(trade.get('amount', 0))
-            price = float(trade.get('price', 0))
-            trade_id = trade.get('id', 'external')
-            timestamp = trade.get('timestamp', '')
-            
-            if not all([symbol, side, amount, price]):
-                return
-            
-            # Skip bot-generated trades to prevent double processing
-            if trade_id in self.bot_trade_ids:
-                logger.debug(f"🤖 Skipping bot-generated trade: {trade_id}")
-                return
-            
-            # Record trade in stats and get action type using enhanced tracking
-            action_type = self.stats.record_trade_with_enhanced_tracking(symbol, side, amount, price, trade_id, "external")
-            
-            # Send enhanced notification based on action type
-            await self._send_enhanced_trade_notification(symbol, side, amount, price, action_type, timestamp)
-            
-            logger.info(f"📋 Processed external trade: {side} {amount} {symbol} @ ${price} ({action_type})")
-            
-        except Exception as e:
-            logger.error(f"❌ Error processing external trade: {e}")
-
-    async def _send_enhanced_trade_notification(self, symbol: str, side: str, amount: float, price: float, action_type: str, timestamp: str = None):
-        """Send enhanced trade notification based on position action type."""
-        try:
-            token = symbol.split('/')[0] if '/' in symbol else symbol
-            position = self.stats.get_enhanced_position_state(symbol)
-            
-            if timestamp is None:
-                time_str = datetime.now().strftime('%H:%M:%S')
-            else:
-                try:
-                    time_obj = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
-                    time_str = time_obj.strftime('%H:%M:%S')
-                except:
-                    time_str = "Unknown"
-            
-            # Handle different action types
-            if action_type in ['long_opened', 'short_opened']:
-                await self._send_position_opened_notification(token, side, amount, price, action_type, time_str)
-            
-            elif action_type in ['long_increased', 'short_increased']:
-                await self._send_position_increased_notification(token, side, amount, price, position, action_type, time_str)
-            
-            elif action_type in ['long_reduced', 'short_reduced']:
-                pnl_data = self.stats.calculate_enhanced_position_pnl(symbol, amount, price)
-                await self._send_position_reduced_notification(token, side, amount, price, position, pnl_data, action_type, time_str)
-            
-            elif action_type in ['long_closed', 'short_closed']:
-                pnl_data = self.stats.calculate_enhanced_position_pnl(symbol, amount, price)
-                await self._send_position_closed_notification(token, side, amount, price, position, pnl_data, action_type, time_str)
-            
-            elif action_type in ['long_closed_and_short_opened', 'short_closed_and_long_opened']:
-                await self._send_position_flipped_notification(token, side, amount, price, action_type, time_str)
-            
-            else:
-                # Fallback to generic notification
-                await self._send_external_trade_notification({
-                    'symbol': symbol,
-                    'side': side,
-                    'amount': amount,
-                    'price': price,
-                    'timestamp': timestamp or datetime.now().isoformat()
-                })
-                
-        except Exception as e:
-            logger.error(f"❌ Error sending enhanced trade notification: {e}")
-
-    async def _send_position_opened_notification(self, token: str, side: str, amount: float, price: float, action_type: str, time_str: str):
-        """Send notification for newly opened position."""
-        position_type = "LONG" if action_type == 'long_opened' else "SHORT"
-        side_emoji = "🟢" if side.lower() == 'buy' else "🔴"
-        trade_value = amount * price
-        
-        message = f"""
-🚀 <b>Position Opened</b>
-
-📊 <b>New {position_type} Position:</b>
-• Token: {token}
-• Direction: {position_type}
-• Entry Size: {amount} {token}
-• Entry Price: ${price:,.2f}
-• Position Value: ${trade_value:,.2f}
-
-{side_emoji} <b>Trade Details:</b>
-• Side: {side.upper()}
-• Order Type: Market/Limit
-• Status: OPENED ✅
-
-⏰ <b>Time:</b> {time_str}
-📈 <b>Note:</b> New {position_type} position established
-📊 Use /positions to view current holdings
-        """
-        
-        await self.send_message(message.strip())
-        logger.info(f"📢 Position opened: {token} {position_type} {amount} @ ${price}")
-
-    async def _send_position_increased_notification(self, token: str, side: str, amount: float, price: float, position: Dict, action_type: str, time_str: str):
-        """Send notification for position increase (additional entry)."""
-        position_type = "LONG" if action_type == 'long_increased' else "SHORT"
-        side_emoji = "🟢" if side.lower() == 'buy' else "🔴"
-        
-        total_size = abs(position['contracts'])
-        avg_entry = position['avg_entry_price']
-        entry_count = position['entry_count']
-        total_value = total_size * avg_entry
-        
-        message = f"""
-📈 <b>Position Increased</b>
-
-📊 <b>{position_type} Position Updated:</b>
-• Token: {token}
-• Direction: {position_type}
-• Added Size: {amount} {token} @ ${price:,.2f}
-• New Total Size: {total_size} {token}
-• Average Entry: ${avg_entry:,.2f}
-
-{side_emoji} <b>Position Summary:</b>
-• Total Value: ${total_value:,.2f}
-• Entry Points: {entry_count}
-• Last Entry: ${price:,.2f}
-• Status: INCREASED ⬆️
-
-⏰ <b>Time:</b> {time_str}
-💡 <b>Strategy:</b> Multiple entry averaging
-📊 Use /positions for complete position details
-        """
-        
-        await self.send_message(message.strip())
-        logger.info(f"📢 Position increased: {token} {position_type} +{amount} @ ${price} (total: {total_size})")
-
-    async def _send_position_reduced_notification(self, token: str, side: str, amount: float, price: float, position: Dict, pnl_data: Dict, action_type: str, time_str: str):
-        """Send notification for partial position close."""
-        position_type = "LONG" if action_type == 'long_reduced' else "SHORT"
-        
-        remaining_size = abs(position['contracts'])
-        avg_entry = pnl_data.get('avg_entry_price', position['avg_entry_price'])
-        pnl = pnl_data['pnl']
-        pnl_percent = pnl_data['pnl_percent']
-        pnl_emoji = "🟢" if pnl >= 0 else "🔴"
-        
-        # Calculate ROE (Return on Equity) for partial close
-        cost_basis = amount * avg_entry
-        roe = (pnl / cost_basis) * 100 if cost_basis > 0 else 0
-        
-        partial_value = amount * price
-        
-        message = f"""
-📉 <b>Position Partially Closed</b>
-
-📊 <b>{position_type} Partial Exit:</b>
-• Token: {token}
-• Direction: {position_type}
-• Closed Size: {amount} {token}
-• Exit Price: ${price:,.2f}
-• Remaining Size: {remaining_size} {token}
-
-{pnl_emoji} <b>Partial P&L:</b>
-• Entry Price: ${avg_entry:,.2f}
-• Exit Value: ${partial_value:,.2f}
-• P&L: ${pnl:,.2f} ({roe:+.2f}% ROE)
-• Result: {"PROFIT" if pnl >= 0 else "LOSS"}
-
-💰 <b>Position Status:</b>
-• Status: PARTIALLY CLOSED 📉
-• Take Profit Strategy: Active
-
-⏰ <b>Time:</b> {time_str}
-📊 Use /positions to view remaining position
-        """
-        
-        await self.send_message(message.strip())
-        logger.info(f"📢 Position reduced: {token} {position_type} -{amount} @ ${price} P&L: ${pnl:.2f}")
-
-    async def _send_position_closed_notification(self, token: str, side: str, amount: float, price: float, position: Dict, pnl_data: Dict, action_type: str, time_str: str):
-        """Send notification for fully closed position."""
-        position_type = "LONG" if action_type == 'long_closed' else "SHORT"
-        
-        avg_entry = pnl_data.get('avg_entry_price', position['avg_entry_price'])
-        pnl = pnl_data['pnl']
-        pnl_percent = pnl_data['pnl_percent']
-        pnl_emoji = "🟢" if pnl >= 0 else "🔴"
-        
-        # Calculate ROE (Return on Equity) for consistency
-        cost_basis = amount * avg_entry
-        roe = (pnl / cost_basis) * 100 if cost_basis > 0 else 0
-        
-        entry_count = position.get('entry_count', 1)
-        exit_value = amount * price
-        
-        message = f"""
-🎯 <b>Position Fully Closed</b>
-
-📊 <b>{position_type} Position Summary:</b>
-• Token: {token}
-• Direction: {position_type}
-• Total Size: {amount} {token}
-• Average Entry: ${avg_entry:,.2f}
-• Exit Price: ${price:,.2f}
-• Exit Value: ${exit_value:,.2f}
-
-{pnl_emoji} <b>Total P&L:</b>
-• P&L: ${pnl:,.2f} ({roe:+.2f}% ROE)
-• Result: {"PROFIT" if pnl >= 0 else "LOSS"}
-• Entry Points Used: {entry_count}
-
-✅ <b>Trade Complete:</b>
-• Status: FULLY CLOSED 🎯
-• Position: FLAT
-
-⏰ <b>Time:</b> {time_str}
-📊 Use /stats to view updated performance
-        """
-        
-        await self.send_message(message.strip())
-        logger.info(f"📢 Position closed: {token} {position_type} {amount} @ ${price} Total P&L: ${pnl:.2f}")
-
-    async def _send_position_flipped_notification(self, token: str, side: str, amount: float, price: float, action_type: str, time_str: str):
-        """Send notification for position flip (close and reverse)."""
-        if action_type == 'long_closed_and_short_opened':
-            old_type = "LONG"
-            new_type = "SHORT"
-        else:
-            old_type = "SHORT"
-            new_type = "LONG"
-        
-        message = f"""
-🔄 <b>Position Flipped</b>
-
-📊 <b>Direction Change:</b>
-• Token: {token}
-• Previous: {old_type} position
-• New: {new_type} position
-• Size: {amount} {token}
-• Price: ${price:,.2f}
-
-🎯 <b>Trade Summary:</b>
-• {old_type} position: CLOSED ✅
-• {new_type} position: OPENED 🚀
-• Flip Price: ${price:,.2f}
-• Status: POSITION REVERSED
-
-⏰ <b>Time:</b> {time_str}
-💡 <b>Strategy:</b> Directional change
-📊 Use /positions to view new position
-        """
-        
-        await self.send_message(message.strip())
-        logger.info(f"📢 Position flipped: {token} {old_type} -> {new_type} @ ${price}")
-
-    async def monitoring_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle the /monitoring command to show monitoring status."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        # Get alarm statistics
-        alarm_stats = self.alarm_manager.get_statistics()
-        
-        # Get balance adjustments info
-        adjustments_summary = self.stats.get_balance_adjustments_summary()
-        last_deposit_check = "Never"
-        next_deposit_check = "Unknown"
-        
-        if hasattr(self, 'last_deposit_withdrawal_check') and self.last_deposit_withdrawal_check:
-            last_deposit_check = self.last_deposit_withdrawal_check.strftime('%H:%M:%S')
-            next_check_time = self.last_deposit_withdrawal_check + timedelta(seconds=self.deposit_withdrawal_check_interval)
-            next_deposit_check = next_check_time.strftime('%H:%M:%S')
-        
-        # Safety checks for monitoring attributes
-        monitoring_active = getattr(self, 'monitoring_active', False)
-        last_known_orders = getattr(self, 'last_known_orders', set())
-        last_known_positions = getattr(self, 'last_known_positions', {})
-        deposit_withdrawal_check_interval = getattr(self, 'deposit_withdrawal_check_interval', 3600)
-        
-        status_text = f"""
-🔄 <b>System Monitoring Status</b>
-
-📊 <b>Order Monitoring:</b>
-• Active: {'✅ Yes' if monitoring_active else '❌ No'}
-• Check Interval: {Config.BOT_HEARTBEAT_SECONDS} seconds
-• Orders Tracked: {len(last_known_orders)}
-• Positions Tracked: {len(last_known_positions)}
-• Pending Stop Losses: {len(getattr(self, 'pending_stop_losses', {}))}
-
-💰 <b>Deposit/Withdrawal Monitoring:</b>
-• Check Interval: {deposit_withdrawal_check_interval // 3600} hour(s)
-• Last Check: {last_deposit_check}
-• Next Check: {next_deposit_check}
-• Total Adjustments: {adjustments_summary['adjustment_count']}
-• Net Adjustment: ${adjustments_summary['net_adjustment']:,.2f}
-
-🔔 <b>Price Alarms:</b>
-• Active Alarms: {alarm_stats['total_active']}
-• Triggered Today: {alarm_stats['total_triggered']}
-• Tokens Monitored: {alarm_stats['tokens_tracked']}
-• Next Alarm ID: {alarm_stats['next_id']}
-
-🔄 <b>External Trade Monitoring:</b>
-• Last Check: {self.last_processed_trade_time or 'Not started'}
-• Auto Stats Update: ✅ Enabled
-• External Notifications: ✅ Enabled
-
-🛡️ <b>Risk Management:</b>
-• Automatic Stop Loss: {'✅ Enabled' if Config.RISK_MANAGEMENT_ENABLED else '❌ Disabled'}
-• Stop Loss Threshold: {Config.STOP_LOSS_PERCENTAGE}%
-• Position Monitoring: {'✅ Active' if Config.RISK_MANAGEMENT_ENABLED else '❌ Inactive'}
-• Order-based Stop Loss: ✅ Enabled
-
-📈 <b>Notifications:</b>
-• 🚀 Position Opened/Increased
-• 📉 Position Partially/Fully Closed
-• 🎯 P&L Calculations
-• 🔔 Price Alarm Triggers
-• 🔄 External Trade Detection
-• 💰 Deposit/Withdrawal Detection
-• 🛑 Automatic Stop Loss Triggers
-• 🛑 Order-based Stop Loss Placement
-
-💾 <b>Bot State Persistence:</b>
-• Pending Stop Losses: Saved to disk
-• Order Tracking: Saved to disk  
-• External Trade Times: Saved to disk
-• Deposit Check Times: Saved to disk
-• State File: bot_state.json
-• Survives Bot Restarts: ✅ Yes
-
-⏰ <b>Last Check:</b> {datetime.now().strftime('%H:%M:%S')}
-
-💡 <b>Monitoring Features:</b>
-• Real-time order fill detection
-• Automatic P&L calculation
-• Position change tracking
-• Price alarm monitoring
-• External trade monitoring
-• Deposit/withdrawal tracking
-• Auto stats synchronization
-• Order-based stop loss placement
-• Instant Telegram notifications
-        """
-        
-        if alarm_stats['token_breakdown']:
-            status_text += f"\n\n📋 <b>Active Alarms by Token:</b>\n"
-            for token, count in alarm_stats['token_breakdown'].items():
-                status_text += f"• {token}: {count} alarm{'s' if count != 1 else ''}\n"
-        
-        await update.message.reply_text(status_text.strip(), parse_mode='HTML')
-
-    async def alarm_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle the /alarm command for price alerts."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        try:
-            if not context.args or len(context.args) == 0:
-                # No arguments - list all alarms
-                alarms = self.alarm_manager.get_all_active_alarms()
-                message = self.alarm_manager.format_alarm_list(alarms)
-                await update.message.reply_text(message, parse_mode='HTML')
-                return
-            
-            elif len(context.args) == 1:
-                arg = context.args[0]
-                
-                # Check if argument is a number (alarm ID to remove)
-                try:
-                    alarm_id = int(arg)
-                    # Remove alarm by ID
-                    if self.alarm_manager.remove_alarm(alarm_id):
-                        await update.message.reply_text(f"✅ Alarm ID {alarm_id} has been removed.")
-                    else:
-                        await update.message.reply_text(f"❌ Alarm ID {alarm_id} not found.")
-                    return
-                except ValueError:
-                    # Not a number, treat as token
-                    token = arg.upper()
-                    alarms = self.alarm_manager.get_alarms_by_token(token)
-                    message = self.alarm_manager.format_alarm_list(alarms, f"{token} Price Alarms")
-                    await update.message.reply_text(message, parse_mode='HTML')
-                    return
-            
-            elif len(context.args) == 2:
-                # Set new alarm: /alarm TOKEN PRICE
-                token = context.args[0].upper()
-                target_price = float(context.args[1])
-                
-                # Get current market price
-                symbol = f"{token}/USDC:USDC"
-                market_data = self.client.get_market_data(symbol)
-                
-                if not market_data or not market_data.get('ticker'):
-                    await update.message.reply_text(f"❌ Could not fetch current price for {token}")
-                    return
-                
-                current_price = float(market_data['ticker'].get('last', 0))
-                if current_price <= 0:
-                    await update.message.reply_text(f"❌ Invalid current price for {token}")
-                    return
-                
-                # Create the alarm
-                alarm = self.alarm_manager.create_alarm(token, target_price, current_price)
-                
-                # Format confirmation message
-                direction_emoji = "📈" if alarm['direction'] == 'above' else "📉"
-                price_diff = abs(target_price - current_price)
-                price_diff_percent = (price_diff / current_price) * 100
-                
-                message = f"""
-✅ <b>Price Alarm Created</b>
-
-📊 <b>Alarm Details:</b>
-• Alarm ID: {alarm['id']}
-• Token: {token}
-• Target Price: ${target_price:,.2f}
-• Current Price: ${current_price:,.2f}
-• Direction: {alarm['direction'].upper()}
-
-{direction_emoji} <b>Alert Condition:</b>
-Will trigger when {token} price moves {alarm['direction']} ${target_price:,.2f}
-
-💰 <b>Price Difference:</b>
-• Distance: ${price_diff:,.2f} ({price_diff_percent:.2f}%)
-• Status: ACTIVE ✅
-
-⏰ <b>Created:</b> {datetime.now().strftime('%H:%M:%S')}
-
-💡 The alarm will be checked every {Config.BOT_HEARTBEAT_SECONDS} seconds and you'll receive a notification when triggered.
-                """
-                
-                await update.message.reply_text(message.strip(), parse_mode='HTML')
-                
-            else:
-                # Too many arguments
-                await update.message.reply_text(
-                    "❌ Invalid usage. Examples:\n\n"
-                    "• <code>/alarm</code> - List all alarms\n"
-                    "• <code>/alarm BTC</code> - List BTC alarms\n"
-                    "• <code>/alarm BTC 50000</code> - Set alarm for BTC at $50,000\n"
-                    "• <code>/alarm 3</code> - Remove alarm ID 3",
-                    parse_mode='HTML'
-                )
-                
-        except ValueError:
-            await update.message.reply_text("❌ Invalid price format. Please use numbers only.")
-        except Exception as e:
-            error_message = f"❌ Error processing alarm command: {str(e)}"
-            await update.message.reply_text(error_message)
-            logger.error(f"Error in alarm command: {e}")
-
-    async def logs_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle the /logs command to show log file statistics and cleanup options."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        try:
-            # Check for cleanup argument
-            if context.args and len(context.args) >= 1:
-                if context.args[0].lower() == 'cleanup':
-                    # Get days parameter (default 30)
-                    days_to_keep = 30
-                    if len(context.args) >= 2:
-                        try:
-                            days_to_keep = int(context.args[1])
-                        except ValueError:
-                            await update.message.reply_text("❌ Invalid number of days. Using default (30).")
-                    
-                    # Perform cleanup
-                    await update.message.reply_text(f"🧹 Cleaning up log files older than {days_to_keep} days...")
-                    cleanup_logs(days_to_keep)
-                    await update.message.reply_text(f"✅ Log cleanup completed!")
-                    return
-            
-            # Show log statistics
-            log_stats_text = format_log_stats()
-            
-            # Add additional info
-            status_text = f"""
-📊 <b>System Logging Status</b>
-
-{log_stats_text}
-
-📈 <b>Log Configuration:</b>
-• Log Level: {Config.LOG_LEVEL}
-• Heartbeat Interval: {Config.BOT_HEARTBEAT_SECONDS}s
-• Bot Uptime: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
-
-💡 <b>Log Management:</b>
-• <code>/logs cleanup</code> - Clean old logs (30 days)
-• <code>/logs cleanup 7</code> - Clean logs older than 7 days
-• Log rotation happens automatically
-• Old backups are removed automatically
-
-🔧 <b>Configuration:</b>
-• Rotation Type: {Config.LOG_ROTATION_TYPE}
-• Max Size: {Config.LOG_MAX_SIZE_MB}MB (size rotation)
-• Backup Count: {Config.LOG_BACKUP_COUNT}
-            """
-            
-            await update.message.reply_text(status_text.strip(), parse_mode='HTML')
-            
-        except Exception as e:
-            error_message = f"❌ Error processing logs command: {str(e)}"
-            await update.message.reply_text(error_message)
-            logger.error(f"Error in logs command: {e}")
-
-    async def performance_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle the /performance command to show token performance ranking or detailed stats."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        try:
-            # Check if specific token is requested
-            if context.args and len(context.args) >= 1:
-                # Detailed performance for specific token
-                token = context.args[0].upper()
-                await self._show_token_performance(update, token)
-            else:
-                # Show token performance ranking
-                await self._show_performance_ranking(update)
-                
-        except Exception as e:
-            error_message = f"❌ Error processing performance command: {str(e)}"
-            await update.message.reply_text(error_message)
-            logger.error(f"Error in performance command: {e}")
-
-    async def _show_performance_ranking(self, update: Update):
-        """Show token performance ranking (compressed view)."""
-        token_performance = self.stats.get_token_performance()
-        
-        if not token_performance:
-            await update.message.reply_text(
-                "📊 <b>Token Performance</b>\n\n"
-                "📭 No trading data available yet.\n\n"
-                "💡 Performance tracking starts after your first completed trades.\n"
-                "Use /long or /short to start trading!",
-                parse_mode='HTML'
-            )
-            return
-        
-        # Sort tokens by total P&L (best to worst)
-        sorted_tokens = sorted(
-            token_performance.items(),
-            key=lambda x: x[1]['total_pnl'],
-            reverse=True
-        )
-        
-        performance_text = "🏆 <b>Token Performance Ranking</b>\n\n"
-        
-        # Add ranking with emojis
-        for i, (token, stats) in enumerate(sorted_tokens, 1):
-            # Ranking emoji
-            if i == 1:
-                rank_emoji = "🥇"
-            elif i == 2:
-                rank_emoji = "🥈"
-            elif i == 3:
-                rank_emoji = "🥉"
-            else:
-                rank_emoji = f"#{i}"
-            
-            # P&L emoji
-            pnl_emoji = "🟢" if stats['total_pnl'] >= 0 else "🔴"
-            
-            # Format the line
-            performance_text += f"{rank_emoji} <b>{token}</b>\n"
-            performance_text += f"   {pnl_emoji} P&L: ${stats['total_pnl']:,.2f} ({stats['pnl_percentage']:+.1f}%)\n"
-            performance_text += f"   📊 Trades: {stats['completed_trades']}"
-            
-            # Add win rate if there are completed trades
-            if stats['completed_trades'] > 0:
-                performance_text += f" | Win: {stats['win_rate']:.0f}%"
-            
-            performance_text += "\n\n"
-        
-        # Add summary
-        total_pnl = sum(stats['total_pnl'] for stats in token_performance.values())
-        total_trades = sum(stats['completed_trades'] for stats in token_performance.values())
-        total_pnl_emoji = "🟢" if total_pnl >= 0 else "🔴"
-        
-        performance_text += f"💼 <b>Portfolio Summary:</b>\n"
-        performance_text += f"   {total_pnl_emoji} Total P&L: ${total_pnl:,.2f}\n"
-        performance_text += f"   📈 Tokens Traded: {len(token_performance)}\n"
-        performance_text += f"   🔄 Completed Trades: {total_trades}\n\n"
-        
-        performance_text += f"💡 <b>Usage:</b> <code>/performance BTC</code> for detailed {Config.DEFAULT_TRADING_TOKEN} stats"
-        
-        await update.message.reply_text(performance_text.strip(), parse_mode='HTML')
-
-    async def _show_token_performance(self, update: Update, token: str):
-        """Show detailed performance for a specific token."""
-        token_stats = self.stats.get_token_detailed_stats(token)
-        
-        # Check if token has any data
-        if token_stats.get('total_trades', 0) == 0:
-            await update.message.reply_text(
-                f"📊 <b>{token} Performance</b>\n\n"
-                f"📭 No trading history found for {token}.\n\n"
-                f"💡 Start trading {token} with:\n"
-                f"• <code>/long {token} 100</code>\n"
-                f"• <code>/short {token} 100</code>\n\n"
-                f"🔄 Use <code>/performance</code> to see all token rankings.",
-                parse_mode='HTML'
-            )
-            return
-        
-        # Check if there's a message (no completed trades)
-        if 'message' in token_stats and token_stats.get('completed_trades', 0) == 0:
-            await update.message.reply_text(
-                f"📊 <b>{token} Performance</b>\n\n"
-                f"{token_stats['message']}\n\n"
-                f"📈 <b>Current Activity:</b>\n"
-                f"• Total Trades: {token_stats['total_trades']}\n"
-                f"• Buy Orders: {token_stats.get('buy_trades', 0)}\n"
-                f"• Sell Orders: {token_stats.get('sell_trades', 0)}\n"
-                f"• Volume: ${token_stats.get('total_volume', 0):,.2f}\n\n"
-                f"💡 Complete some trades to see P&L statistics!\n"
-                f"🔄 Use <code>/performance</code> to see all token rankings.",
-                parse_mode='HTML'
-            )
-            return
-        
-        # Detailed stats display
-        pnl_emoji = "🟢" if token_stats['total_pnl'] >= 0 else "🔴"
-        
-        performance_text = f"""
-📊 <b>{token} Detailed Performance</b>
-
-💰 <b>P&L Summary:</b>
-• {pnl_emoji} Total P&L: ${token_stats['total_pnl']:,.2f} ({token_stats['pnl_percentage']:+.2f}%)
-• 💵 Total Volume: ${token_stats['completed_volume']:,.2f}
-• 📈 Expectancy: ${token_stats['expectancy']:,.2f}
-
-📊 <b>Trading Activity:</b>
-• Total Trades: {token_stats['total_trades']}
-• Completed: {token_stats['completed_trades']}
-• Buy Orders: {token_stats['buy_trades']}
-• Sell Orders: {token_stats['sell_trades']}
-
-🏆 <b>Performance Metrics:</b>
-• Win Rate: {token_stats['win_rate']:.1f}%
-• Profit Factor: {token_stats['profit_factor']:.2f}
-• Wins: {token_stats['total_wins']} | Losses: {token_stats['total_losses']}
-
-💡 <b>Best/Worst:</b>
-• Largest Win: ${token_stats['largest_win']:,.2f}
-• Largest Loss: ${token_stats['largest_loss']:,.2f}
-• Avg Win: ${token_stats['avg_win']:,.2f}
-• Avg Loss: ${token_stats['avg_loss']:,.2f}
-        """
-        
-        # Add recent trades if available
-        if token_stats.get('recent_trades'):
-            performance_text += f"\n🔄 <b>Recent Trades:</b>\n"
-            for trade in token_stats['recent_trades'][-3:]:  # Last 3 trades
-                trade_time = datetime.fromisoformat(trade['timestamp']).strftime('%m/%d %H:%M')
-                side_emoji = "🟢" if trade['side'] == 'buy' else "🔴"
-                pnl_display = f" | P&L: ${trade.get('pnl', 0):.2f}" if trade.get('pnl', 0) != 0 else ""
-                
-                performance_text += f"• {side_emoji} {trade['side'].upper()} ${trade['value']:,.0f} @ {trade_time}{pnl_display}\n"
-        
-        performance_text += f"\n🔄 Use <code>/performance</code> to see all token rankings"
-        
-        await update.message.reply_text(performance_text.strip(), parse_mode='HTML')
-
-    async def daily_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle the /daily command to show daily performance stats."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        try:
-            daily_stats = self.stats.get_daily_stats(10)
-            
-            if not daily_stats:
-                await update.message.reply_text(
-                    "📅 <b>Daily Performance</b>\n\n"
-                    "📭 No daily performance data available yet.\n\n"
-                    "💡 Daily stats are calculated from completed trades.\n"
-                    "Start trading to see daily performance!",
-                    parse_mode='HTML'
-                )
-                return
-            
-            daily_text = "📅 <b>Daily Performance (Last 10 Days)</b>\n\n"
-            
-            total_pnl = 0
-            total_trades = 0
-            trading_days = 0
-            
-            for day_stats in daily_stats:
-                if day_stats['has_trades']:
-                    # Day with completed trades
-                    pnl_emoji = "🟢" if day_stats['pnl'] >= 0 else "🔴"
-                    daily_text += f"📊 <b>{day_stats['date_formatted']}</b>\n"
-                    daily_text += f"   {pnl_emoji} P&L: ${day_stats['pnl']:,.2f} ({day_stats['pnl_pct']:+.1f}%)\n"
-                    daily_text += f"   🔄 Trades: {day_stats['trades']}\n\n"
-                    
-                    total_pnl += day_stats['pnl']
-                    total_trades += day_stats['trades']
-                    trading_days += 1
-                else:
-                    # Day with no trades
-                    daily_text += f"📊 <b>{day_stats['date_formatted']}</b>\n"
-                    daily_text += f"   📭 No completed trades\n\n"
-            
-            # Add summary
-            if trading_days > 0:
-                total_pnl_emoji = "🟢" if total_pnl >= 0 else "🔴"
-                daily_text += f"💼 <b>10-Day Summary:</b>\n"
-                daily_text += f"   {total_pnl_emoji} Total P&L: ${total_pnl:,.2f}\n"
-                daily_text += f"   🔄 Total Trades: {total_trades}\n"
-                daily_text += f"   📈 Trading Days: {trading_days}/10\n"
-                daily_text += f"   📊 Avg per Trading Day: ${total_pnl/trading_days:,.2f}"
-            else:
-                daily_text += f"💼 <b>10-Day Summary:</b>\n"
-                daily_text += f"   📭 No completed trades in the last 10 days\n"
-                daily_text += f"   💡 Start trading to see daily performance!"
-            
-            await update.message.reply_text(daily_text.strip(), parse_mode='HTML')
-            
-        except Exception as e:
-            error_message = f"❌ Error processing daily command: {str(e)}"
-            await update.message.reply_text(error_message)
-            logger.error(f"Error in daily command: {e}")
-
-    async def weekly_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle the /weekly command to show weekly performance stats."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        try:
-            weekly_stats = self.stats.get_weekly_stats(10)
-            
-            if not weekly_stats:
-                await update.message.reply_text(
-                    "📊 <b>Weekly Performance</b>\n\n"
-                    "📭 No weekly performance data available yet.\n\n"
-                    "💡 Weekly stats are calculated from completed trades.\n"
-                    "Start trading to see weekly performance!",
-                    parse_mode='HTML'
-                )
-                return
-            
-            weekly_text = "📊 <b>Weekly Performance (Last 10 Weeks)</b>\n\n"
-            
-            total_pnl = 0
-            total_trades = 0
-            trading_weeks = 0
-            
-            for week_stats in weekly_stats:
-                if week_stats['has_trades']:
-                    # Week with completed trades
-                    pnl_emoji = "🟢" if week_stats['pnl'] >= 0 else "🔴"
-                    weekly_text += f"📈 <b>{week_stats['week_formatted']}</b>\n"
-                    weekly_text += f"   {pnl_emoji} P&L: ${week_stats['pnl']:,.2f} ({week_stats['pnl_pct']:+.1f}%)\n"
-                    weekly_text += f"   🔄 Trades: {week_stats['trades']}\n\n"
-                    
-                    total_pnl += week_stats['pnl']
-                    total_trades += week_stats['trades']
-                    trading_weeks += 1
-                else:
-                    # Week with no trades
-                    weekly_text += f"📈 <b>{week_stats['week_formatted']}</b>\n"
-                    weekly_text += f"   📭 No completed trades\n\n"
-            
-            # Add summary
-            if trading_weeks > 0:
-                total_pnl_emoji = "🟢" if total_pnl >= 0 else "🔴"
-                weekly_text += f"💼 <b>10-Week Summary:</b>\n"
-                weekly_text += f"   {total_pnl_emoji} Total P&L: ${total_pnl:,.2f}\n"
-                weekly_text += f"   🔄 Total Trades: {total_trades}\n"
-                weekly_text += f"   📈 Trading Weeks: {trading_weeks}/10\n"
-                weekly_text += f"   📊 Avg per Trading Week: ${total_pnl/trading_weeks:,.2f}"
-            else:
-                weekly_text += f"💼 <b>10-Week Summary:</b>\n"
-                weekly_text += f"   📭 No completed trades in the last 10 weeks\n"
-                weekly_text += f"   💡 Start trading to see weekly performance!"
-            
-            await update.message.reply_text(weekly_text.strip(), parse_mode='HTML')
-            
-        except Exception as e:
-            error_message = f"❌ Error processing weekly command: {str(e)}"
-            await update.message.reply_text(error_message)
-            logger.error(f"Error in weekly command: {e}")
-
-    async def monthly_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle the /monthly command to show monthly performance stats."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        try:
-            monthly_stats = self.stats.get_monthly_stats(10)
-            
-            if not monthly_stats:
-                await update.message.reply_text(
-                    "📆 <b>Monthly Performance</b>\n\n"
-                    "📭 No monthly performance data available yet.\n\n"
-                    "💡 Monthly stats are calculated from completed trades.\n"
-                    "Start trading to see monthly performance!",
-                    parse_mode='HTML'
-                )
-                return
-            
-            monthly_text = "📆 <b>Monthly Performance (Last 10 Months)</b>\n\n"
-            
-            total_pnl = 0
-            total_trades = 0
-            trading_months = 0
-            
-            for month_stats in monthly_stats:
-                if month_stats['has_trades']:
-                    # Month with completed trades
-                    pnl_emoji = "🟢" if month_stats['pnl'] >= 0 else "🔴"
-                    monthly_text += f"📅 <b>{month_stats['month_formatted']}</b>\n"
-                    monthly_text += f"   {pnl_emoji} P&L: ${month_stats['pnl']:,.2f} ({month_stats['pnl_pct']:+.1f}%)\n"
-                    monthly_text += f"   🔄 Trades: {month_stats['trades']}\n\n"
-                    
-                    total_pnl += month_stats['pnl']
-                    total_trades += month_stats['trades']
-                    trading_months += 1
-                else:
-                    # Month with no trades
-                    monthly_text += f"📅 <b>{month_stats['month_formatted']}</b>\n"
-                    monthly_text += f"   📭 No completed trades\n\n"
-            
-            # Add summary
-            if trading_months > 0:
-                total_pnl_emoji = "🟢" if total_pnl >= 0 else "🔴"
-                monthly_text += f"💼 <b>10-Month Summary:</b>\n"
-                monthly_text += f"   {total_pnl_emoji} Total P&L: ${total_pnl:,.2f}\n"
-                monthly_text += f"   🔄 Total Trades: {total_trades}\n"
-                monthly_text += f"   📈 Trading Months: {trading_months}/10\n"
-                monthly_text += f"   📊 Avg per Trading Month: ${total_pnl/trading_months:,.2f}"
-            else:
-                monthly_text += f"💼 <b>10-Month Summary:</b>\n"
-                monthly_text += f"   📭 No completed trades in the last 10 months\n"
-                monthly_text += f"   💡 Start trading to see monthly performance!"
-            
-            await update.message.reply_text(monthly_text.strip(), parse_mode='HTML')
-            
-        except Exception as e:
-            error_message = f"❌ Error processing monthly command: {str(e)}"
-            await update.message.reply_text(error_message)
-            logger.error(f"Error in monthly command: {e}")
-    
-    async def risk_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle the /risk command to show advanced risk metrics."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        try:
-            # Get current balance for context
-            balance = self.client.get_balance()
-            current_balance = 0
-            if balance and balance.get('total'):
-                current_balance = float(balance['total'].get('USDC', 0))
-            
-            # Get risk metrics and basic stats
-            risk_metrics = self.stats.get_risk_metrics()
-            basic_stats = self.stats.get_basic_stats()
-            
-            # Check if we have enough data for risk calculations
-            if basic_stats['completed_trades'] < 2:
-                await update.message.reply_text(
-                    "📊 <b>Risk Analysis</b>\n\n"
-                    "📭 <b>Insufficient Data</b>\n\n"
-                    f"• Current completed trades: {basic_stats['completed_trades']}\n"
-                    f"• Required for risk analysis: 2+ trades\n"
-                    f"• Daily balance snapshots: {len(self.stats.data.get('daily_balances', []))}\n\n"
-                    "💡 <b>To enable risk analysis:</b>\n"
-                    "• Complete more trades to generate returns data\n"
-                    "• Bot automatically records daily balance snapshots\n"
-                    "• Risk metrics will be available after sufficient trading history\n\n"
-                    "📈 Use /stats for current performance metrics",
-                    parse_mode='HTML'
-                )
-                return
-            
-            # Format the risk analysis message
-            risk_text = f"""
-📊 <b>Risk Analysis & Advanced Metrics</b>
-
-🎯 <b>Risk-Adjusted Performance:</b>
-• Sharpe Ratio: {risk_metrics['sharpe_ratio']:.3f}
-• Sortino Ratio: {risk_metrics['sortino_ratio']:.3f}
-• Annual Volatility: {risk_metrics['volatility']:.2f}%
-
-📉 <b>Drawdown Analysis:</b>
-• Maximum Drawdown: {risk_metrics['max_drawdown']:.2f}%
-• Value at Risk (95%): {risk_metrics['var_95']:.2f}%
-
-💰 <b>Portfolio Context:</b>
-• Current Balance: ${current_balance:,.2f}
-• Initial Balance: ${basic_stats['initial_balance']:,.2f}
-• Total P&L: ${basic_stats['total_pnl']:,.2f}
-• Days Active: {basic_stats['days_active']}
-
-📊 <b>Risk Interpretation:</b>
-"""
-            
-            # Add interpretive guidance
-            sharpe = risk_metrics['sharpe_ratio']
-            if sharpe > 2.0:
-                risk_text += "• 🟢 <b>Excellent</b> risk-adjusted returns (Sharpe > 2.0)\n"
-            elif sharpe > 1.0:
-                risk_text += "• 🟡 <b>Good</b> risk-adjusted returns (Sharpe > 1.0)\n"
-            elif sharpe > 0.5:
-                risk_text += "• 🟠 <b>Moderate</b> risk-adjusted returns (Sharpe > 0.5)\n"
-            elif sharpe > 0:
-                risk_text += "• 🔴 <b>Poor</b> risk-adjusted returns (Sharpe > 0)\n"
-            else:
-                risk_text += "• ⚫ <b>Negative</b> risk-adjusted returns (Sharpe < 0)\n"
-            
-            max_dd = risk_metrics['max_drawdown']
-            if max_dd < 5:
-                risk_text += "• 🟢 <b>Low</b> maximum drawdown (< 5%)\n"
-            elif max_dd < 15:
-                risk_text += "• 🟡 <b>Moderate</b> maximum drawdown (< 15%)\n"
-            elif max_dd < 30:
-                risk_text += "• 🟠 <b>High</b> maximum drawdown (< 30%)\n"
-            else:
-                risk_text += "• 🔴 <b>Very High</b> maximum drawdown (> 30%)\n"
-            
-            volatility = risk_metrics['volatility']
-            if volatility < 10:
-                risk_text += "• 🟢 <b>Low</b> portfolio volatility (< 10%)\n"
-            elif volatility < 25:
-                risk_text += "• 🟡 <b>Moderate</b> portfolio volatility (< 25%)\n"
-            elif volatility < 50:
-                risk_text += "• 🟠 <b>High</b> portfolio volatility (< 50%)\n"
-            else:
-                risk_text += "• 🔴 <b>Very High</b> portfolio volatility (> 50%)\n"
-            
-            risk_text += f"""
-💡 <b>Risk Definitions:</b>
-• <b>Sharpe Ratio:</b> Risk-adjusted return (excess return / volatility)
-• <b>Sortino Ratio:</b> Return / downside volatility (focuses on bad volatility)
-• <b>Max Drawdown:</b> Largest peak-to-trough decline
-• <b>VaR 95%:</b> Maximum expected loss 95% of the time
-• <b>Volatility:</b> Annualized standard deviation of returns
-
-📈 <b>Data Based On:</b>
-• Completed Trades: {basic_stats['completed_trades']}
-• Daily Balance Records: {len(self.stats.data.get('daily_balances', []))}
-• Trading Period: {basic_stats['days_active']} days
-
-🔄 Use /stats for trading performance metrics
-            """
-            
-            await update.message.reply_text(risk_text.strip(), parse_mode='HTML')
-            
-        except Exception as e:
-            error_message = f"❌ Error processing risk command: {str(e)}"
-            await update.message.reply_text(error_message)
-            logger.error(f"Error in risk command: {e}")
-    
-    async def version_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle the /version command to show bot version and system info."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        try:
-            # Get system info
-            import platform
-            import sys
-            from datetime import datetime
-            
-            uptime_info = "Unknown"
-            try:
-                # Try to get process uptime if available
-                import psutil
-                process = psutil.Process()
-                create_time = datetime.fromtimestamp(process.create_time())
-                uptime = datetime.now() - create_time
-                days = uptime.days
-                hours, remainder = divmod(uptime.seconds, 3600)
-                minutes, _ = divmod(remainder, 60)
-                uptime_info = f"{days}d {hours}h {minutes}m"
-            except ImportError:
-                # psutil not available, skip uptime
-                pass
-            
-            # Get stats info
-            basic_stats = self.stats.get_basic_stats()
-            
-            # Safety checks for monitoring attributes
-            order_monitoring_task = getattr(self, 'order_monitoring_task', None)
-            alarms = getattr(self, 'alarms', [])
-            
-            version_text = f"""
-🤖 <b>Trading Bot Version & System Info</b>
-
-📱 <b>Bot Information:</b>
-• Version: <code>{self.version}</code>
-• Network: {'Testnet' if Config.HYPERLIQUID_TESTNET else 'Mainnet'}
-• Uptime: {uptime_info}
-• Default Token: {Config.DEFAULT_TRADING_TOKEN}
-
-💻 <b>System Information:</b>
-• Python: {sys.version.split()[0]}
-• Platform: {platform.system()} {platform.release()}
-• Architecture: {platform.machine()}
-
-📊 <b>Trading Stats:</b>
-• Total Orders: {basic_stats['total_trades']}
-• Completed Trades: {basic_stats['completed_trades']}
-• Days Active: {basic_stats['days_active']}
-• Start Date: {basic_stats['start_date']}
-
-🔄 <b>Monitoring Status:</b>
-• Order Monitoring: {'✅ Active' if order_monitoring_task and not order_monitoring_task.done() else '❌ Inactive'}
-• External Trades: ✅ Active
-• Price Alarms: ✅ Active ({len(alarms)} active)
-• Risk Management: {'✅ Enabled' if Config.RISK_MANAGEMENT_ENABLED else '❌ Disabled'}
-
-⏰ <b>Current Time:</b> {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
-            """
-            
-            await update.message.reply_text(version_text.strip(), parse_mode='HTML')
-            
-        except Exception as e:
-            error_message = f"❌ Error processing version command: {str(e)}"
-            await update.message.reply_text(error_message)
-            logger.error(f"Error in version command: {e}")
-    
-    async def balance_adjustments_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle the /balance_adjustments command to show deposit/withdrawal history."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        try:
-            # Get balance adjustments summary
-            adjustments_summary = self.stats.get_balance_adjustments_summary()
-            
-            # Get detailed adjustments
-            all_adjustments = self.stats.data.get('balance_adjustments', [])
-            
-            if not all_adjustments:
-                await update.message.reply_text(
-                    "💰 <b>Balance Adjustments</b>\n\n"
-                    "📭 No deposits or withdrawals detected yet.\n\n"
-                    "💡 The bot automatically monitors for deposits and withdrawals\n"
-                    "every hour to maintain accurate P&L calculations.",
-                    parse_mode='HTML'
-                )
-                return
-            
-            # Format the message
-            adjustments_text = f"""
-💰 <b>Balance Adjustments History</b>
-
-📊 <b>Summary:</b>
-• Total Deposits: ${adjustments_summary['total_deposits']:,.2f}
-• Total Withdrawals: ${adjustments_summary['total_withdrawals']:,.2f}
-• Net Adjustment: ${adjustments_summary['net_adjustment']:,.2f}
-• Total Transactions: {adjustments_summary['adjustment_count']}
-
-📅 <b>Recent Adjustments:</b>
-"""
-            
-            # Show last 10 adjustments
-            recent_adjustments = sorted(all_adjustments, key=lambda x: x['timestamp'], reverse=True)[:10]
-            
-            for adj in recent_adjustments:
-                try:
-                    # Format timestamp
-                    adj_time = datetime.fromisoformat(adj['timestamp']).strftime('%m/%d %H:%M')
-                    
-                    # Format type and amount
-                    if adj['type'] == 'deposit':
-                        emoji = "💰"
-                        amount_str = f"+${adj['amount']:,.2f}"
-                    else:  # withdrawal
-                        emoji = "💸"
-                        amount_str = f"-${abs(adj['amount']):,.2f}"
-                    
-                    adjustments_text += f"• {emoji} {adj_time}: {amount_str}\n"
-                    
-                except Exception as adj_error:
-                    logger.warning(f"Error formatting adjustment: {adj_error}")
-                    continue
-            
-            adjustments_text += f"""
-
-💡 <b>How it Works:</b>
-• Bot checks for deposits/withdrawals every hour
-• Adjustments maintain accurate P&L calculations
-• Non-trading balance changes don't affect performance metrics
-• Trading statistics remain pure and accurate
-
-⏰ <b>Last Check:</b> {adjustments_summary['last_adjustment'][:16] if adjustments_summary['last_adjustment'] else 'Never'}
-            """
-            
-            await update.message.reply_text(adjustments_text.strip(), parse_mode='HTML')
-            
-        except Exception as e:
-            error_message = f"❌ Error processing balance adjustments command: {str(e)}"
-            await update.message.reply_text(error_message)
-            logger.error(f"Error in balance_adjustments command: {e}")
-
-    def _get_position_state(self, symbol: str) -> Dict[str, Any]:
-        """Get current position state for a symbol."""
-        if symbol not in self.position_tracker:
-            self.position_tracker[symbol] = {
-                'contracts': 0.0,
-                'avg_entry_price': 0.0,
-                'total_cost_basis': 0.0,
-                'entry_count': 0,
-                'entry_history': [],  # List of {price, amount, timestamp}
-                'last_update': datetime.now().isoformat()
-            }
-        return self.position_tracker[symbol]
-    
-    def _update_position_state(self, symbol: str, side: str, amount: float, price: float, timestamp: str = None):
-        """Update position state with a new trade."""
-        if timestamp is None:
-            timestamp = datetime.now().isoformat()
-            
-        position = self._get_position_state(symbol)
-        
-        if side.lower() == 'buy':
-            # Adding to long position or reducing short position
-            if position['contracts'] >= 0:
-                # Opening/adding to long position
-                new_cost = amount * price
-                old_cost = position['total_cost_basis']
-                old_contracts = position['contracts']
-                
-                position['contracts'] += amount
-                position['total_cost_basis'] += new_cost
-                position['avg_entry_price'] = position['total_cost_basis'] / position['contracts'] if position['contracts'] > 0 else 0
-                position['entry_count'] += 1
-                position['entry_history'].append({
-                    'price': price,
-                    'amount': amount,
-                    'timestamp': timestamp,
-                    'side': 'buy'
-                })
-                
-                logger.info(f"📈 Position updated: {symbol} LONG {position['contracts']:.6f} @ avg ${position['avg_entry_price']:.2f}")
-                
-                # Check if this is truly a position open vs increase
-                # For very small previous positions (< 0.001), consider it a new position open
-                if old_contracts < 0.001:
-                    return 'long_opened'
-                else:
-                    return 'long_increased'
-            else:
-                # Reducing short position
-                reduction = min(amount, abs(position['contracts']))
-                position['contracts'] += reduction
-                
-                if position['contracts'] >= 0:
-                    # Short position fully closed or flipped to long
-                    if position['contracts'] == 0:
-                        self._reset_position_state(symbol)
-                        return 'short_closed'
-                    else:
-                        # Flipped to long - need to track new long position
-                        remaining_amount = amount - reduction
-                        position['contracts'] = remaining_amount
-                        position['total_cost_basis'] = remaining_amount * price
-                        position['avg_entry_price'] = price
-                        return 'short_closed_and_long_opened'
-                else:
-                    return 'short_reduced'
-        
-        elif side.lower() == 'sell':
-            # Adding to short position or reducing long position
-            if position['contracts'] <= 0:
-                # Opening/adding to short position
-                old_contracts = abs(position['contracts'])
-                position['contracts'] -= amount
-                position['entry_count'] += 1
-                position['entry_history'].append({
-                    'price': price,
-                    'amount': amount,
-                    'timestamp': timestamp,
-                    'side': 'sell'
-                })
-                
-                logger.info(f"📉 Position updated: {symbol} SHORT {abs(position['contracts']):.6f} @ ${price:.2f}")
-                
-                # Check if this is truly a position open vs increase
-                # For very small previous positions (< 0.001), consider it a new position open
-                if old_contracts < 0.001:
-                    return 'short_opened'
-                else:
-                    return 'short_increased'
-            else:
-                # Reducing long position
-                reduction = min(amount, position['contracts'])
-                position['contracts'] -= reduction
-                
-                # Adjust cost basis proportionally
-                if position['contracts'] > 0:
-                    reduction_ratio = reduction / (position['contracts'] + reduction)
-                    position['total_cost_basis'] *= (1 - reduction_ratio)
-                    return 'long_reduced'
-                else:
-                    # Long position fully closed
-                    if position['contracts'] == 0:
-                        self._reset_position_state(symbol)
-                        return 'long_closed'
-                    else:
-                        # Flipped to short
-                        remaining_amount = amount - reduction
-                        position['contracts'] = -remaining_amount
-                        return 'long_closed_and_short_opened'
-        
-        position['last_update'] = timestamp
-        return 'unknown'
-    
-    def _reset_position_state(self, symbol: str):
-        """Reset position state when position is fully closed."""
-        if symbol in self.position_tracker:
-            del self.position_tracker[symbol]
-    
-    def _calculate_position_pnl(self, symbol: str, exit_amount: float, exit_price: float) -> Dict[str, float]:
-        """Calculate P&L for a position exit."""
-        position = self._get_position_state(symbol)
-        
-        if position['contracts'] == 0:
-            return {'pnl': 0.0, 'pnl_percent': 0.0}
-        
-        avg_entry = position['avg_entry_price']
-        
-        if position['contracts'] > 0:  # Long position
-            pnl = exit_amount * (exit_price - avg_entry)
-        else:  # Short position
-            pnl = exit_amount * (avg_entry - exit_price)
-        
-        cost_basis = exit_amount * avg_entry
-        pnl_percent = (pnl / cost_basis * 100) if cost_basis > 0 else 0
-        
-        return {
-            'pnl': pnl,
-            'pnl_percent': pnl_percent,
-            'avg_entry_price': avg_entry
-        }
-
-    async def _send_external_trade_notification(self, trade: Dict[str, Any]):
-        """Send generic notification for external trades (fallback)."""
-        try:
-            symbol = trade.get('symbol', '')
-            side = trade.get('side', '')
-            amount = float(trade.get('amount', 0))
-            price = float(trade.get('price', 0))
-            timestamp = trade.get('timestamp', '')
-            
-            # Extract token from symbol
-            token = symbol.split('/')[0] if '/' in symbol else symbol
-            
-            # Format timestamp
-            try:
-                trade_time = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
-                time_str = trade_time.strftime('%H:%M:%S')
-            except:
-                time_str = "Unknown"
-            
-            # Determine trade type and emoji
-            side_emoji = "🟢" if side.lower() == 'buy' else "🔴"
-            trade_value = amount * price
-            
-            message = f"""
-🔄 <b>External Trade Detected</b>
-
-📊 <b>Trade Details:</b>
-• Token: {token}
-• Side: {side.upper()}
-• Amount: {amount} {token}
-• Price: ${price:,.2f}
-• Value: ${trade_value:,.2f}
-
-{side_emoji} <b>Source:</b> External Platform Trade
-⏰ <b>Time:</b> {time_str}
-
-📈 <b>Note:</b> This trade was executed outside the Telegram bot
-📊 Stats have been automatically updated
-            """
-            
-            await self.send_message(message.strip())
-            logger.info(f"📢 Sent generic external trade notification: {side} {amount} {token}")
-            
-        except Exception as e:
-            logger.error(f"❌ Error sending external trade notification: {e}")
-
-    async def _check_stop_losses(self, current_positions: list):
-        """Check all positions for stop loss triggers and execute automatic exits."""
-        try:
-            if not current_positions:
-                return
-            
-            stop_loss_triggers = []
-            
-            for position in current_positions:
-                symbol = position.get('symbol')
-                contracts = float(position.get('contracts', 0))
-                entry_price = float(position.get('entryPx', 0))
-                
-                if not symbol or contracts == 0 or entry_price == 0:
-                    continue
-                
-                # Get current market price
-                market_data = self.client.get_market_data(symbol)
-                if not market_data or not market_data.get('ticker'):
-                    continue
-                
-                current_price = float(market_data['ticker'].get('last', 0))
-                if current_price == 0:
-                    continue
-                
-                # Calculate current P&L percentage
-                if contracts > 0:  # Long position
-                    pnl_percent = ((current_price - entry_price) / entry_price) * 100
-                else:  # Short position
-                    pnl_percent = ((entry_price - current_price) / entry_price) * 100
-                
-                # Check if stop loss should trigger
-                if pnl_percent <= -Config.STOP_LOSS_PERCENTAGE:
-                    token = symbol.split('/')[0] if '/' in symbol else symbol
-                    stop_loss_triggers.append({
-                        'symbol': symbol,
-                        'token': token,
-                        'contracts': contracts,
-                        'entry_price': entry_price,
-                        'current_price': current_price,
-                        'pnl_percent': pnl_percent
-                    })
-            
-            # Execute stop losses
-            for trigger in stop_loss_triggers:
-                await self._execute_automatic_stop_loss(trigger)
-                
-        except Exception as e:
-            logger.error(f"❌ Error checking stop losses: {e}")
-
-    async def _execute_automatic_stop_loss(self, trigger: Dict[str, Any]):
-        """Execute an automatic stop loss order."""
-        try:
-            symbol = trigger['symbol']
-            token = trigger['token']
-            contracts = trigger['contracts']
-            entry_price = trigger['entry_price']
-            current_price = trigger['current_price']
-            pnl_percent = trigger['pnl_percent']
-            
-            # Determine the exit side (opposite of position)
-            exit_side = 'sell' if contracts > 0 else 'buy'
-            contracts_abs = abs(contracts)
-            
-            # Send notification before executing
-            await self._send_stop_loss_notification(trigger, "triggered")
-            
-            # Execute the stop loss order (market order for immediate execution)
-            try:
-                if exit_side == 'sell':
-                    order = self.client.create_market_sell_order(symbol, contracts_abs)
-                else:
-                    order = self.client.create_market_buy_order(symbol, contracts_abs)
-                
-                if order:
-                    logger.info(f"🛑 Stop loss executed: {token} {exit_side} {contracts_abs} @ ${current_price}")
-                    
-                    # Record the trade in stats and update position tracking
-                    action_type = self.stats.record_trade_with_enhanced_tracking(symbol, exit_side, contracts_abs, current_price, order.get('id', 'stop_loss'), "auto_stop_loss")
-                    
-                    # Send success notification
-                    await self._send_stop_loss_notification(trigger, "executed", order)
-                else:
-                    logger.error(f"❌ Stop loss order failed for {token}")
-                    await self._send_stop_loss_notification(trigger, "failed")
-                    
-            except Exception as order_error:
-                logger.error(f"❌ Stop loss order execution failed for {token}: {order_error}")
-                await self._send_stop_loss_notification(trigger, "failed", error=str(order_error))
-                
-        except Exception as e:
-            logger.error(f"❌ Error executing automatic stop loss: {e}")
-
-    async def _send_stop_loss_notification(self, trigger: Dict[str, Any], status: str, order: Dict = None, error: str = None):
-        """Send notification for stop loss events."""
-        try:
-            token = trigger['token']
-            contracts = trigger['contracts']
-            entry_price = trigger['entry_price']
-            current_price = trigger['current_price']
-            pnl_percent = trigger['pnl_percent']
-            
-            position_type = "LONG" if contracts > 0 else "SHORT"
-            contracts_abs = abs(contracts)
-            
-            if status == "triggered":
-                title = "🛑 Stop Loss Triggered"
-                status_text = f"Stop loss triggered at {Config.STOP_LOSS_PERCENTAGE}% loss"
-                emoji = "🚨"
-            elif status == "executed":
-                title = "✅ Stop Loss Executed"
-                status_text = "Position closed automatically"
-                emoji = "🛑"
-            elif status == "failed":
-                title = "❌ Stop Loss Failed"
-                status_text = f"Stop loss execution failed{': ' + error if error else ''}"
-                emoji = "⚠️"
-            else:
-                return
-            
-            # Calculate loss
-            loss_value = contracts_abs * abs(current_price - entry_price)
-            
-            message = f"""
-{title}
-
-{emoji} <b>Risk Management Alert</b>
-
-📊 <b>Position Details:</b>
-• Token: {token}
-• Direction: {position_type}
-• Size: {contracts_abs} contracts
-• Entry Price: ${entry_price:,.2f}
-• Current Price: ${current_price:,.2f}
-
-🔴 <b>Loss Details:</b>
-• Loss: ${loss_value:,.2f} ({pnl_percent:.2f}%)
-• Stop Loss Threshold: {Config.STOP_LOSS_PERCENTAGE}%
-
-📋 <b>Action:</b> {status_text}
-⏰ <b>Time:</b> {datetime.now().strftime('%H:%M:%S')}
-            """
-            
-            if order and status == "executed":
-                order_id = order.get('id', 'N/A')
-                message += f"\n🆔 <b>Order ID:</b> {order_id}"
-            
-            await self.send_message(message.strip())
-            logger.info(f"📢 Sent stop loss notification: {token} {status}")
-            
-        except Exception as e:
-            logger.error(f"❌ Error sending stop loss notification: {e}")
-
-    async def _process_filled_orders(self, filled_order_ids: set, current_positions: list):
-        """Process filled orders using enhanced position tracking."""
-        try:
-            # For bot-initiated orders, we'll detect changes in position size
-            # and send appropriate notifications using the enhanced system
-            
-            # This method will be triggered when orders placed through the bot are filled
-            # The external trade monitoring will handle trades made outside the bot
-            
-            # Update position tracking based on current positions
-            await self._update_position_tracking(current_positions)
-                
-        except Exception as e:
-            logger.error(f"❌ Error processing filled orders: {e}")
-    
-    async def _update_position_tracking(self, current_positions: list):
-        """Update the legacy position tracking data for compatibility."""
-        new_position_map = {}
-        
-        for position in current_positions:
-            symbol = position.get('symbol')
-            contracts = float(position.get('contracts', 0))
-            entry_price = float(position.get('entryPx', 0))
-            
-            if symbol and contracts != 0:
-                new_position_map[symbol] = {
-                    'contracts': contracts,
-                    'entry_price': entry_price
-                }
-                
-                # Also update our enhanced position tracker if not already present
-                if symbol not in self.position_tracker:
-                    self._get_position_state(symbol)
-                    self.position_tracker[symbol]['contracts'] = contracts
-                    self.position_tracker[symbol]['avg_entry_price'] = entry_price
-                    self.position_tracker[symbol]['total_cost_basis'] = contracts * entry_price
-        
-        self.last_known_positions = new_position_map
-
-    async def keyboard_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle the /keyboard command to enable/show custom keyboard."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        custom_keyboard = self._create_custom_keyboard()
-        
-        if custom_keyboard:
-            await update.message.reply_text(
-                "⌨️ <b>Custom Keyboard Activated!</b>\n\n"
-                "🎯 <b>Your quick buttons are now ready:</b>\n"
-                "• Daily - Daily performance\n"
-                "• Performance - Performance stats\n"
-                "• Balance - Account balance\n"
-                "• Stats - Trading statistics\n"
-                "• Positions - Open positions\n"
-                "• Orders - Active orders\n"
-                "• Price - Quick price check\n"
-                "• Market - Market overview\n"
-                "• Help - Help guide\n"
-                "• Commands - Command menu\n\n"
-                "💡 <b>How to use:</b>\n"
-                "Tap any button below instead of typing the command manually!\n\n"
-                "🔧 These buttons will stay at the bottom of your chat.",
-                parse_mode='HTML',
-                reply_markup=custom_keyboard
-            )
-        else:
-            await update.message.reply_text(
-                "❌ <b>Custom Keyboard Disabled</b>\n\n"
-                "🔧 <b>To enable:</b>\n"
-                "• Set TELEGRAM_CUSTOM_KEYBOARD_ENABLED=true in your .env file\n"
-                "• Restart the bot\n"
-                "• Run /keyboard again\n\n"
-                f"📋 <b>Current config:</b>\n"
-                f"• Enabled: {Config.TELEGRAM_CUSTOM_KEYBOARD_ENABLED}\n"
-                f"• Layout: {Config.TELEGRAM_CUSTOM_KEYBOARD_LAYOUT}",
-                parse_mode='HTML'
-            )
-
-    async def handle_keyboard_message(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Handle messages from custom keyboard buttons."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        message_text = update.message.text.lower()
-        
-        # Map keyboard button text to commands
-        command_map = {
-            'balance': '/balance',
-            'positions': '/positions',
-            'orders': '/orders',
-            'stats': '/stats',
-            'trades': '/trades',
-            'market': '/market',
-            'price': '/price',
-            'help': '/help',
-            'commands': '/commands',
-            'monitoring': '/monitoring',
-            'logs': '/logs',
-            'performance': '/performance',
-            'daily': '/daily',
-            'weekly': '/weekly',
-            'monthly': '/monthly',
-            'risk': '/risk',
-            'alarm': '/alarm',
-            'keyboard': '/keyboard'
-        }
-        
-        # Check if the message matches any keyboard command
-        if message_text in command_map:
-            # Create a fake update object with the corresponding command
-            update.message.text = command_map[message_text]
-            # Get the handler for this command and call it
-            handlers = self.application.handlers[0]  # Get default group handlers
-            for handler in handlers:
-                if hasattr(handler, 'callback') and hasattr(handler, 'filters'):
-                    if await handler.check_update(update):
-                        await handler.callback(update, context)
-                        return
-        
-        # If no keyboard command matched, show a help message
-        await update.message.reply_text("❓ Unknown command. Use /help to see available commands.")
-
-    async def debug_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
-        """Debug command to show internal bot state."""
-        if not self.is_authorized(update.effective_chat.id):
-            await update.message.reply_text("❌ Unauthorized access.")
-            return
-        
-        try:
-            # Get bot state information
-            debug_info = f"🔧 <b>Bot Debug Information</b>\\n\\n"
-            
-            # Bot version and status
-            debug_info += f"🤖 <b>Version:</b> {self.version}\\n"
-            debug_info += f"⚡ <b>Status:</b> Running\\n"
-            
-            # Position tracking information
-            debug_info += f"\\n📊 <b>Position State Tracking:</b>\\n"
-            if hasattr(self, '_internal_positions') and self._internal_positions:
-                for symbol, pos_state in self._internal_positions.items():
-                    token = symbol.split('/')[0] if '/' in symbol else symbol
-                    contracts = pos_state.get('contracts', 0)
-                    avg_price = pos_state.get('average_price', 0)
-                    debug_info += f"  • {token}: {contracts:.6f} @ ${avg_price:.2f}\\n"
-            else:
-                debug_info += f"  No internal position tracking data\\n"
-            
-            # Get raw Hyperliquid position data for comparison
-            debug_info += f"\\n🔍 <b>Raw Hyperliquid Position Data:</b>\\n"
-            raw_positions = self.client.get_positions()
-            if raw_positions:
-                for pos in raw_positions:
-                    if float(pos.get('contracts', 0)) != 0:
-                        symbol = pos.get('symbol', 'Unknown')
-                        contracts = pos.get('contracts', 0)
-                        side = pos.get('side', 'Unknown')
-                        entry_price = pos.get('entryPrice', 0)
-                        unrealized_pnl = pos.get('unrealizedPnl', 0)
-                        notional = pos.get('notional', 0)
-                        
-                        # Get raw Hyperliquid data from info field
-                        raw_info = pos.get('info', {})
-                        raw_position = raw_info.get('position', {}) if raw_info else {}
-                        raw_szi = raw_position.get('szi', 'N/A')
-                        
-                        token = symbol.split('/')[0] if '/' in symbol else symbol
-                        debug_info += f"  • <b>{token}:</b>\\n"
-                        debug_info += f"    - CCXT contracts: {contracts} (always positive)\\n"
-                        debug_info += f"    - CCXT side: {side}\\n"
-                        debug_info += f"    - Raw Hyperliquid szi: {raw_szi} (negative = short)\\n"
-                        debug_info += f"    - Entry price: ${entry_price}\\n"
-                        debug_info += f"    - Unrealized P&L: ${unrealized_pnl}\\n"
-                        debug_info += f"    - Notional: ${notional}\\n"
-                        
-                        # Show what the bot would interpret this as
-                        side_field = pos.get('side', '').lower()
-                        if side_field in ['long', 'short']:
-                            interpreted_direction = side_field.upper()
-                        else:
-                            interpreted_direction = "LONG" if float(contracts) > 0 else "SHORT"
-                        debug_info += f"    - Bot interprets as: {interpreted_direction} (using CCXT side field)\\n\\n"
-            else:
-                debug_info += f"  No positions found or API error\\n"
-            
-            # Show monitoring status
-            debug_info += f"\\n🔍 <b>Monitoring:</b> {'Active' if self.monitoring_active else 'Inactive'}\\n"
-            debug_info += f"📋 <b>Tracked Orders:</b> {len(self.last_known_orders)}\\n"
-            debug_info += f"🤖 <b>Bot Trade IDs:</b> {len(self.bot_trade_ids)}\\n"
-            if self.bot_trade_ids:
-                debug_info += "  Recent bot trades: " + ", ".join(list(self.bot_trade_ids)[-5:]) + "\\n"
-            
-            await update.message.reply_text(debug_info, parse_mode='HTML')
-            
-        except Exception as e:
-            logger.error(f"❌ Error in debug command: {e}")
-            await update.message.reply_text(f"❌ Debug error: {e}")
-
-
-async def main_async():
-    """Async main entry point for the Telegram bot."""
-    try:
-        # Validate configuration
-        if not Config.validate():
-            logger.error("❌ Configuration validation failed!")
-            return
-        
-        if not Config.TELEGRAM_ENABLED:
-            logger.error("❌ Telegram is not enabled in configuration")
-            return
-        
-        # Create and run the bot
-        bot = TelegramTradingBot()
-        await bot.run()
-        
-    except KeyboardInterrupt:
-        logger.info("👋 Bot stopped by user")
-    except Exception as e:
-        logger.error(f"❌ Unexpected error: {e}")
-        raise
-
-
-def main():
-    """Main entry point for the Telegram bot."""
-    try:
-        # Check if we're already in an asyncio context
-        try:
-            loop = asyncio.get_running_loop()
-            # If we get here, we're already in an asyncio context
-            logger.error("❌ Cannot run main() from within an asyncio context. Use main_async() instead.")
-            return
-        except RuntimeError:
-            # No running loop, safe to use asyncio.run()
-            pass
-        
-        # Run the async main function
-        asyncio.run(main_async())
-        
-    except Exception as e:
-        logger.error(f"❌ Failed to start telegram bot: {e}")
-        raise
-
-
-if __name__ == "__main__":
-    main() 

+ 0 - 1579
src/backup/trading_stats_original_backup.py

@@ -1,1579 +0,0 @@
-#!/usr/bin/env python3
-# MOVED TO src/trading/stats/ - This file kept for reference
-# Use: from src.stats import TradingStats
-#!/usr/bin/env python3
-"""
-Trading Statistics Tracker (SQLite Version)
-
-Tracks and calculates comprehensive trading statistics using an SQLite database.
-"""
-
-import sqlite3
-import os
-import logging
-from datetime import datetime, timedelta, timezone
-from typing import Dict, List, Any, Optional, Tuple, Union
-import math
-from collections import defaultdict
-import uuid
-import numpy as np # Ensure numpy is imported as np
-
-# 🆕 Import the migration runner
-from src.migrations.migrate_db import run_migrations as run_db_migrations
-from src.utils.token_display_formatter import get_formatter # Added import
-from src.config.config import Config
-
-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."""
-
-    def __init__(self, db_path: str = "data/trading_stats.sqlite"):
-        """Initialize the stats tracker and connect to SQLite DB."""
-        self.db_path = db_path
-        self._ensure_data_directory()
-        
-        # 🆕 Run database migrations before connecting and creating tables
-        # This ensures the schema is up-to-date when the connection is made
-        # and tables are potentially created for the first time.
-        logger.info("Running database migrations if needed...")
-        run_db_migrations(self.db_path) # Pass the correct db_path
-        logger.info("Database migration check complete.")
-        
-        self.conn = sqlite3.connect(self.db_path, detect_types=sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES)
-        self.conn.row_factory = self._dict_factory
-        self._create_tables() # CREATE IF NOT EXISTS will still be useful for first-time setup
-        self._initialize_metadata() # Also potentially sets schema_version if DB was just created
-
-        # 🆕 Purge old daily aggregated stats on startup
-        self.purge_old_daily_aggregated_stats()
-        # 🆕 Purge old balance history on startup
-        self.purge_old_balance_history()
-
-    def _dict_factory(self, cursor, row):
-        """Convert SQLite rows to dictionaries."""
-        d = {}
-        for idx, col in enumerate(cursor.description):
-            d[col[0]] = row[idx]
-        return d
-
-    def _ensure_data_directory(self):
-        """Ensure the data directory for the SQLite file exists."""
-        data_dir = os.path.dirname(self.db_path)
-        if data_dir and not os.path.exists(data_dir):
-            os.makedirs(data_dir)
-            logger.info(f"Created data directory for TradingStats DB: {data_dir}")
-
-    def _execute_query(self, query: str, params: tuple = ()):
-        """Execute a query (INSERT, UPDATE, DELETE)."""
-        with self.conn:
-            self.conn.execute(query, params)
-
-    def _fetch_query(self, query: str, params: tuple = ()) -> List[Dict[str, Any]]:
-        """Execute a SELECT query and fetch all results."""
-        cur = self.conn.cursor()
-        cur.execute(query, params)
-        return cur.fetchall()
-
-    def _fetchone_query(self, query: str, params: tuple = ()) -> Optional[Dict[str, Any]]:
-        """Execute a SELECT query and fetch one result."""
-        cur = self.conn.cursor()
-        cur.execute(query, params)
-        return cur.fetchone()
-
-    def _create_tables(self):
-        """Create SQLite tables if they don't exist."""
-        queries = [
-            """
-            CREATE TABLE IF NOT EXISTS metadata (
-                key TEXT PRIMARY KEY,
-                value TEXT
-            )
-            """,
-            """
-            CREATE TABLE IF NOT EXISTS trades (
-                id INTEGER PRIMARY KEY AUTOINCREMENT,
-                exchange_fill_id TEXT UNIQUE,
-                timestamp TEXT NOT NULL,
-                symbol TEXT NOT NULL,
-                side TEXT NOT NULL,
-                amount REAL NOT NULL,
-                price REAL NOT NULL,
-                value REAL NOT NULL,
-                trade_type TEXT NOT NULL,
-                pnl REAL DEFAULT 0.0,
-                linked_order_table_id INTEGER,
-                
-                -- 🆕 PHASE 4: Lifecycle tracking fields (merged from active_trades)
-                status TEXT DEFAULT 'executed', -- 'pending', 'executed', 'position_opened', 'position_closed', 'cancelled'
-                trade_lifecycle_id TEXT, -- Groups related trades into one lifecycle
-                position_side TEXT, -- 'long', 'short', 'flat' - the resulting position side
-                
-                -- Position tracking
-                entry_price REAL,
-                current_position_size REAL DEFAULT 0,
-                
-                -- Order IDs (exchange IDs)
-                entry_order_id TEXT,
-                stop_loss_order_id TEXT,
-                take_profit_order_id TEXT,
-                
-                -- Risk management
-                stop_loss_price REAL,
-                take_profit_price REAL,
-                
-                -- P&L tracking
-                realized_pnl REAL DEFAULT 0,
-                unrealized_pnl REAL DEFAULT 0,
-                mark_price REAL DEFAULT 0,
-                position_value REAL DEFAULT NULL,
-                unrealized_pnl_percentage REAL DEFAULT NULL, 
-                
-                -- Risk Info from Exchange
-                liquidation_price REAL DEFAULT NULL,
-                margin_used REAL DEFAULT NULL,
-                leverage REAL DEFAULT NULL,
-                
-                -- Timestamps
-                position_opened_at TEXT,
-                position_closed_at TEXT,
-                updated_at TEXT DEFAULT CURRENT_TIMESTAMP,
-                
-                -- Notes
-                notes TEXT
-            )
-            """,
-            """
-            CREATE TABLE IF NOT EXISTS balance_history (
-                timestamp TEXT PRIMARY KEY,
-                balance REAL NOT NULL
-            )
-            """,
-            """
-            CREATE TABLE IF NOT EXISTS balance_adjustments (
-                id INTEGER PRIMARY KEY AUTOINCREMENT,
-                adjustment_id TEXT UNIQUE,
-                timestamp TEXT NOT NULL,
-                type TEXT NOT NULL, -- 'deposit' or 'withdrawal'
-                amount REAL NOT NULL, -- Always positive, type indicates direction
-                description TEXT
-            )
-            """,
-            """
-            CREATE TABLE IF NOT EXISTS orders (
-                id INTEGER PRIMARY KEY AUTOINCREMENT,
-                bot_order_ref_id TEXT UNIQUE,
-                exchange_order_id TEXT UNIQUE,
-                symbol TEXT NOT NULL,
-                side TEXT NOT NULL,
-                type TEXT NOT NULL,
-                amount_requested REAL NOT NULL,
-                amount_filled REAL DEFAULT 0.0,
-                price REAL, -- For limit, stop, etc.
-                status TEXT NOT NULL, -- e.g., 'open', 'partially_filled', 'filled', 'cancelled', 'rejected', 'expired', 'pending_trigger'
-                timestamp_created TEXT NOT NULL,
-                timestamp_updated TEXT NOT NULL,
-                parent_bot_order_ref_id TEXT NULLABLE -- To link conditional orders (like SL triggers) to their parent order
-            )
-            """,
-            """
-            CREATE INDEX IF NOT EXISTS idx_orders_bot_order_ref_id ON orders (bot_order_ref_id);
-            """,
-            """
-            CREATE INDEX IF NOT EXISTS idx_orders_exchange_order_id ON orders (exchange_order_id);
-            """,
-            """
-            CREATE INDEX IF NOT EXISTS idx_trades_exchange_fill_id ON trades (exchange_fill_id);
-            """,
-            """
-            CREATE INDEX IF NOT EXISTS idx_trades_linked_order_table_id ON trades (linked_order_table_id);
-            """,
-            """
-            CREATE INDEX IF NOT EXISTS idx_orders_parent_bot_order_ref_id ON orders (parent_bot_order_ref_id);
-            """,
-            """
-            CREATE INDEX IF NOT EXISTS idx_orders_status_type ON orders (status, type);
-            """,
-            """
-            CREATE INDEX IF NOT EXISTS idx_trades_status ON trades (status);
-            """,
-            """
-            CREATE INDEX IF NOT EXISTS idx_trades_lifecycle_id ON trades (trade_lifecycle_id);
-            """,
-            """
-            CREATE INDEX IF NOT EXISTS idx_trades_position_side ON trades (position_side);
-            """,
-            """
-            CREATE INDEX IF NOT EXISTS idx_trades_symbol_status ON trades (symbol, status);
-            """,
-            """
-            CREATE TABLE IF NOT EXISTS daily_balances (
-                date TEXT PRIMARY KEY,
-                balance REAL NOT NULL,
-                timestamp TEXT NOT NULL
-            )
-            """,
-        ]
-        # 🆕 Add new table creation queries
-        queries.extend([
-            """
-            CREATE TABLE IF NOT EXISTS token_stats (
-                token TEXT PRIMARY KEY,
-                total_realized_pnl REAL DEFAULT 0.0,
-                total_completed_cycles INTEGER DEFAULT 0,
-                winning_cycles INTEGER DEFAULT 0,
-                losing_cycles INTEGER DEFAULT 0,
-                total_entry_volume REAL DEFAULT 0.0, -- Sum of (amount * entry_price) for completed cycles
-                total_exit_volume REAL DEFAULT 0.0, -- Sum of (amount * exit_price) for completed cycles
-                sum_of_winning_pnl REAL DEFAULT 0.0,
-                sum_of_losing_pnl REAL DEFAULT 0.0, -- Stored as a positive value
-                largest_winning_cycle_pnl REAL DEFAULT 0.0,
-                largest_losing_cycle_pnl REAL DEFAULT 0.0, -- Stored as a positive value
-                first_cycle_closed_at TEXT,
-                last_cycle_closed_at TEXT,
-                total_cancelled_cycles INTEGER DEFAULT 0, -- Count of lifecycles that ended in 'cancelled'
-                updated_at TEXT DEFAULT CURRENT_TIMESTAMP,
-                total_duration_seconds INTEGER DEFAULT 0
-            )
-            """,
-            """
-            CREATE TABLE IF NOT EXISTS daily_aggregated_stats (
-                date TEXT NOT NULL, -- YYYY-MM-DD
-                token TEXT NOT NULL, -- Specific token or a general identifier like '_OVERALL_'
-                realized_pnl REAL DEFAULT 0.0,
-                completed_cycles INTEGER DEFAULT 0,
-                entry_volume REAL DEFAULT 0.0,
-                exit_volume REAL DEFAULT 0.0,
-                PRIMARY KEY (date, token)
-            )
-            """,
-            """
-            CREATE INDEX IF NOT EXISTS idx_daily_stats_date_token ON daily_aggregated_stats (date, token);
-            """
-        ])
-        for query in queries:
-            self._execute_query(query)
-        
-        logger.info("SQLite tables ensured for TradingStats.")
-
-    def _initialize_metadata(self):
-        """Initialize metadata if not already present."""
-        start_date = self._get_metadata('start_date')
-        initial_balance = self._get_metadata('initial_balance')
-
-        if start_date is None:
-            self._set_metadata('start_date', datetime.now(timezone.utc).isoformat())
-            logger.info("Initialized 'start_date' in metadata.")
-        
-        if initial_balance is None:
-            self._set_metadata('initial_balance', '0.0')
-            logger.info("Initialized 'initial_balance' in metadata.")
-        
-        logger.info(f"TradingStats initialized. Start Date: {self._get_metadata('start_date')}, Initial Balance: {self._get_metadata('initial_balance')}")
-
-    def _get_metadata(self, key: str) -> Optional[str]:
-        """Retrieve a value from the metadata table."""
-        row = self._fetchone_query("SELECT value FROM metadata WHERE key = ?", (key,))
-        return row['value'] if row else None
-
-    def _set_metadata(self, key: str, value: str):
-        """Set a value in the metadata table."""
-        self._execute_query("INSERT OR REPLACE INTO metadata (key, value) VALUES (?, ?)", (key, value))
-
-    def set_initial_balance(self, balance: float):
-        """Set the initial balance if not already set or zero."""
-        current_initial_balance_str = self._get_metadata('initial_balance')
-        current_initial_balance = float(current_initial_balance_str) if current_initial_balance_str else 0.0
-        
-        if current_initial_balance == 0.0: # Only set if it's effectively unset
-            self._set_metadata('initial_balance', str(balance))
-            # Also set start_date if it's the first time setting balance
-            if self._get_metadata('start_date') is None or float(current_initial_balance_str if current_initial_balance_str else '0.0') == 0.0:
-                 self._set_metadata('start_date', datetime.now(timezone.utc).isoformat())
-            formatter = get_formatter()
-            logger.info(f"Initial balance set to: {formatter.format_price_with_symbol(balance)}")
-        else:
-            formatter = get_formatter()
-            logger.info(f"Initial balance already set to {formatter.format_price_with_symbol(current_initial_balance)}. Not changing.")
-
-
-    def record_balance(self, balance: float):
-        """Record daily balance snapshot."""
-        today_iso = datetime.now(timezone.utc).date().isoformat()
-        now_iso = datetime.now(timezone.utc).isoformat()
-        
-        existing_entry = self._fetchone_query("SELECT date FROM daily_balances WHERE date = ?", (today_iso,))
-        if existing_entry:
-            self._execute_query("UPDATE daily_balances SET balance = ?, timestamp = ? WHERE date = ?",
-                                (balance, now_iso, today_iso))
-        else:
-            self._execute_query("INSERT INTO daily_balances (date, balance, timestamp) VALUES (?, ?, ?)",
-                                (today_iso, balance, now_iso))
-        # logger.debug(f"Recorded balance for {today_iso}: ${balance:.2f}") # Potentially too verbose
-
-    def record_trade(self, symbol: str, side: str, amount: float, price: float,
-                     exchange_fill_id: Optional[str] = None, trade_type: str = "manual",
-                     pnl: Optional[float] = None, timestamp: Optional[str] = None,
-                     linked_order_table_id_to_link: Optional[int] = None):
-        """Record a trade in the database."""
-        if timestamp is None:
-            timestamp = datetime.now(timezone.utc).isoformat()
-        
-        value = amount * price
-        self._execute_query(
-            "INSERT OR IGNORE INTO trades (symbol, side, amount, price, value, trade_type, timestamp, exchange_fill_id, pnl, linked_order_table_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
-            (symbol, side, amount, price, value, trade_type, timestamp, exchange_fill_id, pnl or 0.0, linked_order_table_id_to_link)
-        )
-        formatter = get_formatter()
-        # Assuming symbol's base asset for amount formatting. If symbol is like BTC/USDT, base is BTC.
-        base_asset_for_amount = symbol.split('/')[0] if '/' in symbol else symbol 
-        logger.info(f"📈 Trade recorded: {side.upper()} {formatter.format_amount(amount, base_asset_for_amount)} {symbol} @ {formatter.format_price(price, symbol)} ({formatter.format_price(value, symbol)}) [{trade_type}]")
-
-    def get_all_trades(self) -> List[Dict[str, Any]]:
-        """Fetch all trades from the database, ordered by timestamp."""
-        return self._fetch_query("SELECT * FROM trades ORDER BY timestamp ASC")
-
-    def get_trade_by_symbol_and_status(self, symbol: str, status: str) -> Optional[Dict[str, Any]]:
-        """
-        Fetches a single trade record for a given symbol and status.
-        Typically used to find an open position master record.
-        Assumes that for a given symbol, there's at most one trade record with a specific
-        active status like 'position_opened'. If multiple could exist, this fetches the most recent.
-        """
-        query = "SELECT * FROM trades WHERE symbol = ? AND status = ? ORDER BY id DESC LIMIT 1"
-        trade = self._fetchone_query(query, (symbol, status))
-        if trade:
-            logger.debug(f"Found trade for {symbol} with status {status}: ID {trade.get('id')}")
-        # else: # Can be noisy if not finding a trade is a common occurrence
-            # logger.debug(f"No trade found for {symbol} with status {status}")
-        return trade
-        
-    def get_basic_stats(self, current_balance: Optional[float] = None) -> Dict[str, Any]:
-        """Get basic trading statistics from DB, primarily using aggregated tables."""
-        
-        # Get counts of open positions (trades that are not yet migrated)
-        open_positions_count = self._get_open_positions_count_from_db()
-
-        # Get overall aggregated stats from token_stats table
-        query_token_stats_summary = """
-            SELECT 
-                SUM(total_realized_pnl) as total_pnl_from_cycles,
-                SUM(total_completed_cycles) as total_completed_cycles_sum,
-                MIN(first_cycle_closed_at) as overall_first_cycle_closed,
-                MAX(last_cycle_closed_at) as overall_last_cycle_closed
-            FROM token_stats
-        """
-        token_stats_summary = self._fetchone_query(query_token_stats_summary)
-
-        total_pnl_from_cycles = token_stats_summary['total_pnl_from_cycles'] if token_stats_summary and token_stats_summary['total_pnl_from_cycles'] is not None else 0.0
-        total_completed_cycles_sum = token_stats_summary['total_completed_cycles_sum'] if token_stats_summary and token_stats_summary['total_completed_cycles_sum'] is not None else 0
-
-        # Total trades considered as sum of completed cycles and currently open positions
-        # This redefines 'total_trades' from its previous meaning of individual fills.
-        total_trades_redefined = total_completed_cycles_sum + open_positions_count
-
-        initial_balance_str = self._get_metadata('initial_balance')
-        initial_balance = float(initial_balance_str) if initial_balance_str else 0.0
-        
-        start_date_iso = self._get_metadata('start_date')
-        start_date_obj = datetime.fromisoformat(start_date_iso) if start_date_iso else datetime.now(timezone.utc)
-        days_active = (datetime.now(timezone.utc) - start_date_obj).days + 1
-        
-        # 'last_trade' timestamp could be the last update to token_stats or an open trade
-        last_activity_ts = token_stats_summary['overall_last_cycle_closed'] if token_stats_summary else None
-        last_open_trade_ts_row = self._fetchone_query("SELECT MAX(updated_at) as last_update FROM trades WHERE status = 'position_opened'")
-        if last_open_trade_ts_row and last_open_trade_ts_row['last_update']:
-            if not last_activity_ts or datetime.fromisoformat(last_open_trade_ts_row['last_update']) > datetime.fromisoformat(last_activity_ts):
-                last_activity_ts = last_open_trade_ts_row['last_update']
-
-        # Buy/Sell trades count from individual fills is no longer directly available for completed cycles.
-        # If needed, this requires schema change in token_stats or a different approach.
-        # For now, these are omitted from basic_stats.
-
-        return {
-            'total_trades': total_trades_redefined, # This is now cycles + open positions
-            'completed_trades': total_completed_cycles_sum, # This is sum of total_completed_cycles from token_stats
-            # 'buy_trades': buy_trades_count, # Omitted
-            # 'sell_trades': sell_trades_count, # Omitted
-            'initial_balance': initial_balance,
-            'total_pnl': total_pnl_from_cycles, # PNL from closed cycles via token_stats
-            'days_active': days_active,
-            'start_date': start_date_obj.strftime('%Y-%m-%d'),
-            'last_trade': last_activity_ts, # Reflects last known activity (cycle close or open trade update)
-            'open_positions_count': open_positions_count
-        }
-
-    def get_performance_stats(self) -> Dict[str, Any]:
-        """Calculate advanced performance statistics using aggregated data from token_stats."""
-        query = """
-            SELECT 
-                SUM(total_completed_cycles) as total_cycles,
-                SUM(winning_cycles) as total_wins,
-                SUM(losing_cycles) as total_losses,
-                SUM(sum_of_winning_pnl) as total_winning_pnl,
-                SUM(sum_of_losing_pnl) as total_losing_pnl, -- Stored positive
-                MAX(largest_winning_cycle_pnl) as overall_largest_win,
-                MAX(largest_losing_cycle_pnl) as overall_largest_loss -- Stored positive
-            FROM token_stats
-        """
-        summary = self._fetchone_query(query)
-
-        # Add total volume
-        volume_summary = self._fetchone_query("SELECT SUM(total_entry_volume) as total_volume FROM token_stats")
-        total_trading_volume = volume_summary['total_volume'] if volume_summary and volume_summary['total_volume'] is not None else 0.0
-        
-        # 🆕 Calculate Average Trade Duration
-        duration_summary = self._fetchone_query("SELECT SUM(total_duration_seconds) as total_seconds, SUM(total_completed_cycles) as total_cycles FROM token_stats")
-        avg_trade_duration_formatted = "N/A"
-        if duration_summary and duration_summary['total_cycles'] and duration_summary['total_cycles'] > 0:
-            avg_seconds = duration_summary['total_seconds'] / duration_summary['total_cycles']
-            avg_trade_duration_formatted = self._format_duration(avg_seconds)
-
-        # Get individual token performances for best/worst
-        all_token_perf_stats = self.get_token_performance() 
-        best_token_pnl_pct = -float('inf')
-        best_token_name = "N/A"
-        worst_token_pnl_pct = float('inf')
-        worst_token_name = "N/A"
-
-        if all_token_perf_stats:
-            for token_name_iter, stats_data in all_token_perf_stats.items():
-                pnl_pct = stats_data.get('pnl_percentage', 0.0)
-                # Ensure token has completed trades and pnl_pct is a valid number
-                if stats_data.get('completed_trades', 0) > 0 and isinstance(pnl_pct, (int, float)) and not math.isinf(pnl_pct) and not math.isnan(pnl_pct):
-                    if pnl_pct > best_token_pnl_pct:
-                        best_token_pnl_pct = pnl_pct
-                        best_token_name = token_name_iter
-                    if pnl_pct < worst_token_pnl_pct:
-                        worst_token_pnl_pct = pnl_pct
-                        worst_token_name = token_name_iter
-        
-        # Handle cases where no valid tokens were found for best/worst
-        if best_token_name == "N/A":
-            best_token_pnl_pct = 0.0
-        if worst_token_name == "N/A":
-            worst_token_pnl_pct = 0.0
-
-        if not summary or summary['total_cycles'] is None or summary['total_cycles'] == 0:
-            return {
-                'win_rate': 0.0, 'profit_factor': 0.0, 'avg_win': 0.0, 'avg_loss': 0.0,
-                'largest_win': 0.0, 'largest_loss': 0.0, 
-                'total_wins': 0, 'total_losses': 0, 'expectancy': 0.0,
-                'total_trading_volume': total_trading_volume,
-                'best_performing_token': {'name': best_token_name, 'pnl_percentage': best_token_pnl_pct},
-                'worst_performing_token': {'name': worst_token_name, 'pnl_percentage': worst_token_pnl_pct},
-                'avg_trade_duration': avg_trade_duration_formatted,
-            }
-
-        total_completed_count = summary['total_cycles']
-        total_wins_count = summary['total_wins'] if summary['total_wins'] is not None else 0
-        total_losses_count = summary['total_losses'] if summary['total_losses'] is not None else 0
-        
-        win_rate = (total_wins_count / total_completed_count * 100) if total_completed_count > 0 else 0.0
-        
-        sum_of_wins = summary['total_winning_pnl'] if summary['total_winning_pnl'] is not None else 0.0
-        sum_of_losses = summary['total_losing_pnl'] if summary['total_losing_pnl'] is not None else 0.0 # This is sum of absolute losses
-
-        profit_factor = (sum_of_wins / sum_of_losses) if sum_of_losses > 0 else float('inf') if sum_of_wins > 0 else 0.0
-        
-        avg_win = (sum_of_wins / total_wins_count) if total_wins_count > 0 else 0.0
-        avg_loss = (sum_of_losses / total_losses_count) if total_losses_count > 0 else 0.0 # Avg of absolute losses
-        
-        largest_win = summary['overall_largest_win'] if summary['overall_largest_win'] is not None else 0.0
-        largest_loss = summary['overall_largest_loss'] if summary['overall_largest_loss'] is not None else 0.0 # Largest absolute loss
-
-        # Consecutive wins/losses removed as it's hard to track with this aggregation model.
-            
-        expectancy = (avg_win * (win_rate / 100)) - (avg_loss * (1 - (win_rate / 100)))
-
-        return {
-            'win_rate': win_rate, 'profit_factor': profit_factor, 'avg_win': avg_win, 'avg_loss': avg_loss,
-            'largest_win': largest_win, 'largest_loss': largest_loss, 
-            'total_wins': total_wins_count, 'total_losses': total_losses_count, 'expectancy': expectancy,
-            'total_trading_volume': total_trading_volume,
-            'best_performing_token': {'name': best_token_name, 'pnl_percentage': best_token_pnl_pct},
-            'worst_performing_token': {'name': worst_token_name, 'pnl_percentage': worst_token_pnl_pct},
-            'avg_trade_duration': avg_trade_duration_formatted,
-        }
-
-    def get_risk_metrics(self) -> Dict[str, Any]:
-        """Calculate risk-adjusted metrics from daily balances."""
-        # Get live max drawdown from metadata
-        max_drawdown_live_str = self._get_metadata('drawdown_max_drawdown_pct')
-        max_drawdown_live = float(max_drawdown_live_str) if max_drawdown_live_str else 0.0
-
-        daily_balances_data = self._fetch_query("SELECT balance FROM daily_balances ORDER BY date ASC")
-        
-        if not daily_balances_data or len(daily_balances_data) < 2:
-            return {'sharpe_ratio': 0.0, 'sortino_ratio': 0.0, 'max_drawdown': 0.0, 'volatility': 0.0, 'var_95': 0.0, 'max_drawdown_live': max_drawdown_live}
-
-        balances = [entry['balance'] for entry in daily_balances_data]
-        returns = np.diff(balances) / balances[:-1] # Calculate daily returns
-        returns = returns[np.isfinite(returns)] # Remove NaNs or Infs if any balance was 0
-
-        if returns.size == 0:
-             return {'sharpe_ratio': 0.0, 'sortino_ratio': 0.0, 'max_drawdown': 0.0, 'volatility': 0.0, 'var_95': 0.0, 'max_drawdown_live': max_drawdown_live}
-
-        risk_free_rate_daily = (1 + 0.02)**(1/365) - 1 # Approx 2% annual risk-free rate, daily
-        
-        excess_returns = returns - risk_free_rate_daily
-        sharpe_ratio = np.mean(excess_returns) / np.std(returns) * np.sqrt(365) if np.std(returns) > 0 else 0.0
-        
-        downside_returns = returns[returns < 0]
-        downside_std = np.std(downside_returns) if len(downside_returns) > 0 else 0.0
-        sortino_ratio = np.mean(excess_returns) / downside_std * np.sqrt(365) if downside_std > 0 else 0.0
-        
-        cumulative_returns = np.cumprod(1 + returns)
-        peak = np.maximum.accumulate(cumulative_returns)
-        drawdown = (cumulative_returns - peak) / peak
-        max_drawdown_daily_pct = abs(np.min(drawdown) * 100) if drawdown.size > 0 else 0.0
-        
-        volatility_pct = np.std(returns) * np.sqrt(365) * 100
-        var_95_pct = abs(np.percentile(returns, 5) * 100) if returns.size > 0 else 0.0
-        
-        return {
-            'sharpe_ratio': sharpe_ratio, 'sortino_ratio': sortino_ratio, 
-            'max_drawdown': max_drawdown_daily_pct, 'volatility': volatility_pct, 
-            'var_95': var_95_pct, 'max_drawdown_live': max_drawdown_live
-        }
-
-    def get_comprehensive_stats(self, current_balance: Optional[float] = None) -> Dict[str, Any]:
-        """Get all statistics combined."""
-        if current_balance is not None: # Ensure it's not just None, but explicitly provided
-            self.record_balance(current_balance) # Record current balance for today
-        
-        basic = self.get_basic_stats(current_balance) # Pass current_balance for P&L context if needed
-        performance = self.get_performance_stats()
-        risk = self.get_risk_metrics()
-        
-        initial_balance = basic['initial_balance']
-        total_return_pct = 0.0
-        # Use current_balance if available and valid for total return calculation
-        # Otherwise, PNL from basic_stats (closed trades) is the primary PNL source
-        # This needs careful thought: current_balance reflects unrealized PNL too.
-        # The original code used current_balance - initial_balance for total_pnl if current_balance provided.
-        
-        effective_balance_for_return = current_balance if current_balance is not None else (initial_balance + basic['total_pnl'])
-
-        if initial_balance > 0:
-            total_return_pct = ((effective_balance_for_return - initial_balance) / initial_balance) * 100
-        
-        return {
-            'basic': basic,
-            'performance': performance,
-            'risk': risk,
-            'current_balance': current_balance if current_balance is not None else initial_balance + basic['total_pnl'], # Best estimate
-            'total_return': total_return_pct, # Percentage
-            'last_updated': datetime.now(timezone.utc).isoformat()
-        }
-
-    def _get_open_positions_count_from_db(self) -> int:
-        """🧹 PHASE 4: Get count of open positions from enhanced trades table."""
-        row = self._fetchone_query("SELECT COUNT(DISTINCT symbol) as count FROM trades WHERE status = 'position_opened'")
-        return row['count'] if row else 0
-
-    def format_stats_message(self, current_balance: Optional[float] = None) -> str:
-        """Format stats for Telegram display using data from DB."""
-        try:
-            stats = self.get_comprehensive_stats(current_balance)
-            formatter = get_formatter()
-            
-            basic = stats['basic']
-            perf = stats['performance']
-            risk = stats['risk'] # For portfolio drawdown
-            
-            effective_current_balance = stats['current_balance']
-            initial_bal = basic['initial_balance']
-
-            total_pnl_val = effective_current_balance - initial_bal if initial_bal > 0 and current_balance is not None else basic['total_pnl']
-            total_return_pct = (total_pnl_val / initial_bal * 100) if initial_bal > 0 else 0.0
-            pnl_emoji = "🟢" if total_pnl_val >= 0 else "🔴"
-            open_positions_count = basic['open_positions_count']
-
-            stats_text_parts = []
-            stats_text_parts.append(f"📊 <b>Trading Statistics</b>\n")
-            
-            # Account Overview
-            stats_text_parts.append(f"\n💰 <b>Account Overview:</b>")
-            stats_text_parts.append(f"• Current Balance: {formatter.format_price_with_symbol(effective_current_balance)}")
-            stats_text_parts.append(f"• Initial Balance: {formatter.format_price_with_symbol(initial_bal)}")
-            stats_text_parts.append(f"• Open Positions: {open_positions_count}")
-            stats_text_parts.append(f"• {pnl_emoji} Total P&L: {formatter.format_price_with_symbol(total_pnl_val)} ({total_return_pct:+.2f}%)")
-            stats_text_parts.append(f"• Days Active: {basic['days_active']}\n")
-            
-            # Performance Metrics
-            stats_text_parts.append(f"\n🏆 <b>Performance Metrics:</b>")
-            stats_text_parts.append(f"• Total Completed Trades: {basic['completed_trades']}")
-            stats_text_parts.append(f"• Trading Volume (Entry Vol.): {formatter.format_price_with_symbol(perf.get('total_trading_volume', 0.0))}")
-            stats_text_parts.append(f"• Profit Factor: {perf['profit_factor']:.2f}")
-            stats_text_parts.append(f"• Expectancy: {formatter.format_price_with_symbol(perf['expectancy'])} (Value per trade)")
-            # Note for Expectancy Percentage: \"[Info: Percentage representation requires further definition]\" might be too verbose for typical display.
-            
-            stats_text_parts.append(f"• Largest Winning Trade: {formatter.format_price_with_symbol(perf['largest_win'])} (Value)")
-            stats_text_parts.append(f"• Largest Losing Trade: {formatter.format_price_with_symbol(-perf['largest_loss'])} (Value)")
-            # Note for Largest Trade P&L %: Similar to expectancy, noting \"[Info: P&L % for specific trades requires data enhancement]\" in the bot message might be too much.
-
-            best_token_stats = perf.get('best_performing_token', {'name': 'N/A', 'pnl_percentage': 0.0})
-            worst_token_stats = perf.get('worst_performing_token', {'name': 'N/A', 'pnl_percentage': 0.0})
-            stats_text_parts.append(f"• Best Performing Token: {best_token_stats['name']} ({best_token_stats['pnl_percentage']:+.2f}%)")
-            stats_text_parts.append(f"• Worst Performing Token: {worst_token_stats['name']} ({worst_token_stats['pnl_percentage']:+.2f}%)")
-            
-            stats_text_parts.append(f"• Average Trade Duration: {perf.get('avg_trade_duration', 'N/A')}")
-            stats_text_parts.append(f"• Portfolio Max Drawdown: {risk.get('max_drawdown_live', 0.0):.2f}% <i>(Live)</i>")
-            # Future note: \"[Info: Trading P&L specific drawdown analysis planned]\"
-            
-            # Session Info
-            stats_text_parts.append(f"\n\n⏰ <b>Session Info:</b>")
-            stats_text_parts.append(f"• Bot Started: {basic['start_date']}")
-            stats_text_parts.append(f"• Stats Last Updated: {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M:%S UTC')}")
-            
-            return "\n".join(stats_text_parts).strip()
-                
-        except Exception as e:
-            logger.error(f"Error formatting stats message: {e}", exc_info=True)
-            return f"""📊 <b>Trading Statistics</b>\n\n❌ <b>Error loading statistics</b>\n\n🔧 <b>Debug info:</b> {str(e)[:100]}"""
-
-    def get_recent_trades(self, limit: int = 10) -> List[Dict[str, Any]]:
-        """Get recent trades from DB (these are active/open trades, as completed ones are migrated)."""
-        return self._fetch_query("SELECT * FROM trades WHERE status = 'position_opened' ORDER BY updated_at DESC LIMIT ?", (limit,))
-
-    def get_token_performance(self) -> Dict[str, Dict[str, Any]]:
-        """Get performance statistics grouped by token using the token_stats table."""
-        all_token_stats = self._fetch_query("SELECT * FROM token_stats ORDER BY token ASC")
-        
-        token_performance_map = {}
-        for record in all_token_stats:
-            token = record['token']
-            total_pnl = record.get('total_realized_pnl', 0.0)
-            # total_volume_sold now refers to total_exit_volume from token_stats
-            total_volume = record.get('total_entry_volume', 0.0) 
-            
-            pnl_percentage = (total_pnl / total_volume * 100) if total_volume > 0 else 0.0
-            
-            total_completed_count = record.get('total_completed_cycles', 0)
-            total_wins_count = record.get('winning_cycles', 0)
-            total_losses_count = record.get('losing_cycles', 0)
-            
-            win_rate = (total_wins_count / total_completed_count * 100) if total_completed_count > 0 else 0.0
-            
-            sum_of_wins = record.get('sum_of_winning_pnl', 0.0)
-            sum_of_losses = record.get('sum_of_losing_pnl', 0.0) # Stored positive
-            profit_factor = (sum_of_wins / sum_of_losses) if sum_of_losses > 0 else float('inf') if sum_of_wins > 0 else 0.0
-            
-            avg_win = (sum_of_wins / total_wins_count) if total_wins_count > 0 else 0.0
-            avg_loss = (sum_of_losses / total_losses_count) if total_losses_count > 0 else 0.0
-            expectancy = (avg_win * (win_rate / 100)) - (avg_loss * (1 - (win_rate / 100)))
-            
-            largest_win = record.get('largest_winning_cycle_pnl', 0.0)
-            largest_loss = record.get('largest_losing_cycle_pnl', 0.0) # Stored positive
-
-            token_performance_map[token] = {
-                'token': token, # Added for easier access if iterating over values
-                'total_pnl': total_pnl, 
-                'pnl_percentage': pnl_percentage,
-                'completed_trades': total_completed_count, 
-                'total_volume': total_volume, # This is total_entry_volume
-                'win_rate': win_rate, 
-                'total_wins': total_wins_count, 
-                'total_losses': total_losses_count,
-                'profit_factor': profit_factor, 
-                'expectancy': expectancy,
-                'largest_win': largest_win, 
-                'largest_loss': largest_loss,
-                'avg_win': avg_win, 
-                'avg_loss': avg_loss,
-                'first_cycle_closed_at': record.get('first_cycle_closed_at'),
-                'last_cycle_closed_at': record.get('last_cycle_closed_at'),
-                'total_cancelled': record.get('total_cancelled_cycles', 0),
-                'total_duration_seconds': record.get('total_duration_seconds', 0),
-                'avg_trade_duration': self._format_duration(record.get('total_duration_seconds', 0) / total_completed_count) if total_completed_count > 0 else "N/A"
-            }
-        return token_performance_map
-
-    def get_token_detailed_stats(self, token: str) -> Dict[str, Any]:
-        """Get detailed statistics for a specific token using token_stats and current open trades."""
-        upper_token = _normalize_token_case(token)
-        
-        # Get aggregated performance from token_stats
-        token_agg_stats = self._fetchone_query("SELECT * FROM token_stats WHERE token = ?", (upper_token,))
-        
-        # Get currently open trades for this token from the 'trades' table (not yet migrated)
-        # These are not completed cycles but represent current exposure.
-        open_trades_for_token = self._fetch_query(
-            "SELECT * FROM trades WHERE symbol LIKE ? AND status = 'position_opened' ORDER BY timestamp ASC", 
-            (f"{upper_token}/%",)
-        )
-        
-        if not token_agg_stats and not open_trades_for_token:
-            return {
-                'token': upper_token, 'total_trades': 0, 'total_pnl': 0.0, 'win_rate': 0.0,
-                'message': f"No trading history or open positions found for {upper_token}"
-            }
-
-        # Initialize with empty performance if no aggregated data
-        perf_stats = {}
-        if token_agg_stats:
-            perf_stats = {
-                'completed_trades': token_agg_stats.get('total_completed_cycles', 0),
-                'total_pnl': token_agg_stats.get('total_realized_pnl', 0.0),
-                'pnl_percentage': 0.0, # Recalculate if needed, or store avg pnl_percentage
-                'win_rate': 0.0,
-                'profit_factor': token_agg_stats.get('profit_factor'), # Placeholder, need to calc from sums
-                'avg_win': 0.0,
-                'avg_loss': 0.0,
-                'largest_win': token_agg_stats.get('largest_winning_cycle_pnl', 0.0),
-                'largest_loss': token_agg_stats.get('largest_losing_cycle_pnl', 0.0),
-                'expectancy': 0.0,
-                'total_wins': token_agg_stats.get('winning_cycles',0),
-                'total_losses': token_agg_stats.get('losing_cycles',0),
-                'completed_entry_volume': token_agg_stats.get('total_entry_volume', 0.0),
-                'completed_exit_volume': token_agg_stats.get('total_exit_volume', 0.0),
-                'total_cancelled': token_agg_stats.get('total_cancelled_cycles', 0),
-                'total_duration_seconds': token_agg_stats.get('total_duration_seconds', 0),
-                'avg_trade_duration': self._format_duration(token_agg_stats.get('total_duration_seconds', 0) / token_agg_stats.get('total_completed_cycles', 0)) if token_agg_stats.get('total_completed_cycles', 0) > 0 else "N/A"
-            }
-            if perf_stats['completed_trades'] > 0:
-                perf_stats['win_rate'] = (perf_stats['total_wins'] / perf_stats['completed_trades'] * 100) if perf_stats['completed_trades'] > 0 else 0.0
-                sum_wins = token_agg_stats.get('sum_of_winning_pnl', 0.0)
-                sum_losses = token_agg_stats.get('sum_of_losing_pnl', 0.0)
-                perf_stats['profit_factor'] = (sum_wins / sum_losses) if sum_losses > 0 else float('inf') if sum_wins > 0 else 0.0
-                perf_stats['avg_win'] = (sum_wins / perf_stats['total_wins']) if perf_stats['total_wins'] > 0 else 0.0
-                perf_stats['avg_loss'] = (sum_losses / perf_stats['total_losses']) if perf_stats['total_losses'] > 0 else 0.0
-                perf_stats['expectancy'] = (perf_stats['avg_win'] * (perf_stats['win_rate'] / 100)) - (perf_stats['avg_loss'] * (1 - (perf_stats['win_rate'] / 100)))
-            if perf_stats['completed_entry_volume'] > 0:
-                 perf_stats['pnl_percentage'] = (perf_stats['total_pnl'] / perf_stats['completed_entry_volume'] * 100)
-        else: # No completed cycles for this token yet
-             perf_stats = {
-                'completed_trades': 0, 'total_pnl': 0.0, 'pnl_percentage': 0.0, 'win_rate': 0.0,
-                'profit_factor': 0.0, 'avg_win': 0.0, 'avg_loss': 0.0, 'largest_win': 0.0, 'largest_loss': 0.0,
-                'expectancy': 0.0, 'total_wins':0, 'total_losses':0,
-                'completed_entry_volume': 0.0, 'completed_exit_volume': 0.0, 'total_cancelled': 0,
-                'total_duration_seconds': 0, 'avg_trade_duration': "N/A"
-            }
-
-        # Info about open positions for this token (raw trades, not cycles)
-        open_positions_summary = []
-        total_open_value = 0.0
-        total_open_unrealized_pnl = 0.0
-        for op_trade in open_trades_for_token:
-            open_positions_summary.append({
-                'lifecycle_id': op_trade.get('trade_lifecycle_id'),
-                'side': op_trade.get('position_side'),
-                'amount': op_trade.get('current_position_size'),
-                'entry_price': op_trade.get('entry_price'),
-                'mark_price': op_trade.get('mark_price'),
-                'unrealized_pnl': op_trade.get('unrealized_pnl'),
-                'opened_at': op_trade.get('position_opened_at')
-            })
-            total_open_value += op_trade.get('value', 0.0) # Initial value of open positions
-            total_open_unrealized_pnl += op_trade.get('unrealized_pnl', 0.0)
-
-        # Raw individual orders from 'orders' table for this token can be complex to summarize here
-        # The old version counted 'buy_orders' and 'sell_orders' from all trades for the token.
-        # This is no longer straightforward for completed cycles.
-        # We can count open orders for this token.
-        open_orders_count_row = self._fetchone_query(
-            "SELECT COUNT(*) as count FROM orders WHERE symbol LIKE ? AND status IN ('open', 'submitted', 'pending_trigger')",
-             (f"{upper_token}/%",)
-        )
-        current_open_orders_for_token = open_orders_count_row['count'] if open_orders_count_row else 0
-
-        # 'total_trades' here could mean total orders ever placed for this token, or completed cycles + open positions
-        # Let's define it as completed cycles + number of currently open positions for consistency with get_basic_stats
-        effective_total_trades = perf_stats['completed_trades'] + len(open_trades_for_token)
-
-        return {
-            'token': upper_token,
-            'message': f"Statistics for {upper_token}",
-            'performance_summary': perf_stats, # From token_stats table
-            'open_positions': open_positions_summary, # List of currently open positions
-            'open_positions_count': len(open_trades_for_token),
-            'current_open_orders_count': current_open_orders_for_token,
-            'summary_total_trades': effective_total_trades, # Completed cycles + open positions
-            'summary_total_realized_pnl': perf_stats['total_pnl'],
-            'summary_total_unrealized_pnl': total_open_unrealized_pnl,
-            # 'cycles': token_cycles # Raw cycle data for completed trades is no longer stored directly after migration
-        }
-
-    def get_daily_stats(self, limit: int = 10) -> List[Dict[str, Any]]:
-        """Get daily performance stats for the last N days from daily_aggregated_stats."""
-        daily_stats_list = []
-        today_utc = datetime.now(timezone.utc).date()
-
-        for i in range(limit):
-            target_date = today_utc - timedelta(days=i)
-            date_str = target_date.strftime('%Y-%m-%d')
-            date_formatted = target_date.strftime('%m/%d') # For display
-
-            # Query for all tokens for that day and sum them up
-            # Or, if daily_aggregated_stats stores an _OVERALL_ record, query that.
-            # Assuming for now we sum up all token records for a given day.
-            day_aggregated_data = self._fetch_query(
-                "SELECT SUM(realized_pnl) as pnl, SUM(completed_cycles) as trades, SUM(exit_volume) as volume FROM daily_aggregated_stats WHERE date = ?",
-                (date_str,)
-            )
-            
-            stats_for_day = None
-            if day_aggregated_data and len(day_aggregated_data) > 0 and day_aggregated_data[0]['trades'] is not None:
-                stats_for_day = day_aggregated_data[0]
-                # Calculate pnl_pct if volume is present and positive
-                pnl = stats_for_day.get('pnl', 0.0) or 0.0
-                volume = stats_for_day.get('volume', 0.0) or 0.0
-                stats_for_day['pnl_pct'] = (pnl / volume * 100) if volume > 0 else 0.0
-                # Ensure trades is an int
-                stats_for_day['trades'] = int(stats_for_day.get('trades', 0) or 0)
-
-            if stats_for_day and stats_for_day['trades'] > 0:
-                daily_stats_list.append({
-                    'date': date_str, 'date_formatted': date_formatted, 'has_trades': True,
-                    **stats_for_day
-                })
-            else:
-                daily_stats_list.append({
-                    'date': date_str, 'date_formatted': date_formatted, 'has_trades': False,
-                    'trades': 0, 'pnl': 0.0, 'volume': 0.0, 'pnl_pct': 0.0
-                })
-        return daily_stats_list
-
-    def get_weekly_stats(self, limit: int = 10) -> List[Dict[str, Any]]:
-        """Get weekly performance stats for the last N weeks by aggregating daily_aggregated_stats."""
-        weekly_stats_list = []
-        today_utc = datetime.now(timezone.utc).date()
-
-        for i in range(limit):
-            target_monday = today_utc - timedelta(days=today_utc.weekday() + (i * 7))
-            target_sunday = target_monday + timedelta(days=6)
-            
-            week_key_display = f"{target_monday.strftime('%Y-W%W')}" # For internal key if needed
-            week_formatted_display = f"{target_monday.strftime('%m/%d')}-{target_sunday.strftime('%m/%d/%y')}"
-
-            # Fetch daily records for this week range
-            daily_records_for_week = self._fetch_query(
-                "SELECT date, realized_pnl, completed_cycles, exit_volume FROM daily_aggregated_stats WHERE date BETWEEN ? AND ?",
-                (target_monday.strftime('%Y-%m-%d'), target_sunday.strftime('%Y-%m-%d'))
-            )
-            
-            if daily_records_for_week:
-                total_pnl_week = sum(d.get('realized_pnl', 0.0) or 0.0 for d in daily_records_for_week)
-                total_trades_week = sum(d.get('completed_cycles', 0) or 0 for d in daily_records_for_week)
-                total_volume_week = sum(d.get('exit_volume', 0.0) or 0.0 for d in daily_records_for_week)
-                pnl_pct_week = (total_pnl_week / total_volume_week * 100) if total_volume_week > 0 else 0.0
-                
-                if total_trades_week > 0:
-                    weekly_stats_list.append({
-                        'week': week_key_display, 
-                        'week_formatted': week_formatted_display, 
-                        'has_trades': True,
-                        'pnl': total_pnl_week,
-                        'trades': total_trades_week,
-                        'volume': total_volume_week,
-                        'pnl_pct': pnl_pct_week
-                    })
-                else:
-                    weekly_stats_list.append({
-                        'week': week_key_display, 'week_formatted': week_formatted_display, 'has_trades': False,
-                        'trades': 0, 'pnl': 0.0, 'volume': 0.0, 'pnl_pct': 0.0
-                    })
-            else:
-                weekly_stats_list.append({
-                    'week': week_key_display, 'week_formatted': week_formatted_display, 'has_trades': False,
-                    'trades': 0, 'pnl': 0.0, 'volume': 0.0, 'pnl_pct': 0.0
-                })
-        return weekly_stats_list
-
-    def get_monthly_stats(self, limit: int = 10) -> List[Dict[str, Any]]:
-        """Get monthly performance stats for the last N months by aggregating daily_aggregated_stats."""
-        monthly_stats_list = []
-        current_month_start_utc = datetime.now(timezone.utc).date().replace(day=1)
-
-        for i in range(limit):
-            year = current_month_start_utc.year
-            month = current_month_start_utc.month - i
-            while month <= 0:
-                month += 12
-                year -= 1
-            
-            target_month_start_date = datetime(year, month, 1, tzinfo=timezone.utc).date()
-            # Find end of target month
-            next_month_start_date = datetime(year + (month // 12), (month % 12) + 1, 1, tzinfo=timezone.utc).date() if month < 12 else datetime(year + 1, 1, 1, tzinfo=timezone.utc).date()
-            target_month_end_date = next_month_start_date - timedelta(days=1)
-            
-            month_key_display = target_month_start_date.strftime('%Y-%m')
-            month_formatted_display = target_month_start_date.strftime('%b %Y')
-
-            daily_records_for_month = self._fetch_query(
-                "SELECT date, realized_pnl, completed_cycles, exit_volume FROM daily_aggregated_stats WHERE date BETWEEN ? AND ?",
-                (target_month_start_date.strftime('%Y-%m-%d'), target_month_end_date.strftime('%Y-%m-%d'))
-            )
-
-            if daily_records_for_month:
-                total_pnl_month = sum(d.get('realized_pnl', 0.0) or 0.0 for d in daily_records_for_month)
-                total_trades_month = sum(d.get('completed_cycles', 0) or 0 for d in daily_records_for_month)
-                total_volume_month = sum(d.get('exit_volume', 0.0) or 0.0 for d in daily_records_for_month)
-                pnl_pct_month = (total_pnl_month / total_volume_month * 100) if total_volume_month > 0 else 0.0
-
-                if total_trades_month > 0:
-                    monthly_stats_list.append({
-                        'month': month_key_display, 
-                        'month_formatted': month_formatted_display, 
-                        'has_trades': True,
-                        'pnl': total_pnl_month,
-                        'trades': total_trades_month,
-                        'volume': total_volume_month,
-                        'pnl_pct': pnl_pct_month
-                    })
-                else:
-                    monthly_stats_list.append({
-                        'month': month_key_display, 'month_formatted': month_formatted_display, 'has_trades': False,
-                        'trades': 0, 'pnl': 0.0, 'volume': 0.0, 'pnl_pct': 0.0
-                    })
-            else:
-                 monthly_stats_list.append({
-                    'month': month_key_display, 'month_formatted': month_formatted_display, 'has_trades': False,
-                    'trades': 0, 'pnl': 0.0, 'volume': 0.0, 'pnl_pct': 0.0
-                })
-        return monthly_stats_list
-
-    def record_deposit(self, amount: float, timestamp: Optional[str] = None, 
-                       deposit_id: Optional[str] = None, description: Optional[str] = None):
-        """Record a deposit."""
-        ts = timestamp if timestamp else datetime.now(timezone.utc).isoformat()
-        formatter = get_formatter()
-        formatted_amount_str = formatter.format_price_with_symbol(amount)
-        desc = description if description else f'Deposit of {formatted_amount_str}'
-        
-        self._execute_query(
-            "INSERT INTO balance_adjustments (adjustment_id, timestamp, type, amount, description) VALUES (?, ?, ?, ?, ?)",
-            (deposit_id or str(uuid.uuid4()), ts, 'deposit', amount, desc) # Ensured uuid is string
-        )
-        # Adjust initial_balance in metadata to reflect capital changes
-        current_initial = float(self._get_metadata('initial_balance') or '0.0')
-        self._set_metadata('initial_balance', str(current_initial + amount))
-        logger.info(f"💰 Recorded deposit: {formatted_amount_str}. New effective initial balance: {formatter.format_price_with_symbol(current_initial + amount)}")
-
-    def record_withdrawal(self, amount: float, timestamp: Optional[str] = None, 
-                          withdrawal_id: Optional[str] = None, description: Optional[str] = None):
-        """Record a withdrawal."""
-        ts = timestamp if timestamp else datetime.now(timezone.utc).isoformat()
-        formatter = get_formatter()
-        formatted_amount_str = formatter.format_price_with_symbol(amount)
-        desc = description if description else f'Withdrawal of {formatted_amount_str}'
-        
-        self._execute_query(
-            "INSERT INTO balance_adjustments (adjustment_id, timestamp, type, amount, description) VALUES (?, ?, ?, ?, ?)",
-            (withdrawal_id or str(uuid.uuid4()), ts, 'withdrawal', amount, desc) # Ensured uuid is string
-        )
-        current_initial = float(self._get_metadata('initial_balance') or '0.0')
-        self._set_metadata('initial_balance', str(current_initial - amount))
-        logger.info(f"💸 Recorded withdrawal: {formatted_amount_str}. New effective initial balance: {formatter.format_price_with_symbol(current_initial - amount)}")
-
-    def get_balance_adjustments_summary(self) -> Dict[str, Any]:
-        """Get summary of all balance adjustments from DB."""
-        adjustments = self._fetch_query("SELECT type, amount, timestamp FROM balance_adjustments ORDER BY timestamp ASC")
-        
-        if not adjustments:
-            return {'total_deposits': 0.0, 'total_withdrawals': 0.0, 'net_adjustment': 0.0, 
-                    'adjustment_count': 0, 'last_adjustment': None}
-        
-        total_deposits = sum(adj['amount'] for adj in adjustments if adj['type'] == 'deposit')
-        total_withdrawals = sum(adj['amount'] for adj in adjustments if adj['type'] == 'withdrawal') # Amounts stored positive
-        net_adjustment = total_deposits - total_withdrawals
-        
-        return {
-            'total_deposits': total_deposits, 'total_withdrawals': total_withdrawals,
-            'net_adjustment': net_adjustment, 'adjustment_count': len(adjustments),
-            'last_adjustment': adjustments[-1]['timestamp'] if adjustments else None
-        }
-
-    def close_connection(self):
-        """Close the SQLite database connection."""
-        if self.conn:
-            self.conn.close()
-            logger.info("TradingStats SQLite connection closed.")
-
-    def __del__(self):
-        """Ensure connection is closed when object is deleted."""
-        self.close_connection()
-
-    # --- Order Table Management --- 
-
-    def record_order_placed(self, symbol: str, side: str, order_type: str, 
-                            amount_requested: float, price: Optional[float] = None, 
-                            bot_order_ref_id: Optional[str] = None, 
-                            exchange_order_id: Optional[str] = None, 
-                            status: str = 'open',
-                            parent_bot_order_ref_id: Optional[str] = None) -> Optional[int]:
-        """Record a newly placed order in the 'orders' table. Returns the ID of the inserted order or None on failure."""
-        now_iso = datetime.now(timezone.utc).isoformat()
-        query = """
-            INSERT INTO orders (bot_order_ref_id, exchange_order_id, symbol, side, type, 
-                                amount_requested, price, status, timestamp_created, timestamp_updated, parent_bot_order_ref_id)
-            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
-        """
-        params = (bot_order_ref_id, exchange_order_id, symbol, side.lower(), order_type.lower(), 
-                  amount_requested, price, status.lower(), now_iso, now_iso, parent_bot_order_ref_id)
-        try:
-            cur = self.conn.cursor()
-            cur.execute(query, params)
-            self.conn.commit()
-            order_db_id = cur.lastrowid
-            logger.info(f"Recorded order placed: ID {order_db_id}, Symbol {symbol}, Side {side}, Type {order_type}, Amount {amount_requested}, BotRef {bot_order_ref_id}, ExchID {exchange_order_id}")
-            return order_db_id
-        except sqlite3.IntegrityError as e:
-            logger.error(f"Failed to record order due to IntegrityError (likely duplicate bot_order_ref_id '{bot_order_ref_id}' or exchange_order_id '{exchange_order_id}'): {e}")
-            return None
-        except Exception as e:
-            logger.error(f"Failed to record order: {e}")
-            return None
-
-    def update_order_status(self, order_db_id: Optional[int] = None, bot_order_ref_id: Optional[str] = None, exchange_order_id: Optional[str] = None,
-                            new_status: Optional[str] = None, amount_filled_increment: Optional[float] = None, set_exchange_order_id: Optional[str] = None) -> bool:
-        """Update an existing order's status and/or amount_filled. Identify order by order_db_id, bot_order_ref_id, or exchange_order_id.
-        
-        Args:
-            order_db_id: Database ID to identify the order
-            bot_order_ref_id: Bot's internal reference ID to identify the order
-            exchange_order_id: Exchange's order ID to identify the order
-            new_status: New status to set
-            amount_filled_increment: Amount to add to current filled amount
-            set_exchange_order_id: If provided, sets/updates the exchange_order_id field in the database
-        """
-        if not any([order_db_id, bot_order_ref_id, exchange_order_id]):
-            logger.error("Must provide one of order_db_id, bot_order_ref_id, or exchange_order_id to update order.")
-            return False
-
-        now_iso = datetime.now(timezone.utc).isoformat()
-        set_clauses = []
-        params = []
-
-        if new_status:
-            set_clauses.append("status = ?")
-            params.append(new_status.lower())
-        
-        if set_exchange_order_id is not None:
-            set_clauses.append("exchange_order_id = ?")
-            params.append(set_exchange_order_id)
-        
-        current_amount_filled = 0.0
-        identifier_clause = ""
-        identifier_param = None
-
-        if order_db_id:
-            identifier_clause = "id = ?"
-            identifier_param = order_db_id
-        elif bot_order_ref_id:
-            identifier_clause = "bot_order_ref_id = ?"
-            identifier_param = bot_order_ref_id
-        elif exchange_order_id:
-            identifier_clause = "exchange_order_id = ?"
-            identifier_param = exchange_order_id
-
-        if amount_filled_increment is not None and amount_filled_increment > 0:
-            # To correctly increment, we might need to fetch current filled amount first if DB doesn't support direct increment easily or atomically with other updates.
-            # For simplicity here, assuming we can use SQL's increment if other fields are not changing, or we do it in two steps.
-            # Let's assume we fetch first then update to be safe and clear.
-            order_data = self._fetchone_query(f"SELECT amount_filled FROM orders WHERE {identifier_clause}", (identifier_param,))
-            if order_data:
-                current_amount_filled = order_data.get('amount_filled', 0.0)
-            else:
-                logger.warning(f"Order not found by {identifier_clause}={identifier_param} when trying to increment amount_filled.")
-                # Potentially still update status if new_status is provided, but amount_filled won't be right.
-                # For now, let's proceed with update if status is there.
-
-            set_clauses.append("amount_filled = ?")
-            params.append(current_amount_filled + amount_filled_increment)
-
-        if not set_clauses:
-            logger.info("No fields to update for order.")
-            return True # No update needed, not an error
-
-        set_clauses.append("timestamp_updated = ?")
-        params.append(now_iso)
-        
-        params.append(identifier_param) # Add identifier param at the end for WHERE clause
-
-        query = f"UPDATE orders SET { ', '.join(set_clauses) } WHERE {identifier_clause}"
-        
-        try:
-            self._execute_query(query, tuple(params))
-            log_msg = f"Updated order ({identifier_clause}={identifier_param}): Status to '{new_status or 'N/A'}', Filled increment {amount_filled_increment or 0.0}"
-            if set_exchange_order_id is not None:
-                log_msg += f", Exchange ID set to '{set_exchange_order_id}'"
-            logger.info(log_msg)
-            return True
-        except Exception as e:
-            logger.error(f"Failed to update order ({identifier_clause}={identifier_param}): {e}")
-            return False
-
-    def get_order_by_db_id(self, order_db_id: int) -> Optional[Dict[str, Any]]:
-        """Fetch an order by its database primary key ID."""
-        return self._fetchone_query("SELECT * FROM orders WHERE id = ?", (order_db_id,))
-
-    def get_order_by_bot_ref_id(self, bot_order_ref_id: str) -> Optional[Dict[str, Any]]:
-        """Fetch an order by the bot's internal reference ID."""
-        return self._fetchone_query("SELECT * FROM orders WHERE bot_order_ref_id = ?", (bot_order_ref_id,))
-
-    def get_order_by_exchange_id(self, exchange_order_id: str) -> Optional[Dict[str, Any]]:
-        """Fetch an order by the exchange's order ID."""
-        return self._fetchone_query("SELECT * FROM orders WHERE exchange_order_id = ?", (exchange_order_id,))
-
-    def get_orders_by_status(self, status: str, order_type_filter: Optional[str] = None, parent_bot_order_ref_id: Optional[str] = None) -> List[Dict[str, Any]]:
-        """Fetch all orders with a specific status, optionally filtering by order_type and parent_bot_order_ref_id."""
-        query = "SELECT * FROM orders WHERE status = ?"
-        params = [status.lower()]
-        if order_type_filter:
-            query += " AND type = ?"
-            params.append(order_type_filter.lower())
-        if parent_bot_order_ref_id:
-            query += " AND parent_bot_order_ref_id = ?"
-            params.append(parent_bot_order_ref_id)
-        query += " ORDER BY timestamp_created ASC"
-        return self._fetch_query(query, tuple(params))
-
-    def cancel_linked_orders(self, parent_bot_order_ref_id: str, new_status: str = 'cancelled_parent_filled') -> int:
-        """Cancel all orders linked to a parent order (e.g., pending stop losses when parent order fills or gets cancelled).
-        Returns the number of orders that were cancelled."""
-        linked_orders = self.get_orders_by_status('pending_trigger', parent_bot_order_ref_id=parent_bot_order_ref_id)
-        cancelled_count = 0
-        
-        for order in linked_orders:
-            order_db_id = order.get('id')
-            if order_db_id:
-                success = self.update_order_status(order_db_id=order_db_id, new_status=new_status)
-                if success:
-                    cancelled_count += 1
-                    logger.info(f"Cancelled linked order ID {order_db_id} (parent: {parent_bot_order_ref_id}) -> status: {new_status}")
-        
-        return cancelled_count
-
-    def cancel_pending_stop_losses_by_symbol(self, symbol: str, new_status: str = 'cancelled_position_closed') -> int:
-        """Cancel all pending stop loss orders for a specific symbol (when position is closed).
-        Returns the number of stop loss orders that were cancelled."""
-        query = "SELECT * FROM orders WHERE symbol = ? AND status = 'pending_trigger' AND type = 'stop_limit_trigger'"
-        pending_stop_losses = self._fetch_query(query, (symbol,))
-        cancelled_count = 0
-        
-        for order in pending_stop_losses:
-            order_db_id = order.get('id')
-            if order_db_id:
-                success = self.update_order_status(order_db_id=order_db_id, new_status=new_status)
-                if success:
-                    cancelled_count += 1
-                    logger.info(f"Cancelled pending SL order ID {order_db_id} for {symbol} -> status: {new_status}")
-        
-        return cancelled_count
-
-    def get_order_cleanup_summary(self) -> Dict[str, Any]:
-        """Get summary of order cleanup actions for monitoring and debugging."""
-        try:
-            # Get counts of different cancellation types
-            cleanup_stats = {}
-            
-            cancellation_types = [
-                'cancelled_parent_cancelled',
-                'cancelled_parent_disappeared', 
-                'cancelled_manual_exit',
-                'cancelled_auto_exit',
-                'cancelled_no_position',
-                'cancelled_external_position_close',
-                'cancelled_orphaned_no_position',
-                'cancelled_externally',
-                'immediately_executed_on_activation',
-                'activation_execution_failed',
-                'activation_execution_error'
-            ]
-            
-            for cancel_type in cancellation_types:
-                count_result = self._fetchone_query(
-                    "SELECT COUNT(*) as count FROM orders WHERE status = ?", 
-                    (cancel_type,)
-                )
-                cleanup_stats[cancel_type] = count_result['count'] if count_result else 0
-            
-            # Get currently pending stop losses
-            pending_sls = self.get_orders_by_status('pending_trigger', 'stop_limit_trigger')
-            cleanup_stats['currently_pending_stop_losses'] = len(pending_sls)
-            
-            # Get total orders in various states
-            active_orders = self._fetchone_query(
-                "SELECT COUNT(*) as count FROM orders WHERE status IN ('open', 'submitted', 'partially_filled')",
-                ()
-            )
-            cleanup_stats['currently_active_orders'] = active_orders['count'] if active_orders else 0
-            
-            return cleanup_stats
-            
-        except Exception as e:
-            logger.error(f"Error getting order cleanup summary: {e}")
-            return {}
-
-    def get_external_activity_summary(self, days: int = 7) -> Dict[str, Any]:
-        """Get summary of external activity (trades and cancellations) over the last N days."""
-        try:
-            from datetime import timedelta
-            cutoff_date = (datetime.now(timezone.utc) - timedelta(days=days)).isoformat()
-            
-            # External trades
-            external_trades = self._fetch_query(
-                "SELECT COUNT(*) as count, side FROM trades WHERE trade_type = 'external' AND timestamp >= ? GROUP BY side",
-                (cutoff_date,)
-            )
-            
-            external_trade_summary = {
-                'external_buy_trades': 0,
-                'external_sell_trades': 0,
-                'total_external_trades': 0
-            }
-            
-            for trade_group in external_trades:
-                side = trade_group['side']
-                count = trade_group['count']
-                external_trade_summary['total_external_trades'] += count
-                if side == 'buy':
-                    external_trade_summary['external_buy_trades'] = count
-                elif side == 'sell':
-                    external_trade_summary['external_sell_trades'] = count
-            
-            # External cancellations
-            external_cancellations = self._fetchone_query(
-                "SELECT COUNT(*) as count FROM orders WHERE status = 'cancelled_externally' AND timestamp_updated >= ?",
-                (cutoff_date,)
-            )
-            external_trade_summary['external_cancellations'] = external_cancellations['count'] if external_cancellations else 0
-            
-            # Cleanup actions
-            cleanup_cancellations = self._fetchone_query(
-                """SELECT COUNT(*) as count FROM orders 
-                   WHERE status LIKE 'cancelled_%' 
-                   AND status != 'cancelled_externally' 
-                   AND timestamp_updated >= ?""",
-                (cutoff_date,)
-            )
-            external_trade_summary['cleanup_cancellations'] = cleanup_cancellations['count'] if cleanup_cancellations else 0
-            
-            external_trade_summary['period_days'] = days
-            
-            return external_trade_summary
-            
-        except Exception as e:
-            logger.error(f"Error getting external activity summary: {e}")
-            return {'period_days': days, 'total_external_trades': 0, 'external_cancellations': 0}
-
-    # --- End Order Table Management ---
-
-    # =============================================================================
-    # TRADE LIFECYCLE MANAGEMENT - PHASE 4: UNIFIED TRADES TABLE
-    # =============================================================================
-    
-    def create_trade_lifecycle(self, symbol: str, side: str, entry_order_id: Optional[str] = None,
-                              entry_bot_order_ref_id: Optional[str] = None, # New parameter
-                              stop_loss_price: Optional[float] = None, 
-                              take_profit_price: Optional[float] = None,
-                              trade_type: str = 'manual') -> Optional[str]:
-        """Create a new trade lifecycle.
-        If stop_loss_price is provided, also creates a conceptual 'pending_sl_activation' order.
-        """
-        try:
-            lifecycle_id = str(uuid.uuid4())
-            
-            # Main lifecycle record in 'trades' table
-            query = """
-                INSERT INTO trades (
-                    symbol, side, amount, price, value, trade_type, timestamp,
-                    status, trade_lifecycle_id, position_side, entry_order_id,
-                    stop_loss_price, take_profit_price, updated_at
-                ) VALUES (?, ?, 0, 0, 0, ?, ?, 'pending', ?, 'flat', ?, ?, ?, ?)
-            """
-            timestamp = datetime.now(timezone.utc).isoformat()
-            params = (symbol, side.lower(), trade_type, timestamp, lifecycle_id, 
-                     entry_order_id, stop_loss_price, take_profit_price, timestamp)
-            
-            self._execute_query(query, params)
-            logger.info(f"📊 Created trade lifecycle {lifecycle_id}: {side.upper()} {symbol} (pending for exch_id: {entry_order_id or 'N/A'})")
-
-            # If SL price is provided, create a conceptual pending SL order in 'orders' table
-            if stop_loss_price is not None and entry_bot_order_ref_id is not None:
-                sl_order_side = 'sell' if side.lower() == 'buy' else 'buy'
-                # Using entry_bot_order_ref_id ensures this conceptual SL is linked to the specific entry attempt
-                conceptual_sl_bot_ref_id = f"pending_sl_activation_{entry_bot_order_ref_id}"
-                
-                # Record this conceptual order. Amount is 0 for now.
-                # The actual amount will be determined when the SL is placed after entry fill.
-                sl_order_db_id = self.record_order_placed(
-                    symbol=symbol,
-                    side=sl_order_side,
-                    order_type='pending_sl_activation', # New conceptual type
-                    amount_requested=0, # Placeholder amount
-                    price=stop_loss_price,
-                    bot_order_ref_id=conceptual_sl_bot_ref_id,
-                    status='pending_activation', # New conceptual status
-                    parent_bot_order_ref_id=entry_bot_order_ref_id, # Link to the main entry order
-                    exchange_order_id=None # Not on exchange yet
-                )
-                if sl_order_db_id:
-                    logger.info(f"💡 Recorded conceptual 'pending_sl_activation' order (DB ID: {sl_order_db_id}, BotRef: {conceptual_sl_bot_ref_id}) for lifecycle {lifecycle_id} at SL price {stop_loss_price}.")
-                else:
-                    logger.error(f"⚠️ Failed to record conceptual 'pending_sl_activation' order for lifecycle {lifecycle_id} (Entry BotRef: {entry_bot_order_ref_id}).")
-
-            return lifecycle_id
-            
-        except Exception as e:
-            logger.error(f"❌ Error creating trade lifecycle: {e}")
-            return None
-    
-    def update_trade_position_opened(self, lifecycle_id: str, entry_price: float, 
-                                   entry_amount: float, exchange_fill_id: str) -> bool:
-        """Update trade when position is opened (entry order filled)."""
-        try:
-            query = """
-                UPDATE trades 
-                SET status = 'position_opened',
-                    amount = ?,
-                    price = ?,
-                    value = ?,
-                    entry_price = ?,
-                    current_position_size = ?,
-                    position_side = CASE 
-                        WHEN side = 'buy' THEN 'long'
-                        WHEN side = 'sell' THEN 'short'
-                        ELSE position_side
-                    END,
-                    exchange_fill_id = ?,
-                    position_opened_at = ?,
-                    updated_at = ?
-                WHERE trade_lifecycle_id = ? AND status = 'pending'
-            """
-            timestamp = datetime.now(timezone.utc).isoformat()
-            value = entry_amount * entry_price
-            params = (entry_amount, entry_price, value, entry_price, entry_amount,
-                     exchange_fill_id, timestamp, timestamp, lifecycle_id)
-            
-            self._execute_query(query, params)
-            
-            formatter = get_formatter()
-            trade_info = self.get_trade_by_lifecycle_id(lifecycle_id) # Fetch to get symbol for formatting
-            symbol_for_formatting = trade_info.get('symbol', 'UNKNOWN_SYMBOL') if trade_info else 'UNKNOWN_SYMBOL'
-            base_asset_for_amount = symbol_for_formatting.split('/')[0] if '/' in symbol_for_formatting else symbol_for_formatting
-
-            logger.info(f"📈 Trade lifecycle {lifecycle_id} position opened: {formatter.format_amount(entry_amount, base_asset_for_amount)} {symbol_for_formatting} @ {formatter.format_price(entry_price, symbol_for_formatting)}")
-            return True
-            
-        except Exception as e:
-            logger.error(f"❌ Error updating trade position opened: {e}")
-            return False
-    
-    def update_trade_position_closed(self, lifecycle_id: str, exit_price: float,
-                                   realized_pnl: float, exchange_fill_id: str) -> bool:
-        """Update trade when position is fully closed."""
-        try:
-            query = """
-                UPDATE trades 
-                SET status = 'position_closed',
-                    current_position_size = 0,
-                    position_side = 'flat',
-                    realized_pnl = ?,
-                    position_closed_at = ?,
-                    updated_at = ?
-                WHERE trade_lifecycle_id = ? AND status = 'position_opened'
-            """
-            timestamp = datetime.now(timezone.utc).isoformat()
-            params = (realized_pnl, timestamp, timestamp, lifecycle_id)
-            
-            self._execute_query(query, params)
-            
-            formatter = get_formatter()
-            trade_info = self.get_trade_by_lifecycle_id(lifecycle_id) # Fetch to get symbol for P&L formatting context
-            symbol_for_formatting = trade_info.get('symbol', 'USD') # Default to USD for PNL if symbol unknown
-            pnl_emoji = "🟢" if realized_pnl >= 0 else "🔴"
-            logger.info(f"{pnl_emoji} Trade lifecycle {lifecycle_id} position closed: P&L {formatter.format_price_with_symbol(realized_pnl)}")
-            return True
-            
-        except Exception as e:
-            logger.error(f"❌ Error updating trade position closed: {e}")
-            return False
-    
-    def update_trade_cancelled(self, lifecycle_id: str, reason: str = "order_cancelled") -> bool:
-        """Update trade when entry order is cancelled (never opened)."""
-        try:
-            query = """
-                UPDATE trades 
-                SET status = 'cancelled',
-                    notes = ?,
-                    updated_at = ?
-                WHERE trade_lifecycle_id = ? AND status = 'pending'
-            """
-            timestamp = datetime.now(timezone.utc).isoformat()
-            params = (f"Cancelled: {reason}", timestamp, lifecycle_id)
-            
-            self._execute_query(query, params)
-            
-            logger.info(f"❌ Trade lifecycle {lifecycle_id} cancelled: {reason}")
-            return True
-            
-        except Exception as e:
-            logger.error(f"❌ Error updating trade cancelled: {e}")
-            return False
-    
-    def link_stop_loss_to_trade(self, lifecycle_id: str, stop_loss_order_id: str,
-                               stop_loss_price: float) -> bool:
-        """Link a stop loss order to a trade lifecycle."""
-        try:
-            query = """
-                UPDATE trades 
-                SET stop_loss_order_id = ?,
-                    stop_loss_price = ?,
-                    updated_at = ?
-                WHERE trade_lifecycle_id = ? AND status = 'position_opened'
-            """
-            timestamp = datetime.now(timezone.utc).isoformat()
-            params = (stop_loss_order_id, stop_loss_price, timestamp, lifecycle_id)
-            
-            self._execute_query(query, params)
-            
-            formatter = get_formatter()
-            trade_info = self.get_trade_by_lifecycle_id(lifecycle_id) # Fetch to get symbol for formatting
-            symbol_for_formatting = trade_info.get('symbol', 'UNKNOWN_SYMBOL') if trade_info else 'UNKNOWN_SYMBOL'
-            logger.info(f"🛑 Linked stop loss order {stop_loss_order_id} ({formatter.format_price(stop_loss_price, symbol_for_formatting)}) to trade {lifecycle_id}")
-            return True
-            
-        except Exception as e:
-            logger.error(f"❌ Error linking stop loss to trade: {e}")
-            return False
-    
-    def link_take_profit_to_trade(self, lifecycle_id: str, take_profit_order_id: str,
-                                 take_profit_price: float) -> bool:
-        """Link a take profit order to a trade lifecycle."""
-        try:
-            query = """
-                UPDATE trades 
-                SET take_profit_order_id = ?,
-                    take_profit_price = ?,
-                    updated_at = ?
-                WHERE trade_lifecycle_id = ? AND status = 'position_opened'
-            """
-            timestamp = datetime.now(timezone.utc).isoformat()
-            params = (take_profit_order_id, take_profit_price, timestamp, lifecycle_id)
-            
-            self._execute_query(query, params)
-            
-            formatter = get_formatter()
-            trade_info = self.get_trade_by_lifecycle_id(lifecycle_id) # Fetch to get symbol for formatting
-            symbol_for_formatting = trade_info.get('symbol', 'UNKNOWN_SYMBOL') if trade_info else 'UNKNOWN_SYMBOL'
-            logger.info(f"🎯 Linked take profit order {take_profit_order_id} ({formatter.format_price(take_profit_price, symbol_for_formatting)}) to trade {lifecycle_id}")
-            return True
-            
-        except Exception as e:
-            logger.error(f"❌ Error linking take profit to trade: {e}")
-            return False
-    
-    def get_trade_by_lifecycle_id(self, lifecycle_id: str) -> Optional[Dict[str, Any]]:
-        """Get trade by lifecycle ID."""
-        query = "SELECT * FROM trades WHERE trade_lifecycle_id = ?"
-        return self._fetchone_query(query, (lifecycle_id,))
-    
-    # Re-instating the correct get_trade_by_symbol_and_status from earlier version in case it was overwritten by file read
-    def get_trade_by_symbol_and_status(self, symbol: str, status: str = 'position_opened') -> Optional[Dict[str, Any]]: # Copied from earlier state
-        """Get trade by symbol and status."""
-        query = "SELECT * FROM trades WHERE symbol = ? AND status = ? ORDER BY updated_at DESC LIMIT 1"
-        return self._fetchone_query(query, (symbol, status))
-    
-    def get_open_positions(self, symbol: Optional[str] = None) -> List[Dict[str, Any]]:
-        """Get all open positions, optionally filtered by symbol."""
-        if symbol:
-            query = "SELECT * FROM trades WHERE status = 'position_opened' AND symbol = ? ORDER BY position_opened_at DESC"
-            return self._fetch_query(query, (symbol,))
-        else:
-            query = "SELECT * FROM trades WHERE status = 'position_opened' ORDER BY position_opened_at DESC"
-            return self._fetch_query(query)
-    
-    def get_trades_by_status(self, status: str, limit: int = 50) -> List[Dict[str, Any]]:
-        """Get trades by status."""
-        query = "SELECT * FROM trades WHERE status = ? ORDER BY updated_at DESC LIMIT ?"
-        return self._fetch_query(query, (status, limit))
-
-    def _format_duration(self, seconds: float) -> str:
-        """Formats a duration in seconds into a human-readable string (e.g., 1h 25m 3s)."""
-        hours = int(seconds // 3600)
-        minutes = int((seconds % 3600) // 60)
-        remaining_seconds = int(seconds % 60)
-        return f"{hours}h {minutes}m {remaining_seconds}s"
-
-    # --- End Trade Lifecycle Management ---
-
-    def get_balance_history_record_count(self) -> int:
-        """Get the total number of balance history records."""
-        row = self._fetchone_query("SELECT COUNT(*) as count FROM balance_history")
-        return row['count'] if row and 'count' in row else 0
-
-    # 🆕 PHASE 5: AGGREGATION AND PURGING LOGIC
-    def _migrate_trade_to_aggregated_stats(self, trade_lifecycle_id: str):
-        """Migrate a completed/cancelled trade's stats to aggregate tables and delete the original trade."""
-        # Implement the logic to migrate trade stats to aggregate tables and delete the original trade
-        pass
-
-    def purge_old_daily_aggregated_stats(self, months_to_keep: int = 10):
-        """Purge records from daily_aggregated_stats older than a specified number of months."""
-        try:
-            cutoff_date = datetime.now(timezone.utc).date() - timedelta(days=months_to_keep * 30)
-            cutoff_datetime_str = cutoff_date.isoformat()
-
-            query = "DELETE FROM daily_aggregated_stats WHERE date < ?"
-            
-            with self.conn:
-                cursor = self.conn.cursor()
-                cursor.execute(query, (cutoff_datetime_str,))
-                rows_deleted = cursor.rowcount
-            
-            if rows_deleted > 0:
-                logger.info(f"Purged {rows_deleted} old records from daily_aggregated_stats (older than {months_to_keep} months).")
-            else:
-                logger.debug(f"No old records found in daily_aggregated_stats to purge (older than {months_to_keep} months).")
-
-        except sqlite3.Error as e:
-            logger.error(f"Database error purging old daily_aggregated_stats: {e}", exc_info=True)
-        except Exception as e:
-            logger.error(f"Unexpected error purging old daily_aggregated_stats: {e}", exc_info=True)
-
-    def purge_old_balance_history(self):
-        """Purge records from balance_history older than the configured retention period."""
-        days_to_keep = Config.BALANCE_HISTORY_RETENTION_DAYS
-        if days_to_keep <= 0:
-            logger.info("Not purging balance_history as retention days is not positive.")
-            return
-
-        try:
-            cutoff_date = datetime.now(timezone.utc).date() - timedelta(days=days_to_keep)
-            cutoff_datetime_str = cutoff_date.isoformat()
-
-            query = "DELETE FROM balance_history WHERE timestamp < ?"
-            
-            with self.conn:
-                cursor = self.conn.cursor()
-                cursor.execute(query, (cutoff_datetime_str,))
-                rows_deleted = cursor.rowcount
-            
-            if rows_deleted > 0:
-                logger.info(f"Purged {rows_deleted} old records from balance_history (older than {days_to_keep} days).")
-            else:
-                logger.debug(f"No old records found in balance_history to purge (older than {days_to_keep} days).")
-
-        except sqlite3.Error as e:
-            logger.error(f"Database error purging old balance_history: {e}", exc_info=True)
-        except Exception as e:
-            logger.error(f"Unexpected error purging old balance_history: {e}", exc_info=True)
-
-    def get_daily_balance_record_count(self) -> int:
-        """Get the total number of daily balance records."""
-        row = self._fetchone_query("SELECT COUNT(*) as count FROM daily_balances")
-        return row['count'] if row and 'count' in row else 0

+ 39 - 30
src/monitoring/external_event_monitor.py

@@ -186,47 +186,38 @@ class ExternalEventMonitor:
                                                action_type: str, timestamp_dt: datetime, 
                                                existing_lc: Optional[Dict] = None, 
                                                realized_pnl: Optional[float] = None):
-        """Send appropriate notification based on position action type."""
+        """Send position change notification."""
         try:
             if not self.notification_manager:
                 return
-            
+                
             token = full_symbol.split('/')[0] if '/' in full_symbol else full_symbol.split(':')[0]
+            time_str = timestamp_dt.strftime('%Y-%m-%d %H:%M:%S UTC')
             formatter = get_formatter()
             
-            # Format timestamp
-            time_str = timestamp_dt.strftime('%H:%M:%S')
-            
-            if action_type == 'position_opened':
-                position_side = 'LONG' if side_from_fill.lower() == 'buy' else 'SHORT'
-                message = f"""
-🚀 <b>Position Opened (External)</b>
-
-📊 <b>Trade Details:</b>
-• Token: {token}
-• Direction: {position_side}
-• Size: {formatter.format_amount(amount_from_fill, token)}
-• Entry Price: {formatter.format_price_with_symbol(price_from_fill, token)}
-• Position Value: {formatter.format_price_with_symbol(amount_from_fill * price_from_fill)}
-
-✅ <b>Status:</b> New {position_side} position opened externally
-⏰ <b>Time:</b> {time_str}
-
-📱 Use /positions to view all positions
-                """
-                
-            elif action_type == 'position_closed' and existing_lc:
+            if action_type == 'position_closed' and existing_lc:
                 position_side = existing_lc.get('position_side', 'unknown').upper()
                 entry_price = existing_lc.get('entry_price', 0)
                 pnl_emoji = "🟢" if realized_pnl and realized_pnl >= 0 else "🔴"
                 pnl_text = f"{formatter.format_price_with_symbol(realized_pnl)}" if realized_pnl is not None else "N/A"
                 
-                # Calculate ROE (Return on Equity)
-                roe_text = ""
-                if realized_pnl is not None and entry_price > 0 and amount_from_fill > 0:
-                    cost_basis = amount_from_fill * entry_price
-                    roe = (realized_pnl / cost_basis) * 100
-                    roe_text = f" ({roe:+.2f}%)"
+                # Get ROE directly from exchange data
+                info_data = existing_lc.get('info', {})
+                position_info = info_data.get('position', {})
+                roe_raw = position_info.get('percentage')  # This is the same field used by /positions
+                
+                if roe_raw is not None:
+                    try:
+                        # The exchange provides ROE as a decimal (e.g., -0.326 for -32.6%)
+                        # We need to multiply by 100 and keep the sign
+                        roe = float(roe_raw) * 100
+                        roe_text = f" ({roe:+.2f}%)"
+                    except (ValueError, TypeError):
+                        logger.warning(f"Could not parse ROE value: {roe_raw} for {full_symbol}")
+                        roe_text = ""
+                else:
+                    logger.warning(f"No ROE data available from exchange for {full_symbol}")
+                    roe_text = ""
                 
                 message = f"""
 🎯 <b>Position Closed (External)</b>
@@ -246,6 +237,24 @@ class ExternalEventMonitor:
 📊 Use /stats to view updated performance
                 """
                 
+            elif action_type == 'position_opened':
+                position_side = 'LONG' if side_from_fill.lower() == 'buy' else 'SHORT'
+                message = f"""
+🚀 <b>Position Opened (External)</b>
+
+📊 <b>Trade Details:</b>
+• Token: {token}
+• Direction: {position_side}
+• Size: {formatter.format_amount(amount_from_fill, token)}
+• Entry Price: {formatter.format_price_with_symbol(price_from_fill, token)}
+• Position Value: {formatter.format_price_with_symbol(amount_from_fill * price_from_fill)}
+
+✅ <b>Status:</b> New {position_side} position opened externally
+⏰ <b>Time:</b> {time_str}
+
+📱 Use /positions to view all positions
+                """
+                
             elif action_type == 'position_increased' and existing_lc:
                 position_side = existing_lc.get('position_side', 'unknown').upper()
                 previous_size = existing_lc.get('current_position_size', 0)

+ 15 - 37
src/monitoring/risk_cleanup_manager.py

@@ -143,48 +143,26 @@ class RiskCleanupManager:
                     if contracts == 0 or entry_price <= 0 or mark_price <= 0:
                         continue
 
-                    # Calculate ROE (Return on Equity) percentage instead of standard PnL percentage
-                    # ROE is what the user sees in the Hyperliquid UI and represents actual return on cash invested
-                    roe_percentage = 0.0
-                    
-                    # Try to get ROE directly from exchange
+                    # Get ROE directly from exchange data
                     info_data = position.get('info', {})
                     position_info = info_data.get('position', {})
-                    roe_raw = position_info.get('returnOnEquity')
+                    roe_raw = position_info.get('percentage')  # This is the same field used by /positions
                     
                     if roe_raw is not None:
                         try:
-                            roe_percentage = float(roe_raw) * 100  # Convert to percentage
+                            # The exchange provides ROE as a decimal (e.g., -0.326 for -32.6%)
+                            # We need to multiply by 100 and keep the sign
+                            roe_percentage = float(roe_raw) * 100
+                            logger.debug(f"Using exchange-provided ROE for {symbol}: {roe_percentage:+.2f}%")
                         except (ValueError, TypeError):
                             logger.warning(f"Could not parse ROE value: {roe_raw} for {symbol}")
                             roe_percentage = 0.0
-                    
-                    # Fallback to calculating ROE if not available from exchange
-                    if roe_percentage == 0.0 and unrealized_pnl != 0:
-                        # Calculate equity used: For leveraged positions, equity is less than margin
-                        # We can estimate it from the margin and leverage
-                        margin_used = position.get('marginUsed') or position.get('initialMargin')
-                        leverage = position.get('leverage', 1.0)
-                        
-                        if margin_used and leverage and leverage > 0:
-                            try:
-                                equity_used = float(margin_used) / float(leverage)
-                                if equity_used > 0:
-                                    roe_percentage = (unrealized_pnl / equity_used) * 100
-                                    logger.debug(f"Calculated ROE for {symbol}: {roe_percentage:.2f}% (margin: ${margin_used}, leverage: {leverage}x, equity: ${equity_used})")
-                            except (ValueError, TypeError, ZeroDivisionError):
-                                pass
-                    
-                    # Final fallback to standard percentage if ROE calculation fails
-                    if roe_percentage == 0.0 and unrealized_pnl != 0:
-                        entry_value = abs(contracts) * entry_price
-                        if entry_value > 0:
-                            roe_percentage = (unrealized_pnl / entry_value) * 100
-                            logger.debug(f"Using standard PnL calculation for {symbol} as ROE fallback: {roe_percentage:.2f}%")
-                    
-                    pnl_percentage = roe_percentage  # Use ROE as the percentage for stop loss comparison
+                    else:
+                        logger.warning(f"No ROE data available from exchange for {symbol}")
+                        roe_percentage = 0.0
 
-                    if pnl_percentage <= -Config.STOP_LOSS_PERCENTAGE:
+                    # The exchange shows losses as negative percentages, so we compare against negative threshold
+                    if roe_percentage <= -Config.STOP_LOSS_PERCENTAGE:
                         token = symbol.split('/')[0] if '/' in symbol else symbol.split(':')[0]
                         position_side = "LONG" if contracts > 0 else "SHORT"
                         stats = self.trading_engine.get_stats()
@@ -194,7 +172,7 @@ class RiskCleanupManager:
                             if active_trade_lc:
                                 lifecycle_id_str = active_trade_lc.get('trade_lifecycle_id', "N/A")[:8] + "..."
 
-                        logger.warning(f"🚨 AUTOMATIC STOP LOSS TRIGGERED: {token} {position_side} position (Lifecycle: {lifecycle_id_str}) has {pnl_percentage:.2f}% ROE loss (threshold: -{Config.STOP_LOSS_PERCENTAGE}%)")
+                        logger.warning(f"🚨 AUTOMATIC STOP LOSS TRIGGERED: {token} {position_side} position (Lifecycle: {lifecycle_id_str}) has {roe_percentage:+.2f}% ROE loss (threshold: -{Config.STOP_LOSS_PERCENTAGE}%)")
                         
                         if self.notification_manager:
                             await self.notification_manager.send_generic_notification(
@@ -204,7 +182,7 @@ Lifecycle ID: {lifecycle_id_str}\\n
 Position: {position_side} {abs(contracts):.6f}\\n
 Entry Price: ${entry_price:.4f}\\n
 Current Price: ${mark_price:.4f}\\n
-Unrealized P&L: ${unrealized_pnl:.2f} ({pnl_percentage:.2f}% ROE)\\n
+Unrealized P&L: ${unrealized_pnl:+.2f} ({roe_percentage:+.2f}% ROE)\\n
 Safety Threshold: -{Config.STOP_LOSS_PERCENTAGE}% ROE\\n
 Action: Executing emergency exit order..."""
                             )
@@ -227,7 +205,7 @@ Action: Executing emergency exit order..."""
                                     f"""✅ <b>Emergency Exit Initiated</b>\\n\\n
 📊 <b>Position:</b> {token} {position_side}\\n
 🆔 <b>Lifecycle ID:</b> {lifecycle_id_str}\\n
-📉 <b>Loss at Trigger:</b> {pnl_percentage:.2f}% ROE (${unrealized_pnl:.2f})\\n
+📉 <b>Loss at Trigger:</b> {roe_percentage:+.2f}% ROE (${unrealized_pnl:+.2f})\\n
 ⚠️ <b>Threshold:</b> -{Config.STOP_LOSS_PERCENTAGE}% ROE\\n
 ✅ <b>Action:</b> Market exit order placed successfully\\n
 🆔 <b>Exit Order ID:</b> {placed_order_details.get('exchange_order_id', 'N/A')}\\n
@@ -243,7 +221,7 @@ Action: Executing emergency exit order..."""
                                     f"""❌ <b>CRITICAL: Emergency Exit Failed!</b>\\n\\n
 📊 <b>Position:</b> {token} {position_side}\\n
 🆔 <b>Lifecycle ID:</b> {lifecycle_id_str}\\n
-📉 <b>Loss:</b> {pnl_percentage:.2f}% ROE\\n
+📉 <b>Loss:</b> {roe_percentage:+.2f}% ROE\\n
 ❌ <b>Error Placing Order:</b> {error_msg}\\n\\n
 ⚠️ <b>MANUAL INTERVENTION REQUIRED</b>\\n
 Please close this position manually via /exit {token}"""

+ 32 - 17
src/monitoring/simple_position_tracker.py

@@ -328,6 +328,12 @@ class SimplePositionTracker:
                 if pos.get('symbol') and abs(float(pos.get('contracts', 0))) > 1e-9
             }
             
+            # Get symbols with open orders
+            symbols_with_open_orders = {
+                order.get('symbol') for order in exchange_orders 
+                if order.get('symbol') and order.get('status') in ['open', 'pending_submission', 'submitted']
+            }
+            
             for trade in pending_trades:
                 try:
                     lifecycle_id = trade['trade_lifecycle_id']
@@ -361,10 +367,13 @@ class SimplePositionTracker:
                                 created_at = datetime.fromisoformat(created_at_str.replace('Z', '+00:00'))
                                 if datetime.now(timezone.utc) - created_at > timedelta(hours=1):
                                     # Very old pending trade, likely orphaned
-                                    if symbol not in exchange_position_symbols:
+                                    # Only cancel if no position AND no open orders
+                                    if symbol not in exchange_position_symbols and symbol not in symbols_with_open_orders:
                                         should_cancel = True
                                         cancel_reason = "old_pending_trade_no_position"
-                                        logger.debug(f"🗑️ Pending trade {lifecycle_id[:8]} for {symbol}: very old ({created_at}) with no position")
+                                        logger.debug(f"🗑️ Pending trade {lifecycle_id[:8]} for {symbol}: very old ({created_at}) with no position and no open orders")
+                                    else:
+                                        logger.debug(f"⏳ Keeping old pending trade {lifecycle_id[:8]} for {symbol}: has position or open orders")
                             except (ValueError, TypeError) as e:
                                 logger.warning(f"Could not parse timestamp for pending trade {lifecycle_id}: {e}")
                     
@@ -428,28 +437,24 @@ class SimplePositionTracker:
                 return
             
             token = symbol.split('/')[0] if '/' in symbol else symbol.split(':')[0]
-            timestamp = details.get('timestamp', datetime.now(timezone.utc))
-            time_str = timestamp.strftime('%H:%M:%S')
-            
-            from src.utils.token_display_formatter import get_formatter
+            time_str = datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M:%S UTC')
             formatter = get_formatter()
             
             if change_type == 'opened':
                 side = details['side'].upper()
                 size = details['size']
-                price = details['price']
+                entry_price = details['price']
                 
-                message = f"""🚀 <b>Position Opened</b>
+                message = f"""🎯 <b>Position Opened</b>
 
 📊 <b>Details:</b>
 • Token: {token}
 • Direction: {side}
 • Size: {formatter.format_amount(size, token)}
-• Entry Price: {formatter.format_price_with_symbol(price, token)}
-• Value: {formatter.format_price_with_symbol(size * price)}
+• Entry: {formatter.format_price_with_symbol(entry_price, token)}
 
 ⏰ <b>Time:</b> {time_str}
-📱 Use /positions to view all positions"""
+📈 Use /positions to view current status"""
                 
             elif change_type == 'closed':
                 side = details['side'].upper()
@@ -459,13 +464,23 @@ class SimplePositionTracker:
                 pnl = details['realized_pnl']
                 pnl_emoji = "🟢" if pnl >= 0 else "🔴"
                 
-                # Calculate ROE (Return on Equity)
-                roe = 0
-                if entry_price > 0 and size > 0:
-                    cost_basis = size * entry_price
-                    roe = (pnl / cost_basis) * 100
+                # Get ROE directly from exchange data
+                info_data = details.get('info', {})
+                position_info = info_data.get('position', {})
+                roe_raw = position_info.get('percentage')  # This is the same field used by /positions
                 
-                roe_text = f"({roe:+.2f}%)" if entry_price > 0 else ""
+                if roe_raw is not None:
+                    try:
+                        # The exchange provides ROE as a decimal (e.g., -0.326 for -32.6%)
+                        # We need to multiply by 100 and keep the sign
+                        roe = float(roe_raw) * 100
+                        roe_text = f"({roe:+.2f}%)"
+                    except (ValueError, TypeError):
+                        logger.warning(f"Could not parse ROE value: {roe_raw} for {symbol}")
+                        roe_text = ""
+                else:
+                    logger.warning(f"No ROE data available from exchange for {symbol}")
+                    roe_text = ""
                 
                 message = f"""🎯 <b>Position Closed</b>
 

+ 16 - 4
src/stats/performance_calculator.py

@@ -229,10 +229,22 @@ class PerformanceCalculator:
             sum_losing = token.get('sum_of_losing_pnl', 0)
             token['profit_factor'] = sum_winning / sum_losing if sum_losing > 0 else float('inf') if sum_winning > 0 else 0
             
-            # Calculate ROE (Return on Equity) - PnL percentage based on entry volume
-            total_pnl = token.get('total_realized_pnl', 0)
-            entry_volume = token.get('total_entry_volume', 0)
-            token['roe_percentage'] = (total_pnl / entry_volume * 100) if entry_volume > 0 else 0
+            # Get ROE directly from exchange data
+            info_data = token.get('info', {})
+            position_info = info_data.get('position', {})
+            roe_raw = position_info.get('percentage')  # This is the same field used by /positions
+            
+            if roe_raw is not None:
+                try:
+                    # The exchange provides ROE as a decimal (e.g., -0.326 for -32.6%)
+                    # We need to multiply by 100 and keep the sign
+                    token['roe_percentage'] = float(roe_raw) * 100
+                except (ValueError, TypeError):
+                    logger.warning(f"Could not parse ROE value: {roe_raw} for {token['token']}")
+                    token['roe_percentage'] = 0.0
+            else:
+                logger.warning(f"No ROE data available from exchange for {token['token']}")
+                token['roe_percentage'] = 0.0
             
             # Format durations
             total_duration = token.get('total_duration_seconds', 0)

+ 4 - 0
src/stats/trading_stats.py

@@ -281,6 +281,10 @@ class TradingStats:
         """Get all trades."""
         return self.trade_manager.get_all_trades()
 
+    def cancel_linked_orders(self, parent_bot_order_ref_id: str, new_status: str = 'cancelled_parent_filled') -> int:
+        """Cancel all orders linked to a parent order. Returns count of cancelled orders."""
+        return self.order_manager.cancel_linked_orders(parent_bot_order_ref_id, new_status)
+
     # =============================================================================
     # AGGREGATION MANAGEMENT DELEGATION
     # =============================================================================

+ 1 - 1
trading_bot.py

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