orders.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import logging
  2. from typing import Dict, Any, List, Optional
  3. from telegram import Update
  4. from telegram.ext import ContextTypes
  5. from .base import InfoCommandsBase
  6. logger = logging.getLogger(__name__)
  7. class OrdersCommands(InfoCommandsBase):
  8. """Handles all order-related commands."""
  9. async def orders_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
  10. """Handle the /orders command."""
  11. try:
  12. if not self._is_authorized(update):
  13. await self._reply(update, "❌ Unauthorized access.")
  14. return
  15. # Get open orders from the exchange
  16. open_orders = self.trading_engine.get_orders() or []
  17. # Get pending stop loss orders from the database
  18. stats = self.trading_engine.get_stats()
  19. pending_sl_lifecycles = []
  20. if stats:
  21. pending_sl_lifecycles = stats.get_pending_stop_losses_from_lifecycles()
  22. # Combine both lists
  23. all_orders = open_orders + pending_sl_lifecycles
  24. if not all_orders:
  25. await self._reply(update, "📭 No open or pending orders")
  26. return
  27. # Format orders text
  28. orders_text = "📋 <b>Open & Pending Orders</b>\n\n"
  29. for order in all_orders:
  30. try:
  31. is_pending_sl = 'trade_lifecycle_id' in order and order.get('stop_loss_price') is not None
  32. symbol = order.get('symbol', 'unknown')
  33. base_asset = symbol.split('/')[0] if '/' in symbol else symbol.split(':')[0]
  34. if is_pending_sl:
  35. # This is a pending SL from a trade lifecycle
  36. entry_side = order.get('side', 'unknown').upper()
  37. order_type = "STOP (PENDING)"
  38. side = "SELL" if entry_side == "BUY" else "BUY"
  39. price = float(order.get('stop_loss_price', 0))
  40. amount = float(order.get('amount', 0)) # Amount from the entry order
  41. status = f"Awaiting {order.get('status', '').upper()} Entry" # e.g. Awaiting PENDING Entry
  42. order_id = order.get('trade_lifecycle_id', 'unknown')
  43. else:
  44. # This is a regular exchange order
  45. order_type = order.get('type', 'unknown').upper()
  46. side = order.get('side', 'unknown').upper()
  47. price = float(order.get('price', 0))
  48. amount = float(order.get('amount', 0))
  49. filled = float(order.get('filled', 0))
  50. amount = amount - filled # Show remaining amount
  51. status = order.get('status', 'unknown').upper()
  52. order_id = order.get('id', 'unknown')
  53. # Skip fully filled orders
  54. if amount <= 0 and not is_pending_sl:
  55. continue
  56. # Format order details
  57. formatter = self._get_formatter()
  58. price_str = await formatter.format_price_with_symbol(price, base_asset)
  59. amount_str = await formatter.format_amount(amount, base_asset) if amount > 0 else "N/A"
  60. # Order header
  61. side_emoji = "🟢" if side == "BUY" else "🔴"
  62. orders_text += f"{side_emoji} <b>{base_asset} {side} {order_type}</b>\n"
  63. orders_text += f" Status: {status.replace('_', ' ')}\n"
  64. orders_text += f" 📏 Amount: {amount_str}\n"
  65. orders_text += f" 💰 {'Trigger Price' if 'STOP' in order_type else 'Price'}: {price_str}\n"
  66. # Add order type specific info for exchange orders
  67. if not is_pending_sl and (order_type == "STOP_LOSS" or order_type == "TAKE_PROFIT"):
  68. trigger_price = order.get('info', {}).get('triggerPrice')
  69. if trigger_price:
  70. trigger_price_str = await formatter.format_price_with_symbol(float(trigger_price), base_asset)
  71. orders_text += f" 🎯 Trigger: {trigger_price_str}\n"
  72. # Add order ID
  73. id_label = "Lifecycle ID" if is_pending_sl else "Order ID"
  74. orders_text += f" 🆔 {id_label}: {str(order_id)[:12]}\n\n"
  75. except Exception as e:
  76. logger.error(f"Error processing order {order.get('symbol', 'unknown')}: {e}", exc_info=True)
  77. continue
  78. # Add footer
  79. orders_text += "💡 Pending SL orders are activated when the entry order fills."
  80. await self._reply(update, orders_text.strip())
  81. except Exception as e:
  82. logger.error(f"Error in orders command: {e}", exc_info=True)
  83. await self._reply(update, "❌ Error retrieving order information.")