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:
+35
-1
@@ -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
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
|
||||
Reference in New Issue
Block a user