Comprehensive fix: live node resilience + CSS contrast + win_rate + equity curves
- Mainnet API fallback when testnet unavailable (prices, orderbook, instruments) - Bypassed broken SDK instrument loading, uses raw mainnet meta API - Dynamic BTC/ETH perp ID lookup (handles "-USD-PERP" suffix changes) - Strategy-level equity tracking for per-strategy detail charts - Win rate fixed: checks pnl_net/pnl_gross not just pnl field - CSS contrast improved: --tx #6b6b7b→#9e9eae, borders/highlights brightened - Equity curve recalculated on fee tier change (chart adjusts visually) - Added Open Positions & Orders panel placeholder
This commit is contained in:
+18
-10
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user