#!/usr/bin/env python3
"""
Trading Commands - Handles all trading-related Telegram commands.
"""
import logging
from typing import Optional
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import ContextTypes
from src.config.config import Config
logger = logging.getLogger(__name__)
class TradingCommands:
"""Handles all trading-related Telegram commands."""
def __init__(self, trading_engine, notification_manager):
"""Initialize with trading engine and notification manager."""
self.trading_engine = trading_engine
self.notification_manager = notification_manager
def _is_authorized(self, chat_id: str) -> bool:
"""Check if the chat ID is authorized."""
return str(chat_id) == str(Config.TELEGRAM_CHAT_ID)
async def long_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Handle the /long command for opening long positions."""
chat_id = update.effective_chat.id
if not self._is_authorized(chat_id):
await context.bot.send_message(chat_id=chat_id, text="❌ Unauthorized access.")
return
try:
if not context.args or len(context.args) < 2:
await context.bot.send_message(chat_id=chat_id, 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 context.bot.send_message(chat_id=chat_id, 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 context.bot.send_message(chat_id=chat_id, text="❌ Invalid limit price format. Please use numbers only.")
return
# Get current market price
market_data = self.trading_engine.get_market_data(f"{token}/USDC:USDC")
if not market_data:
await context.bot.send_message(chat_id=chat_id, text=f"❌ Could not fetch market data for {token}")
return
current_price = float(market_data['ticker'].get('last', 0))
if current_price <= 0:
await context.bot.send_message(chat_id=chat_id, text=f"❌ Invalid current price for {token}")
return
# Determine order type and price
if limit_price:
order_type = "Limit"
price = limit_price
token_amount = usdc_amount / price
else:
order_type = "Market"
price = current_price
token_amount = usdc_amount / current_price
# Validate stop loss for long positions
if stop_loss_price and stop_loss_price >= price:
await context.bot.send_message(chat_id=chat_id, text=(
f"⚠️ Stop loss price should be BELOW entry price for long positions\n\n"
f"📊 Your order:\n"
f"• Entry Price: ${price:,.2f}\n"
f"• Stop Loss: ${stop_loss_price:,.2f} ❌\n\n"
f"💡 Try a lower stop loss like: sl:{price * 0.95:.0f}"
))
return
# Create confirmation message
confirmation_text = f"""
🟢 Long Order Confirmation
📊 Order Details:
• Token: {token}
• USDC Amount: ${usdc_amount:,.2f}
• Token Amount: {token_amount:.6f} {token}
• Order Type: {order_type}
• Price: ${price:,.2f}
• Current Price: ${current_price:,.2f}
• Est. Value: ${token_amount * price:,.2f}
{f"🛑 Stop Loss: ${stop_loss_price:,.2f}" if stop_loss_price else ""}
⚠️ Are you sure you want to open this LONG position?
This will {"place a limit buy order" if limit_price else "execute a market buy order"} for {token}.
"""
# Create callback data for confirmation
callback_data = f"confirm_long_{token}_{usdc_amount}_{price if limit_price else 'market'}"
if stop_loss_price:
callback_data += f"_sl_{stop_loss_price}"
keyboard = [
[
InlineKeyboardButton("✅ Execute Long", callback_data=callback_data),
InlineKeyboardButton("❌ Cancel", callback_data="cancel_order")
]
]
reply_markup = InlineKeyboardMarkup(keyboard)
await context.bot.send_message(chat_id=chat_id, text=confirmation_text, parse_mode='HTML', reply_markup=reply_markup)
except ValueError as e:
await context.bot.send_message(chat_id=chat_id, text=f"❌ Invalid input format: {e}")
except Exception as e:
await context.bot.send_message(chat_id=chat_id, text=f"❌ Error processing long command: {e}")
logger.error(f"Error in long command: {e}")
async def short_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Handle the /short command for opening short positions."""
chat_id = update.effective_chat.id
if not self._is_authorized(chat_id):
await context.bot.send_message(chat_id=chat_id, text="❌ Unauthorized access.")
return
try:
if not context.args or len(context.args) < 2:
await context.bot.send_message(chat_id=chat_id, text=(
"❌ Usage: /short [token] [USDC amount] [price (optional)] [sl:price (optional)]\n"
"Examples:\n"
"• /short BTC 100 - Market order\n"
"• /short BTC 100 45000 - Limit order at $45,000\n"
"• /short BTC 100 sl:46000 - Market order with stop loss at $46,000\n"
"• /short BTC 100 45000 sl:46000 - Limit order at $45,000 with stop loss at $46,000"
))
return
token = context.args[0].upper()
usdc_amount = float(context.args[1])
# Parse arguments (similar to long_command)
limit_price = None
stop_loss_price = None
for i, arg in enumerate(context.args[2:], 2):
if arg.startswith('sl:'):
try:
stop_loss_price = float(arg[3:])
except ValueError:
await context.bot.send_message(chat_id=chat_id, text="❌ Invalid stop loss price format. Use sl:price (e.g., sl:46000)")
return
elif limit_price is None:
try:
limit_price = float(arg)
except ValueError:
await context.bot.send_message(chat_id=chat_id, text="❌ Invalid limit price format. Please use numbers only.")
return
# Get current market price
market_data = self.trading_engine.get_market_data(f"{token}/USDC:USDC")
if not market_data:
await context.bot.send_message(chat_id=chat_id, text=f"❌ Could not fetch market data for {token}")
return
current_price = float(market_data['ticker'].get('last', 0))
if current_price <= 0:
await context.bot.send_message(chat_id=chat_id, text=f"❌ Invalid current price for {token}")
return
# Determine order type and price
if limit_price:
order_type = "Limit"
price = limit_price
token_amount = usdc_amount / price
else:
order_type = "Market"
price = current_price
token_amount = usdc_amount / current_price
# Validate stop loss for short positions
if stop_loss_price and stop_loss_price <= price:
await context.bot.send_message(chat_id=chat_id, text=(
f"⚠️ Stop loss price should be ABOVE entry price for short positions\n\n"
f"📊 Your order:\n"
f"• Entry Price: ${price:,.2f}\n"
f"• Stop Loss: ${stop_loss_price:,.2f} ❌\n\n"
f"💡 Try a higher stop loss like: sl:{price * 1.05:.0f}"
))
return
# Create confirmation message
confirmation_text = f"""
🔴 Short Order Confirmation
📊 Order Details:
• Token: {token}
• USDC Amount: ${usdc_amount:,.2f}
• Token Amount: {token_amount:.6f} {token}
• Order Type: {order_type}
• Price: ${price:,.2f}
• Current Price: ${current_price:,.2f}
• Est. Value: ${token_amount * price:,.2f}
{f"🛑 Stop Loss: ${stop_loss_price:,.2f}" if stop_loss_price else ""}
⚠️ Are you sure you want to open this SHORT position?
This will {"place a limit sell order" if limit_price else "execute a market sell order"} for {token}.
"""
# Create callback data for confirmation
callback_data = f"confirm_short_{token}_{usdc_amount}_{price if limit_price else 'market'}"
if stop_loss_price:
callback_data += f"_sl_{stop_loss_price}"
keyboard = [
[
InlineKeyboardButton("✅ Execute Short", callback_data=callback_data),
InlineKeyboardButton("❌ Cancel", callback_data="cancel_order")
]
]
reply_markup = InlineKeyboardMarkup(keyboard)
await context.bot.send_message(chat_id=chat_id, text=confirmation_text, parse_mode='HTML', reply_markup=reply_markup)
except ValueError as e:
await context.bot.send_message(chat_id=chat_id, text=f"❌ Invalid input format: {e}")
except Exception as e:
await context.bot.send_message(chat_id=chat_id, text=f"❌ Error processing short command: {e}")
logger.error(f"Error in short command: {e}")
async def exit_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Handle the /exit command for closing positions."""
chat_id = update.effective_chat.id
if not self._is_authorized(chat_id):
await context.bot.send_message(chat_id=chat_id, text="❌ Unauthorized access.")
return
try:
if not context.args or len(context.args) < 1:
await context.bot.send_message(chat_id=chat_id, text=(
"❌ Usage: /exit [token]\n"
"Example: /exit BTC\n\n"
"This closes your entire position for the specified token."
))
return
token = context.args[0].upper()
# Find the position
position = self.trading_engine.find_position(token)
if not position:
await context.bot.send_message(chat_id=chat_id, text=f"📭 No open position found for {token}")
return
# Get position details
position_type, exit_side, contracts = self.trading_engine.get_position_direction(position)
entry_price = float(position.get('entryPx', 0))
unrealized_pnl = float(position.get('unrealizedPnl', 0))
# Get current market price
market_data = self.trading_engine.get_market_data(f"{token}/USDC:USDC")
if not market_data:
await context.bot.send_message(chat_id=chat_id, text=f"❌ Could not fetch current price for {token}")
return
current_price = float(market_data['ticker'].get('last', 0))
exit_value = contracts * current_price
# Create confirmation message
pnl_emoji = "🟢" if unrealized_pnl >= 0 else "🔴"
exit_emoji = "🔴" if position_type == "LONG" else "🟢"
confirmation_text = f"""
{exit_emoji} Exit Position Confirmation
📊 Position Details:
• Token: {token}
• Position: {position_type}
• Size: {contracts:.6f} contracts
• Entry Price: ${entry_price:,.2f}
• Current Price: ${current_price:,.2f}
• {pnl_emoji} Unrealized P&L: ${unrealized_pnl:,.2f}
🎯 Exit Order:
• Action: {exit_side.upper()} (Close {position_type})
• Amount: {contracts:.6f} {token}
• Est. Value: ~${exit_value:,.2f}
• Order Type: Market Order
⚠️ Are you sure you want to close this {position_type} position?
"""
keyboard = [
[
InlineKeyboardButton(f"✅ Close {position_type}", callback_data=f"confirm_exit_{token}"),
InlineKeyboardButton("❌ Cancel", callback_data="cancel_order")
]
]
reply_markup = InlineKeyboardMarkup(keyboard)
await context.bot.send_message(chat_id=chat_id, text=confirmation_text, parse_mode='HTML', reply_markup=reply_markup)
except Exception as e:
await context.bot.send_message(chat_id=chat_id, text=f"❌ Error processing exit command: {e}")
logger.error(f"Error in exit command: {e}")
async def sl_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Handle the /sl (stop loss) command."""
chat_id = update.effective_chat.id
if not self._is_authorized(chat_id):
await context.bot.send_message(chat_id=chat_id, text="❌ Unauthorized access.")
return
try:
if not context.args or len(context.args) < 2:
await context.bot.send_message(chat_id=chat_id, text=(
"❌ Usage: /sl [token] [price]\n"
"Example: /sl BTC 44000\n\n"
"This sets a stop loss order for your existing position."
))
return
token = context.args[0].upper()
stop_price = float(context.args[1])
# Find the position
position = self.trading_engine.find_position(token)
if not position:
await context.bot.send_message(chat_id=chat_id, text=f"📭 No open position found for {token}")
return
# Get position details
position_type, exit_side, contracts = self.trading_engine.get_position_direction(position)
entry_price = float(position.get('entryPx', 0))
# Validate stop loss price based on position direction
if position_type == "LONG" and stop_price >= entry_price:
await context.bot.send_message(chat_id=chat_id, 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 position_type == "SHORT" and stop_price <= entry_price:
await context.bot.send_message(chat_id=chat_id, 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\n"
f"💡 Try a higher price like: /sl {token} {entry_price * 1.05:.0f}"
))
return
# Get current market price
market_data = self.trading_engine.get_market_data(f"{token}/USDC:USDC")
current_price = 0
if market_data:
current_price = float(market_data['ticker'].get('last', 0))
# Calculate estimated P&L at stop loss
if position_type == "LONG":
pnl_at_stop = (stop_price - entry_price) * contracts
else: # SHORT
pnl_at_stop = (entry_price - stop_price) * contracts
pnl_emoji = "🟢" if pnl_at_stop >= 0 else "🔴"
confirmation_text = f"""
🛑 Stop Loss Order Confirmation
📊 Position Details:
• Token: {token}
• Position: {position_type}
• Size: {contracts:.6f} contracts
• Entry Price: ${entry_price:,.2f}
• Current Price: ${current_price:,.2f}
🎯 Stop Loss Order:
• Stop Price: ${stop_price:,.2f}
• Action: {exit_side.upper()} (Close {position_type})
• Amount: {contracts:.6f} {token}
• Order Type: Limit Order
• {pnl_emoji} Est. P&L: ${pnl_at_stop:,.2f}
⚠️ Are you sure you want to set this stop loss?
This will place a limit {exit_side} order at ${stop_price:,.2f} to protect your {position_type} position.
"""
keyboard = [
[
InlineKeyboardButton("✅ Set Stop Loss", callback_data=f"confirm_sl_{token}_{stop_price}"),
InlineKeyboardButton("❌ Cancel", callback_data="cancel_order")
]
]
reply_markup = InlineKeyboardMarkup(keyboard)
await context.bot.send_message(chat_id=chat_id, text=confirmation_text, parse_mode='HTML', reply_markup=reply_markup)
except ValueError:
await context.bot.send_message(chat_id=chat_id, text="❌ Invalid price format. Please use numbers only.")
except Exception as e:
await context.bot.send_message(chat_id=chat_id, text=f"❌ Error processing stop loss command: {e}")
logger.error(f"Error in sl command: {e}")
async def tp_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Handle the /tp (take profit) command."""
chat_id = update.effective_chat.id
if not self._is_authorized(chat_id):
await context.bot.send_message(chat_id=chat_id, text="❌ Unauthorized access.")
return
try:
if not context.args or len(context.args) < 2:
await context.bot.send_message(chat_id=chat_id, text=(
"❌ Usage: /tp [token] [price]\n"
"Example: /tp BTC 50000\n\n"
"This sets a take profit order for your existing position."
))
return
token = context.args[0].upper()
tp_price = float(context.args[1])
# Find the position
position = self.trading_engine.find_position(token)
if not position:
await context.bot.send_message(chat_id=chat_id, text=f"📭 No open position found for {token}")
return
# Get position details
position_type, exit_side, contracts = self.trading_engine.get_position_direction(position)
entry_price = float(position.get('entryPx', 0))
# Validate take profit price based on position direction
if position_type == "LONG" and tp_price <= entry_price:
await context.bot.send_message(chat_id=chat_id, 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: ${tp_price:,.2f} ❌\n\n"
f"💡 Try a higher price like: /tp {token} {entry_price * 1.05:.0f}"
))
return
elif position_type == "SHORT" and tp_price >= entry_price:
await context.bot.send_message(chat_id=chat_id, 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: ${tp_price:,.2f} ❌\n\n"
f"💡 Try a lower price like: /tp {token} {entry_price * 0.95:.0f}"
))
return
# Get current market price
market_data = self.trading_engine.get_market_data(f"{token}/USDC:USDC")
current_price = 0
if market_data:
current_price = float(market_data['ticker'].get('last', 0))
# Calculate estimated P&L at take profit
if position_type == "LONG":
pnl_at_tp = (tp_price - entry_price) * contracts
else: # SHORT
pnl_at_tp = (entry_price - tp_price) * contracts
pnl_emoji = "🟢" if pnl_at_tp >= 0 else "🔴"
confirmation_text = f"""
🎯 Take Profit Order Confirmation
📊 Position Details:
• Token: {token}
• Position: {position_type}
• Size: {contracts:.6f} contracts
• Entry Price: ${entry_price:,.2f}
• Current Price: ${current_price:,.2f}
🎯 Take Profit Order:
• Take Profit Price: ${tp_price:,.2f}
• Action: {exit_side.upper()} (Close {position_type})
• Amount: {contracts:.6f} {token}
• Order Type: Limit Order
• {pnl_emoji} Est. P&L: ${pnl_at_tp:,.2f}
⚠️ Are you sure you want to set this take profit?
This will place a limit {exit_side} order at ${tp_price:,.2f} to secure profits from your {position_type} position.
"""
keyboard = [
[
InlineKeyboardButton("✅ Set Take Profit", callback_data=f"confirm_tp_{token}_{tp_price}"),
InlineKeyboardButton("❌ Cancel", callback_data="cancel_order")
]
]
reply_markup = InlineKeyboardMarkup(keyboard)
await context.bot.send_message(chat_id=chat_id, text=confirmation_text, parse_mode='HTML', reply_markup=reply_markup)
except ValueError:
await context.bot.send_message(chat_id=chat_id, text="❌ Invalid price format. Please use numbers only.")
except Exception as e:
await context.bot.send_message(chat_id=chat_id, text=f"❌ Error processing take profit command: {e}")
logger.error(f"Error in tp command: {e}")
async def coo_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Handle the /coo (cancel all orders) command."""
chat_id = update.effective_chat.id
if not self._is_authorized(chat_id):
await context.bot.send_message(chat_id=chat_id, text="❌ Unauthorized access.")
return
try:
if not context.args or len(context.args) < 1:
await context.bot.send_message(chat_id=chat_id, text=(
"❌ Usage: /coo [token]\n"
"Example: /coo BTC\n\n"
"This cancels all open orders for the specified token."
))
return
token = context.args[0].upper()
confirmation_text = f"""
🚫 Cancel All Orders Confirmation
📊 Action: Cancel all open orders for {token}
⚠️ Are you sure you want to cancel all {token} orders?
This will cancel ALL pending orders for {token}, including:
• Limit orders
• Stop loss orders
• Take profit orders
This action cannot be undone.
"""
keyboard = [
[
InlineKeyboardButton(f"✅ Cancel All {token} Orders", callback_data=f"confirm_coo_{token}"),
InlineKeyboardButton("❌ Keep Orders", callback_data="cancel_order")
]
]
reply_markup = InlineKeyboardMarkup(keyboard)
await context.bot.send_message(chat_id=chat_id, text=confirmation_text, parse_mode='HTML', reply_markup=reply_markup)
except Exception as e:
await context.bot.send_message(chat_id=chat_id, text=f"❌ Error processing cancel orders command: {e}")
logger.error(f"Error in coo command: {e}")
async def button_callback(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Handle button callbacks for trading commands."""
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
try:
if callback_data.startswith('confirm_long_'):
await self._execute_long_callback(query, callback_data)
elif callback_data.startswith('confirm_short_'):
await self._execute_short_callback(query, callback_data)
elif callback_data.startswith('confirm_exit_'):
await self._execute_exit_callback(query, callback_data)
elif callback_data.startswith('confirm_sl_'):
await self._execute_sl_callback(query, callback_data)
elif callback_data.startswith('confirm_tp_'):
await self._execute_tp_callback(query, callback_data)
elif callback_data.startswith('confirm_coo_'):
await self._execute_coo_callback(query, callback_data)
elif callback_data == 'cancel_order':
await query.edit_message_text("❌ Order cancelled.")
# Handle info command button callbacks
elif callback_data in ['balance', 'positions', 'orders', 'stats', 'trades', 'market', 'price',
'performance', 'daily', 'weekly', 'monthly', 'alarm', 'monitoring', 'logs', 'help']:
await query.edit_message_text(f"✅ Please use /{callback_data} command to get the latest data.")
except Exception as e:
await query.edit_message_text(f"❌ Error processing order: {e}")
logger.error(f"Error in button callback: {e}")
async def _execute_long_callback(self, query, callback_data):
"""Execute long order from callback."""
parts = callback_data.split('_')
token = parts[2]
usdc_amount = float(parts[3])
price = None if parts[4] == 'market' else float(parts[4])
stop_loss_price = None
# Check for stop loss
if len(parts) > 5 and parts[5] == 'sl':
stop_loss_price = float(parts[6])
await query.edit_message_text("⏳ Executing long order...")
result = await self.trading_engine.execute_long_order(token, usdc_amount, price, stop_loss_price)
if result["success"]:
await self.notification_manager.send_long_success_notification(
query, token, result["token_amount"], result["actual_price"], result["order"], stop_loss_price
)
else:
await query.edit_message_text(f"❌ Long order failed: {result['error']}")
async def _execute_short_callback(self, query, callback_data):
"""Execute short order from callback."""
parts = callback_data.split('_')
token = parts[2]
usdc_amount = float(parts[3])
price = None if parts[4] == 'market' else float(parts[4])
stop_loss_price = None
# Check for stop loss
if len(parts) > 5 and parts[5] == 'sl':
stop_loss_price = float(parts[6])
await query.edit_message_text("⏳ Executing short order...")
result = await self.trading_engine.execute_short_order(token, usdc_amount, price, stop_loss_price)
if result["success"]:
await self.notification_manager.send_short_success_notification(
query, token, result["token_amount"], result["actual_price"], result["order"], stop_loss_price
)
else:
await query.edit_message_text(f"❌ Short order failed: {result['error']}")
async def _execute_exit_callback(self, query, callback_data):
"""Execute exit order from callback."""
parts = callback_data.split('_')
token = parts[2]
await query.edit_message_text("⏳ Closing position...")
result = await self.trading_engine.execute_exit_order(token)
if result["success"]:
await self.notification_manager.send_exit_success_notification(
query, token, result["position_type"], result["contracts"],
result["actual_price"], result["pnl"], result["order"]
)
else:
await query.edit_message_text(f"❌ Exit order failed: {result['error']}")
async def _execute_sl_callback(self, query, callback_data):
"""Execute stop loss order from callback."""
parts = callback_data.split('_')
token = parts[2]
stop_price = float(parts[3])
await query.edit_message_text("⏳ Setting stop loss...")
result = await self.trading_engine.execute_sl_order(token, stop_price)
if result["success"]:
await self.notification_manager.send_sl_success_notification(
query, token, result["position_type"], result["contracts"],
stop_price, result["order"]
)
else:
await query.edit_message_text(f"❌ Stop loss failed: {result['error']}")
async def _execute_tp_callback(self, query, callback_data):
"""Execute take profit order from callback."""
parts = callback_data.split('_')
token = parts[2]
tp_price = float(parts[3])
await query.edit_message_text("⏳ Setting take profit...")
result = await self.trading_engine.execute_tp_order(token, tp_price)
if result["success"]:
await self.notification_manager.send_tp_success_notification(
query, token, result["position_type"], result["contracts"],
tp_price, result["order"]
)
else:
await query.edit_message_text(f"❌ Take profit failed: {result['error']}")
async def _execute_coo_callback(self, query, callback_data):
"""Execute cancel all orders from callback."""
parts = callback_data.split('_')
token = parts[2]
await query.edit_message_text("⏳ Cancelling orders...")
result = await self.trading_engine.execute_coo_order(token)
if result["success"]:
await self.notification_manager.send_coo_success_notification(
query, token, result["cancelled_count"], result["failed_count"]
)
else:
await query.edit_message_text(f"❌ Cancel orders failed: {result['error']}")