diff --git a/src/web_dashboard.py b/src/web_dashboard.py
index 6236f8f..a898292 100644
--- a/src/web_dashboard.py
+++ b/src/web_dashboard.py
@@ -6,6 +6,7 @@ from datetime import datetime
import json, os, time, sqlite3
app = FastAPI()
+
env = {}
with open('/home/marc/bot-deploy/.env') as f:
for line in f:
@@ -13,675 +14,78 @@ with open('/home/marc/bot-deploy/.env') as f:
env[k.strip()] = v.strip()
binance = Client(env.get('BINANCE_API_KEY_LIVE'), env.get('BINANCE_API_SECRET_LIVE'))
-DB_PATH = '/home/marc/bot-deploy/pnl_history.db'
+DB = '/home/marc/bot-deploy/pnl_charts.db'
def init_db():
- conn = sqlite3.connect(DB_PATH)
- c = conn.cursor()
- c.execute("""CREATE TABLE IF NOT EXISTS pnl_snapshots (timestamp INTEGER PRIMARY KEY, portfolio_value REAL, pnl_usdt REAL, pnl_pct REAL, usdt_free REAL, active_positions INTEGER)""")
- conn.commit()
- conn.close()
+ c = sqlite3.connect(DB).cursor()
+ c.execute("""CREATE TABLE IF NOT EXISTS history (
+ ts INTEGER PRIMARY KEY, pv REAL, pu REAL, pp REAL, uf REAL, ap INTEGER)""")
+ sqlite3.connect(DB).commit()
init_db()
-# Rest des Codes...
-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():
+async def state():
try:
- account = binance.get_account()
- balance = {}
+ acc = binance.get_account()
+ bal = {}
+ for a in acc['balances']:
+ ast, free, locked = a['asset'], float(a['free']), float(a['locked'])
+ if free + locked > 1e-5:
+ bal[ast] = {'free': free, 'locked': locked, 'total': free + locked}
- 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)
-
- # P&L CALCULATION
- initial_capital = 137.79
- pnl_usdt = portfolio_value - initial_capital
- pnl_pct = (pnl_usdt / initial_capital * 100) if initial_capital > 0 else 0
- pnl_status = "🟢 PROFIT" if pnl_usdt > 0.01 else ("🔴 LOSS" if pnl_usdt < -0.01 else "⚪ BREAK")
- pnl_color = "accent" if pnl_usdt > 0.01 else ("negative" if pnl_usdt < -0.01 else "neutral")
-
- # Count active positions from bot's active_trades.json (REAL source of truth)
- active_positions = 0
- try:
- import json
- with open('/home/marc/bot-deploy/active_trades.json', 'r') as f:
- bot_state = json.load(f)
- active_positions = bot_state.get('count', 0)
- except:
- # Fallback: count from Binance open orders
+ prices = {'USDT': 1.0}
+ for p in ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT']:
try:
- open_orders = binance.get_open_orders()
- active_positions = len(open_orders)
- except:
- # Last resort: count locked coins
- 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', {}),
- 'pnl_usdt': round(pnl_usdt, 2),
- 'pnl_pct': round(pnl_pct, 2),
- 'pnl_status': pnl_status,
- 'pnl_color': pnl_color,
- '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 V0.3
-
-
-
-
-
-
-
-
-
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', {})
+ t = binance.get_ticker(symbol=p)
+ prices[p.replace('USDT', '')] = float(t['lastPrice'])
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()
- }
+ pv = sum(bal.get(a, {}).get('total', 0) * prices.get(a, 0) for a in ['BTC', 'ETH', 'SOL', 'BNB', 'XRP', 'USDT'])
+ uf = bal.get('USDT', {}).get('free', 0)
+ pu = pv - 137.79
+ pp = (pu / 137.79 * 100) if pv > 0 else 0
+
+ ap = 0
+ try:
+ with open('/home/marc/bot-deploy/active_trades.json') as f:
+ ap = json.load(f).get('count', 0)
+ except:
+ pass
+
+ conn = sqlite3.connect(DB)
+ conn.execute("INSERT OR REPLACE INTO history VALUES (?, ?, ?, ?, ?, ?)",
+ (int(time.time()), pv, pu, pp, uf, ap))
+ conn.commit()
+ conn.close()
+
+ return {'portfolio_value': round(pv, 2), 'pnl_usdt': round(pu, 2), 'pnl_pct': round(pp, 2),
+ 'usdt_free': round(uf, 2), 'active_positions': ap, 'balance': bal, 'prices': prices}
except Exception as e:
return {'error': str(e)}
+@app.get('/api/pnl-history')
+async def history(hours: int = 24):
+ conn = sqlite3.connect(DB)
+ cutoff = int(time.time()) - hours * 3600
+ rows = conn.execute("SELECT ts, pp, pu FROM history WHERE ts > ? ORDER BY ts", (cutoff,)).fetchall()
+ conn.close()
+
+ ts_list, pcts, usdts = [], [], []
+ for t, p, u in rows:
+ dt = datetime.fromtimestamp(t)
+ ts_list.append(dt.strftime('%H:%M' if hours <= 24 else '%m-%d'))
+ pcts.append(round(p, 2))
+ usdts.append(round(u, 2))
+
+ return {'timestamps': ts_list, 'pnl_pcts': pcts, 'pnl_usdts': usdts,
+ 'current_pct': pcts[-1] if pcts else 0, 'min_pct': min(pcts) if pcts else 0,
+ 'max_pct': max(pcts) if pcts else 0, 'avg_pct': sum(pcts)/len(pcts) if pcts else 0}
+
+@app.get('/')
+async def dashboard():
+ return HTMLResponse("""Trading Bot v0.32📈 P&L Performance (Live)
""")
if __name__ == '__main__':
import uvicorn