Update Telegram Bot to v22.x API (fix import error)

Changes:
- Updated from python-telegram-bot 13.15 to 22.5
- Changed from sync API to async/await pattern
- Updated all command handlers to async
- Updated Application builder (new API)
- Fixed ModuleNotFoundError: No module named 'telegram'

Technical changes:
- Updater -> Application.builder()
- CommandHandler now uses async functions
- Context.DEFAULT_TYPE instead of CallbackContext
- await for all telegram API calls

Compatibility: python-telegram-bot 22.5 works with Python 3.12
This commit is contained in:
2025-12-26 20:09:41 +01:00
parent ac9a8ac969
commit 9475730769
2 changed files with 605 additions and 94 deletions
+66 -94
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
"""
🤖 TELEGRAM BOT COMMANDS - Interactive Bot Control
Erlaubt Remote-Control des Trading Bots via Telegram Commands
🤖 TELEGRAM BOT COMMANDS V2 - Interactive Bot Control
Updated for python-telegram-bot 22.x (latest version)
COMMANDS:
/status - Bot Status, offene Positionen, Balance
@@ -11,28 +11,22 @@ COMMANDS:
/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 asyncio
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
# New API imports for python-telegram-bot 22.x
from telegram import Update
from telegram.ext import Application, CommandHandler, ContextTypes
# Import existing modules
from telegram_notifier import TelegramNotifier, load_telegram_config
from telegram_notifier import load_telegram_config
class TradingBotController:
@@ -77,12 +71,7 @@ class TradingBotController:
def close_all_positions(self) -> Dict:
"""
Schließt ALLE offenen Positionen (Emergency Exit)
Returns:
Dict mit Ergebnis
"""
"""Schließt ALLE offenen Positionen (Emergency Exit)"""
if not mt5.initialize():
return {
'success': False,
@@ -142,12 +131,7 @@ class TradingBotController:
def get_status(self) -> Dict:
"""
Holt aktuellen Bot Status
Returns:
Dict mit Status-Informationen
"""
"""Holt aktuellen Bot Status"""
if not mt5.initialize():
return {
'success': False,
@@ -238,17 +222,11 @@ class TradingBotController:
class TelegramBotCommander:
"""
Telegram Bot Command Handler
Läuft im Hintergrund und reagiert auf Commands
Telegram Bot Command Handler (python-telegram-bot 22.x)
"""
def __init__(self, config_file: str = "telegram_config.json"):
"""
Initialize Telegram Bot Commander
Args:
config_file: Path to telegram config file
"""
"""Initialize Telegram Bot Commander"""
# Load config
self.config = load_telegram_config()
@@ -258,87 +236,82 @@ class TelegramBotCommander:
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()
# Application (new API)
self.application = None
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
async def start_async(self):
"""Start bot asynchronously (new API)"""
# Create application
self.application = Application.builder().token(self.bot_token).build()
# 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))
# Add command handlers
self.application.add_handler(CommandHandler("start", self.cmd_start))
self.application.add_handler(CommandHandler("help", self.cmd_help))
self.application.add_handler(CommandHandler("status", self.cmd_status))
self.application.add_handler(CommandHandler("pause", self.cmd_pause))
self.application.add_handler(CommandHandler("resume", self.cmd_resume))
self.application.add_handler(CommandHandler("close", self.cmd_close))
self.application.add_handler(CommandHandler("balance", self.cmd_balance))
self.application.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"
await self.application.bot.send_message(
chat_id=self.chat_id,
text="🤖 *Telegram Bot Commander STARTED*\n\n"
"✅ Bot is now listening for commands\n"
"Send /help for available commands",
parse_mode='Markdown'
)
# Start polling
self.updater.start_polling()
print("✅ Bot is running. Press Ctrl+C to stop.")
# Initialize and start polling
await self.application.initialize()
await self.application.start()
await self.application.updater.start_polling()
# Run until interrupted
self.updater.idle()
print("✅ Bot is running")
def start_background(self):
"""Start bot in background thread"""
thread = threading.Thread(target=self.start, daemon=True)
def run_async_loop():
asyncio.run(self.start_async())
thread = threading.Thread(target=run_async_loop, daemon=True)
thread.start()
print("✅ Telegram Bot running in background")
return thread
# ==========================================
# COMMAND HANDLERS
# COMMAND HANDLERS (async in new API)
# ==========================================
def cmd_start(self, update: Update, context: CallbackContext):
async def cmd_start(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""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')
await update.message.reply_text(message, parse_mode='Markdown')
def cmd_help(self, update: Update, context: CallbackContext):
async def cmd_help(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Handle /help command"""
message = """
📚 *Available Commands*
@@ -359,28 +332,28 @@ class TelegramBotCommander:
✅ /pause is instant
🔒 Only authorized user can use commands
"""
update.message.reply_text(message, parse_mode='Markdown')
await update.message.reply_text(message, parse_mode='Markdown')
def cmd_status(self, update: Update, context: CallbackContext):
async def cmd_status(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Handle /status command"""
result = self.controller.get_status()
update.message.reply_text(result['message'], parse_mode='Markdown')
await update.message.reply_text(result['message'], parse_mode='Markdown')
def cmd_pause(self, update: Update, context: CallbackContext):
async def cmd_pause(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Handle /pause command"""
result = self.controller.pause_trading("Manual pause via Telegram")
update.message.reply_text(result['message'], parse_mode='Markdown')
await update.message.reply_text(result['message'], parse_mode='Markdown')
def cmd_resume(self, update: Update, context: CallbackContext):
async def cmd_resume(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Handle /resume command"""
result = self.controller.resume_trading()
update.message.reply_text(result['message'], parse_mode='Markdown')
await update.message.reply_text(result['message'], parse_mode='Markdown')
def cmd_close(self, update: Update, context: CallbackContext):
async def cmd_close(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Handle /close command"""
# Safety check - require confirmation
args = context.args
@@ -392,25 +365,25 @@ class TelegramBotCommander:
"To confirm, send:\n"
"`/close confirm`"
)
update.message.reply_text(message, parse_mode='Markdown')
await update.message.reply_text(message, parse_mode='Markdown')
return
# Execute close
update.message.reply_text("🚨 Closing all positions...", parse_mode='Markdown')
await 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')
await update.message.reply_text(result['message'], parse_mode='Markdown')
def cmd_balance(self, update: Update, context: CallbackContext):
async def cmd_balance(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Handle /balance command"""
result = self.controller.get_balance()
update.message.reply_text(result['message'], parse_mode='Markdown')
await update.message.reply_text(result['message'], parse_mode='Markdown')
def cmd_stats(self, update: Update, context: CallbackContext):
async def cmd_stats(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Handle /stats command"""
stats = self._get_performance_stats()
update.message.reply_text(stats, parse_mode='Markdown')
await update.message.reply_text(stats, parse_mode='Markdown')
# ==========================================
@@ -504,7 +477,6 @@ class TelegramBotCommander:
# GLOBAL CONTROLLER INSTANCE
# ==========================================
# Global instance that can be accessed from notebook
_global_controller = None
def get_bot_controller() -> TradingBotController:
@@ -516,20 +488,20 @@ def get_bot_controller() -> TradingBotController:
# ==========================================
# USAGE
# MAIN
# ==========================================
if __name__ == "__main__":
print("="*70)
print("🤖 TELEGRAM BOT COMMANDER - Starting")
print("🤖 TELEGRAM BOT COMMANDER V2 - Starting")
print("="*70)
try:
# Create bot commander
commander = TelegramBotCommander()
asyncio.run(commander.start_async())
# Start bot (blocking)
commander.start()
# Keep running
asyncio.get_event_loop().run_forever()
except KeyboardInterrupt:
print("\n⏸️ Bot stopped by user")
+539
View File
@@ -0,0 +1,539 @@
#!/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()