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:
ramseshk
2026-08-05 04:51:58 +00:00
parent e6dd16908c
commit eb2dc32c53
97 changed files with 515 additions and 9 deletions
+15 -9
View File
@@ -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()