From 6df91d26e4e1d6d0c63df041869901e14f2c824d Mon Sep 17 00:00:00 2001 From: Marc Blatter Date: Wed, 29 Jul 2026 10:53:55 +0200 Subject: [PATCH] Dashboard: Restore v0.5.1 layout, update version to v0.6 --- src/web_dashboard.py | 453 +++++++++++++++++++++++++++++++------------ 1 file changed, 324 insertions(+), 129 deletions(-) diff --git a/src/web_dashboard.py b/src/web_dashboard.py index f897bff..6516272 100644 --- a/src/web_dashboard.py +++ b/src/web_dashboard.py @@ -1,141 +1,336 @@ -"""Trading Bot Dashboard v0.6 (Contrarian Mean Reversion) - Auto-load 1-Day chart on page load""" -import os, json, logging, sqlite3 -from datetime import datetime, timedelta -from flask import Flask, render_template_string, jsonify +#!/usr/bin/env python3 +"""Trading Bot Dashboard v0.6 (RSI + Bollinger Bands HYBRID) - Auto-load 1-Day chart on page load""" +import sqlite3 +from fastapi import FastAPI +from fastapi.responses import HTMLResponse from binance.client import Client -from dotenv import load_dotenv +from datetime import datetime +import json, os, time -load_dotenv() -API_KEY = os.getenv('BINANCE_API_KEY_LIVE') -API_SECRET = os.getenv('BINANCE_API_SECRET_LIVE') -client = Client(API_KEY, API_SECRET) +app = FastAPI() -app = Flask(__name__) +env = {} +with open('/home/marc/bot-deploy/.env') as f: + for line in f: + k, _, v = line.partition('=') + env[k.strip()] = v.strip() -@app.route('/') -def dashboard(): - html = """ - - - - Trading Bot v0.6 - - - -
-
-

🤖 Trading Bot v0.6

Contrarian Mean Reversion Strategy

-
- -
-

Portfolio

-
-
- -
-

Analytics

-
-
- -
-

Active Trades

- - -
SymbolQtyEntry PriceEntry Time
-
-
- - - - - """ - return render_template_string(html) +binance = Client(env.get('BINANCE_API_KEY_LIVE'), env.get('BINANCE_API_SECRET_LIVE')) +DB = '/home/marc/bot-deploy/pnl_charts.db' -@app.route('/api/state') -def api_state(): +def init_db(): + 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() + +@app.get('/api/state') +async def state(): try: - with open('/home/marc/bot-deploy/active_trades.json') as f: - trades_data = json.load(f) + 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} - return jsonify({ - 'active_trades': trades_data.get('active_trades', {}), - 'active_positions': trades_data.get('count', 0), - 'portfolio_value': trades_data.get('portfolio_value', 0), - 'pnl_pct': 0, # Fetched from DB - 'pnl_usdt': 0 - }) - except: - return jsonify({'error': 'No data'}), 404 - -@app.route('/api/pnl-history') -def api_pnl_history(): - try: - conn = sqlite3.connect('/home/marc/bot-deploy/pnl_charts.db') - rows = conn.execute('SELECT pp FROM history ORDER BY ts DESC LIMIT 1').fetchall() + prices = {'USDT': 1.0} + for p in ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT']: + try: + t = binance.get_ticker(symbol=p) + prices[p.replace('USDT', '')] = float(t['lastPrice']) + except: pass + + 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) + # Get latest P&L from database + try: + conn = sqlite3.connect('/home/marc/bot-deploy/pnl_charts.db') + row = conn.execute('SELECT pu, pp FROM history ORDER BY ts DESC LIMIT 1').fetchone() + conn.close() + if row: + pu, pp = row[0], row[1] + else: + pu, pp = 0.0, 0.0 + except: + pu, pp = 0.0, 0.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() - if rows: - return jsonify({'current_pct': rows[0][0], 'current_usdt': 0, 'entries': []}) + 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 = [], [], [] + seen_ts = set() + + for t, p, u in rows: + dt = datetime.fromtimestamp(t) - return jsonify({'current_pct': 0, 'current_usdt': 0, 'entries': []}) - except: - return jsonify({'error': 'No data'}), 404 + if hours <= 24: + ts = dt.strftime('%H:00') + else: + ts = dt.strftime('%d.%m.%y') + + if ts in seen_ts: + continue + + seen_ts.add(ts) + ts_list.append(ts) + 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, 'current_usdt': usdts[-1] if usdts else 0, + 'min_pct': min(pcts) if pcts else 0, 'min_usdt': min(usdts) if usdts else 0, + 'max_pct': max(pcts) if pcts else 0, 'max_usdt': max(usdts) if usdts else 0, + 'avg_pct': sum(pcts)/len(pcts) if pcts else 0, 'avg_usdt': sum(usdts)/len(usdts) if usdts else 0} + +@app.get('/') +async def dashboard(): + html = """ + + + + +Trading Bot v0.6 + + + + +
+ +
+

🤖 Trading Bot v0.6

P&L Analytics

+
● LOADING
+
+ +
+ + +
+ +
+
+
Portfolio
-
+
P&L
-
-
+
USDT
-
+
Trades
-
+
+ +
+

Holdings

+ +
+
+
+ +
+
+
📈 P&L Performance (Live)
+
+ + + +
+ +
+
+
Current
+- +- +
+
+
Min
+- +- +
+
+
Max
+- +- +
+
+
Avg
+- +- +
+
+
+
+ +
+ + + +""" + return HTMLResponse(content=html) if __name__ == '__main__': - app.run(host='0.0.0.0', port=7000, debug=False) + import uvicorn + uvicorn.run(app, host='0.0.0.0', port=7000)