Bot auto-update: src/web_dashboard.py
This commit is contained in:
parent
ec07f47438
commit
d60b1cb839
|
|
@ -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):
|
||||||
with open(state_file) as f:
|
try:
|
||||||
return json.load(f)
|
with open(state_file) as 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']
|
||||||
price = prices.get(asset, 0)
|
|
||||||
portfolio_value += data['total'] * price
|
for asset in tracked_assets:
|
||||||
|
if asset in balance:
|
||||||
|
data = balance[asset]
|
||||||
|
price = prices.get(asset, 0)
|
||||||
|
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
|
|
||||||
|
|
||||||
html += f'''<tr>
|
for asset in tracked:
|
||||||
<td>{pair}</td>
|
if asset in balance:
|
||||||
<td>{qty:.6f}</td>
|
data = balance[asset]
|
||||||
<td>${entry:.4f}</td>
|
price = prices.get(asset, 0)
|
||||||
<td>${current_price:.4f}</td>
|
value = data['total'] * price
|
||||||
|
html += f'''<tr>
|
||||||
|
<td>{asset}</td>
|
||||||
|
<td>{data['free']:.6f}</td>
|
||||||
|
<td>{data['locked']:.6f}</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>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue