ROLLBACK: Remove Middleware chaos, back to simple working Bot+Dashboard

This commit is contained in:
Marc Blatter 2026-07-04 14:15:48 +02:00
parent 8d28a7e2fc
commit b8606906fb
2 changed files with 16 additions and 46 deletions

View File

@ -113,7 +113,7 @@ class MLTradingBot:
self.pairs = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT'] self.pairs = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT']
# Track open positions # Track open positions
self.current_trades = {} # CLEAN START - reset on bot restart self.open_positions = {} # CLEAN START - reset on bot restart
logger.info('🗑️ RESET: Cleared all stored positions (dashboard will show REAL Binance state only)') logger.info('🗑️ RESET: Cleared all stored positions (dashboard will show REAL Binance state only)')
@ -338,7 +338,6 @@ class MLTradingBot:
) )
if result and result.get('status') in ['FILLED', 'NEW', 'PARTIALLY_FILLED']: if result and result.get('status') in ['FILLED', 'NEW', 'PARTIALLY_FILLED']:
# Ensure current_trades reflects completed trade
logger.info(f'✅ BTC LIQUIDATED! Order ID: {result.get("orderId")}, Status: {result.get("status")}') logger.info(f'✅ BTC LIQUIDATED! Order ID: {result.get("orderId")}, Status: {result.get("status")}')
# Wait for balance to update # Wait for balance to update
await asyncio.sleep(3) await asyncio.sleep(3)
@ -467,11 +466,11 @@ class MLTradingBot:
logger.warning(f'Failed to get price for {pair}: {e}') logger.warning(f'Failed to get price for {pair}: {e}')
continue continue
if pair not in self.current_trades: if pair not in self.open_positions:
logger.debug(f'⏭️ {pair} not in open_positions (keys: {list(self.current_trades.keys())})') logger.debug(f'⏭️ {pair} not in open_positions (keys: {list(self.open_positions.keys())})')
continue continue
pos = self.current_trades[pair] pos = self.open_positions[pair]
buy_price = pos['buy_price'] buy_price = pos['buy_price']
buy_qty = pos['qty'] buy_qty = pos['qty']
buy_time = datetime.fromisoformat(pos['buy_time']) buy_time = datetime.fromisoformat(pos['buy_time'])
@ -533,7 +532,6 @@ class MLTradingBot:
) )
if result and result.get('status') in ['FILLED', 'NEW', 'PARTIALLY_FILLED']: if result and result.get('status') in ['FILLED', 'NEW', 'PARTIALLY_FILLED']:
# Ensure current_trades reflects completed trade
# ONLY record if order was actually EXECUTED # ONLY record if order was actually EXECUTED
profit_usd = (current_qty * current_price) - (buy_qty * buy_price) profit_usd = (current_qty * current_price) - (buy_qty * buy_price)
self.daily_pnl += profit_usd self.daily_pnl += profit_usd
@ -553,7 +551,7 @@ class MLTradingBot:
f'{icon} CLOSED {exit_reason}\n' f'{icon} CLOSED {exit_reason}\n'
f'{pair}: {current_qty:.8f} @ ${current_price:.2f}\n' f'{pair}: {current_qty:.8f} @ ${current_price:.2f}\n'
f'Profit: ${profit_usd:+.2f} ({profit_pct:+.2f}%)\n' f'Profit: ${profit_usd:+.2f} ({profit_pct:+.2f}%)\n'
f'Hold: {(datetime.now() - datetime.fromisoformat(self.current_trades[pair]["buy_time"])).total_seconds() / 60:.0f} min' f'Hold: {(datetime.now() - datetime.fromisoformat(self.open_positions[pair]["buy_time"])).total_seconds() / 60:.0f} min'
) )
else: else:
logger.info(f'Loss trade skipped from Telegram (visible on dashboard): {profit_pct:.2f}%') logger.info(f'Loss trade skipped from Telegram (visible on dashboard): {profit_pct:.2f}%')
@ -566,11 +564,11 @@ class MLTradingBot:
self.error_count = 0 self.error_count = 0
# Send to dashboard BEFORE deleting position (ONLY for REAL executed trades!) # Send to dashboard BEFORE deleting position (ONLY for REAL executed trades!)
hold_time_s = (datetime.now() - datetime.fromisoformat(self.current_trades[pair]["buy_time"])).total_seconds() hold_time_s = (datetime.now() - datetime.fromisoformat(self.open_positions[pair]["buy_time"])).total_seconds()
hold_time_min = hold_time_s / 60 hold_time_min = hold_time_s / 60
await self.dashboard.record_sell(pair, current_qty, current_price, profit_usd, profit_pct, hold_time_min) await self.dashboard.record_sell(pair, current_qty, current_price, profit_usd, profit_pct, hold_time_min)
del self.current_trades[pair] del self.open_positions[pair]
logger.info(f'✅ EXIT EXECUTED & RECORDED TO DASHBOARD!') logger.info(f'✅ EXIT EXECUTED & RECORDED TO DASHBOARD!')
else: else:
logger.warning(f'❌ EXIT order FAILED or returned no result for {pair} - NOT recording') logger.warning(f'❌ EXIT order FAILED or returned no result for {pair} - NOT recording')
@ -653,6 +651,7 @@ class MLTradingBot:
if free > 0 or locked > 0: if free > 0 or locked > 0:
logger.info(f' {asset}: FREE={free:.8f}, LOCKED={locked:.8f}, TOTAL={free+locked:.8f}') logger.info(f' {asset}: FREE={free:.8f}, LOCKED={locked:.8f}, TOTAL={free+locked:.8f}')
self.starting_capital = usdt 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}') logger.info(f'💰 Balance: {usdt:.2f} USDT | Daily P&L: ${self.daily_pnl:.2f}')
@ -736,10 +735,9 @@ class MLTradingBot:
result = None result = None
# ONLY RECORD if order was SUCCESSFUL # ONLY RECORD if order was SUCCESSFUL
if result and (result.get('status') in ['FILLED', 'NEW', 'PARTIALLY_FILLED'] or order_type == 'MARKET'): if result and result.get('status') in ['FILLED', 'NEW', 'PARTIALLY_FILLED']:
# Record immediately — market orders always fill
logger.info(f'🔍 DEBUG: Storing BUY position {pair}: qty={qty}, price={price}') logger.info(f'🔍 DEBUG: Storing BUY position {pair}: qty={qty}, price={price}')
self.current_trades[pair] = { self.open_positions[pair] = {
'qty': qty, 'qty': qty,
'buy_price': price, 'buy_price': price,
'buy_time': datetime.now().isoformat(), 'buy_time': datetime.now().isoformat(),
@ -747,14 +745,13 @@ class MLTradingBot:
'trailing_stop': None, 'trailing_stop': None,
'order_id': result.get('orderId', 'unknown') 'order_id': result.get('orderId', 'unknown')
} }
logger.info(f'🔍 DEBUG: After storage, open_positions keys = {list(self.current_trades.keys())}') logger.info(f'🔍 DEBUG: After storage, open_positions keys = {list(self.open_positions.keys())}')
self.trades_today += 1 self.trades_today += 1
self.error_count = 0 self.error_count = 0
# BUY Alert disabled — user only wants profit notifications # BUY Alert disabled — user only wants profit notifications
logger.info(f'✅ BUY FILLED & RECORDED! Order ID: {result.get("orderId", "unknown")}') logger.info(f'✅ BUY FILLED & RECORDED! Order ID: {result.get("orderId", "unknown")}')
# Ensure current_trades reflects completed trade
# Send to dashboard # Send to dashboard
await self.dashboard.record_buy(pair, qty, price) await self.dashboard.record_buy(pair, qty, price)
@ -815,7 +812,7 @@ class MLTradingBot:
Max Drawdown: ${self.max_drawdown:.2f} (CHF {self.max_drawdown * 0.84:.2f}) 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'} 🤖 BOT STATUS: {'🟢 RUNNING' if time.time() >= self.error_cooldown_until else '🟡 ERROR_COOLDOWN'}
Open Positions: {len(self.current_trades)} Open Positions: {len(self.open_positions)}
Error Count: {self.error_count}/{self.error_threshold}''' Error Count: {self.error_count}/{self.error_threshold}'''
logger.info(report) logger.info(report)
@ -967,13 +964,13 @@ class MLTradingBot:
except: except:
pass 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.current_trades)}') 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)}')
# SYNC open_positions with dashboard # SYNC open_positions with dashboard
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
async with session.post('http://localhost:7000/api/update', json={ async with session.post('http://localhost:7000/api/update', json={
'balance': {'USDT': usdt_live}, 'balance': {'USDT': usdt_live},
'current_trades': self.current_trades, # Send ALL open positions! 'current_trades': self.open_positions, # Send ALL open positions!
'daily_pnl': self.daily_pnl, 'daily_pnl': self.daily_pnl,
'total_pnl': self.total_pnl, 'total_pnl': self.total_pnl,
'trades_today': self.trades_today, 'trades_today': self.trades_today,

View File

@ -1,4 +1,3 @@
import httpx
""" """
Trading Bot Web Dashboard Trading Bot Web Dashboard
Real-time tracking of trades, swaps, and performance Real-time tracking of trades, swaps, and performance
@ -67,21 +66,11 @@ async def websocket_endpoint(websocket: WebSocket):
finally: finally:
active_connections.remove(websocket) active_connections.remove(websocket)
# @app.get("/api/state")
@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(): async def get_state():
"""Get current trading state""" """Get current trading state"""
return trading_state return trading_state
@app.post("/api/clear") @app.post("/api/clear")
async def clear_state(): async def clear_state():
"""RESET: Clear all historical data, start fresh""" """RESET: Clear all historical data, start fresh"""
@ -208,22 +197,6 @@ async def trigger_liquidation():
@app.get("/") @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(): async def get_dashboard():
"""Serve web dashboard HTML""" """Serve web dashboard HTML"""
return HTMLResponse(html_content) return HTMLResponse(html_content)
@ -558,13 +531,13 @@ html_content = """
const dailyPnl = data.daily_pnl || 0; const dailyPnl = data.daily_pnl || 0;
const dailyPnl_chf = dailyPnl * 0.84; const dailyPnl_chf = dailyPnl * 0.84;
const dailyPnlEl = document.getElementById('daily-pnl'); const dailyPnlEl = document.getElementById('daily-pnl');
dailyPnlEl.textContent = `$${dailyPnl >= 0 ? '+' : ''}${dailyPnl.toFixed(2)}`; dailyPnlEl.textContent = `$${dailyPnl >= 0 ? '+' : ''}${dailyPnl.toFixed(2)}${dailyPnl_chf >= 0 ? '+' : ''}`;
dailyPnlEl.className = 'card-value ' + (dailyPnl >= 0 ? 'positive' : 'negative'); dailyPnlEl.className = 'card-value ' + (dailyPnl >= 0 ? 'positive' : 'negative');
const totalPnl = data.total_pnl || 0; const totalPnl = data.total_pnl || 0;
const totalPnl_chf = totalPnl * 0.84; const totalPnl_chf = totalPnl * 0.84;
const totalPnlEl = document.getElementById('total-pnl'); const totalPnlEl = document.getElementById('total-pnl');
totalPnlEl.textContent = `$${totalPnl >= 0 ? '+' : ''}${totalPnl.toFixed(2)}`; totalPnlEl.textContent = `$${totalPnl >= 0 ? '+' : ''}${totalPnl.toFixed(2)}${totalPnl_chf >= 0 ? '+' : ''}`;
totalPnlEl.className = 'card-value ' + (totalPnl >= 0 ? 'positive' : 'negative'); totalPnlEl.className = 'card-value ' + (totalPnl >= 0 ? 'positive' : 'negative');
// Update performance // Update performance