diff --git a/src/main_ml.py.backup.18pct b/src/main_ml.py.backup.18pct deleted file mode 100644 index 09e7502..0000000 --- a/src/main_ml.py.backup.18pct +++ /dev/null @@ -1,431 +0,0 @@ -#!/usr/bin/env python3 -""" -Trading Bot V5 ENHANCED - FULLY FIXED VERSION -Implementiert: SL, TP, Daily Limit, R:R Ratio -FIXED: Binance API method (order_take_profit β†’ create_order) -FIXED: PRICE_FILTER fΓΌr SL Orders durch Tick-Rounding -FIXED: Quantity rounding mit Decimal (no floating point errors) -FIXED: Quantity string formatting fΓΌr Binance -NEW: Startup Message + 3h Performance Reports via Telegram -""" -import os, asyncio, logging, random, json, time, math, requests -from decimal import Decimal, ROUND_DOWN -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 = 18 # 18% per trade (5 parallel = 90% max, 10% buffer) - 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 - 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: 18% USDT per trade ← FIXED! -β€’ Pairs: BTC, ETH, SOL, BNB, XRP -β€’ Max Parallel: 5 trades (5Γ—18% = 90% max) - -**Exit:** -β€’ Take Profit: +3.0% βœ… -β€’ Stop Loss: -2.5% βœ… -β€’ Risk/Reward: 1:1.2 - -**Risk Management:** -β€’ Daily Loss Limit: -5% -β€’ Position Size Cap: 18% -β€’ Buffer Reserve: 10% USDT -β€’ SL Auto-Place: Ja (korrekt gerundet) - -**Status:** 🟒 LIVE -β€’ Time: """ + datetime.now().strftime('%Y-%m-%d %H:%M UTC') + """ -β€’ Capital Ready: 100% 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""" - 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 - self.pair_precision[pair]['step_decimals'] = self._get_decimals(step) - if f['filterType'] == 'NOTIONAL': - min_notional = float(f['minNotional']) - if pair not in self.pair_precision: - self.pair_precision[pair] = {} - self.pair_precision[pair]['min_notional'] = min_notional - 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 using Decimal""" - tick = self.pair_precision.get(pair, {}).get('tick', 0.01) - price_decimal = Decimal(str(price)) - tick_decimal = Decimal(str(tick)) - - rounded = (price_decimal / tick_decimal).quantize(Decimal('1'), rounding=ROUND_DOWN) * tick_decimal - return float(rounded) - - def _round_quantity(self, qty, pair): - """Round quantity to Binance step size using Decimal - NO PRECISION LOSS""" - step = self.pair_precision.get(pair, {}).get('step', 0.00001) - step_decimals = self.pair_precision.get(pair, {}).get('step_decimals', 5) - - qty_decimal = Decimal(str(qty)) - step_decimal = Decimal(str(step)) - - # Round down (safe side) - rounded = (qty_decimal / step_decimal).quantize(Decimal('1'), rounding=ROUND_DOWN) * step_decimal - - # Format as string with exactly the right decimals - format_str = f"0.{'':<{step_decimals}}" - if step_decimals == 0: - return int(rounded) - - return float(rounded) - - 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 - - # ROUND QUANTITY TO STEP SIZE (CRITICAL FIX WITH DECIMAL!) - 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 - - # VALIDATE NOTIONAL (order_value must be >= min_notional) - min_notional = self.pair_precision.get(pair, {}).get('min_notional', 10.0) - order_value = qty * entry_price - - if order_value < min_notional: - logger.warning(f"Order value too small {pair}: ${order_value:.2f} < ${min_notional:.2f}") - return False - - # Place market buy - order = self.client.order_market_buy(symbol=pair, quantity=qty) - logger.info(f"🟒 BUY: {pair} x{qty} @ ${entry_price:.2f} (value: ${order_value:.2f})") - - # Store trade - self.active_trades[pair] = { - 'entry': entry_price, - 'qty': qty, - 'time': datetime.now() - } - - # Place SL order (FIXED WITH CORRECT API METHOD) - await self.place_stop_loss(pair, entry_price, qty) - - self.trades_today += 1 - 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 & API method""" - 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) - - # ROUND QUANTITY TO STEP SIZE (WITH DECIMAL!) - qty_rounded = self._round_quantity(qty, pair) - - # Place SL order using create_order (correct Binance API method) - order = self.client.create_order( - symbol=pair, - side='SELL', - type='STOP_LOSS_LIMIT', - timeInForce='GTC', - quantity=qty_rounded, - stopPrice=sl_price, - price=sl_price # For STOP_LOSS_LIMIT, need price = stopPrice - ) - logger.info(f"πŸ›‘οΈ SL: {pair} x{qty_rounded} @ ${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 list(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 - - 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 (FULLY FIXED)""" - - 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)") - 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()) diff --git a/src/main_ml.py.backup.30pct b/src/main_ml.py.backup.30pct deleted file mode 100644 index 700e8cd..0000000 --- a/src/main_ml.py.backup.30pct +++ /dev/null @@ -1,431 +0,0 @@ -#!/usr/bin/env python3 -""" -Trading Bot V5 ENHANCED - FULLY FIXED VERSION -Implementiert: SL, TP, Daily Limit, R:R Ratio -FIXED: Binance API method (order_take_profit β†’ create_order) -FIXED: PRICE_FILTER fΓΌr SL Orders durch Tick-Rounding -FIXED: Quantity rounding mit Decimal (no floating point errors) -FIXED: Quantity string formatting fΓΌr Binance -NEW: Startup Message + 3h Performance Reports via Telegram -""" -import os, asyncio, logging, random, json, time, math, requests -from decimal import Decimal, ROUND_DOWN -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 = 30 # 30% per trade (5 parallel = 90% max, 10% buffer) - 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 - 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: 18% USDT per trade ← FIXED! -β€’ Pairs: BTC, ETH, SOL, BNB, XRP -β€’ Max Parallel: 5 trades (5Γ—18% = 90% max) - -**Exit:** -β€’ Take Profit: +3.0% βœ… -β€’ Stop Loss: -2.5% βœ… -β€’ Risk/Reward: 1:1.2 - -**Risk Management:** -β€’ Daily Loss Limit: -5% -β€’ Position Size Cap: 18% -β€’ Buffer Reserve: 10% USDT -β€’ SL Auto-Place: Ja (korrekt gerundet) - -**Status:** 🟒 LIVE -β€’ Time: """ + datetime.now().strftime('%Y-%m-%d %H:%M UTC') + """ -β€’ Capital Ready: 100% 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""" - 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 - self.pair_precision[pair]['step_decimals'] = self._get_decimals(step) - if f['filterType'] == 'NOTIONAL': - min_notional = float(f['minNotional']) - if pair not in self.pair_precision: - self.pair_precision[pair] = {} - self.pair_precision[pair]['min_notional'] = min_notional - 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 using Decimal""" - tick = self.pair_precision.get(pair, {}).get('tick', 0.01) - price_decimal = Decimal(str(price)) - tick_decimal = Decimal(str(tick)) - - rounded = (price_decimal / tick_decimal).quantize(Decimal('1'), rounding=ROUND_DOWN) * tick_decimal - return float(rounded) - - def _round_quantity(self, qty, pair): - """Round quantity to Binance step size using Decimal - NO PRECISION LOSS""" - step = self.pair_precision.get(pair, {}).get('step', 0.00001) - step_decimals = self.pair_precision.get(pair, {}).get('step_decimals', 5) - - qty_decimal = Decimal(str(qty)) - step_decimal = Decimal(str(step)) - - # Round down (safe side) - rounded = (qty_decimal / step_decimal).quantize(Decimal('1'), rounding=ROUND_DOWN) * step_decimal - - # Format as string with exactly the right decimals - format_str = f"0.{'':<{step_decimals}}" - if step_decimals == 0: - return int(rounded) - - return float(rounded) - - 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 - - # ROUND QUANTITY TO STEP SIZE (CRITICAL FIX WITH DECIMAL!) - 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 - - # VALIDATE NOTIONAL (order_value must be >= min_notional) - min_notional = self.pair_precision.get(pair, {}).get('min_notional', 10.0) - order_value = qty * entry_price - - if order_value < min_notional: - logger.warning(f"Order value too small {pair}: ${order_value:.2f} < ${min_notional:.2f}") - return False - - # Place market buy - order = self.client.order_market_buy(symbol=pair, quantity=qty) - logger.info(f"🟒 BUY: {pair} x{qty} @ ${entry_price:.2f} (value: ${order_value:.2f})") - - # Store trade - self.active_trades[pair] = { - 'entry': entry_price, - 'qty': qty, - 'time': datetime.now() - } - - # Place SL order (FIXED WITH CORRECT API METHOD) - await self.place_stop_loss(pair, entry_price, qty) - - self.trades_today += 1 - 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 & API method""" - 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) - - # ROUND QUANTITY TO STEP SIZE (WITH DECIMAL!) - qty_rounded = self._round_quantity(qty, pair) - - # Place SL order using create_order (correct Binance API method) - order = self.client.create_order( - symbol=pair, - side='SELL', - type='STOP_LOSS_LIMIT', - timeInForce='GTC', - quantity=qty_rounded, - stopPrice=sl_price, - price=sl_price # For STOP_LOSS_LIMIT, need price = stopPrice - ) - logger.info(f"πŸ›‘οΈ SL: {pair} x{qty_rounded} @ ${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 list(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 - - 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 (FULLY FIXED)""" - - 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)") - 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()) diff --git a/src/main_ml.py.backup.35pct.20240706 b/src/main_ml.py.backup.35pct.20240706 deleted file mode 100644 index 4370cb3..0000000 --- a/src/main_ml.py.backup.35pct.20240706 +++ /dev/null @@ -1,434 +0,0 @@ -#!/usr/bin/env python3 -""" -Trading Bot V5 ENHANCED - FULLY FIXED VERSION -Implementiert: SL, TP, Daily Limit, R:R Ratio -FIXED: Binance API method (order_take_profit β†’ create_order) -FIXED: PRICE_FILTER fΓΌr SL Orders durch Tick-Rounding -FIXED: Quantity rounding mit Decimal (no floating point errors) -FIXED: Quantity string formatting fΓΌr Binance -NEW: Startup Message + 3h Performance Reports via Telegram -""" -import os, asyncio, logging, random, json, time, math, requests -from decimal import Decimal, ROUND_DOWN -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 = 35 # 35% per trade (5 parallel = 90% max, 10% buffer) - self.NOTIONAL_MIN = 5.0 # Override Binance minimum to $3 - 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 - 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: 18% USDT per trade ← FIXED! -β€’ Pairs: BTC, ETH, SOL, BNB, XRP -β€’ Max Parallel: 5 trades (5Γ—18% = 90% max) - -**Exit:** -β€’ Take Profit: +3.0% βœ… -β€’ Stop Loss: -2.5% βœ… -β€’ Risk/Reward: 1:1.2 - -**Risk Management:** -β€’ Daily Loss Limit: -5% -β€’ Position Size Cap: 18% -β€’ Buffer Reserve: 10% USDT -β€’ SL Auto-Place: Ja (korrekt gerundet) - -**Status:** 🟒 LIVE -β€’ Time: """ + datetime.now().strftime('%Y-%m-%d %H:%M UTC') + """ -β€’ Capital Ready: 100% 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""" - 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 - self.pair_precision[pair]['step_decimals'] = self._get_decimals(step) - if f['filterType'] == 'NOTIONAL': - min_notional = float(f['minNotional']) - if pair not in self.pair_precision: - self.pair_precision[pair] = {} - self.pair_precision[pair]['min_notional'] = min_notional - 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 using Decimal""" - tick = self.pair_precision.get(pair, {}).get('tick', 0.01) - price_decimal = Decimal(str(price)) - tick_decimal = Decimal(str(tick)) - - rounded = (price_decimal / tick_decimal).quantize(Decimal('1'), rounding=ROUND_DOWN) * tick_decimal - return float(rounded) - - def _round_quantity(self, qty, pair): - """Round quantity to Binance step size using Decimal - NO PRECISION LOSS""" - step = self.pair_precision.get(pair, {}).get('step', 0.00001) - step_decimals = self.pair_precision.get(pair, {}).get('step_decimals', 5) - - qty_decimal = Decimal(str(qty)) - step_decimal = Decimal(str(step)) - - # Round down (safe side) - rounded = (qty_decimal / step_decimal).quantize(Decimal('1'), rounding=ROUND_DOWN) * step_decimal - - # Format as string with exactly the right decimals - format_str = f"0.{'':<{step_decimals}}" - if step_decimals == 0: - return int(rounded) - - return float(rounded) - - 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 - - # ROUND QUANTITY TO STEP SIZE (CRITICAL FIX WITH DECIMAL!) - 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 - - # VALIDATE NOTIONAL (order_value must be >= 3.0 MINIMUM) - order_value = qty * entry_price - NOTIONAL_MIN = 5.0 # Minimum $3 - - if order_value < NOTIONAL_MIN: - logger.warning(f"Order value too small {pair}: ${order_value:.2f} < ${NOTIONAL_MIN:.2f} (qty={qty}, price={entry_price})") - return False - - logger.info(f"βœ… NOTIONAL Check Passed: {pair} ${order_value:.2f} >= ${NOTIONAL_MIN:.2f}") - - # Place market buy - order = self.client.order_market_buy(symbol=pair, quantity=qty) - logger.info(f"🟒 BUY: {pair} x{qty} @ ${entry_price:.2f} (value: ${order_value:.2f})") - - # Store trade - self.active_trades[pair] = { - 'entry': entry_price, - 'qty': qty, - 'time': datetime.now() - } - - # Place SL order (FIXED WITH CORRECT API METHOD) - await self.place_stop_loss(pair, entry_price, qty) - - self.trades_today += 1 - 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 & API method""" - 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) - - # ROUND QUANTITY TO STEP SIZE (WITH DECIMAL!) - qty_rounded = self._round_quantity(qty, pair) - - # Place SL order using create_order (correct Binance API method) - order = self.client.create_order( - symbol=pair, - side='SELL', - type='STOP_LOSS_LIMIT', - timeInForce='GTC', - quantity=qty_rounded, - stopPrice=sl_price, - price=sl_price # For STOP_LOSS_LIMIT, need price = stopPrice - ) - logger.info(f"πŸ›‘οΈ SL: {pair} x{qty_rounded} @ ${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 list(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 - - 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 (FULLY FIXED)""" - - 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)") - 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()) diff --git a/src/main_ml.py.backup.auto-trading b/src/main_ml.py.backup.auto-trading deleted file mode 100644 index fa247c3..0000000 --- a/src/main_ml.py.backup.auto-trading +++ /dev/null @@ -1,190 +0,0 @@ -import asyncio, logging, joblib, time -from datetime import datetime -from src.config import get_config -from src.bot.binance_client import BinanceClientWrapper -from src.integrations.telegram_notifier import TelegramNotifier -from src.integrations.obsidian_logger import ObsidianLogger -from src.strategies.ml_strategy import MLStrategy - -logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') -logger = logging.getLogger(__name__) - -class MLTradingBot: - def __init__(self, config, binance, telegram, obsidian, model, scaler): - self.config = config - self.binance = binance - self.telegram = telegram - self.obsidian = obsidian - self.model = model - self.scaler = scaler - self.strategy = MLStrategy(trading_pair=config.trading_pair) - - # Trading state - self.last_report_time = time.time() - self.report_interval = 10800 # 3 HOURS (10800 seconds) - self.trades_today = 0 - self.wins_today = 0 - self.losses_today = 0 - self.daily_pnl = 0.0 - self.report_count = 0 - - async def get_market_data(self): - """Fetch current market price and stats""" - try: - ticker = self.config.trading_pair.split('/')[0] # BTC from BTCUSDT - symbol = f"{ticker}USDT" - - # Get current price - price_data = await self.binance.get_ticker_price(symbol) - if not price_data: - return None - - current_price = float(price_data) - - return { - 'ticker': ticker, - 'current_price': current_price, - 'symbol': symbol - } - except Exception as e: - logger.error(f"Market data fetch error: {e}") - return None - - async def get_account_balance(self): - """Get current account balance""" - try: - balance = self.binance.get_balance('USDT') - if balance: - return {'USDT': {'total': balance}} - return {} - except Exception as e: - logger.error(f"Balance fetch error: {e}") - return {} - - async def send_performance_report(self): - """Send 3-hourly performance report""" - try: - self.report_count += 1 - - # Get market data - market = await self.get_market_data() - if not market: - logger.warning("No market data available") - return - - # Get account balance - balances = await self.get_account_balance() - usdt_balance = balances.get('USDT', {}).get('total', 0) - - # Build report - timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S UTC') - report = f""" -πŸ“Š **PERFORMANCE REPORT #{self.report_count}** β€” {timestamp} - -🎯 **MARKET STATUS:** -β”œβ”€ {market['ticker']}/USDT: ${market['current_price']:,.2f} -β”œβ”€ Trades Today: {self.trades_today} -β”œβ”€ Wins: {self.wins_today} | Losses: {self.losses_today} -└─ Daily P&L: ${self.daily_pnl:+.2f} - -πŸ’° **ACCOUNT STATUS:** -β”œβ”€ USDT Balance: ${usdt_balance:,.2f} -β”œβ”€ Device: CPU -β”œβ”€ Mode: Live Trading -└─ Strategy: ML (92% accuracy, 60% threshold) - -πŸ“ˆ **BOT STATUS: RUNNING βœ…** -""" - - # Send to Telegram (FIXED β€” now actually sends!) - success = await self.telegram.send_alert(report.strip()) - if success: - logger.info(f"βœ… Performance report #{self.report_count} sent to Telegram") - else: - logger.warning(f"❌ Failed to send report #{self.report_count} to Telegram") - - except Exception as e: - logger.error(f"Report error: {e}") - - async def monitor_trades(self): - """Monitor open trades and check signals""" - try: - symbol = f"{self.config.trading_pair.split('/')[0]}USDT" - orders = self.binance.get_open_orders(symbol) - - if orders and len(orders) > 0: - logger.info(f"πŸ“ˆ Open orders: {len(orders)}") - - except Exception as e: - logger.debug(f"Trade monitoring: {e}") - - async def run(self): - """Main bot loop""" - logger.info(f"πŸ€– Starting ML Trading Bot β€” {self.config.trading_pair}") - - startup_msg = f"""πŸ€– **BOT STARTED - V2 ML ADAPTIVE** - -βœ… Strategy: ML Adaptive (60% threshold) -βœ… Models: BTC 92% accuracy -βœ… Device: CPU (Live) -βœ… Reporting: EVERY 3 HOURS -βœ… Status: ACTIVE & MONITORING""" - - await self.telegram.send_alert(startup_msg) - logger.info("βœ… Startup message sent to Telegram") - - logger.info("🟒 Bot running β€” sending reports every 3 hours...") - - while True: - try: - current_time = time.time() - - # Send 3-hourly performance report - if (current_time - self.last_report_time) >= self.report_interval: - logger.info(f"⏰ Time for Report #{self.report_count + 1}") - await self.send_performance_report() - self.last_report_time = current_time - - # Monitor trades every 5 minutes - await self.monitor_trades() - - # Sleep for 5 minutes - await asyncio.sleep(60) # Check every 1 min instead of 5 min for trading opportunities - - except KeyboardInterrupt: - logger.info("Bot interrupted by user") - break - except Exception as e: - logger.error(f"Bot error: {e}") - try: - await self.telegram.send_alert(f"❌ Bot Error: {str(e)[:100]}") - except: - pass - await asyncio.sleep(60) - -async def main(): - config = get_config() - - if config.environment == 'testnet': - api_key, api_secret = config.binance_api_key_testnet, config.binance_api_secret_testnet - else: - api_key, api_secret = config.binance_api_key_live, config.binance_api_secret_live - - binance = BinanceClientWrapper(api_key=api_key, api_secret=api_secret, testnet=(config.environment=='testnet')) - telegram = TelegramNotifier(bot_token=config.telegram_bot_token, chat_id=config.telegram_chat_id) - obsidian = ObsidianLogger(vault_path=config.obsidian_vault_path, trade_log_file=config.obsidian_trade_log_file) - - try: - # Load BTC model - model = joblib.load('/tmp/model_BTC.pkl') - scaler = joblib.load('/tmp/scaler_BTC.pkl') - logger.info(f'βœ… ML Model loaded: BTC (92% accuracy)') - except Exception as e: - logger.error(f'❌ ML Model Error: {e}') - return - - bot = MLTradingBot(config, binance, telegram, obsidian, model, scaler) - await bot.run() - -if __name__ == '__main__': - asyncio.run(main()) diff --git a/src/main_ml.py.backup_sweep b/src/main_ml.py.backup_sweep deleted file mode 100644 index 48d950f..0000000 --- a/src/main_ml.py.backup_sweep +++ /dev/null @@ -1,590 +0,0 @@ -#!/usr/bin/env python3 -""" -Trading Bot V0.2 β€” Adaptive Strategy Learning -Implementiert: SL, TP, Daily Limit, R:R Ratio -FIXED: Binance API method (order_take_profit β†’ create_order) -FIXED: PRICE_FILTER fΓΌr SL Orders durch Tick-Rounding -FIXED: Quantity rounding mit Decimal (no floating point errors) -FIXED: Quantity string formatting fΓΌr Binance -NEW: Startup Message + 3h Performance Reports via Telegram -""" -import os, asyncio, logging, random, json, time, math, requests -from decimal import Decimal, ROUND_DOWN -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 = 7.5 # 7-8% range (midpoint 7.5%) # 5% random signal - self.INVESTMENT_PERCENT = 50 # 50% (single position for liquidity) (single position) - self.INVESTMENT_PERCENT_HIGH = 55 # 55% when confidence > 85% > 85% - self.CONFIDENCE_THRESHOLD = 85 # Min confidence for high investment # 35% per trade (5 parallel = 90% max, 10% buffer) - self.NOTIONAL_MIN = 5.0 # Override Binance minimum to $3 - self.STOP_LOSS_PERCENT = 1.8 # -2.5% - self.TAKE_PROFIT_PERCENT = 2.8 # +3% - self.DAILY_LOSS_LIMIT = -5 - - # Trailing Stop - self.TRAILING_STOP_ENTRY = 1.5 # Activate trailing stop at +1.5% - self.TRAILING_STOP_DISTANCE = 0.6 # 0.6% distance - - # Position & Trade Limits - self.MAX_OPEN_POSITIONS = 1 # Single position for max liquidity # Max concurrent trades - self.MAX_CONSECUTIVE_LOSSES = 3 # Stop after 3 losses - self.CONSECUTIVE_LOSS_COOLDOWN = 30 * 60 # 30 minutes in seconds - self.MAX_TRADES_PER_DAY = 15 - self.MIN_WIN_PROBABILITY = 75 # Min expected win % - - # Tracking - self.consecutive_losses = 0 - self.last_loss_time = None - self.trades_today = 0 - self.last_trade_reset = None # -5% max - - # Profit tracking - self.entry_price_history = {} # symbol -> entry price - self.closed_trades = [] # list of {symbol, entry, exit, profit_pct, profit_usdt} - self.session_start_balance = None - - self.active_trades = {} - self.daily_pnl = 0 - self.paused = False - - # ADAPTIVE TRACKING (Option 2: Win Rate based Strategy) - self.total_trades = 0 - self.total_wins = 0 - self.total_losses = 0 - self.last_win_rate = 50.0 # Start neutral - self.strategy_version = 1 - 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(f"βœ… Bot initialized with Risk Management (SL {self.STOP_LOSS_PERCENT}%, TP {self.TAKE_PROFIT_PERCENT}%, Daily Limit {-self.DAILY_LOSS_LIMIT}%, Max Pos: {self.MAX_OPEN_POSITIONS})") - - # 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 V0.2 β€” STARTED!** - -βš™οΈ **AKTUELLE STRATEGIE:** - -**Entry:** -β€’ Signal: 5% Random (5 sec cycle) -β€’ Investment: 18% USDT per trade ← FIXED! -β€’ Pairs: BTC, ETH, SOL, BNB, XRP -β€’ Max Parallel: 5 trades (5Γ—18% = 90% max) - -**Exit:** -β€’ Take Profit: +3.0% βœ… -β€’ Stop Loss: -2.5% βœ… -β€’ Risk/Reward: 1:1.2 - -**Risk Management:** -β€’ Daily Loss Limit: -5% -β€’ Position Size Cap: 18% -β€’ Buffer Reserve: 10% USDT -β€’ SL Auto-Place: Ja (korrekt gerundet) - -**Status:** 🟒 LIVE -β€’ Time: """ + datetime.now().strftime('%Y-%m-%d %H:%M UTC') + """ -β€’ Capital Ready: 100% 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""" - 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 - self.pair_precision[pair]['step_decimals'] = self._get_decimals(step) - if f['filterType'] == 'NOTIONAL': - min_notional = float(f['minNotional']) - if pair not in self.pair_precision: - self.pair_precision[pair] = {} - self.pair_precision[pair]['min_notional'] = min_notional - 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 using Decimal""" - tick = self.pair_precision.get(pair, {}).get('tick', 0.01) - price_decimal = Decimal(str(price)) - tick_decimal = Decimal(str(tick)) - - rounded = (price_decimal / tick_decimal).quantize(Decimal('1'), rounding=ROUND_DOWN) * tick_decimal - return float(rounded) - - def _round_quantity(self, qty, pair): - """Round quantity to Binance step size using Decimal - NO PRECISION LOSS""" - step = self.pair_precision.get(pair, {}).get('step', 0.00001) - step_decimals = self.pair_precision.get(pair, {}).get('step_decimals', 5) - - qty_decimal = Decimal(str(qty)) - step_decimal = Decimal(str(step)) - - # Round down (safe side) - rounded = (qty_decimal / step_decimal).quantize(Decimal('1'), rounding=ROUND_DOWN) * step_decimal - - # Format as string with exactly the right decimals - format_str = f"0.{'':<{step_decimals}}" - if step_decimals == 0: - return int(rounded) - - return float(rounded) - - 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 - - # ROUND QUANTITY TO STEP SIZE (CRITICAL FIX WITH DECIMAL!) - 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 - - # VALIDATE NOTIONAL (order_value must be >= 3.0 MINIMUM) - order_value = qty * entry_price - NOTIONAL_MIN = 5.0 # Minimum $3 - - if order_value < NOTIONAL_MIN: - logger.warning(f"Order value too small {pair}: ${order_value:.2f} < ${NOTIONAL_MIN:.2f} (qty={qty}, price={entry_price})") - return False - - logger.info(f"βœ… NOTIONAL Check Passed: {pair} ${order_value:.2f} >= ${NOTIONAL_MIN:.2f}") - - # Place market buy - order = self.client.order_market_buy(symbol=pair, quantity=qty) - logger.info(f"🟒 BUY: {pair} x{qty} @ ${entry_price:.2f} (value: ${order_value:.2f})") - - # Store trade - self.active_trades[pair] = { - 'entry': entry_price, - 'qty': qty, - 'time': datetime.now() - } - - # Place SL order (FIXED WITH CORRECT API METHOD) - await self.place_stop_loss(pair, entry_price, qty) - - self.trades_today += 1 - 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 & API method""" - try: - # Calculate SL price with {self.STOP_LOSS_PERCENT}% 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) - - # ROUND QUANTITY TO STEP SIZE (WITH DECIMAL!) - qty_rounded = self._round_quantity(qty, pair) - - # Place SL order using create_order (correct Binance API method) - order = self.client.create_order( - symbol=pair, - side='SELL', - type='STOP_LOSS_LIMIT', - timeInForce='GTC', - quantity=qty_rounded, - stopPrice=sl_price, - price=sl_price # For STOP_LOSS_LIMIT, need price = stopPrice - ) - logger.info(f"πŸ›‘οΈ SL: {pair} x{qty_rounded} @ ${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 list(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 - - 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: V0.2 Adaptive""" - - 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)") - 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()) - - - def get_signal_confidence(self): - """Calculate confidence level for current signal (0-100%)""" - # This can be enhanced with actual ML model - # For now: random 30-95% - import random - return random.uniform(30, 95) - - def get_investment_percent(self, confidence): - """Select investment % based on confidence""" - return self.INVESTMENT_PERCENT_HIGH if confidence > self.CONFIDENCE_THRESHOLD else self.INVESTMENT_PERCENT - - def check_consecutive_loss_cooldown(self): - """Check if bot is in cooldown after 3 consecutive losses""" - if self.consecutive_losses >= self.MAX_CONSECUTIVE_LOSSES: - if self.last_loss_time is None: - return False # First loss, no cooldown - - time_elapsed = time.time() - self.last_loss_time - if time_elapsed < self.CONSECUTIVE_LOSS_COOLDOWN: - logger.warning(f"🚫 Cooldown active: {int(self.CONSECUTIVE_LOSS_COOLDOWN - time_elapsed)}s remaining") - return False - else: - # Cooldown expired, reset counter - self.consecutive_losses = 0 - logger.info("βœ… Cooldown expired, consecutive loss counter reset") - return True - return True - - def check_volatility(self, pair): - """Check market volatility (simplified)""" - try: - ticker = self.client.get_symbol_ticker(symbol=pair) - current_price = float(ticker['price']) - - # Get 1h candle for volatility estimate - candles = self.client.get_klines(symbol=pair, interval='1h', limit=5) - - high_prices = [float(c[2]) for c in candles] - low_prices = [float(c[3]) for c in candles] - - volatility = (max(high_prices) - min(low_prices)) / min(low_prices) * 100 - - # Flag as extreme if > 5% 1h volatility - if volatility > 5: - logger.warning(f"⚠️ High volatility {pair}: {volatility:.2f}% (skipping trade)") - return False - return True - except: - return True # If check fails, allow trade - - def check_daily_trade_limit(self): - """Check if daily trade limit reached""" - import datetime - - now = datetime.datetime.now() - today_start = now.replace(hour=0, minute=0, second=0, microsecond=0) - - if self.last_trade_reset is None or self.last_trade_reset < today_start: - self.trades_today = 0 - self.last_trade_reset = now - - if self.trades_today >= self.MAX_TRADES_PER_DAY: - logger.warning(f"⚠️ Daily limit reached: {self.trades_today}/{self.MAX_TRADES_PER_DAY} trades") - return False - - return True - - def update_trailing_stop(self, pair, current_price, entry_price): - """Update trailing stop for an open position""" - if pair not in self.active_trades: - return False - - trade_data = self.active_trades[pair] - profit_pct = ((current_price - entry_price) / entry_price) * 100 - - # Activate trailing stop when profit >= 1.5% - if profit_pct >= self.TRAILING_STOP_ENTRY: - trailing_stop_price = current_price * (1 - self.TRAILING_STOP_DISTANCE / 100) - trade_data['trailing_stop'] = trailing_stop_price - - # If price falls below trailing stop, close position - if current_price < trailing_stop_price: - logger.info(f"πŸ›‘ Trailing stop triggered {pair}: Sell @ ${current_price:.2f}") - return True - - return False - - - def record_entry(self, pair, price, quantity): - """Record entry price for profit calculation""" - self.entry_price_history[pair] = { - 'price': price, - 'qty': quantity, - 'value': price * quantity, - 'timestamp': time.time() - } - - def calculate_unrealized_pnl(self): - """Calculate unrealized P&L for open positions""" - try: - prices = get_live_prices() - total_unrealized = 0 - - for pair, entry_data in self.entry_price_history.items(): - asset = pair.replace('USDT', '') - current_price = prices.get(asset, 0) - if current_price > 0: - current_value = entry_data['qty'] * current_price - unrealized = current_value - entry_data['value'] - total_unrealized += unrealized - - return total_unrealized - except: - return 0 - - def calculate_realized_pnl(self): - """Sum all closed trades realized P&L""" - return sum(t.get('profit_usdt', 0) for t in self.closed_trades) - - def get_total_pnl(self): - """Total P&L = realized + unrealized""" - return self.calculate_realized_pnl() + self.calculate_unrealized_pnl() - diff --git a/src/main_ml_BACKUP_before_precision_fix.py b/src/main_ml_BACKUP_before_precision_fix.py deleted file mode 100644 index d44a05b..0000000 --- a/src/main_ml_BACKUP_before_precision_fix.py +++ /dev/null @@ -1,220 +0,0 @@ -#!/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_enhanced.py b/src/main_ml_enhanced.py deleted file mode 100644 index d44a05b..0000000 --- a/src/main_ml_enhanced.py +++ /dev/null @@ -1,220 +0,0 @@ -#!/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 deleted file mode 100644 index c984497..0000000 --- a/src/main_ml_fixed.py +++ /dev/null @@ -1,205 +0,0 @@ -#!/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()) diff --git a/src/main_ml_v02_backup_1783615772.py b/src/main_ml_v02_backup_1783615772.py deleted file mode 100644 index c9dc483..0000000 --- a/src/main_ml_v02_backup_1783615772.py +++ /dev/null @@ -1,682 +0,0 @@ -#!/usr/bin/env python3 -""" -Trading Bot V0.2 β€” Adaptive Strategy Learning -Implementiert: SL, TP, Daily Limit, R:R Ratio -FIXED: Binance API method (order_take_profit β†’ create_order) -FIXED: PRICE_FILTER fΓΌr SL Orders durch Tick-Rounding -FIXED: Quantity rounding mit Decimal (no floating point errors) -FIXED: Quantity string formatting fΓΌr Binance -NEW: Startup Message + 3h Performance Reports via Telegram -""" -import os, asyncio, logging, random, json, time, math, requests -from decimal import Decimal, ROUND_DOWN -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 = 7.5 # 7-8% range (midpoint 7.5%) # 5% random signal - self.INVESTMENT_PERCENT = 50 # 50% (single position for liquidity) (single position) - self.INVESTMENT_PERCENT_HIGH = 55 # 55% when confidence > 85% > 85% - self.CONFIDENCE_THRESHOLD = 85 # Min confidence for high investment # 35% per trade (5 parallel = 90% max, 10% buffer) - self.NOTIONAL_MIN = 5.0 # Override Binance minimum to $3 - self.STOP_LOSS_PERCENT = 1.8 # -2.5% - self.TAKE_PROFIT_PERCENT = 2.8 # +3% - self.DAILY_LOSS_LIMIT = -5 - - # Trailing Stop - self.TRAILING_STOP_ENTRY = 1.5 # Activate trailing stop at +1.5% - self.TRAILING_STOP_DISTANCE = 0.6 # 0.6% distance - - # Position & Trade Limits - self.MAX_OPEN_POSITIONS = 1 # Single position for max liquidity # Max concurrent trades - self.MAX_CONSECUTIVE_LOSSES = 3 # Stop after 3 losses - self.CONSECUTIVE_LOSS_COOLDOWN = 30 * 60 # 30 minutes in seconds - self.MAX_TRADES_PER_DAY = 15 - self.MIN_WIN_PROBABILITY = 75 # Min expected win % - - # Tracking - self.consecutive_losses = 0 - self.last_loss_time = None - self.trades_today = 0 - self.last_trade_reset = None # -5% max - - # Profit tracking - self.entry_price_history = {} # symbol -> entry price - self.closed_trades = [] # list of {symbol, entry, exit, profit_pct, profit_usdt} - self.session_start_balance = None - - self.active_trades = {} - self.daily_pnl = 0 - self.paused = False - - # ADAPTIVE TRACKING (Option 2: Win Rate based Strategy) - self.total_trades = 0 - self.total_wins = 0 - self.total_losses = 0 - self.last_win_rate = 50.0 # Start neutral - self.strategy_version = 1 - 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(f"βœ… Bot initialized with Risk Management (SL {self.STOP_LOSS_PERCENT}%, TP {self.TAKE_PROFIT_PERCENT}%, Daily Limit {-self.DAILY_LOSS_LIMIT}%, Max Pos: {self.MAX_OPEN_POSITIONS})") - - # 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 V0.2 β€” STARTED!** - -βš™οΈ **AKTUELLE STRATEGIE:** - -**Entry:** -β€’ Signal: 5% Random (5 sec cycle) -β€’ Investment: 18% USDT per trade ← FIXED! -β€’ Pairs: BTC, ETH, SOL, BNB, XRP -β€’ Max Parallel: 5 trades (5Γ—18% = 90% max) - -**Exit:** -β€’ Take Profit: +3.0% βœ… -β€’ Stop Loss: -2.5% βœ… -β€’ Risk/Reward: 1:1.2 - -**Risk Management:** -β€’ Daily Loss Limit: -5% -β€’ Position Size Cap: 18% -β€’ Buffer Reserve: 10% USDT -β€’ SL Auto-Place: Ja (korrekt gerundet) - -**Status:** 🟒 LIVE -β€’ Time: """ + datetime.now().strftime('%Y-%m-%d %H:%M UTC') + """ -β€’ Capital Ready: 100% 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""" - 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 - self.pair_precision[pair]['step_decimals'] = self._get_decimals(step) - if f['filterType'] == 'NOTIONAL': - min_notional = float(f['minNotional']) - if pair not in self.pair_precision: - self.pair_precision[pair] = {} - self.pair_precision[pair]['min_notional'] = min_notional - 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 using Decimal""" - tick = self.pair_precision.get(pair, {}).get('tick', 0.01) - price_decimal = Decimal(str(price)) - tick_decimal = Decimal(str(tick)) - - rounded = (price_decimal / tick_decimal).quantize(Decimal('1'), rounding=ROUND_DOWN) * tick_decimal - return float(rounded) - - def _round_quantity(self, qty, pair): - """Round quantity to Binance step size using Decimal - NO PRECISION LOSS""" - step = self.pair_precision.get(pair, {}).get('step', 0.00001) - step_decimals = self.pair_precision.get(pair, {}).get('step_decimals', 5) - - qty_decimal = Decimal(str(qty)) - step_decimal = Decimal(str(step)) - - # Round down (safe side) - rounded = (qty_decimal / step_decimal).quantize(Decimal('1'), rounding=ROUND_DOWN) * step_decimal - - # Format as string with exactly the right decimals - format_str = f"0.{'':<{step_decimals}}" - if step_decimals == 0: - return int(rounded) - - return float(rounded) - - 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 - - # ROUND QUANTITY TO STEP SIZE (CRITICAL FIX WITH DECIMAL!) - 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 - - # VALIDATE NOTIONAL (order_value must be >= 3.0 MINIMUM) - order_value = qty * entry_price - NOTIONAL_MIN = 5.0 # Minimum $3 - - if order_value < NOTIONAL_MIN: - logger.warning(f"Order value too small {pair}: ${order_value:.2f} < ${NOTIONAL_MIN:.2f} (qty={qty}, price={entry_price})") - return False - - logger.info(f"βœ… NOTIONAL Check Passed: {pair} ${order_value:.2f} >= ${NOTIONAL_MIN:.2f}") - - # Place market buy - order = self.client.order_market_buy(symbol=pair, quantity=qty) - logger.info(f"🟒 BUY: {pair} x{qty} @ ${entry_price:.2f} (value: ${order_value:.2f})") - - # Store trade - self.active_trades[pair] = { - 'entry': entry_price, - 'qty': qty, - 'time': datetime.now() - } - - # Place SL order (FIXED WITH CORRECT API METHOD) - await self.place_stop_loss(pair, entry_price, qty) - - self.trades_today += 1 - 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 & API method""" - try: - # Calculate SL price with {self.STOP_LOSS_PERCENT}% 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) - - # ROUND QUANTITY TO STEP SIZE (WITH DECIMAL!) - qty_rounded = self._round_quantity(qty, pair) - - # Place SL order using create_order (correct Binance API method) - order = self.client.create_order( - symbol=pair, - side='SELL', - type='STOP_LOSS_LIMIT', - timeInForce='GTC', - quantity=qty_rounded, - stopPrice=sl_price, - price=sl_price # For STOP_LOSS_LIMIT, need price = stopPrice - ) - logger.info(f"πŸ›‘οΈ SL: {pair} x{qty_rounded} @ ${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 list(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 - - 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 swap_coins_to_usdt(self): - """ - AUTO-SWAP: Konvertiere alle freien (unlocked) Coins β†’ USDT - Ignoriert locked Coins (von aktiven Trades) - Skip-list: LDBTTC (shitcoin), LDDOGE (shitcoin), USDC (dust) - """ - skip_coins = ['USDT', 'LDBTTC', 'LDDOGE', 'USDC'] # Never swap these - - try: - balance = self.client.get_account() - - swapped_total_usdt = 0 - swap_log = [] - - for asset in balance['balances']: - coin = asset['asset'] - free_qty = float(asset['free']) - - # Skip: small amounts, USDT, locked coins, skip-list - if free_qty < 0.00001 or coin in skip_coins: - continue - - try: - symbol = f"{coin}USDT" - - # Get current price to estimate value - ticker = self.client.get_symbol_info(symbol) - if not ticker: - logger.warning(f"No ticker for {symbol}") - continue - - # Round quantity to step size - qty_to_sell = self._round_quantity(free_qty, symbol) - if qty_to_sell < 0.00001: - continue - - # MARKET SELL (immediate) - order = self.client.order_market_sell(symbol=symbol, quantity=qty_to_sell) - - # Calculate USDT received - fills = order.get('fills', []) - usdt_received = sum(float(f['qty']) * float(f['price']) for f in fills) - - swapped_total_usdt += usdt_received - swap_log.append(f"βœ… {coin}: {qty_to_sell:.6f} β†’ ${usdt_received:.2f}") - logger.info(f"Sweep: Sold {qty_to_sell} {coin} for ${usdt_received:.2f}") - - except BinanceAPIException as e: - logger.warning(f"Sweep {coin}: Binance Error {e.status_code} - {e.message}") - swap_log.append(f"❌ {coin}: {e.message}") - except Exception as e: - logger.warning(f"Sweep {coin}: {e}") - swap_log.append(f"❌ {coin}: {str(e)}") - - # RESULT - result = { - 'success': True, - 'total_usdt_acquired': swapped_total_usdt, - 'swaps_attempted': len(swap_log), - 'log': swap_log - } - - # Send Telegram notification - msg = f"""πŸ”„ **COINS TO USDT SWAP COMPLETE** - -**Total Converted:** ${swapped_total_usdt:.2f} β†’ USDT - -{chr(10).join(swap_log)} - -**New USDT Balance:** ${self.get_usdt_balance():.2f} -""" - self._send_telegram(msg) - - logger.info(f"Swap complete: ${swapped_total_usdt:.2f} converted") - return result - - except Exception as e: - logger.error(f"Swap error: {e}") - self._send_telegram(f"❌ **SWAP FAILED**: {e}") - return {'success': False, 'error': str(e)} - - def get_usdt_balance(self): - """Get current USDT balance""" - try: - balance = self.client.get_account() - for asset in balance['balances']: - if asset['asset'] == 'USDT': - return float(asset['free']) - return 0.0 - except: - return 0.0 - - 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: V0.2 Adaptive""" - - 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)") - 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()) - - - def get_signal_confidence(self): - """Calculate confidence level for current signal (0-100%)""" - # This can be enhanced with actual ML model - # For now: random 30-95% - import random - return random.uniform(30, 95) - - def get_investment_percent(self, confidence): - """Select investment % based on confidence""" - return self.INVESTMENT_PERCENT_HIGH if confidence > self.CONFIDENCE_THRESHOLD else self.INVESTMENT_PERCENT - - def check_consecutive_loss_cooldown(self): - """Check if bot is in cooldown after 3 consecutive losses""" - if self.consecutive_losses >= self.MAX_CONSECUTIVE_LOSSES: - if self.last_loss_time is None: - return False # First loss, no cooldown - - time_elapsed = time.time() - self.last_loss_time - if time_elapsed < self.CONSECUTIVE_LOSS_COOLDOWN: - logger.warning(f"🚫 Cooldown active: {int(self.CONSECUTIVE_LOSS_COOLDOWN - time_elapsed)}s remaining") - return False - else: - # Cooldown expired, reset counter - self.consecutive_losses = 0 - logger.info("βœ… Cooldown expired, consecutive loss counter reset") - return True - return True - - def check_volatility(self, pair): - """Check market volatility (simplified)""" - try: - ticker = self.client.get_symbol_ticker(symbol=pair) - current_price = float(ticker['price']) - - # Get 1h candle for volatility estimate - candles = self.client.get_klines(symbol=pair, interval='1h', limit=5) - - high_prices = [float(c[2]) for c in candles] - low_prices = [float(c[3]) for c in candles] - - volatility = (max(high_prices) - min(low_prices)) / min(low_prices) * 100 - - # Flag as extreme if > 5% 1h volatility - if volatility > 5: - logger.warning(f"⚠️ High volatility {pair}: {volatility:.2f}% (skipping trade)") - return False - return True - except: - return True # If check fails, allow trade - - def check_daily_trade_limit(self): - """Check if daily trade limit reached""" - import datetime - - now = datetime.datetime.now() - today_start = now.replace(hour=0, minute=0, second=0, microsecond=0) - - if self.last_trade_reset is None or self.last_trade_reset < today_start: - self.trades_today = 0 - self.last_trade_reset = now - - if self.trades_today >= self.MAX_TRADES_PER_DAY: - logger.warning(f"⚠️ Daily limit reached: {self.trades_today}/{self.MAX_TRADES_PER_DAY} trades") - return False - - return True - - def update_trailing_stop(self, pair, current_price, entry_price): - """Update trailing stop for an open position""" - if pair not in self.active_trades: - return False - - trade_data = self.active_trades[pair] - profit_pct = ((current_price - entry_price) / entry_price) * 100 - - # Activate trailing stop when profit >= 1.5% - if profit_pct >= self.TRAILING_STOP_ENTRY: - trailing_stop_price = current_price * (1 - self.TRAILING_STOP_DISTANCE / 100) - trade_data['trailing_stop'] = trailing_stop_price - - # If price falls below trailing stop, close position - if current_price < trailing_stop_price: - logger.info(f"πŸ›‘ Trailing stop triggered {pair}: Sell @ ${current_price:.2f}") - return True - - return False - - - def record_entry(self, pair, price, quantity): - """Record entry price for profit calculation""" - self.entry_price_history[pair] = { - 'price': price, - 'qty': quantity, - 'value': price * quantity, - 'timestamp': time.time() - } - - def calculate_unrealized_pnl(self): - """Calculate unrealized P&L for open positions""" - try: - prices = get_live_prices() - total_unrealized = 0 - - for pair, entry_data in self.entry_price_history.items(): - asset = pair.replace('USDT', '') - current_price = prices.get(asset, 0) - if current_price > 0: - current_value = entry_data['qty'] * current_price - unrealized = current_value - entry_data['value'] - total_unrealized += unrealized - - return total_unrealized - except: - return 0 - - def calculate_realized_pnl(self): - """Sum all closed trades realized P&L""" - return sum(t.get('profit_usdt', 0) for t in self.closed_trades) - - def get_total_pnl(self): - """Total P&L = realized + unrealized""" - return self.calculate_realized_pnl() + self.calculate_unrealized_pnl() - diff --git a/src/main_ml_v2.py b/src/main_ml_v2.py deleted file mode 100644 index 073afab..0000000 --- a/src/main_ml_v2.py +++ /dev/null @@ -1,157 +0,0 @@ -import asyncio, logging, joblib, time -from datetime import datetime -from src.config import get_config -from src.bot.binance_client import BinanceClientWrapper -from src.integrations.telegram_notifier import TelegramNotifier -from src.integrations.obsidian_logger import ObsidianLogger -from src.strategies.ml_strategy import MLStrategy - -logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') -logger = logging.getLogger(__name__) - -class MLTradingBot: - def __init__(self, config, binance, telegram, obsidian, model, scaler): - self.config = config - self.binance = binance - self.telegram = telegram - self.obsidian = obsidian - self.model = model - self.scaler = scaler - self.strategy = MLStrategy(trading_pair=config.trading_pair) - - self.last_report_time = time.time() - self.report_interval = 10800 - self.trades_today = 0 - self.wins_today = 0 - self.losses_today = 0 - self.daily_pnl = 0.0 - self.report_count = 0 - - async def auto_swap_to_usdt(self): - """Auto-swap holdings to USDT if needed""" - try: - balance = await self.binance.get_balance() - usdt_free = float(balance.get('USDT', {}).get('free', 0)) if balance else 0 - - # If low on USDT, sell any BTC/ETH/SOL holdings - for crypto in ['BTC', 'ETH', 'SOL']: - crypto_balance = float(balance.get(crypto, {}).get('free', 0)) if balance else 0 - if usdt_free < 20 and crypto_balance > 0.0001: - pair = crypto + 'USDT' - logger.info(f'SWAP: Selling {crypto_balance:.6f} {crypto} for USDT') - try: - await self.binance.place_order(pair, 'SELL', 'MARKET', crypto_balance * 0.95) - await self.telegram.send_alert(f'SWAP: Sold {crypto_balance:.6f} {crypto}') - return True - except Exception as e: - logger.error(f'Swap failed: {e}') - except Exception as e: - logger.error(f'Auto-swap error: {e}') - return False - - async def find_best_trade(self): - """Scan multiple pairs for best signal""" - pairs = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT'] - - for pair in pairs: - try: - price = await self.binance.get_ticker_price(pair) - signal = self.strategy.predict(price) if hasattr(self.strategy, 'predict') else 'HOLD' - - if signal == 'BUY': - logger.info(f'BUY signal: {pair} at {price:.2f}') - return {'pair': pair, 'price': price, 'signal': signal} - - except Exception as e: - logger.debug(f'{pair}: {e}') - - return {'pair': None, 'signal': 'HOLD'} - - async def monitor_trades(self): - """Monitor & execute trades""" - try: - balance = await self.binance.get_balance() - usdt = float(balance.get('USDT', {}).get('free', 0)) if balance else 0 - - # Auto-swap if needed - if usdt < 15: - await self.auto_swap_to_usdt() - return - - # Find best trade - trade = await self.find_best_trade() - - if trade['signal'] == 'BUY' and usdt > 15: - pair = trade['pair'] - price = trade['price'] - qty = (usdt * 0.7) / price - - logger.info(f'EXECUTE BUY: {qty:.6f} {pair} @ {price:.2f}') - try: - await self.binance.place_order(pair, 'BUY', 'MARKET', qty) - self.trades_today += 1 - await self.telegram.send_alert(f'BUY {pair}\n{qty:.6f} @ {price:.2f}') - except Exception as e: - logger.error(f'Trade failed: {e}') - - except Exception as e: - logger.debug(f'Monitor: {e}') - - async def send_performance_report(self): - """Send 3-hourly report""" - try: - self.report_count += 1 - price = await self.binance.get_ticker_price(self.config.trading_pair) - balance = await self.binance.get_balance() - usdt = float(balance.get('USDT', {}).get('free', 0)) if balance else 0 - - report = f'''REPORT #{self.report_count} -BTC: {price:.2f} -Balance: {usdt:.2f} USDT -Trades: {self.trades_today} -Wins: {self.wins_today}''' - - logger.info(report) - await self.telegram.send_alert(report) - - except Exception as e: - logger.error(f'Report error: {e}') - - async def run(self): - """Main bot loop""" - logger.info('BOT STARTED - Multi-Crypto Auto-Trading') - await self.telegram.send_alert('BOT STARTED - Multi-Crypto Mode with Auto-Swap') - - while True: - try: - current_time = time.time() - - if (current_time - self.last_report_time) >= self.report_interval: - await self.send_performance_report() - self.last_report_time = current_time - - await self.monitor_trades() - await asyncio.sleep(60) - - except Exception as e: - logger.error(f'Bot error: {e}') - await asyncio.sleep(60) - -async def main(): - config = get_config() - binance = BinanceClientWrapper( - api_key=config.binance_api_key_live, - api_secret=config.binance_api_secret_live, - testnet=False - ) - telegram = TelegramNotifier(bot_token=config.telegram_bot_token, chat_id=config.telegram_chat_id) - obsidian = ObsidianLogger(vault_path=config.obsidian_vault_path, trade_log_file=config.obsidian_trade_log_file) - - model = joblib.load(config.model_path) if hasattr(config, 'model_path') else None - scaler = None - - bot = MLTradingBot(config, binance, telegram, obsidian, model, scaler) - await bot.run() - -if __name__ == '__main__': - asyncio.run(main()) diff --git a/src/main_ml_v4_backup.py b/src/main_ml_v4_backup.py deleted file mode 100644 index 657417f..0000000 --- a/src/main_ml_v4_backup.py +++ /dev/null @@ -1,173 +0,0 @@ -#!/usr/bin/env python3 -import os, asyncio, aiohttp, logging, random -from datetime import datetime -from binance.client import Client -from decimal import Decimal - -logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') -logger = logging.getLogger(__name__) - -with open("/home/marc/bot-deploy/.env") as f: - env = {} - for line in f: - k, _, v = line.partition("=") - env[k.strip()] = v.strip() - -class Bot: - def __init__(self): - self.binance = Client(env.get("BINANCE_API_KEY_LIVE"), env.get("BINANCE_API_SECRET_LIVE")) - self.current_trades = {} - self.completed_trades = [] - self.balance = {} - self.trades_today = 0 - self.daily_pnl = 0.0 - self.dashboard = "http://localhost:7000/api/update" - logger.info("πŸ€– Bot initialized") - - def get_balance(self): - try: - acc = self.binance.get_account() - self.balance = {} - for a in acc["balances"]: - free, locked = float(a["free"]), float(a["locked"]) - if free + locked > 0: - self.balance[a["asset"]] = {"free": free, "locked": locked, "total": free+locked} - logger.info(f"πŸ’° Balance updated: USDT") - except Exception as e: - logger.error(f"Balance error: {e}") - - def place_buy(self, pair): - try: - usdt_free = self.balance.get("USDT", {}).get("free", 0) - if usdt_free < 5: - return None - - # Use 25% per trade - qty_usdt = usdt_free * 0.25 - - ticker = self.binance.get_symbol_ticker(symbol=pair) - price = float(ticker["price"]) - - # Get symbol info for filters - info = self.binance.get_symbol_info(pair) - filters = {f["filterType"]: f for f in info["filters"]} - - # LOT_SIZE check - if "LOT_SIZE" in filters: - lot = filters["LOT_SIZE"] - min_qty = float(lot["minQty"]) - step = float(lot["stepSize"]) - - # Calculate quantity - qty_calc = qty_usdt / price - - # Round down to step - qty = round(qty_calc / step) * step - - if qty < min_qty or qty <= 0: - return None - else: - qty = float(round(qty_usdt / price, 6)) - - # Format as string to avoid scientific notation - qty_str = f"{qty:.8f}".rstrip("0").rstrip(".") - - try: - order = self.binance.order_market_buy(symbol=pair, quantity=qty_str) - logger.info(f"🟒 BUY: {pair} x{qty_str}") - - self.current_trades[pair] = { - "qty": float(qty_str), - "buy_price": price, - "buy_time": datetime.now().isoformat(), - "order_id": order["orderId"] - } - self.trades_today += 1 - return order - except Exception as e: - logger.error(f"Buy {pair} error: {e}") - return None - except Exception as e: - logger.error(f"place_buy error: {e}") - return None - - def check_tp(self): - remove = [] - for pair in list(self.current_trades.keys()): - try: - trade = self.current_trades[pair] - ticker = self.binance.get_symbol_ticker(symbol=pair) - current = float(ticker["price"]) - - profit_pct = (current / trade["buy_price"]) - 1 - - if profit_pct >= 0.01: - logger.info(f"🎯 TP HIT: {pair} +{profit_pct*100:.2f}%") - - sell = self.binance.order_market_sell(symbol=pair, quantity=trade["qty"]) - sell_price = float(sell["fills"][0]["price"]) if sell.get("fills") else current - profit = (sell_price - trade["buy_price"]) * trade["qty"] - - self.completed_trades.append({ - "pair": pair, - "buy_price": trade["buy_price"], - "sell_price": sell_price, - "qty": trade["qty"], - "profit_usd": profit, - "profit_pct": profit_pct, - "buy_time": trade["buy_time"], - "sell_time": datetime.now().isoformat() - }) - - self.daily_pnl += profit - remove.append(pair) - except Exception as e: - pass - - for p in remove: - del self.current_trades[p] - - async def send_dashboard(self): - try: - state = { - "current_trades": self.current_trades, - "completed_trades": self.completed_trades[-20:], - "balance": self.balance, - "trades_today": self.trades_today, - "daily_pnl": self.daily_pnl, - "total_pnl": self.daily_pnl, - "wins_today": len([t for t in self.completed_trades if t.get("profit_usd", 0) > 0]), - "losses_today": len([t for t in self.completed_trades if t.get("profit_usd", 0) < 0]), - "last_update": datetime.now().isoformat() - } - async with aiohttp.ClientSession() as s: - async with s.post(self.dashboard, json=state, timeout=2) as r: - pass - except: - pass - - async def run(self): - logger.info("🎯 Bot started") - - while True: - try: - self.get_balance() - self.check_tp() - - pairs = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT'] - - for pair in pairs: - if pair not in self.current_trades and random.random() < 0.05: - logger.info(f"🟒 Signal: {pair}") - self.place_buy(pair) - - await self.send_dashboard() - await asyncio.sleep(5) - - except Exception as e: - logger.error(f"Run error: {e}") - await asyncio.sleep(10) - -if __name__ == "__main__": - bot = Bot() - asyncio.run(bot.run()) diff --git a/src/main_ml_v6.py b/src/main_ml_v6.py deleted file mode 100644 index 78c8abb..0000000 --- a/src/main_ml_v6.py +++ /dev/null @@ -1,200 +0,0 @@ -#!/usr/bin/env python3 -""" -Trading Bot V5 CLEAN β€” Minimal, Reliable, Profitable -Architecture: Single trading loop, live dashboard updates -""" - -import os -import asyncio -import aiohttp -from datetime import datetime -from binance.client import Client -from dotenv import load_dotenv -import logging - -logging.basicConfig( - level=logging.INFO, - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' -) -logger = logging.getLogger(__name__) - -load_dotenv() - -class TradingBotClean: - def __init__(self): - self.binance = Client( - os.getenv('BINANCE_API_KEY'), - os.getenv('BINANCE_API_SECRET') - ) - self.pairs = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT'] - - # Trading state - SINGLE SOURCE OF TRUTH - self.current_trades = {} - self.completed_trades = [] - self.balance = {} - self.trades_today = 0 - self.daily_pnl = 0.0 - self.total_pnl = 0.0 - self.wins_today = 0 - self.losses_today = 0 - - self.dashboard_url = 'http://localhost:7000/api/update' - self.TP = 1.01 - self.SL = 0.97 - self.BUY_AMOUNT = 0.5 - self.MIN_ORDER = 10 - - logger.info('πŸ€– Bot CLEAN initialized') - - async def update_balance(self): - """Get current balance from Binance""" - try: - account = self.binance.get_account() - self.balance = {} - for asset in account['balances']: - free = float(asset['free']) - locked = float(asset['locked']) - if free + locked > 0: - self.balance[asset['asset']] = { - 'free': free, - 'locked': locked, - 'total': free + locked - } - except Exception as e: - logger.error(f'Balance error: {e}') - - async def get_ml_signal(self, pair, price): - """Get ML trading signal""" - import random - return 'BUY' if random.random() > 0.95 else None - - async def place_buy_order(self, pair, price): - """Place BUY order""" - try: - usdt_free = self.balance.get('USDT', {}).get('free', 0) - qty_usdt = usdt_free * self.BUY_AMOUNT - - if qty_usdt < self.MIN_ORDER: - return None - - qty = qty_usdt / price - order = self.binance.order_market_buy(symbol=pair, quantity=qty) - - logger.info(f'🟒 BUY: {pair} x{qty:.4f} @ ${price:.2f}') - - self.current_trades[pair] = { - 'qty': qty, - 'buy_price': price, - 'buy_time': datetime.now().isoformat(), - 'order_id': order['orderId'], - } - self.trades_today += 1 - - return order - - except Exception as e: - logger.error(f'Buy error {pair}: {e}') - return None - - async def check_take_profit(self): - """Check for +1% take profit""" - pairs_to_remove = [] - - for pair in list(self.current_trades.keys()): - try: - trade = self.current_trades[pair] - ticker = self.binance.get_symbol_ticker(symbol=pair) - current_price = float(ticker['price']) - - profit_pct = (current_price / trade['buy_price']) - 1 - - if profit_pct >= (self.TP - 1): # +1% - logger.info(f'🎯 TP HIT: {pair} +{profit_pct*100:.2f}%') - - sell_order = self.binance.order_market_sell(symbol=pair, quantity=trade['qty']) - sell_price = float(sell_order['fills'][0]['price']) if sell_order.get('fills') else current_price - profit_usd = (sell_price - trade['buy_price']) * trade['qty'] - - self.completed_trades.append({ - 'pair': pair, - 'buy_price': trade['buy_price'], - 'sell_price': sell_price, - 'qty': trade['qty'], - 'profit_usd': profit_usd, - 'profit_pct': profit_pct, - 'buy_time': trade['buy_time'], - 'sell_time': datetime.now().isoformat() - }) - - self.daily_pnl += profit_usd - self.total_pnl += profit_usd - self.wins_today += 1 - - pairs_to_remove.append(pair) - - except Exception as e: - logger.warning(f'TP check error {pair}: {e}') - - for pair in pairs_to_remove: - del self.current_trades[pair] - - async def send_to_dashboard(self): - """Send state to dashboard""" - try: - state = { - 'current_trades': self.current_trades, - 'completed_trades': self.completed_trades[-20:], - 'balance': self.balance, - 'trades_today': self.trades_today, - 'daily_pnl': self.daily_pnl, - 'total_pnl': self.total_pnl, - 'wins_today': self.wins_today, - 'losses_today': self.losses_today, - 'last_update': datetime.now().isoformat() - } - - async with aiohttp.ClientSession() as session: - async with session.post(self.dashboard_url, json=state, timeout=2) as resp: - pass - except Exception as e: - logger.warning(f'Dashboard send error: {e}') - - async def run(self): - """Main trading loop""" - logger.info('🎯 Bot started') - - while True: - try: - await self.update_balance() - - for pair in self.pairs: - if pair in self.current_trades: - continue - - try: - ticker = self.binance.get_symbol_ticker(symbol=pair) - price = float(ticker['price']) - signal = await self.get_ml_signal(pair, price) - - if signal == 'BUY': - logger.info(f'🟒 BUY signal: {pair}') - await self.place_buy_order(pair, price) - - except Exception as e: - pass - - await self.check_take_profit() - await self.send_to_dashboard() - - await asyncio.sleep(1) - - except Exception as e: - logger.error(f'Loop error: {e}') - await asyncio.sleep(5) - -async def main(): - bot = TradingBotClean() - await bot.run() - -if __name__ == '__main__': - asyncio.run(main())