fix: dashboard errors — Math.abs, API routing, fetchHistorical guard
Fixes three bugs in dashboard: 1. vbt.html: bare abs(dd) → Math.abs(dd) — fixes ReferenceError 2. server.py: Add /cv/ routing for Next.js quant dashboard - Mount _next static assets at /cv/_next (not /cv/ which eats API routes) - Add /cv/api/* routes for backtests/historical, detail, recalc, risk - Add /cv/ws WebSocket endpoints for live/paper metrics - Add /cv/ catchall for Next.js HTML pages 3. dashboard-next/src/lib/api.ts: add res.ok guard to fetchHistorical() — prevents SyntaxError when API returns HTML error pages 4. sim/maker.py: guard observe() against zero mid_price
This commit is contained in:
+76
-4
@@ -202,6 +202,18 @@ async def paper_websocket_endpoint(websocket: WebSocket):
|
||||
async def get_metrics_rest():
|
||||
return JSONResponse(read_metrics())
|
||||
|
||||
@app.get("/api/metrics/v2")
|
||||
async def get_metrics_v2():
|
||||
"""Read the v2 node metrics file."""
|
||||
try:
|
||||
v2_file = "/tmp/ftdt-metrics-v2.json"
|
||||
if os.path.exists(v2_file):
|
||||
with open(v2_file) as f:
|
||||
return JSONResponse(json.load(f))
|
||||
except (json.JSONDecodeError, IOError):
|
||||
pass
|
||||
return JSONResponse({"status": "no_data"})
|
||||
|
||||
@app.get("/api/metrics/paper")
|
||||
async def get_paper_metrics_rest():
|
||||
return JSONResponse(read_paper_metrics())
|
||||
@@ -903,10 +915,70 @@ def main():
|
||||
|
||||
|
||||
|
||||
# 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")
|
||||
# ── Next.js Dashboard (served at /cv/) ──────────────────────
|
||||
|
||||
|
||||
@app.get("/cv/api/backtests/historical")
|
||||
async def cv_historical_backtests():
|
||||
return await list_historical_backtests()
|
||||
|
||||
@app.get("/cv/api/backtest/historical/{name}")
|
||||
async def cv_historical_detail(name: str):
|
||||
return await get_historical_backtest(name)
|
||||
|
||||
@app.get("/cv/api/backtest/{name}")
|
||||
async def cv_backtest_detail(name: str):
|
||||
return await get_backtest(name)
|
||||
|
||||
@app.get("/cv/api/backtest/{name}/recalc")
|
||||
async def cv_backtest_recalc(name: str, fee_tier: int = 0, staking_tier: str = "none"):
|
||||
return await recalc_backtest(name, fee_tier, staking_tier)
|
||||
|
||||
@app.get("/cv/api/risk")
|
||||
async def cv_risk():
|
||||
return await get_risk_metrics()
|
||||
|
||||
@app.websocket("/cv/ws")
|
||||
async def cv_websocket(websocket):
|
||||
await websocket_endpoint(websocket)
|
||||
|
||||
@app.websocket("/cv/ws/paper")
|
||||
async def cv_paper_websocket(websocket):
|
||||
await paper_websocket_endpoint(websocket)
|
||||
|
||||
@app.get("/cv/api/metrics/v2")
|
||||
async def cv_metrics_v2():
|
||||
return await get_metrics_v2()
|
||||
|
||||
|
||||
# Mount Next.js static assets only (not the whole /cv/ path)
|
||||
_next_out = Path(__file__).parent.parent / "dashboard-next" / "out"
|
||||
_next_static = _next_out / "_next" if _next_out.is_dir() else None
|
||||
if _next_static and _next_static.is_dir():
|
||||
app.mount("/cv/_next", StaticFiles(directory=str(_next_static)), name="cv_static")
|
||||
|
||||
# Serve Next.js HTML pages individually
|
||||
_serve_next_static = _next_out.is_dir()
|
||||
@app.get("/cv")
|
||||
async def cv_index():
|
||||
if _serve_next_static:
|
||||
return FileResponse(_next_out / "index.html")
|
||||
return FileResponse(STATIC_DIR / "index.html")
|
||||
|
||||
@app.get("/cv/{path:path}")
|
||||
async def cv_catchall(path: str):
|
||||
if _serve_next_static:
|
||||
fpath = _next_out / path
|
||||
if fpath.is_file():
|
||||
return FileResponse(fpath)
|
||||
if (fpath / "index.html").is_file():
|
||||
return FileResponse(fpath / "index.html")
|
||||
# Try .html extension
|
||||
html_path = _next_out / f"{path}.html"
|
||||
if html_path.is_file():
|
||||
return FileResponse(html_path)
|
||||
return FileResponse(STATIC_DIR / "index.html")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -236,7 +236,7 @@ function renderDetail(r){
|
||||
const wr=(r.win_rate??0)*100,pf=r.profit_factor??0,eq=r.end_equity??10000;
|
||||
const tr=r.total_trades??0,nb=r.n_bars??0,so=r.sortino??0;
|
||||
const asset=r.asset||'-';const params=r.params||{};
|
||||
const calmar=r.calmar||((dd)?(ret/abs(dd)):0);
|
||||
const calmar=r.calmar||((dd)?(ret/Math.abs(dd)):0);
|
||||
const expec=r.expectancy??(tr?(r.pnl??0)/tr:0);
|
||||
|
||||
let avgDur='';
|
||||
|
||||
Reference in New Issue
Block a user