diff --git a/web_dashboard.py b/web_dashboard.py
new file mode 100644
index 0000000..052051d
--- /dev/null
+++ b/web_dashboard.py
@@ -0,0 +1,646 @@
+#!/usr/bin/env python3
+from fastapi import FastAPI, Response
+from binance.client import Client
+import json, os, time
+from datetime import datetime
+
+app = FastAPI()
+
+env = {}
+with open('/home/marc/bot-deploy/.env') as f:
+ for line in f:
+ k,_,v = line.partition('=')
+ env[k.strip()] = v.strip()
+
+binance = Client(env.get('BINANCE_API_KEY_LIVE'), env.get('BINANCE_API_SECRET_LIVE'))
+
+price_cache = {'prices': {}, 'timestamp': 0}
+
+def get_live_prices():
+ global price_cache
+ if time.time() - price_cache['timestamp'] < 5:
+ return price_cache['prices']
+
+ prices = {'USDT': 1.0}
+ pairs = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT']
+ for pair in pairs:
+ try:
+ ticker = binance.get_ticker(symbol=pair)
+ asset = pair.replace('USDT', '')
+ prices[asset] = float(ticker['lastPrice'])
+ except:
+ pass
+
+ price_cache['prices'] = prices
+ price_cache['timestamp'] = time.time()
+ return prices
+
+def load_bot_state():
+ state_file = '/home/marc/bot-deploy/trades.json'
+ if os.path.exists(state_file):
+ try:
+ with open(state_file) as f:
+ return json.load(f)
+ except:
+ pass
+ return {'current': {}, 'completed': [], 'balance': {}}
+
+@app.get('/api/state')
+async def get_state():
+ try:
+ account = binance.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
+ }
+
+ prices = get_live_prices()
+
+ portfolio_value = 0
+ tracked_assets = ['BTC', 'ETH', 'SOL', 'BNB', 'XRP', 'USDT', 'USDC']
+
+ for asset in tracked_assets:
+ if asset in balance:
+ data = balance[asset]
+ price = prices.get(asset, 0)
+ portfolio_value += data['total'] * price
+
+ usdt_free = balance.get('USDT', {}).get('free', 0)
+
+ # Count active positions = locked coins (NOT trades.json)
+ active_positions = 0
+ for asset in ['BTC', 'ETH', 'SOL', 'BNB', 'XRP']:
+ if asset in balance and balance[asset]['locked'] > 0.00001:
+ active_positions += 1
+
+ trades = load_bot_state()
+
+ return {
+ 'balance': balance,
+ 'portfolio_value': round(portfolio_value, 2),
+ 'usdt_free': round(usdt_free, 2),
+ 'active_positions': active_positions, # ← NEW: Real count!
+ 'current_trades': trades.get('current', {}),
+ 'completed_trades': trades.get('completed', []),
+ 'prices': prices,
+ 'timestamp': datetime.now().isoformat()
+ }
+ except Exception as e:
+ return {'error': str(e), 'portfolio_value': 0, 'usdt_free': 0, 'active_positions': 0}
+
+@app.get('/')
+async def root():
+ state = await get_state()
+ portfolio_val = state.get('portfolio_value', 0)
+ usdt_free = state.get('usdt_free', 0)
+ trades_count = state.get('active_positions', 0) # ← FIXED: Use real count!
+ prices = state.get('prices', {})
+
+
+ # P&L from state
+ pnl_usdt = state.get("pnl_usdt", 0)
+ pnl_pct = state.get("pnl_pct", 0)
+ pnl_status = state.get("pnl_status", "⚪ BREAK")
+ pnl_color = state.get("pnl_color", "neutral")
+ html = f'''
+
+
+
+
+Trading Bot V10
+
+
+
+
+
+
+
+
+
Portfolio Value
+
${portfolio_val:.2f}
+
+
+
USDT Available
+
${usdt_free:.2f}
+
+
+
Open Positions
+
{trades_count}
+
+
+
Total P&L
+
${pnl_usdt:+.2f} ({pnl_pct:+.1f}%)
+
+
+
P&L Status
+
{pnl_status}
+
+
+
+
+
+
+
+
+
+
+ | Asset |
+ Price |
+
+
+ '''
+
+ for asset, price in prices.items():
+ html += f'''
+ | {asset} |
+ ${price:.2f} |
+
'''
+
+ html += '''
+
+
+
+
+
+
+
+
+
+
+
+
+ | Asset |
+ Free |
+ Total |
+ Value |
+
+
+ '''
+
+ tracked = ['BTC', 'ETH', 'SOL', 'BNB', 'XRP', 'USDT', 'USDC']
+ balance = state.get('balance', {})
+
+ for asset in tracked:
+ if asset in balance:
+ data = balance[asset]
+ price = prices.get(asset, 0)
+ value = data['total'] * price
+ html += f'''
+ | {asset} |
+ {data['free']:.4f} |
+ {data['total']:.4f} |
+ ${value:.2f} |
+
'''
+
+ html += '''
+
+
+
+
+
+
+
+
+'''
+
+ return Response(content=html, media_type='text/html')
+
+
+@app.get('/api/pnl')
+async def get_pnl():
+ """Get live Profit & Loss (P&L) calculation"""
+ try:
+ account = binance.get_account()
+
+ # Get current account value
+ prices = get_live_prices()
+ current_value = 0
+
+ for asset_data in account['balances']:
+ asset = asset_data['asset']
+ total = float(asset_data['free']) + float(asset_data['locked'])
+
+ if total > 0.00001 and asset != 'LDDOGE' and asset != 'LDBTTC':
+ price = prices.get(asset, 1.0)
+ current_value += total * price
+
+ # Benchmark: Initial capital was $137.79 (before trading)
+ # This should be stored, but for now use a reference
+ initial_capital = 137.79
+
+ pnl_usdt = current_value - initial_capital
+ pnl_pct = (pnl_usdt / initial_capital * 100) if initial_capital > 0 else 0
+
+ # Get open trades for unrealized portion
+ state_file = '/home/marc/bot-deploy/trades.json'
+ open_trades = {}
+ if os.path.exists(state_file):
+ try:
+ data = json.load(state_file)
+ open_trades = data.get('current', {})
+ except:
+ pass
+
+ return {
+ 'current_value': round(current_value, 2),
+ 'initial_capital': initial_capital,
+ 'total_pnl_usdt': round(pnl_usdt, 2),
+ 'total_pnl_percent': round(pnl_pct, 2),
+ 'status': '🟢 PROFIT' if pnl_usdt > 0 else ('🔴 LOSS' if pnl_usdt < 0 else '⚪ BREAK'),
+ 'open_positions': len(open_trades),
+ 'timestamp': datetime.now().isoformat()
+ }
+ except Exception as e:
+ return {'error': str(e)}
+
+
+if __name__ == '__main__':
+ import uvicorn
+ uvicorn.run(app, host='0.0.0.0', port=7000)