Paper trading dashboard — 7 strategies on Hyperliquid MAINNET data

New paper trading engine (live/paper_trader.py):
- Pulls real mainnet prices, orderbooks, funding rates every 2s
- Runs all 7 strategies in simulation without placing orders
- Simulates fills at market with realistic taker fees (0.05%) and slip (1bp)
- Avellaneda-Stoikov: simulates spread capture with 15%/tick fill probability
- Tracks virtual positions and PnL per strategy
- $5,000 capital ($1,000 per strategy, $1,000 reserve)
- Writes to /tmp/ftdt-paper-metrics.json

Dashboard updated with 3 tabs:
- Live Trading (Testnet) — real orders on testnet
- Paper Trading (Mainnet) — simulated fills on real mainnet data
- Backtesting — 30-day simulated results

Server.py: added /ws/paper WebSocket endpoint, paper_clients set,
paper metrics reader and broadcast loop.
This commit is contained in:
ramseshk
2026-08-04 04:18:52 +00:00
parent 4d5ddc5f18
commit f26892f8b2
3 changed files with 663 additions and 183 deletions
+35 -1
View File
@@ -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
# ═══════════════════════════════════════════════════════════