Bot auto-update: src/web_dashboard.py

This commit is contained in:
Marc Blatter 2026-07-04 23:10:01 +02:00
parent ec07f47438
commit d60b1cb839
1 changed files with 67 additions and 44 deletions

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from fastapi import FastAPI, Response from fastapi import FastAPI, Response
from binance.client import Client from binance.client import Client
import json, os import json, os, time
from datetime import datetime from datetime import datetime
app = FastAPI() app = FastAPI()
@ -14,8 +14,17 @@ with open('/home/marc/bot-deploy/.env') as f:
binance = Client(env.get('BINANCE_API_KEY_LIVE'), env.get('BINANCE_API_SECRET_LIVE')) binance = Client(env.get('BINANCE_API_KEY_LIVE'), env.get('BINANCE_API_SECRET_LIVE'))
# CACHE for prices (update every 5 seconds)
price_cache = {'prices': {}, 'timestamp': 0}
def get_live_prices(): def get_live_prices():
"""Get LIVE prices from Binance API""" """Get LIVE prices from Binance API with 5-second cache"""
global price_cache
# Return cached if less than 5 seconds old
if time.time() - price_cache['timestamp'] < 5:
return price_cache['prices']
prices = {'USDT': 1.0} prices = {'USDT': 1.0}
pairs = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT'] pairs = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT']
@ -24,24 +33,31 @@ def get_live_prices():
ticker = binance.get_ticker(symbol=pair) ticker = binance.get_ticker(symbol=pair)
asset = pair.replace('USDT', '') asset = pair.replace('USDT', '')
prices[asset] = float(ticker['lastPrice']) prices[asset] = float(ticker['lastPrice'])
except: except Exception as e:
pass pass
# Update cache
price_cache['prices'] = prices
price_cache['timestamp'] = time.time()
return prices return prices
def load_bot_state(): def load_bot_state():
"""Load bot state from file""" """Load bot state from file"""
state_file = '/home/marc/bot-deploy/trades.json' state_file = '/home/marc/bot-deploy/trades.json'
if os.path.exists(state_file): if os.path.exists(state_file):
try:
with open(state_file) as f: with open(state_file) as f:
return json.load(f) return json.load(f)
except:
pass
return {'current': {}, 'completed': [], 'balance': {}} return {'current': {}, 'completed': [], 'balance': {}}
@app.get('/api/state') @app.get('/api/state')
async def get_state(): async def get_state():
"""Return complete bot state with LIVE prices from Binance""" """Return complete bot state with LIVE prices"""
try: try:
# Get balance from Binance (LIVE) # Get balance from Binance ONCE (not per asset)
account = binance.get_account() account = binance.get_account()
balance = {} balance = {}
@ -51,42 +67,52 @@ async def get_state():
locked = float(asset_data['locked']) locked = float(asset_data['locked'])
total = free + locked total = free + locked
if total > 0: # Only include meaningful balances
if total > 0.00001:
balance[asset] = { balance[asset] = {
'free': free, 'free': free,
'locked': locked, 'locked': locked,
'total': total 'total': total
} }
# Get LIVE prices from Binance (CRITICAL FIX) # Get cached prices (5-second update)
prices = get_live_prices() prices = get_live_prices()
# Calculate portfolio value with LIVE prices # Calculate portfolio value with LIVE prices
# ONLY for known trading pairs (exclude junk tokens)
portfolio_value = 0 portfolio_value = 0
for asset, data in balance.items(): 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) price = prices.get(asset, 0)
portfolio_value += data['total'] * price portfolio_value += data['total'] * price
# Get USDT free specifically
usdt_free = balance.get('USDT', {}).get('free', 0)
# Load trade state # Load trade state
trades = load_bot_state() trades = load_bot_state()
return { return {
'balance': balance, 'balance': balance,
'portfolio_value': portfolio_value, 'portfolio_value': round(portfolio_value, 2),
'usdt_free': round(usdt_free, 2),
'current_trades': trades.get('current', {}), 'current_trades': trades.get('current', {}),
'completed_trades': trades.get('completed', []), 'completed_trades': trades.get('completed', []),
'prices': prices, 'prices': prices,
'timestamp': datetime.now().isoformat(), 'timestamp': datetime.now().isoformat(),
'note': 'Portfolio calculated with LIVE Binance prices' 'note': 'Portfolio calculated with LIVE Binance prices (tracked assets only, excludes junk tokens)'
} }
except Exception as e: except Exception as e:
return {'error': str(e), 'portfolio_value': 0} return {'error': str(e), 'portfolio_value': 0, 'usdt_free': 0}
@app.get('/') @app.get('/')
async def root(): async def root():
state = await get_state() state = await get_state()
portfolio_val = state.get('portfolio_value', 0) portfolio_val = state.get('portfolio_value', 0)
usdt_free = state.get('balance', {}).get('USDT', {}).get('free', 0) usdt_free = state.get('usdt_free', 0)
trades_count = len(state.get('current_trades', {})) trades_count = len(state.get('current_trades', {}))
prices = state.get('prices', {}) prices = state.get('prices', {})
@ -156,44 +182,41 @@ tr:nth-child(even) {{ background: #0d0d0d; }}
html += '''</table> html += '''</table>
<h2>📈 OPEN POSITIONS</h2> <h2>💰 BALANCE BREAKDOWN</h2>
<table> <table>
<tr> <tr>
<th>Pair</th> <th>Asset</th>
<th>Qty</th> <th>Free</th>
<th>Entry Price</th> <th>Locked</th>
<th>Current Price</th> <th>Total</th>
<th>Position Value</th> <th>Value</th>
<th>SL Level</th>
<th>TP Level</th>
</tr>''' </tr>'''
current_trades = state.get('current_trades', {}) tracked = ['BTC', 'ETH', 'SOL', 'BNB', 'XRP', 'USDT', 'USDC']
for pair, trade in current_trades.items(): balance = state.get('balance', {})
qty = trade.get('qty', 0)
entry = trade.get('buy_price', 0)
asset = pair.replace('USDT', '')
current_price = prices.get(asset, entry)
value = qty * current_price
sl = entry * 0.975
tp = entry * 1.03
for asset in tracked:
if asset in balance:
data = balance[asset]
price = prices.get(asset, 0)
value = data['total'] * price
html += f'''<tr> html += f'''<tr>
<td>{pair}</td> <td>{asset}</td>
<td>{qty:.6f}</td> <td>{data['free']:.6f}</td>
<td>${entry:.4f}</td> <td>{data['locked']:.6f}</td>
<td>${current_price:.4f}</td> <td>{data['total']:.6f}</td>
<td>${value:.2f}</td> <td>${value:.2f}</td>
<td>${sl:.4f}</td>
<td>${tp:.4f}</td>
</tr>''' </tr>'''
html += '''</table> html += '''</table>
<div class="note"> <div class="note">
<strong>FIXED:</strong> Portfolio now uses LIVE Binance prices (not hardcoded)<br> <strong>Portfolio:</strong> Calculated from LIVE Binance prices (tracked assets only)<br>
All prices updated every API call<br> <strong>Cache:</strong> Prices cached for 5 seconds (fast response)<br>
Matches Binance Spot portfolio exactly<br> <strong>Accuracy:</strong> Real Binance Spot balance<br>
<br>
Tracked assets: BTC, ETH, SOL, BNB, XRP, USDT, USDC<br>
(Excludes junk/dust tokens)<br>
</div> </div>
</div> </div>