#!/usr/bin/env python3 """ πŸ€– TELEGRAM BOT COMMANDS - Interactive Bot Control Erlaubt Remote-Control des Trading Bots via Telegram Commands COMMANDS: /status - Bot Status, offene Positionen, Balance /pause - Trading pausieren (keine neuen Trades) /resume - Trading fortsetzen /close - ALLE offenen Positionen schließen (Emergency) /stats - Performance Statistiken (heute, Woche, gesamt) /balance - Aktueller Kontostand + Equity /help - Liste aller Commands USAGE: 1. In separate Cell im Notebook starten: bot_commander = TelegramBotCommander() bot_commander.start() 2. Oder als Background-Service: python telegram_bot_commands.py """ import threading import time import json import MetaTrader5 as mt5 from datetime import datetime, timedelta from typing import Optional, Dict from telegram import Update, Bot from telegram.ext import Updater, CommandHandler, CallbackContext import sqlite3 # Import existing modules from telegram_notifier import TelegramNotifier, load_telegram_config class TradingBotController: """ Controller fΓΌr Trading Bot - ermΓΆglicht Pause/Resume/Close """ def __init__(self): self.is_paused = False self.pause_reason = "" self.pause_timestamp = None def pause_trading(self, reason: str = "Manual pause via Telegram"): """Pausiert Trading (keine neuen Trades)""" self.is_paused = True self.pause_reason = reason self.pause_timestamp = datetime.now() return { 'success': True, 'message': f"βœ… Trading PAUSED\nReason: {reason}" } def resume_trading(self): """Aktiviert Trading wieder""" if not self.is_paused: return { 'success': False, 'message': "⚠️ Trading is not paused" } pause_duration = datetime.now() - self.pause_timestamp self.is_paused = False self.pause_reason = "" return { 'success': True, 'message': f"βœ… Trading RESUMED\nWas paused for: {pause_duration}" } def close_all_positions(self) -> Dict: """ Schließt ALLE offenen Positionen (Emergency Exit) Returns: Dict mit Ergebnis """ if not mt5.initialize(): return { 'success': False, 'message': "❌ MT5 connection failed" } positions = mt5.positions_get() if not positions: return { 'success': True, 'message': "ℹ️ No open positions to close" } closed_count = 0 failed_count = 0 total_profit = 0 for position in positions: # Prepare close request close_request = { "action": mt5.TRADE_ACTION_DEAL, "symbol": position.symbol, "volume": position.volume, "type": mt5.ORDER_TYPE_SELL if position.type == mt5.ORDER_TYPE_BUY else mt5.ORDER_TYPE_BUY, "position": position.ticket, "price": mt5.symbol_info_tick(position.symbol).bid if position.type == mt5.ORDER_TYPE_BUY else mt5.symbol_info_tick(position.symbol).ask, "deviation": 20, "magic": 234000, "comment": "Emergency close via Telegram", "type_time": mt5.ORDER_TIME_GTC, "type_filling": mt5.ORDER_FILLING_IOC, } # Execute close result = mt5.order_send(close_request) if result.retcode == mt5.TRADE_RETCODE_DONE: closed_count += 1 total_profit += position.profit else: failed_count += 1 message = f"🚨 *EMERGENCY CLOSE EXECUTED*\n\n" message += f"βœ… Closed: {closed_count} positions\n" if failed_count > 0: message += f"❌ Failed: {failed_count} positions\n" message += f"πŸ’° Total P&L: ${total_profit:.2f}" return { 'success': True, 'message': message, 'closed': closed_count, 'failed': failed_count, 'profit': total_profit } def get_status(self) -> Dict: """ Holt aktuellen Bot Status Returns: Dict mit Status-Informationen """ if not mt5.initialize(): return { 'success': False, 'message': "❌ MT5 connection failed" } # Account Info account_info = mt5.account_info() # Open Positions positions = mt5.positions_get() # Calculate floating P&L floating_pl = sum(p.profit for p in positions) if positions else 0 # Build status message status_msg = "πŸ“Š *BOT STATUS*\n\n" # Trading Status if self.is_paused: status_msg += "⏸️ *Status:* PAUSED\n" status_msg += f"*Reason:* {self.pause_reason}\n" duration = datetime.now() - self.pause_timestamp status_msg += f"*Duration:* {duration}\n\n" else: status_msg += "βœ… *Status:* ACTIVE\n\n" # Account Info status_msg += "πŸ’° *ACCOUNT*\n" status_msg += f"Balance: ${account_info.balance:.2f}\n" status_msg += f"Equity: ${account_info.equity:.2f}\n" status_msg += f"Margin: ${account_info.margin:.2f}\n" status_msg += f"Free Margin: ${account_info.margin_free:.2f}\n\n" # Open Positions status_msg += f"πŸ“ˆ *POSITIONS*\n" status_msg += f"Open: {len(positions) if positions else 0}\n" status_msg += f"Floating P&L: ${floating_pl:.2f}\n\n" # Position Details if positions: status_msg += "*Open Trades:*\n" for i, p in enumerate(positions[:5], 1): # Max 5 positions type_emoji = "🟒" if p.type == mt5.ORDER_TYPE_BUY else "πŸ”΄" status_msg += f"{i}. {type_emoji} {p.symbol} | {p.volume} lots | ${p.profit:.2f}\n" if len(positions) > 5: status_msg += f"... and {len(positions) - 5} more\n" status_msg += f"\n⏰ {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} UTC" return { 'success': True, 'message': status_msg, 'is_paused': self.is_paused, 'positions': len(positions) if positions else 0, 'balance': account_info.balance, 'equity': account_info.equity } def get_balance(self) -> Dict: """Holt Balance + Equity Info""" if not mt5.initialize(): return { 'success': False, 'message': "❌ MT5 connection failed" } account_info = mt5.account_info() positions = mt5.positions_get() floating_pl = sum(p.profit for p in positions) if positions else 0 message = "πŸ’° *ACCOUNT BALANCE*\n\n" message += f"Balance: ${account_info.balance:.2f}\n" message += f"Equity: ${account_info.equity:.2f}\n" message += f"Floating P&L: ${floating_pl:.2f}\n\n" message += f"Margin Used: ${account_info.margin:.2f}\n" message += f"Free Margin: ${account_info.margin_free:.2f}\n" message += f"Margin Level: {account_info.margin_level:.2f}%\n\n" message += f"⏰ {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} UTC" return { 'success': True, 'message': message } class TelegramBotCommander: """ Telegram Bot Command Handler LΓ€uft im Hintergrund und reagiert auf Commands """ def __init__(self, config_file: str = "telegram_config.json"): """ Initialize Telegram Bot Commander Args: config_file: Path to telegram config file """ # Load config self.config = load_telegram_config() if not self.config or 'bot_token' not in self.config: raise ValueError("❌ Telegram config not found or invalid") self.bot_token = self.config['bot_token'] self.chat_id = self.config['chat_id'] # Initialize Bot self.bot = Bot(token=self.bot_token) self.updater = Updater(token=self.bot_token, use_context=True) # Controller self.controller = TradingBotController() # Notifier (for sending messages) self.notifier = TelegramNotifier(self.bot_token, self.chat_id) # Database path self.db_path = "trading_bot.db" # Setup command handlers self._setup_handlers() print("βœ… Telegram Bot Commander initialized") print(f"πŸ“± Bot Token: {self.bot_token[:20]}...") print(f"πŸ‘€ Chat ID: {self.chat_id}") def _setup_handlers(self): """Setup command handlers""" dispatcher = self.updater.dispatcher # Register commands dispatcher.add_handler(CommandHandler("start", self.cmd_start)) dispatcher.add_handler(CommandHandler("help", self.cmd_help)) dispatcher.add_handler(CommandHandler("status", self.cmd_status)) dispatcher.add_handler(CommandHandler("pause", self.cmd_pause)) dispatcher.add_handler(CommandHandler("resume", self.cmd_resume)) dispatcher.add_handler(CommandHandler("close", self.cmd_close)) dispatcher.add_handler(CommandHandler("balance", self.cmd_balance)) dispatcher.add_handler(CommandHandler("stats", self.cmd_stats)) print("βœ… Command handlers registered") def start(self): """Start the bot (blocking)""" print("πŸš€ Starting Telegram Bot...") print("πŸ“± Send /help to see available commands") # Send startup message self.notifier.send_message( "πŸ€– *Telegram Bot Commander STARTED*\n\n" "βœ… Bot is now listening for commands\n" "Send /help for available commands" ) # Start polling self.updater.start_polling() print("βœ… Bot is running. Press Ctrl+C to stop.") # Run until interrupted self.updater.idle() def start_background(self): """Start bot in background thread""" thread = threading.Thread(target=self.start, daemon=True) thread.start() print("βœ… Telegram Bot running in background") return thread # ========================================== # COMMAND HANDLERS # ========================================== def cmd_start(self, update: Update, context: CallbackContext): """Handle /start command""" message = ( "πŸ€– *Trading Bot Commander*\n\n" "Welcome! I can help you control your trading bot remotely.\n\n" "Send /help to see available commands." ) update.message.reply_text(message, parse_mode='Markdown') def cmd_help(self, update: Update, context: CallbackContext): """Handle /help command""" message = """ πŸ“š *Available Commands* *Bot Control:* /status - Bot status, positions, balance /pause - Pause trading (no new trades) /resume - Resume trading /close - Close ALL positions (emergency) *Information:* /balance - Account balance & equity /stats - Performance statistics /help - Show this help *Safety Features:* ⚠️ /close requires confirmation βœ… /pause is instant πŸ”’ Only authorized user can use commands """ update.message.reply_text(message, parse_mode='Markdown') def cmd_status(self, update: Update, context: CallbackContext): """Handle /status command""" result = self.controller.get_status() update.message.reply_text(result['message'], parse_mode='Markdown') def cmd_pause(self, update: Update, context: CallbackContext): """Handle /pause command""" result = self.controller.pause_trading("Manual pause via Telegram") update.message.reply_text(result['message'], parse_mode='Markdown') def cmd_resume(self, update: Update, context: CallbackContext): """Handle /resume command""" result = self.controller.resume_trading() update.message.reply_text(result['message'], parse_mode='Markdown') def cmd_close(self, update: Update, context: CallbackContext): """Handle /close command""" # Safety check - require confirmation args = context.args if not args or args[0].lower() != 'confirm': message = ( "⚠️ *EMERGENCY CLOSE*\n\n" "This will close ALL open positions!\n\n" "To confirm, send:\n" "`/close confirm`" ) update.message.reply_text(message, parse_mode='Markdown') return # Execute close update.message.reply_text("🚨 Closing all positions...", parse_mode='Markdown') result = self.controller.close_all_positions() update.message.reply_text(result['message'], parse_mode='Markdown') def cmd_balance(self, update: Update, context: CallbackContext): """Handle /balance command""" result = self.controller.get_balance() update.message.reply_text(result['message'], parse_mode='Markdown') def cmd_stats(self, update: Update, context: CallbackContext): """Handle /stats command""" stats = self._get_performance_stats() update.message.reply_text(stats, parse_mode='Markdown') # ========================================== # HELPER FUNCTIONS # ========================================== def _get_performance_stats(self) -> str: """Get performance statistics from database""" try: conn = sqlite3.connect(self.db_path) cursor = conn.cursor() # Today's stats today_stats = cursor.execute(""" SELECT COUNT(*) as trades, SUM(CASE WHEN net_profit > 0 THEN 1 ELSE 0 END) as wins, SUM(CASE WHEN net_profit < 0 THEN 1 ELSE 0 END) as losses, ROUND(SUM(net_profit), 2) as profit FROM trades WHERE DATE(entry_time) = DATE('now') """).fetchone() # This week's stats week_stats = cursor.execute(""" SELECT COUNT(*) as trades, SUM(CASE WHEN net_profit > 0 THEN 1 ELSE 0 END) as wins, SUM(CASE WHEN net_profit < 0 THEN 1 ELSE 0 END) as losses, ROUND(SUM(net_profit), 2) as profit FROM trades WHERE entry_time >= datetime('now', '-7 days') """).fetchone() # Overall stats overall_stats = cursor.execute(""" SELECT COUNT(*) as trades, SUM(CASE WHEN net_profit > 0 THEN 1 ELSE 0 END) as wins, SUM(CASE WHEN net_profit < 0 THEN 1 ELSE 0 END) as losses, ROUND(SUM(net_profit), 2) as profit, ROUND(AVG(net_profit), 2) as avg_profit FROM trades WHERE net_profit IS NOT NULL """).fetchone() conn.close() # Build message message = "πŸ“Š *PERFORMANCE STATISTICS*\n\n" # Today message += "πŸ“… *TODAY*\n" if today_stats[0] > 0: today_wr = (today_stats[1] / today_stats[0] * 100) if today_stats[0] > 0 else 0 message += f"Trades: {today_stats[0]} | WR: {today_wr:.1f}%\n" message += f"Profit: ${today_stats[3]:.2f}\n\n" else: message += "No trades today\n\n" # This Week message += "πŸ“ˆ *THIS WEEK (7 days)*\n" if week_stats[0] > 0: week_wr = (week_stats[1] / week_stats[0] * 100) if week_stats[0] > 0 else 0 message += f"Trades: {week_stats[0]} | WR: {week_wr:.1f}%\n" message += f"Wins: {week_stats[1]} | Losses: {week_stats[2]}\n" message += f"Profit: ${week_stats[3]:.2f}\n\n" else: message += "No trades this week\n\n" # Overall message += "🎯 *OVERALL*\n" if overall_stats[0] > 0: overall_wr = (overall_stats[1] / overall_stats[0] * 100) if overall_stats[0] > 0 else 0 message += f"Total Trades: {overall_stats[0]}\n" message += f"Win Rate: {overall_wr:.1f}%\n" message += f"Total Profit: ${overall_stats[3]:.2f}\n" message += f"Avg/Trade: ${overall_stats[4]:.2f}\n" else: message += "No trade history\n" message += f"\n⏰ {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} UTC" return message except Exception as e: return f"❌ Error fetching stats: {e}" # ========================================== # GLOBAL CONTROLLER INSTANCE # ========================================== # Global instance that can be accessed from notebook _global_controller = None def get_bot_controller() -> TradingBotController: """Get global bot controller instance""" global _global_controller if _global_controller is None: _global_controller = TradingBotController() return _global_controller # ========================================== # USAGE # ========================================== if __name__ == "__main__": print("="*70) print("πŸ€– TELEGRAM BOT COMMANDER - Starting") print("="*70) try: # Create bot commander commander = TelegramBotCommander() # Start bot (blocking) commander.start() except KeyboardInterrupt: print("\n⏸️ Bot stopped by user") except Exception as e: print(f"❌ Error: {e}") import traceback traceback.print_exc()