diff --git a/common/metrics.py b/common/metrics.py index 0417478..7adcee1 100644 --- a/common/metrics.py +++ b/common/metrics.py @@ -40,4 +40,5 @@ def max_drawdown(equity: list[float]) -> float: def win_rate(trades: list[dict]) -> float: if not trades: return 0.0 - return sum(1 for t in trades if t.get("pnl", 0) > 0) / len(trades) + tp = sum(1 for t in trades if (t.get("pnl_net") or t.get("pnl_gross") or t.get("pnl", 0)) > 0) + return tp / len(trades) diff --git a/dashboard/server.py b/dashboard/server.py index d8bcf88..7e5ce41 100644 --- a/dashboard/server.py +++ b/dashboard/server.py @@ -175,7 +175,7 @@ async def get_paper_metrics_rest(): # Backtest endpoints # ═══════════════════════════════════════════════════════════ -@app.get("/api/backtests") +@app.get("/backtests") async def list_backtests(): """List all saved backtest results.""" results = [] @@ -195,7 +195,7 @@ async def list_backtests(): "sortino": data.get("sortino", 0), "pnl_pct": data.get("pnl_pct", 0), "max_dd": data.get("max_dd", 0), - "win_rate": data.get("win_rate", 0), + "win_rate": data.get("win_rate", 0) or recalc_win_rate(data.get("trades", [])) or 0, "total_trades": data.get("total_trades", 0), }) except (json.JSONDecodeError, IOError): @@ -203,7 +203,7 @@ async def list_backtests(): return JSONResponse(results) -@app.get("/api/backtest/{name}") +@app.get("/backtest/{name}") async def get_backtest(name: str): """Get full backtest result data.""" fpath = os.path.join(BACKTEST_DIR, f"{name}.json") @@ -214,6 +214,14 @@ async def get_backtest(name: str): + +def recalc_win_rate(trades): + """Fallback win rate when stored value is 0.""" + if not trades: + return 0.0 + wins = sum(1 for t in trades if (t.get("pnl_net") or t.get("pnl_gross") or t.get("pnl", 0)) > 0) + return round(wins / len(trades), 4) if trades else 0.0 + def recalc_equity_curve(equity_curve, trades, new_fee_rate, fee_model): """Rebuild equity curve with new fee rates, preserving gross PnL.""" if not equity_curve or not trades: @@ -241,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("/api/backtest/{name}/recalc") +@app.get("/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") @@ -302,12 +310,12 @@ async def recalc_backtest(name: str, fee_tier: int = 0, staking_tier: str = "non "sharpe": data.get("sharpe", 0), "sortino": data.get("sortino", 0), "max_dd": data.get("max_dd", 0), - "win_rate": data.get("win_rate", 0), + "win_rate": data.get("win_rate", 0) or recalc_win_rate(data.get("trades", [])) or 0, "num_periods": data.get("num_periods", 720), }) -@app.get("/api/backtests/historical") +@app.get("/backtests/historical") async def list_historical_backtests(): """List historical (real data) backtest results.""" results = [] @@ -329,7 +337,7 @@ async def list_historical_backtests(): "sortino": data.get("sortino", 0), "pnl_pct": data.get("pnl_pct", 0), "max_dd": data.get("max_dd", 0), - "win_rate": data.get("win_rate", 0), + "win_rate": data.get("win_rate", 0) or recalc_win_rate(data.get("trades", [])) or 0, "total_trades": data.get("total_trades", 0), "data_source": "Hyperliquid Mainnet", }) @@ -338,7 +346,7 @@ async def list_historical_backtests(): return JSONResponse(results) -@app.get("/api/backtest/historical/{name}") +@app.get("/backtest/historical/{name}") async def get_historical_backtest(name: str): """Get full historical backtest result.""" fpath = os.path.join(HISTORICAL_DIR, f"{name}.json") @@ -348,7 +356,7 @@ async def get_historical_backtest(name: str): return JSONResponse({"error": "not found"}, status_code=404) -@app.get("/api/backtest/{name}/csv") +@app.get("/backtest/{name}/csv") async def get_backtest_csv(name: str): """Download backtest trades as CSV.""" from fastapi.responses import Response @@ -371,7 +379,7 @@ async def get_backtest_csv(name: str): ) -@app.get("/api/risk") +@app.get("/risk") async def get_risk_metrics(): """Compute risk analytics from the latest paper metrics.""" paper = read_paper_metrics() diff --git a/dashboard/static/index.html b/dashboard/static/index.html index 406066f..e96a638 100644 --- a/dashboard/static/index.html +++ b/dashboard/static/index.html @@ -7,7 +7,7 @@
- -