123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101 |
- import logging
- from typing import Dict, Any, List, Optional
- from datetime import datetime, timedelta
- from telegram import Update
- from telegram.ext import ContextTypes
- from .base import InfoCommandsBase
- logger = logging.getLogger(__name__)
- class TradesCommands(InfoCommandsBase):
- """Handles all trade history-related commands."""
- async def trades_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
- """Handle the /trades command."""
- try:
- if not self._is_authorized(update):
- await self._reply(update, "❌ Unauthorized access.")
- return
- stats = self.trading_engine.get_stats()
- if not stats:
- await self._reply(update, "❌ Trading stats not available.")
- return
- # Get recent trades
- recent_trades = stats.get_recent_trades()
- if not recent_trades:
- await self._reply(update, "📭 No recent trades")
- return
- # Format trades text
- trades_text = "📜 <b>Recent Trades</b>\n\n"
- for trade in recent_trades:
- try:
- # Defensive check to ensure 'trade' is a dictionary
- if not isinstance(trade, dict):
- logger.warning(f"Skipping non-dict item in recent_trades: {trade}")
- continue
- symbol = trade.get('symbol', 'unknown')
- base_asset = symbol.split('/')[0] if '/' in symbol else symbol.split(':')[0]
- side = trade.get('side', 'unknown').upper()
- price = float(trade.get('price', 0))
- amount = float(trade.get('amount', 0))
- pnl = float(trade.get('pnl', 0))
- pnl_percentage = float(trade.get('pnl_percentage', 0))
- trade_type = trade.get('trade_type', 'unknown')
- trade_time = trade.get('trade_time')
- # Format trade details
- formatter = self._get_formatter()
- price_str = await formatter.format_price_with_symbol(price, base_asset)
- amount_str = await formatter.format_amount(amount, base_asset)
- # Trade header
- side_emoji = "🟢" if side == "BUY" else "🔴"
- trades_text += f"{side_emoji} <b>{base_asset} {side}</b>\n"
- trades_text += f" 📏 Amount: {amount_str} {base_asset}\n"
- trades_text += f" 💰 Price: {price_str}\n"
- # Add P&L info
- pnl_emoji = "🟢" if pnl >= 0 else "🔴"
- trades_text += f" {pnl_emoji} P&L: ${pnl:,.2f} ({pnl_percentage:+.2f}%)\n"
- # Add trade type
- type_indicator = ""
- if trade.get('trade_lifecycle_id'):
- type_indicator = " 🤖"
- elif trade_type == 'external':
- type_indicator = " 🔄"
- trades_text += f" 📝 Type: {trade_type.upper()}{type_indicator}\n"
- # Add trade time
- if trade_time:
- try:
- trade_datetime = datetime.fromisoformat(trade_time.replace('Z', '+00:00'))
- time_diff = datetime.now(trade_datetime.tzinfo) - trade_datetime
- if time_diff.days > 0:
- time_str = f"{time_diff.days}d ago"
- elif time_diff.seconds >= 3600:
- time_str = f"{time_diff.seconds // 3600}h ago"
- else:
- time_str = f"{time_diff.seconds // 60}m ago"
- trades_text += f" ⏰ Time: {time_str}\n"
- except ValueError:
- logger.warning(f"Could not parse trade_time: {trade_time}")
- # Add trade ID
- trade_id = trade.get('id', 'unknown')
- trades_text += f" 🆔 Trade ID: {trade_id[:8]}\n\n"
- except Exception as e:
- logger.error(f"Error processing trade {trade.get('symbol', 'unknown')}: {e}")
- continue
- await self._reply(update, trades_text.strip())
- except Exception as e:
- logger.error(f"Error in trades command: {e}")
- await self._reply(update, "❌ Error retrieving trade history.")
|