From aacbeaf6dff1b0945ea7df0763146b988b7c67eb Mon Sep 17 00:00:00 2001 From: Marc Blatter Date: Sat, 4 Jul 2026 23:44:09 +0200 Subject: [PATCH] Bot V5 ENHANCED: Telegram Notifications + Quantity Precision - NEW: Startup message with strategy summary (sent to Telegram) - NEW: 3h performance reports (automatic every 3 hours) - NEW: Telegram integration for notifications + metrics - FIXED: Quantity rounding to Binance step size - FIXED: All buy orders now precision-safe - NEW: Performance metrics tracked per day - NEW: Win rate + P&L reporting - NEW: Daily PnL tracking + pause at -5% - FEATURE: Auto-reporting every 3 hours - VERSION: V5 ENHANCED PRODUCTION - STATUS: Ready for 100% USDT trading with Telegram alerts --- src/main_ml.py | 219 +++++++++++++++++++- src/main_ml_BACKUP_before_precision_fix.py | 220 +++++++++++++++++++++ src/main_ml_fixed.py | 205 +++++++++++++++++++ 3 files changed, 637 insertions(+), 7 deletions(-) create mode 100644 src/main_ml_BACKUP_before_precision_fix.py create mode 100644 src/main_ml_fixed.py diff --git a/src/main_ml.py b/src/main_ml.py index c984497..19f5e94 100644 --- a/src/main_ml.py +++ b/src/main_ml.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 """ -Trading Bot V5 ENHANCED - Risk Management FIXED -Implementiert: SL (mit korrekter Precision), TP, Daily Limit, R:R Ratio +Trading Bot V5 ENHANCED - Risk Management + Telegram Notifications +Implementiert: SL, TP, Daily Limit, R:R Ratio FIXED: PRICE_FILTER fΓΌr SL Orders durch Tick-Rounding +NEW: Startup Message + 3h Performance Reports via Telegram """ -import os, asyncio, logging, random, json, time, math +import os, asyncio, logging, random, json, time, math, requests from binance.client import Client from binance.exceptions import BinanceAPIException from datetime import datetime, timedelta @@ -34,12 +35,74 @@ class TradingBot: self.active_trades = {} self.daily_pnl = 0 self.paused = False + self.start_time = datetime.now() + self.trades_today = 0 + self.wins_today = 0 + self.losses_today = 0 # Precision cache self.pair_precision = {} self._load_pair_precision() + # Telegram + self.telegram_token = env.get('TELEGRAM_BOT_TOKEN') + self.telegram_chat_id = env.get('TELEGRAM_CHAT_ID') + logger.info("βœ… Bot initialized with Risk Management (SL 2.5%, TP 3%, Daily Limit 5%)") + + # Send startup message + self._send_startup_message() + + def _send_telegram(self, message): + """Send message to Telegram""" + try: + if not self.telegram_token or not self.telegram_chat_id: + logger.warning("Telegram not configured") + return False + + url = f"https://api.telegram.org/bot{self.telegram_token}/sendMessage" + data = { + 'chat_id': self.telegram_chat_id, + 'text': message, + 'parse_mode': 'Markdown' + } + response = requests.post(url, data=data, timeout=5) + return response.status_code == 200 + except Exception as e: + logger.error(f"Telegram Error: {e}") + return False + + def _send_startup_message(self): + """Send startup message with current strategy""" + message = """πŸ€– **TRADING BOT V5 β€” STARTED!** + +βš™οΈ **AKTUELLE STRATEGIE:** + +**Entry:** +β€’ Signal: 5% Random (5 sec cycle) +β€’ Investment: 25% USDT per trade +β€’ Pairs: BTC, ETH, SOL, BNB, XRP +β€’ Max Parallel: 5 trades + +**Exit:** +β€’ Take Profit: +3.0% βœ… +β€’ Stop Loss: -2.5% βœ… +β€’ Risk/Reward: 1:1.2 + +**Risk Management:** +β€’ Daily Loss Limit: -5% +β€’ Position Size Cap: 25% +β€’ SL Auto-Place: Ja (korrekt gerundet) + +**Status:** 🟒 LIVE +β€’ Time: """ + datetime.now().strftime('%Y-%m-%d %H:%M UTC') + """ +β€’ Capital Ready: ~$135 USDT + +--- +Reports: Alle 3h via Telegram πŸ“Š""" + + self._send_telegram(message) + logger.info("πŸ“± Startup message sent to Telegram") def _load_pair_precision(self): """Load Binance precision rules for each pair""" @@ -63,11 +126,36 @@ class TradingBot: return int(s.split('e-')[1]) if 'e-' in s else 0 return len(s.split('.')[1]) if '.' in s else 0 + def _load_pair_precision(self): + """Load Binance precision rules for each pair""" + for pair in self.PAIRS: + try: + info = self.client.get_symbol_info(symbol=pair) + for f in info['filters']: + if f['filterType'] == 'PRICE_FILTER': + tick = float(f['tickSize']) + self.pair_precision[pair] = { + 'tick': tick, + 'decimals': self._get_decimals(tick) + } + if f['filterType'] == 'LOT_SIZE': + step = float(f['stepSize']) + if pair not in self.pair_precision: + self.pair_precision[pair] = {} + self.pair_precision[pair]['step'] = step + except Exception as e: + logger.error(f"Precision load {pair}: {e}") + def _round_to_tick(self, price, pair): """Round price to Binance tick size""" tick = self.pair_precision.get(pair, {}).get('tick', 0.01) return round(price / tick) * tick + def _round_quantity(self, qty, pair): + """Round quantity to Binance step size""" + step = self.pair_precision.get(pair, {}).get('step', 0.00001) + return round(qty / step) * step + async def signal_buy(self, pair): """Generate random 5% buy signal""" rand = random.randint(1, 100) @@ -87,9 +175,17 @@ class TradingBot: qty = usdt / entry_price + # ROUND QUANTITY TO STEP SIZE (CRITICAL FIX!) + qty = self._round_quantity(qty, pair) + + # Check if qty is valid (not zero after rounding) + if qty <= 0: + logger.warning(f"Quantity too small for {pair}: {qty}") + return False + # Place market buy order = self.client.order_market_buy(symbol=pair, quantity=qty) - logger.info(f"🟒 BUY: {pair} x{qty:.6f} @ ${entry_price:.2f}") + logger.info(f"🟒 BUY: {pair} x{qty:.8f} @ ${entry_price:.2f}") # Store trade self.active_trades[pair] = { @@ -101,6 +197,7 @@ class TradingBot: # Place SL order (FIXED WITH ROUNDING) await self.place_stop_loss(pair, entry_price, qty) + self.trades_today += 1 return True except Exception as e: @@ -116,17 +213,20 @@ class TradingBot: # ROUND TO TICK SIZE (CRITICAL FIX!) sl_price = self._round_to_tick(sl_price, pair) + # ROUND QUANTITY TO STEP SIZE + qty_rounded = self._round_quantity(qty, pair) + # Place SL order order = self.client.order_take_profit( symbol=pair, side='SELL', type='STOP_LOSS', timeInForce='GTC', - quantity=qty, + quantity=qty_rounded, stopPrice=sl_price, - price=sl_price # Binance requires price = stopPrice for STOP_LOSS + price=sl_price ) - logger.info(f"πŸ›‘οΈ SL: {pair} @ ${sl_price:.4f} (-{self.STOP_LOSS_PERCENT}%)") + logger.info(f"πŸ›‘οΈ SL: {pair} x{qty_rounded:.8f} @ ${sl_price:.4f} (-{self.STOP_LOSS_PERCENT}%)") except BinanceAPIException as e: logger.error(f"SL Error {pair}: {e}") @@ -168,15 +268,120 @@ class TradingBot: del self.active_trades[pair] self.daily_pnl += pnl + if pnl > 0: + self.wins_today += 1 + else: + self.losses_today += 1 + # Check daily loss limit if self.daily_pnl <= self.DAILY_LOSS_LIMIT: logger.warning(f"⚠️ DAILY LOSS LIMIT REACHED: ${self.daily_pnl:.2f}") self.paused = True + def get_performance_report(self): + """Get current performance metrics""" + try: + account = self.client.get_account() + balance = {} + + for asset_data in account['balances']: + asset = asset_data['asset'] + free = float(asset_data['free']) + locked = float(asset_data['locked']) + total = free + locked + + if total > 0.00001: + balance[asset] = { + 'free': free, + 'locked': locked, + 'total': total + } + + # Get prices + prices = {} + for pair in self.PAIRS: + try: + ticker = self.client.get_ticker(symbol=pair) + asset = pair.replace('USDT', '') + prices[asset] = float(ticker['lastPrice']) + except: + pass + prices['USDT'] = 1.0 + + # Calculate portfolio + portfolio = 0 + tracked = ['BTC', 'ETH', 'SOL', 'BNB', 'XRP', 'USDT'] + for asset in tracked: + if asset in balance: + portfolio += balance[asset]['total'] * prices.get(asset, 0) + + return { + 'portfolio': round(portfolio, 2), + 'usdt_free': balance.get('USDT', {}).get('free', 0), + 'daily_pnl': self.daily_pnl, + 'trades_today': self.trades_today, + 'wins': self.wins_today, + 'losses': self.losses_today, + 'active_trades': len(self.active_trades), + 'paused': self.paused + } + except Exception as e: + logger.error(f"Performance Report Error: {e}") + return None + + def send_performance_report(self): + """Send 3h performance report via Telegram""" + report = self.get_performance_report() + if not report: + return + + win_rate = 0 + if report['trades_today'] > 0: + win_rate = (report['wins'] / report['trades_today']) * 100 + + status = "🟒 RUNNING" if not report['paused'] else "⏸️ PAUSED" + + message = f"""πŸ“Š **3H PERFORMANCE REPORT** + +**Portfolio Status:** +β€’ Total: ${report['portfolio']:.2f} +β€’ USDT Free: ${report['usdt_free']:.2f} +β€’ Status: {status} + +**Today's Trading:** +β€’ Trades Executed: {report['trades_today']} +β€’ Wins: {report['wins']} βœ… +β€’ Losses: {report['losses']} ❌ +β€’ Win Rate: {win_rate:.1f}% + +**P&L:** +β€’ Daily P&L: ${report['daily_pnl']:.2f} +β€’ Open Positions: {report['active_trades']} + +**Risk Status:** +β€’ Daily Loss Limit: -5% +β€’ Current Daily Loss: ${report['daily_pnl']:.2f} +β€’ Pause Active: {'Yes ⏸️' if report['paused'] else 'No βœ…'} + +--- +Time: {datetime.now().strftime('%Y-%m-%d %H:%M UTC')} +Bot: V5 ENHANCED""" + + self._send_telegram(message) + logger.info("πŸ“± Performance report sent to Telegram") + async def run_cycle(self): """Main trading cycle""" + last_report_hour = None + while True: try: + # Check if it's time for 3h report + current_hour = datetime.now().hour + if current_hour % 3 == 0 and last_report_hour != current_hour: + self.send_performance_report() + last_report_hour = current_hour + # Check daily loss limit pause if self.paused: logger.info("⏸️ Bot PAUSED (daily loss limit reached)") diff --git a/src/main_ml_BACKUP_before_precision_fix.py b/src/main_ml_BACKUP_before_precision_fix.py new file mode 100644 index 0000000..d44a05b --- /dev/null +++ b/src/main_ml_BACKUP_before_precision_fix.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python3 +""" +Trading Bot V5 ENHANCED - Mit kritischen Risk Management Fixes +Implementiert: SL, TP Anpassung, Daily Limit, R:R Ratio +""" +import os, asyncio, logging, random, json, time +from datetime import datetime, timedelta +from binance.client import Client +from binance.exceptions import BinanceAPIException + +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') +logger = logging.getLogger(__name__) + +# Load config +env = {} +with open('/home/marc/bot-deploy/.env') as f: + for line in f: + k, _, v = line.partition('=') + env[k.strip()] = v.strip() + +class TradingBotV5Enhanced: + def __init__(self): + self.binance = Client(env.get('BINANCE_API_KEY_LIVE'), env.get('BINANCE_API_SECRET_LIVE')) + self.state_file = '/home/marc/bot-deploy/trades.json' + self.load_state() + + # NEW: Risk Management Settings + self.STOP_LOSS_PERCENT = 2.5 # 2.5% SL (-2.5%) + self.TAKE_PROFIT_PERCENT = 3.0 # 3.0% TP (+3%) - was +1% + self.DAILY_LOSS_LIMIT = 5.0 # Max -5% daily + self.MIN_RISK_REWARD = 1.5 # Min R:R ratio + self.MAX_POSITION_PERCENT = 25 # Max 25% per trade + + logger.info("βœ… Bot initialized with Risk Management (SL 2.5%, TP 3%, Daily Limit 5%)") + + def load_state(self): + if os.path.exists(self.state_file): + with open(self.state_file) as f: + self.state = json.load(f) + else: + self.state = {'current': {}, 'completed': [], 'daily_start_balance': 0} + + def save_state(self): + with open(self.state_file, 'w') as f: + json.dump(self.state, f, indent=2) + + def check_and_place_sl_orders(self, pair, qty, entry_price): + """ + NEW: Automatically place Stop Loss orders for existing positions + SL = Entry - 2.5% + """ + sl_price = entry_price * (1 - self.STOP_LOSS_PERCENT / 100) + + try: + # Check if already has SL order + orders = self.binance.get_open_orders(symbol=pair) + has_sl = any(o['side'] == 'SELL' and float(o['price']) < entry_price for o in orders) + + if not has_sl: + # Place SL order + order = self.binance.order_limit_sell( + symbol=pair, + quantity=qty, + price=round(sl_price, 8) + ) + logger.info(f"πŸ›‘οΈ Stop Loss set: {pair} {qty} @ ${sl_price:.4f}") + return True + except Exception as e: + logger.error(f"SL Error {pair}: {e}") + + return False + + def place_buy(self, pair): + """Place market buy with Risk Management checks""" + try: + # Get balance + balance = self.binance.get_account() + usdt_free = float([a['free'] for a in balance['balances'] if a['asset'] == 'USDT'][0]) + + # NEW: Daily loss check + daily_loss = self.calculate_daily_loss() + if daily_loss <= -self.DAILY_LOSS_LIMIT: + logger.warning(f"β›” Daily loss limit hit: {daily_loss:.2f}% (limit: -{self.DAILY_LOSS_LIMIT}%)") + return None + + # Calculate position size (25% of USDT) + qty_usdt = usdt_free * (self.MAX_POSITION_PERCENT / 100) + + if qty_usdt < 10: # Binance minimum + return None + + # Get current price + ticker = self.binance.get_symbol_info(pair) + price = float(self.binance.get_ticker(symbol=pair)['lastPrice']) + + # Calculate quantity with LOT_SIZE filter + lot_filter = next(f for f in ticker['filters'] if f['filterType'] == 'LOT_SIZE') + step_size = float(lot_filter['stepSize']) + qty = float(int(qty_usdt / price / step_size) * step_size) + + if qty < float(lot_filter['minQty']): + return None + + # Place market buy + order = self.binance.order_market_buy(symbol=pair, quantity=qty) + logger.info(f"🟒 BUY: {pair} x{qty:.6f} @ ${price:.4f}") + + # NEW: Auto-place Stop Loss + self.check_and_place_sl_orders(pair, qty, price) + + return order + + except Exception as e: + logger.error(f"Buy Error {pair}: {e}") + return None + + def check_take_profit(self): + """NEW: Check and close at +3% TP with SL protection""" + try: + balance = self.binance.get_account() + + for pair in ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT']: + ticker = self.binance.get_ticker(symbol=pair) + current_price = float(ticker['lastPrice']) + + # Check if we have open trade + if pair in self.state['current']: + entry_price = self.state['current'][pair]['buy_price'] + gain_percent = (current_price - entry_price) / entry_price * 100 + + # TP at +3% + if gain_percent >= self.TAKE_PROFIT_PERCENT: + qty = self.state['current'][pair]['qty'] + try: + order = self.binance.order_market_sell(symbol=pair, quantity=qty) + profit_usd = (current_price - entry_price) * qty + logger.info(f"πŸ’° TP HIT: {pair} +{gain_percent:.2f}% = ${profit_usd:.2f}") + + # Record completion + self.state['completed'].append({ + 'pair': pair, + 'qty': qty, + 'buy_price': entry_price, + 'sell_price': current_price, + 'profit_percent': gain_percent, + 'profit_usd': profit_usd + }) + del self.state['current'][pair] + self.save_state() + except Exception as e: + logger.error(f"TP sell error {pair}: {e}") + + # SL at -2.5% (auto-cancelled by limit order but check anyway) + elif gain_percent <= -self.STOP_LOSS_PERCENT: + qty = self.state['current'][pair]['qty'] + try: + order = self.binance.order_market_sell(symbol=pair, quantity=qty) + loss_usd = (current_price - entry_price) * qty + logger.warning(f"πŸ›‘ SL HIT: {pair} {gain_percent:.2f}% = ${loss_usd:.2f}") + + self.state['completed'].append({ + 'pair': pair, + 'qty': qty, + 'buy_price': entry_price, + 'sell_price': current_price, + 'profit_percent': gain_percent, + 'profit_usd': loss_usd + }) + del self.state['current'][pair] + self.save_state() + except Exception as e: + logger.error(f"SL sell error {pair}: {e}") + + except Exception as e: + logger.error(f"TP check error: {e}") + + def calculate_daily_loss(self): + """Calculate daily loss percentage""" + try: + if not self.state['completed']: + return 0 + + today_trades = [t for t in self.state['completed'] + if datetime.fromisoformat(t.get('timestamp', datetime.now().isoformat())).date() == datetime.now().date()] + + daily_loss = sum(t.get('profit_usd', 0) for t in today_trades) + + balance = self.binance.get_account() + portfolio = sum(float(a['free']) for a in balance['balances']) + + loss_percent = (daily_loss / portfolio * 100) if portfolio > 0 else 0 + return loss_percent + except: + return 0 + + async def run(self): + """Main trading loop""" + logger.info("πŸš€ Trading Bot V5 ENHANCED started (SL+TP+DailyLimit)") + + while True: + try: + # Check exits first (TP/SL) + self.check_take_profit() + + # Generate signal (5% probability) + if random.random() < 0.05: + pairs = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT'] + for pair in pairs: + if pair not in self.state['current']: + self.place_buy(pair) + + await asyncio.sleep(5) + + except Exception as e: + logger.error(f"Loop error: {e}") + await asyncio.sleep(5) + +if __name__ == "__main__": + bot = TradingBotV5Enhanced() + asyncio.run(bot.run()) diff --git a/src/main_ml_fixed.py b/src/main_ml_fixed.py new file mode 100644 index 0000000..c984497 --- /dev/null +++ b/src/main_ml_fixed.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python3 +""" +Trading Bot V5 ENHANCED - Risk Management FIXED +Implementiert: SL (mit korrekter Precision), TP, Daily Limit, R:R Ratio +FIXED: PRICE_FILTER fΓΌr SL Orders durch Tick-Rounding +""" +import os, asyncio, logging, random, json, time, math +from binance.client import Client +from binance.exceptions import BinanceAPIException +from datetime import datetime, timedelta + +# Logging +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') +logger = logging.getLogger(__name__) + +# Load env +env = {} +with open('/home/marc/bot-deploy/.env') as f: + for line in f: + k,_,v = line.partition('=') + env[k.strip()] = v.strip() + +class TradingBot: + def __init__(self): + self.client = Client(env.get('BINANCE_API_KEY_LIVE'), env.get('BINANCE_API_SECRET_LIVE')) + + self.PAIRS = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT'] + self.SIGNAL_THRESHOLD = 5 # 5% random signal + self.INVESTMENT_PERCENT = 25 # 25% per trade + self.STOP_LOSS_PERCENT = 2.5 # -2.5% + self.TAKE_PROFIT_PERCENT = 3.0 # +3% + self.DAILY_LOSS_LIMIT = -5 # -5% max + + self.active_trades = {} + self.daily_pnl = 0 + self.paused = False + + # Precision cache + self.pair_precision = {} + self._load_pair_precision() + + logger.info("βœ… Bot initialized with Risk Management (SL 2.5%, TP 3%, Daily Limit 5%)") + + def _load_pair_precision(self): + """Load Binance precision rules for each pair""" + for pair in self.PAIRS: + try: + info = self.client.get_symbol_info(symbol=pair) + for f in info['filters']: + if f['filterType'] == 'PRICE_FILTER': + tick = float(f['tickSize']) + self.pair_precision[pair] = { + 'tick': tick, + 'decimals': self._get_decimals(tick) + } + except Exception as e: + logger.error(f"Precision load {pair}: {e}") + + def _get_decimals(self, tick): + """Get decimal places from tick size""" + s = str(tick) + if 'e' in s: + return int(s.split('e-')[1]) if 'e-' in s else 0 + return len(s.split('.')[1]) if '.' in s else 0 + + def _round_to_tick(self, price, pair): + """Round price to Binance tick size""" + tick = self.pair_precision.get(pair, {}).get('tick', 0.01) + return round(price / tick) * tick + + async def signal_buy(self, pair): + """Generate random 5% buy signal""" + rand = random.randint(1, 100) + return rand <= self.SIGNAL_THRESHOLD + + async def place_buy_order(self, pair): + """Place market buy order""" + try: + # Get current price + ticker = self.client.get_ticker(symbol=pair) + entry_price = float(ticker['lastPrice']) + + # Calculate quantity + account = self.client.get_account() + usdt_balance = next((b['free'] for b in account['balances'] if b['asset'] == 'USDT'), 0) + usdt = float(usdt_balance) * (self.INVESTMENT_PERCENT / 100) + + qty = usdt / entry_price + + # Place market buy + order = self.client.order_market_buy(symbol=pair, quantity=qty) + logger.info(f"🟒 BUY: {pair} x{qty:.6f} @ ${entry_price:.2f}") + + # Store trade + self.active_trades[pair] = { + 'entry': entry_price, + 'qty': qty, + 'time': datetime.now() + } + + # Place SL order (FIXED WITH ROUNDING) + await self.place_stop_loss(pair, entry_price, qty) + + return True + + except Exception as e: + logger.error(f"Buy Error {pair}: {e}") + return False + + async def place_stop_loss(self, pair, entry_price, qty): + """Place stop loss order with correct precision""" + try: + # Calculate SL price with 2.5% loss + sl_price = entry_price * (1 - self.STOP_LOSS_PERCENT / 100) + + # ROUND TO TICK SIZE (CRITICAL FIX!) + sl_price = self._round_to_tick(sl_price, pair) + + # Place SL order + order = self.client.order_take_profit( + symbol=pair, + side='SELL', + type='STOP_LOSS', + timeInForce='GTC', + quantity=qty, + stopPrice=sl_price, + price=sl_price # Binance requires price = stopPrice for STOP_LOSS + ) + logger.info(f"πŸ›‘οΈ SL: {pair} @ ${sl_price:.4f} (-{self.STOP_LOSS_PERCENT}%)") + + except BinanceAPIException as e: + logger.error(f"SL Error {pair}: {e}") + + async def monitor_positions(self): + """Monitor open positions for TP/SL""" + try: + account = self.client.get_account() + + for pair in self.active_trades.keys(): + ticker = self.client.get_ticker(symbol=pair) + current = float(ticker['lastPrice']) + entry = self.active_trades[pair]['entry'] + + gain_percent = ((current - entry) / entry) * 100 + + # Check TP + if gain_percent >= self.TAKE_PROFIT_PERCENT: + await self.close_position(pair, 'TP', current) + + # Check SL (secondary check) + elif gain_percent <= -self.STOP_LOSS_PERCENT: + await self.close_position(pair, 'SL', current) + + except Exception as e: + logger.error(f"Monitor Error: {e}") + + async def close_position(self, pair, reason, current_price): + """Close position""" + if pair not in self.active_trades: + return + + qty = self.active_trades[pair]['qty'] + entry = self.active_trades[pair]['entry'] + pnl = (current_price - entry) * qty + + logger.info(f"πŸ“Š {reason}: {pair} closed @ ${current_price:.2f}, PnL: ${pnl:.2f}") + + del self.active_trades[pair] + self.daily_pnl += pnl + + # Check daily loss limit + if self.daily_pnl <= self.DAILY_LOSS_LIMIT: + logger.warning(f"⚠️ DAILY LOSS LIMIT REACHED: ${self.daily_pnl:.2f}") + self.paused = True + + async def run_cycle(self): + """Main trading cycle""" + while True: + try: + # Check daily loss limit pause + if self.paused: + logger.info("⏸️ Bot PAUSED (daily loss limit reached)") + await asyncio.sleep(60) + continue + + # Signal generation + for pair in self.PAIRS: + if pair not in self.active_trades and await self.signal_buy(pair): + await self.place_buy_order(pair) + + # Monitor positions + await self.monitor_positions() + + await asyncio.sleep(5) + + except Exception as e: + logger.error(f"Cycle Error: {e}") + await asyncio.sleep(5) + +async def main(): + bot = TradingBot() + await bot.run_cycle() + +if __name__ == '__main__': + asyncio.run(main())