142 lines
5.7 KiB
Python
142 lines
5.7 KiB
Python
"""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
|
|
from binance.client import Client
|
|
from dotenv import load_dotenv
|
|
|
|
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 = Flask(__name__)
|
|
|
|
@app.route('/')
|
|
def dashboard():
|
|
html = """
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>Trading Bot v0.6</title>
|
|
<style>
|
|
body { font-family: Arial; background: #1e1e1e; color: #d0d0d0; margin: 0; padding: 20px; }
|
|
.header { margin-bottom: 30px; }
|
|
h1 { margin: 0; color: #00ff88; }
|
|
.container { max-width: 1200px; margin: 0 auto; }
|
|
.section { background: #2d2d2d; padding: 15px; margin: 15px 0; border-radius: 5px; }
|
|
.metric { display: inline-block; width: 23%; margin: 1%; background: #1e1e1e; padding: 12px; border-radius: 3px; border-left: 3px solid #00ff88; }
|
|
.metric-label { font-size: 11px; color: #888; }
|
|
.metric-value { font-size: 18px; font-weight: bold; color: #00ff88; }
|
|
button { background: #00ff88; color: #000; border: none; padding: 8px 15px; border-radius: 3px; cursor: pointer; font-weight: bold; }
|
|
button:hover { background: #00dd77; }
|
|
table { width: 100%; border-collapse: collapse; margin-top: 10px; }
|
|
th, td { padding: 8px; text-align: left; border-bottom: 1px solid #444; }
|
|
th { background: #333; color: #00ff88; }
|
|
.pos { color: #00ff88; }
|
|
.neg { color: #ff4444; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="header">
|
|
<div><h1>🤖 Trading Bot v0.6</h1><p>Contrarian Mean Reversion Strategy</p></div>
|
|
</div>
|
|
|
|
<div class="section" id="portfolio">
|
|
<h2>Portfolio</h2>
|
|
<div id="metrics"></div>
|
|
</div>
|
|
|
|
<div class="section" id="analytics">
|
|
<h2>Analytics</h2>
|
|
<div id="pnl-chart"></div>
|
|
</div>
|
|
|
|
<div class="section" id="trades">
|
|
<h2>Active Trades</h2>
|
|
<table id="trades-table">
|
|
<tr><th>Symbol</th><th>Qty</th><th>Entry Price</th><th>Entry Time</th></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
async function loadData() {
|
|
const state = await fetch('/api/state').then(r => r.json());
|
|
const pnl = await fetch('/api/pnl-history?hours=24').then(r => r.json());
|
|
|
|
document.getElementById('metrics').innerHTML = `
|
|
<div class="metric">
|
|
<div class="metric-label">Portfolio Value</div>
|
|
<div class="metric-value">$${state.portfolio_value.toFixed(2)}</div>
|
|
</div>
|
|
<div class="metric">
|
|
<div class="metric-label">P&L</div>
|
|
<div class="metric-value ${pnl.current_pct >= 0 ? 'pos' : 'neg'}">${pnl.current_pct > 0 ? '+' : ''}${pnl.current_pct.toFixed(2)}%</div>
|
|
</div>
|
|
<div class="metric">
|
|
<div class="metric-label">Active Trades</div>
|
|
<div class="metric-value">${state.active_positions}</div>
|
|
</div>
|
|
<div class="metric">
|
|
<div class="metric-label">Strategy</div>
|
|
<div class="metric-value" style="font-size: 12px;">Contrarian</div>
|
|
</div>
|
|
`;
|
|
|
|
let tradesHtml = '';
|
|
for (const [symbol, trade] of Object.entries(state.active_trades || {})) {
|
|
tradesHtml += `
|
|
<tr>
|
|
<td>${symbol}</td>
|
|
<td>${trade.qty.toFixed(8)}</td>
|
|
<td>$${trade.entry_price.toFixed(2)}</td>
|
|
<td>${new Date(trade.entry_time).toLocaleString()}</td>
|
|
</tr>
|
|
`;
|
|
}
|
|
document.getElementById('trades-table').innerHTML += tradesHtml;
|
|
}
|
|
|
|
setInterval(loadData, 5000);
|
|
loadData();
|
|
</script>
|
|
</body>
|
|
</html>
|
|
"""
|
|
return render_template_string(html)
|
|
|
|
@app.route('/api/state')
|
|
def api_state():
|
|
try:
|
|
with open('/home/marc/bot-deploy/active_trades.json') as f:
|
|
trades_data = json.load(f)
|
|
|
|
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()
|
|
conn.close()
|
|
|
|
if rows:
|
|
return jsonify({'current_pct': rows[0][0], 'current_usdt': 0, 'entries': []})
|
|
|
|
return jsonify({'current_pct': 0, 'current_usdt': 0, 'entries': []})
|
|
except:
|
|
return jsonify({'error': 'No data'}), 404
|
|
|
|
if __name__ == '__main__':
|
|
app.run(host='0.0.0.0', port=7000, debug=False)
|