#!/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) # 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 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
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}
Live Prices
''' for asset, price in prices.items(): html += f'''''' html += '''
Asset Price
{asset} ${price:.2f}
Holdings
''' 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'''''' html += '''
Asset Free Total Value
{asset} {data['free']:.4f} {data['total']:.4f} ${value:.2f}
''' 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)