|
| 1 | +from __future__ import annotations |
| 2 | + |
| 3 | +from flask import Flask, jsonify, render_template_string |
| 4 | + |
| 5 | +from copybot import CopyBot |
| 6 | + |
| 7 | +HTML = """ |
| 8 | +<!doctype html> |
| 9 | +<html> |
| 10 | +<head> |
| 11 | + <meta charset="utf-8" /> |
| 12 | + <title>Polymarket Copy Bot</title> |
| 13 | + <style> |
| 14 | + body { font-family: Arial, sans-serif; margin: 24px; background: #0b1020; color: #e4e8f3; } |
| 15 | + .grid { display: grid; grid-template-columns: repeat(4, minmax(130px, 1fr)); gap: 12px; } |
| 16 | + .card { background: #151d36; border-radius: 10px; padding: 12px; } |
| 17 | + button { padding: 10px 16px; border-radius: 8px; border: none; cursor: pointer; } |
| 18 | + .ok { background: #1f9d55; color: white; } |
| 19 | + .warn { background: #d64545; color: white; } |
| 20 | + table { width: 100%; border-collapse: collapse; margin-top: 12px; } |
| 21 | + th, td { text-align: left; border-bottom: 1px solid #2a355d; padding: 8px; font-size: 13px; } |
| 22 | + </style> |
| 23 | +</head> |
| 24 | +<body> |
| 25 | + <h1>Polymarket Copy Bot Dashboard</h1> |
| 26 | + <p>Status: <strong id="status">stopped</strong></p> |
| 27 | + <button class="ok" onclick="startBot()">Start Live</button> |
| 28 | + <button class="warn" onclick="stopBot()">Stop</button> |
| 29 | +
|
| 30 | + <h2>Stats</h2> |
| 31 | + <div class="grid"> |
| 32 | + <div class="card">PnL: <span id="pnl">0</span></div> |
| 33 | + <div class="card">Win rate: <span id="winRate">0</span>%</div> |
| 34 | + <div class="card">Executed: <span id="executed">0</span></div> |
| 35 | + <div class="card">Winners: <span id="winners">0</span></div> |
| 36 | + <div class="card">Losers: <span id="losers">0</span></div> |
| 37 | + <div class="card">Failed: <span id="failed">0</span></div> |
| 38 | + <div class="card">Skipped: <span id="skipped">0</span></div> |
| 39 | + </div> |
| 40 | +
|
| 41 | + <h2>Trade Log</h2> |
| 42 | + <table> |
| 43 | + <thead><tr><th>Time</th><th>Trader</th><th>Market</th><th>Outcome</th><th>Action</th><th>Amount</th><th>Status</th><th>PnL</th></tr></thead> |
| 44 | + <tbody id="trades"></tbody> |
| 45 | + </table> |
| 46 | +
|
| 47 | +<script> |
| 48 | +async function loadStats() { |
| 49 | + const r = await fetch('/api/stats'); |
| 50 | + const data = await r.json(); |
| 51 | + document.getElementById('status').innerText = data.running ? 'running' : 'stopped'; |
| 52 | + document.getElementById('pnl').innerText = data.total_pnl_usd; |
| 53 | + document.getElementById('winRate').innerText = data.win_rate; |
| 54 | + document.getElementById('executed').innerText = data.trades_executed; |
| 55 | + document.getElementById('winners').innerText = data.winners; |
| 56 | + document.getElementById('losers').innerText = data.losers; |
| 57 | + document.getElementById('failed').innerText = data.failed; |
| 58 | + document.getElementById('skipped').innerText = data.skipped; |
| 59 | +
|
| 60 | + const rows = data.recent_trades.map(t => ` |
| 61 | + <tr> |
| 62 | + <td>${t.timestamp}</td> |
| 63 | + <td>${t.source_trader}</td> |
| 64 | + <td>${t.market_slug}</td> |
| 65 | + <td>${t.outcome}</td> |
| 66 | + <td>${t.action}</td> |
| 67 | + <td>${t.copied_amount_usd}</td> |
| 68 | + <td>${t.status}</td> |
| 69 | + <td>${t.pnl_usd}</td> |
| 70 | + </tr> |
| 71 | + `).join(''); |
| 72 | + document.getElementById('trades').innerHTML = rows; |
| 73 | +} |
| 74 | +
|
| 75 | +async function startBot() { |
| 76 | + await fetch('/api/start', { method: 'POST' }); |
| 77 | + await loadStats(); |
| 78 | +} |
| 79 | +
|
| 80 | +async function stopBot() { |
| 81 | + await fetch('/api/stop', { method: 'POST' }); |
| 82 | + await loadStats(); |
| 83 | +} |
| 84 | +
|
| 85 | +setInterval(loadStats, 5000); |
| 86 | +loadStats(); |
| 87 | +</script> |
| 88 | +</body> |
| 89 | +</html> |
| 90 | +""" |
| 91 | + |
| 92 | + |
| 93 | +def create_app() -> Flask: |
| 94 | + bot = CopyBot() |
| 95 | + app = Flask(__name__) |
| 96 | + |
| 97 | + @app.get("/") |
| 98 | + def home() -> str: |
| 99 | + return render_template_string(HTML) |
| 100 | + |
| 101 | + @app.get("/api/stats") |
| 102 | + def stats(): |
| 103 | + return jsonify(bot.stats()) |
| 104 | + |
| 105 | + @app.post("/api/start") |
| 106 | + def start(): |
| 107 | + started = bot.start() |
| 108 | + return jsonify({"started": started, "running": True}) |
| 109 | + |
| 110 | + @app.post("/api/stop") |
| 111 | + def stop(): |
| 112 | + stopped = bot.stop() |
| 113 | + return jsonify({"stopped": stopped, "running": False}) |
| 114 | + |
| 115 | + return app |
| 116 | + |
| 117 | + |
| 118 | +if __name__ == "__main__": |
| 119 | + create_app().run(host="127.0.0.1", port=5055, debug=False) |
0 commit comments