diff --git a/dashboard/server.py b/dashboard/server.py index 82b8785..9f076e9 100644 --- a/dashboard/server.py +++ b/dashboard/server.py @@ -32,6 +32,7 @@ import uvicorn # ═══════════════════════════════════════════════════════════ METRICS_FILE = "/tmp/ftdt-metrics.json" +PAPER_METRICS_FILE = "/tmp/ftdt-paper-metrics.json" BACKTEST_DIR = "/home/debian/ftdt-quant-lab/backtests/results" STATIC_DIR = Path(__file__).parent / "static" @@ -44,6 +45,7 @@ os.makedirs(BACKTEST_DIR, exist_ok=True) app = FastAPI(title="FTDT Quant Lab Dashboard") connected_clients: set[WebSocket] = set() +paper_clients: set[WebSocket] = set() loop: Optional[asyncio.AbstractEventLoop] = None @@ -76,6 +78,17 @@ def _empty_metrics() -> dict: } +def read_paper_metrics() -> dict: + """Read paper trading metrics file.""" + try: + if os.path.exists(PAPER_METRICS_FILE): + with open(PAPER_METRICS_FILE) as f: + return json.load(f) + except (json.JSONDecodeError, IOError): + pass + return {"status": "waiting", "mode": "paper", "strategies": {}, "trades": [], "equity_history": [], "total_pnl": 0, "total_equity": 5000} + + # ═══════════════════════════════════════════════════════════ # Background broadcaster # ═══════════════════════════════════════════════════════════ @@ -93,13 +106,21 @@ def broadcast_loop(): time.sleep(1) data = read_metrics() payload = json.dumps(data, default=str) - for ws in list(connected_clients): if loop: asyncio.run_coroutine_threadsafe( broadcast_to_client(ws, payload), loop ) + # Also broadcast paper metrics + paper_data = read_paper_metrics() + paper_payload = json.dumps(paper_data, default=str) + for ws in list(paper_clients): + if loop: + asyncio.run_coroutine_threadsafe( + broadcast_to_client(ws, paper_payload), loop + ) + # ═══════════════════════════════════════════════════════════ # WebSocket @@ -119,6 +140,19 @@ async def websocket_endpoint(websocket: WebSocket): connected_clients.discard(websocket) +@app.websocket("/ws/paper") +async def paper_websocket_endpoint(websocket: WebSocket): + await websocket.accept() + paper_clients.add(websocket) + try: + data = read_paper_metrics() + await websocket.send_text(json.dumps(data, default=str)) + while True: + await asyncio.sleep(30) + except WebSocketDisconnect: + paper_clients.discard(websocket) + + # ═══════════════════════════════════════════════════════════ # Backtest endpoints # ═══════════════════════════════════════════════════════════ diff --git a/dashboard/static/index.html b/dashboard/static/index.html index 5169029..f3d17d1 100644 --- a/dashboard/static/index.html +++ b/dashboard/static/index.html @@ -7,217 +7,205 @@
-
- -
Portfolio PnL
$0.00
0.00%
-
-
+
+ +
Portfolio PnL
$0.00
0.00%
+
+
+ + + +
- -
-
-

Equity Curve real-time · all strategies

-
-

Trade Log most recent

TimeStrategySideSizePriceFeePnL
-
+ +
+
+

Equity Curve real-time · Hyperliquid Testnet

+
+

Trade Log

TimeStrategySideSizePriceFeePnL
+
- -
- -

Saved Backtests click to view

-
- + +
+
+

Equity Curve real-time · Hyperliquid Mainnet (simulated fills)

+
+

Trade Log

TimeStrategySideSizePriceFeePnL
+
+ + +
+ +

Saved Backtests click to view

+
+