FTDT Dashboard: Next.js shadcn SOTA UI
- Next.js 16 + React + TypeScript static export - shadcn/ui components: Card, Tabs, Badge, Sheet, Collapsible, Table - Claude Blu 2 dark theme via oklch CSS variables - lightweight-charts v4 for equity curve rendering - Framer Motion for layout animations - 3 tabs: Live Testnet, Paper Mainnet (00K), Historical - Full-page strategy detail with equity chart + trade history - Fee tier selector (7 official Hyperliquid tiers + staking) - API routes prefixed with /api/ for clean Caddy proxying - _next/ mount for Next.js static assets - WebSocket data flowing for live metrics and paper trader
This commit is contained in:
+15
-9
@@ -164,18 +164,18 @@ async def paper_websocket_endpoint(websocket: WebSocket):
|
||||
# REST metrics endpoints — polled by Next.js dashboard
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
|
||||
@app.get("/metrics")
|
||||
@app.get("/api/metrics")
|
||||
async def get_metrics_rest():
|
||||
return JSONResponse(read_metrics())
|
||||
|
||||
@app.get("/metrics/paper")
|
||||
@app.get("/api/metrics/paper")
|
||||
async def get_paper_metrics_rest():
|
||||
return JSONResponse(read_paper_metrics())
|
||||
|
||||
# Backtest endpoints
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
|
||||
@app.get("/backtests")
|
||||
@app.get("/api/backtests")
|
||||
async def list_backtests():
|
||||
"""List all saved backtest results."""
|
||||
results = []
|
||||
@@ -203,7 +203,7 @@ async def list_backtests():
|
||||
return JSONResponse(results)
|
||||
|
||||
|
||||
@app.get("/backtest/{name}")
|
||||
@app.get("/api/backtest/{name}")
|
||||
async def get_backtest(name: str):
|
||||
"""Get full backtest result data."""
|
||||
fpath = os.path.join(BACKTEST_DIR, f"{name}.json")
|
||||
@@ -249,7 +249,7 @@ def recalc_equity_curve(equity_curve, trades, new_fee_rate, fee_model):
|
||||
new_curve.append({"t": pt_time, "v": round(pt.get("v", 0) + cum, 6)})
|
||||
return new_curve
|
||||
|
||||
@app.get("/backtest/{name}/recalc")
|
||||
@app.get("/api/backtest/{name}/recalc")
|
||||
async def recalc_backtest(name: str, fee_tier: int = 0, staking_tier: str = "none"):
|
||||
"""Recalculate backtest PnL with different fee tier."""
|
||||
fpath = os.path.join(BACKTEST_DIR, f"{name}.json")
|
||||
@@ -315,7 +315,7 @@ async def recalc_backtest(name: str, fee_tier: int = 0, staking_tier: str = "non
|
||||
})
|
||||
|
||||
|
||||
@app.get("/backtests/historical")
|
||||
@app.get("/api/backtests/historical")
|
||||
async def list_historical_backtests():
|
||||
"""List historical (real data) backtest results."""
|
||||
results = []
|
||||
@@ -346,7 +346,7 @@ async def list_historical_backtests():
|
||||
return JSONResponse(results)
|
||||
|
||||
|
||||
@app.get("/backtest/historical/{name}")
|
||||
@app.get("/api/backtest/historical/{name}")
|
||||
async def get_historical_backtest(name: str):
|
||||
"""Get full historical backtest result."""
|
||||
fpath = os.path.join(HISTORICAL_DIR, f"{name}.json")
|
||||
@@ -356,7 +356,7 @@ async def get_historical_backtest(name: str):
|
||||
return JSONResponse({"error": "not found"}, status_code=404)
|
||||
|
||||
|
||||
@app.get("/backtest/{name}/csv")
|
||||
@app.get("/api/backtest/{name}/csv")
|
||||
async def get_backtest_csv(name: str):
|
||||
"""Download backtest trades as CSV."""
|
||||
from fastapi.responses import Response
|
||||
@@ -379,7 +379,7 @@ async def get_backtest_csv(name: str):
|
||||
)
|
||||
|
||||
|
||||
@app.get("/risk")
|
||||
@app.get("/api/risk")
|
||||
async def get_risk_metrics():
|
||||
"""Compute risk analytics from the latest paper metrics."""
|
||||
paper = read_paper_metrics()
|
||||
@@ -460,5 +460,11 @@ def main():
|
||||
uvicorn.run(app, host=args.host, port=args.port, log_level="warning")
|
||||
|
||||
|
||||
|
||||
# Serve Next.js assets at /_next/static/
|
||||
_next_dir = Path(__file__).parent / "static" / "_next"
|
||||
if _next_dir.is_dir():
|
||||
app.mount("/_next", StaticFiles(directory=str(_next_dir)), name="next_assets")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user