diff --git a/src/main_ml.py b/src/main_ml.py index 04228e2..a249cb9 100644 --- a/src/main_ml.py +++ b/src/main_ml.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -'''Trading Bot v0.4 - Dynamic Position Sizing''' +"""Trading Bot v0.4 Hybrid - Dynamic Position Sizing + Trade Recovery""" import os, json, time, logging from datetime import datetime from dotenv import load_dotenv @@ -17,11 +17,10 @@ if not API_KEY or not API_SECRET: logger.error("Missing API keys") exit(1) -# CONSTANTS - DYNAMIC SIZING SYMBOLS = ['BTCUSDT', 'ETHUSDT', 'BNBUSDT', 'XRPUSDT', 'SOLUSDT'] TRACKED_COINS = ['BTC', 'ETH', 'BNB', 'XRP', 'SOL'] -MIN_TRADE_USDT = 12.00 # Hybrid minimum -MAX_POSITION_PCT = 0.07 # Hybrid 7% +MIN_TRADE_USDT = 12.00 +MAX_POSITION_PCT = 0.07 TAKE_PROFIT_PCT = 0.015 STOP_LOSS_PCT = -0.008 CYCLE_SEC = 60 @@ -33,7 +32,31 @@ class TradingBotV04: self.active_trades = {} self.portfolio_value = 0 self.max_trade_usdt = 0 - logger.info("[v0.4 INIT] Bot initialized | Dynamic Position Sizing") + + # TRADE RECOVERY: Recover orphaned trades from holdings + try: + account = self.client.get_account() + for b in account['balances']: + asset = b['asset'] + free = float(b['free']) + + if asset in TRACKED_COINS and free > 0.0001: + symbol = asset + 'USDT' + try: + price = self.get_current_price(symbol) + if price: + self.active_trades[symbol] = { + 'entry_price': price, + 'qty': free, + 'entry_time': datetime.now().isoformat() + } + logger.info(f"[RECOVERED] {symbol} {free} @ {price}") + except: + pass + except Exception as e: + logger.warning(f"Recovery failed: {e}") + + logger.info("[v0.4 INIT] Bot | Dynamic Sizing (Min 12 + 7%)") def get_fresh_balance(self): try: @@ -59,19 +82,14 @@ class TradingBotV04: elif asset == 'USDT': portfolio_value += free - usdt_available = next( - (float(b['free']) for b in account['balances'] if b['asset'] == 'USDT'), - 0 - ) + usdt_available = next((float(b['free']) for b in account['balances'] if b['asset'] == 'USDT'), 0) self.portfolio_value = portfolio_value - self.max_trade_usdt = max(MIN_TRADE_USDT, portfolio_value * MAX_POSITION_PCT) + self.max_trade_usdt = portfolio_value * MAX_POSITION_PCT - logger.info(f"[v0.4] USDT=${usdt_available:.2f} | Portfolio=${portfolio_value:.2f} | MaxTrade=${self.max_trade_usdt:.2f}") + logger.info(f"[v0.4] USDT={usdt_available:.2f} | Portfolio={portfolio_value:.2f} | Max={self.max_trade_usdt:.2f}") return usdt_available, portfolio_value - - except BinanceAPIException as e: - logger.error(f"Balance fetch failed: {e}") + except: return 0, 0 def get_current_price(self, symbol): @@ -119,7 +137,7 @@ class TradingBotV04: is_min = all(current < p for p in recent[:-1]) if is_min: - logger.info(f"[SIGNAL] Local min: {symbol} @ ${current:.2f}") + logger.info(f"[SIGNAL] Local min: {symbol} @ {current}") return is_min @@ -143,11 +161,10 @@ class TradingBotV04: } pos_pct = (qty * price / self.portfolio_value * 100) if self.portfolio_value > 0 else 0 - logger.info(f"[BUY] {symbol} {qty} @ ${price:.2f} | Position: {pos_pct:.1f}% | [v0.4 DYNAMIC]") + logger.info(f"[BUY] {symbol} {qty} @ {price} | Pos: {pos_pct:.1}% [v0.4 HYBRID]") return order - except BinanceAPIException as e: - logger.error(f"Order failed: {e}") + except: return None def check_and_close_positions(self): @@ -162,7 +179,7 @@ class TradingBotV04: pnl_pct = ((current - entry) / entry) * 100 if pnl_pct >= TAKE_PROFIT_PCT * 100: - logger.info(f"[SELL-TP] {symbol} @ ${current:.2f} | +{pnl_pct:.2f}%") + logger.info(f"[SELL-TP] {symbol} @ {current} | +{pnl_pct:.2f}%") try: self.client.order_market_sell(symbol=symbol, quantity=qty) del self.active_trades[symbol] @@ -170,7 +187,7 @@ class TradingBotV04: pass elif pnl_pct <= STOP_LOSS_PCT * 100: - logger.info(f"[SELL-SL] {symbol} @ ${current:.2f} | {pnl_pct:.2f}%") + logger.info(f"[SELL-SL] {symbol} @ {current} | {pnl_pct:.2f}%") try: self.client.order_market_sell(symbol=symbol, quantity=qty) del self.active_trades[symbol] @@ -180,13 +197,13 @@ class TradingBotV04: pass def run_cycle(self): - logger.info("=" * 70) + logger.info("="*70) usdt_free, portfolio_val = self.get_fresh_balance() if usdt_free < MIN_TRADE_USDT: - logger.warning(f"Insufficient capital: ${usdt_free:.2f} < ${MIN_USDT}") - logger.info("=" * 70) + logger.warning(f"Low capital: {usdt_free:.2f} < {MIN_TRADE_USDT}") + logger.info("="*70) return self.check_and_close_positions() @@ -208,7 +225,7 @@ class TradingBotV04: trade_amount = min(max(MIN_TRADE_USDT, self.max_trade_usdt), usdt_free * 0.5) self.place_buy_order(best_signal, trade_amount) - logger.info(f"[CYCLE-END] Trades: {len(self.active_trades)} | USDT: ${usdt_free:.2f} | Portfolio: ${portfolio_val:.2f} [v0.4]") + logger.info(f"[CYCLE-END] Trades: {len(self.active_trades)} | USDT: {usdt_free:.2f} | Portfolio: {portfolio_val:.2f}") try: temp = '/home/marc/bot-deploy/active_trades.json.tmp' @@ -219,13 +236,13 @@ class TradingBotV04: 'portfolio_value': round(portfolio_val, 2), 'max_trade_usdt': round(self.max_trade_usdt, 2), 'timestamp': datetime.now().isoformat(), - 'version': 'v0.4-dynamic' + 'version': 'v0.4-hybrid' }, f) os.replace(temp, '/home/marc/bot-deploy/active_trades.json') except Exception as e: logger.warning(f"Save failed: {e}") - logger.info("=" * 70) + logger.info("="*70) if __name__ == '__main__': import sys @@ -235,11 +252,11 @@ if __name__ == '__main__': if len(sys.argv) > 1 and sys.argv[1] == '--once': bot.run_cycle() else: - logger.info("[v0.4 START] Trading Bot cycle loop running...") + logger.info("[v0.4 START] Bot cycle loop...") while True: try: bot.run_cycle() except Exception as e: - logger.error(f"Cycle error: {e}") + logger.error(f"Error: {e}") time.sleep(CYCLE_SEC)