feat: VBT trade log — show asset, entry/exit prices, Hyperliquid fees

Backend (vbt_runner.py):
  - Add asset (BTC/ETH) to each trade record
  - Compute per-trade fee using HL taker rate (0.05%)
    entry_fee = size * entry_px * fee_rate
    exit_fee = size * exit_px * fee_rate
  - Add pnl_gross (before fees) and pnl_net (after fees)
  - Add fee_rate field for transparency

Server (server.py):
  - Normalize old backtest trades: add missing asset, fee,
    pnl_net, pnl_gross fields
  - Holyliquid default fee rate: 0.05% taker

Frontend (vbt.html):
  - Trade log table now shows:
    Time | Side + Asset | Size | Entry | Exit | Fee | PnL (net) | Duration
  - Asset shown as inline badge in Side column
  - Fee column with explicit USD amount
  - PnL now explicitly labeled 'net' (after fees)
  - Fallback to old 'pnl' field for legacy backtest files
This commit is contained in:
ramseshk
2026-08-07 15:35:33 +08:00
parent 9ee13a45bb
commit 9d817ac2fa
3 changed files with 60 additions and 8 deletions
+23
View File
@@ -576,6 +576,29 @@ def _normalize_vbt_fields(data: dict) -> dict:
wins = sum(1 for t in trades if (t.get("pnl_net", t.get("pnl_gross", t.get("pnl", 0))) or 0) > 0)
out["win_rate"] = round(wins / len(trades), 3)
# Normalize trade records: add asset, fee, pnl_net/pnl_gross for old files
trades = out.get("trades", [])
if trades:
asset = _infer_asset(out.get("strategy", ""), "")
hl_fee = 0.0005 # default Hyperliquid taker rate
for t in trades:
if not t.get("asset"):
t["asset"] = asset
if not t.get("fee"):
entry_px = float(t.get("entry_px", 0))
exit_px = float(t.get("exit_px", 0))
size = float(t.get("size", 0))
notional_entry = size * entry_px
notional_exit = size * exit_px
t["fee"] = round((notional_entry + notional_exit) * hl_fee, 6)
t["fee_rate"] = f"{hl_fee*100:.3f}%"
if not t.get("pnl_net"):
t["pnl_net"] = t.get("pnl", 0)
if not t.get("pnl_gross"):
pnl_net = float(t.get("pnl_net", t.get("pnl", 0)))
fee = float(t.get("fee", 0))
t["pnl_gross"] = round(pnl_net + fee, 4)
return out