From e67077c0935a6136981b6ddeb29d4844637b11bb Mon Sep 17 00:00:00 2001 From: Marc Blatter Date: Sat, 4 Jul 2026 14:19:41 +0200 Subject: [PATCH] Bot auto-update: src/__pycache__/main_ml.cpython-310.pyc,src/main_ml.py,src/web_dashboard.py --- src/__pycache__/main_ml.cpython-310.pyc | Bin 28046 -> 29235 bytes src/main_ml.py | 75 +++++++++++++++++++----- src/web_dashboard.py | 31 +++++++++- 3 files changed, 90 insertions(+), 16 deletions(-) diff --git a/src/__pycache__/main_ml.cpython-310.pyc b/src/__pycache__/main_ml.cpython-310.pyc index 209782ae70207fd30e2a06b7c16cb5bccd12e32b..de623aa7252b102eb2bcb3fb42d8c62205a3e006 100644 GIT binary patch delta 4259 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 0 or locked > 0: logger.info(f' {asset}: FREE={free:.8f}, LOCKED={locked:.8f}, TOTAL={free+locked:.8f}') self.starting_capital = usdt - logger.info(f'📊 Starting capital set: ${self.starting_capital:.2f}') logger.info(f'💰 Balance: {usdt:.2f} USDT | Daily P&L: ${self.daily_pnl:.2f}') @@ -735,9 +736,10 @@ class MLTradingBot: result = None # ONLY RECORD if order was SUCCESSFUL - if result and result.get('status') in ['FILLED', 'NEW', 'PARTIALLY_FILLED']: + if result and (result.get('status') in ['FILLED', 'NEW', 'PARTIALLY_FILLED'] or order_type == 'MARKET'): + # Record immediately — market orders always fill logger.info(f'🔍 DEBUG: Storing BUY position {pair}: qty={qty}, price={price}') - self.open_positions[pair] = { + self.current_trades[pair] = { 'qty': qty, 'buy_price': price, 'buy_time': datetime.now().isoformat(), @@ -745,13 +747,14 @@ class MLTradingBot: 'trailing_stop': None, 'order_id': result.get('orderId', 'unknown') } - logger.info(f'🔍 DEBUG: After storage, open_positions keys = {list(self.open_positions.keys())}') + logger.info(f'🔍 DEBUG: After storage, open_positions keys = {list(self.current_trades.keys())}') self.trades_today += 1 self.error_count = 0 # BUY Alert disabled — user only wants profit notifications logger.info(f'✅ BUY FILLED & RECORDED! Order ID: {result.get("orderId", "unknown")}') + # Ensure current_trades reflects completed trade # Send to dashboard await self.dashboard.record_buy(pair, qty, price) @@ -812,7 +815,7 @@ class MLTradingBot: Max Drawdown: ${self.max_drawdown:.2f} (CHF {self.max_drawdown * 0.84:.2f}) 🤖 BOT STATUS: {'🟢 RUNNING' if time.time() >= self.error_cooldown_until else '🟡 ERROR_COOLDOWN'} - Open Positions: {len(self.open_positions)} + Open Positions: {len(self.current_trades)} Error Count: {self.error_count}/{self.error_threshold}''' logger.info(report) @@ -896,6 +899,50 @@ class MLTradingBot: """Main bot loop""" logger.info('🤖 BOT STARTED (V5 - SUSTAINABLE)') + # Auto-recover open positions from Binance on restart + logger.info('📥 Recovering trades from Binance...') + try: + for pair in ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT']: + orders = self.binance._client.get_open_orders(symbol=pair) + if orders: + o = orders[0] + self.current_trades[pair] = { + 'qty': float(o['origQty']), + 'buy_price': float(o['price']), + 'buy_time': ''.join([str(i) for i in range(10)]), + 'order_id': o['orderId'], + 'peak_profit': 0.0 + } + logger.info(f' ✅ Recovered {pair}: {o[origQty]} @ {o[price]}') + except Exception as e: + logger.debug(f'Recovery scan: {e}') + + logger.info(f'✅ Recovered {len(self.current_trades)} trades from Binance') + + # STARTUP: Load open orders from Binance so Bot knows its positions + logger.info('📥 Loading open positions from Binance...') + try: + account = await self.binance.get_balance() + for pair in ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT']: + try: + orders = await self.binance.get_open_orders(pair) + if orders: + order = orders[0] + self.current_trades[pair] = { + 'qty': float(order['origQty']), + 'buy_price': float(order['price']), + 'buy_time': datetime.fromtimestamp(order['time']/1000).isoformat(), + 'order_id': order['orderId'], + 'peak_profit': 0.0 + } + logger.info(f' ✅ Loaded {pair}: {order[origQty]} @ ') + except: + pass + except Exception as e: + logger.warning(f'Could not load Binance positions: {e}') + + logger.info(f'✅ Startup complete: {len(self.current_trades)} positions loaded from Binance') + # Create trigger file location trigger_file = '/tmp/bot_liquidate_trigger' @@ -964,13 +1011,13 @@ class MLTradingBot: except: pass - logger.info(f'✅ Sending to dashboard: USDT={usdt_live:.2f}, portfolio={portfolio_value_usd:.2f}, trades_today={self.trades_today}, open_trades={len(self.open_positions)}') + logger.info(f'✅ Sending to dashboard: USDT={usdt_live:.2f}, portfolio={portfolio_value_usd:.2f}, trades_today={self.trades_today}, open_trades={len(self.current_trades)}') # SYNC open_positions with dashboard async with aiohttp.ClientSession() as session: async with session.post('http://localhost:7000/api/update', json={ 'balance': {'USDT': usdt_live}, - 'current_trades': self.open_positions, # Send ALL open positions! + 'current_trades': self.current_trades, # Send ALL open positions! 'daily_pnl': self.daily_pnl, 'total_pnl': self.total_pnl, 'trades_today': self.trades_today, diff --git a/src/web_dashboard.py b/src/web_dashboard.py index a7243c4..0308ce6 100644 --- a/src/web_dashboard.py +++ b/src/web_dashboard.py @@ -1,3 +1,4 @@ +import httpx """ Trading Bot Web Dashboard Real-time tracking of trades, swaps, and performance @@ -66,11 +67,21 @@ async def websocket_endpoint(websocket: WebSocket): finally: active_connections.remove(websocket) +# @app.get("/api/state") @app.get("/api/state") +async def get_state(): + """Proxy to State Manager""" + try: + async with httpx.AsyncClient(timeout=3.0) as client: + resp = await client.get("http://localhost:7001/api/bot-state") + return resp.json() + except: + return trading_state async def get_state(): """Get current trading state""" return trading_state + @app.post("/api/clear") async def clear_state(): """RESET: Clear all historical data, start fresh""" @@ -197,6 +208,22 @@ async def trigger_liquidation(): @app.get("/") +async def get_dashboard(): + """Serve dashboard HTML""" + return HTMLResponse(html_content) + +@app.get("/api/state") +async def get_state(): + """Get current trading state""" + return trading_state +async def get_state(): + """Proxy to State Manager""" + try: + async with httpx.AsyncClient(timeout=3.0) as client: + resp = await client.get("http://localhost:7001/api/bot-state") + return resp.json() + except: + return trading_state async def get_dashboard(): """Serve web dashboard HTML""" return HTMLResponse(html_content) @@ -531,13 +558,13 @@ html_content = """ const dailyPnl = data.daily_pnl || 0; const dailyPnl_chf = dailyPnl * 0.84; const dailyPnlEl = document.getElementById('daily-pnl'); - dailyPnlEl.textContent = `$${dailyPnl >= 0 ? '+' : ''}${dailyPnl.toFixed(2)}${dailyPnl_chf >= 0 ? '+' : ''}`; + dailyPnlEl.textContent = `$${dailyPnl >= 0 ? '+' : ''}${dailyPnl.toFixed(2)}`; dailyPnlEl.className = 'card-value ' + (dailyPnl >= 0 ? 'positive' : 'negative'); const totalPnl = data.total_pnl || 0; const totalPnl_chf = totalPnl * 0.84; const totalPnlEl = document.getElementById('total-pnl'); - totalPnlEl.textContent = `$${totalPnl >= 0 ? '+' : ''}${totalPnl.toFixed(2)}${totalPnl_chf >= 0 ? '+' : ''}`; + totalPnlEl.textContent = `$${totalPnl >= 0 ? '+' : ''}${totalPnl.toFixed(2)}`; totalPnlEl.className = 'card-value ' + (totalPnl >= 0 ? 'positive' : 'negative'); // Update performance