From 526a40b01ea72fc43fac664d3a48c5e7c4a34728 Mon Sep 17 00:00:00 2001 From: ramseshk Date: Tue, 4 Aug 2026 09:12:07 +0000 Subject: [PATCH] =?UTF-8?q?Rename=20VIP=E2=86=92Tier,=20recalculate=20equi?= =?UTF-8?q?ty=20curve=20on=20fee=20tier=20change?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Renamed all "VIP N" to "Tier N" in config, server, and HTML dropdowns. Added recalc_equity_curve() that rebuilds the equity curve with new fee rates. Previously the equity curve was passed through unchanged when switching tiers, so the chart visually stayed identical even though PnL numbers changed. Now each tier produces its own curve. Example OBI historical: Tier 0 equity ends at 141.5, Tier 6 at 149.7 — the chart visibly shifts up as fees drop from 4.5bp to 2.4bp taker. --- config/fee_tiers.py | 28 ++++++++++++++-------------- dashboard/server.py | 35 ++++++++++++++++++++++++++++++++++- dashboard/static/index.html | 14 +++++++------- 3 files changed, 55 insertions(+), 22 deletions(-) diff --git a/config/fee_tiers.py b/config/fee_tiers.py index 3716df1..c348f30 100644 --- a/config/fee_tiers.py +++ b/config/fee_tiers.py @@ -12,24 +12,24 @@ from typing import Optional # ── Perps fee tiers (base rates) ── PERPS_TIERS = { - 0: {"name": "VIP 0", "volume": 0, "taker": 0.00045, "maker": 0.00015}, - 1: {"name": "VIP 1", "volume": 5_000_000, "taker": 0.00040, "maker": 0.00012}, - 2: {"name": "VIP 2", "volume": 25_000_000, "taker": 0.00035, "maker": 0.00008}, - 3: {"name": "VIP 3", "volume": 100_000_000,"taker": 0.00030, "maker": 0.00004}, - 4: {"name": "VIP 4", "volume": 500_000_000, "taker": 0.00028, "maker": 0.00000}, - 5: {"name": "VIP 5", "volume": 2_000_000_000, "taker": 0.00026, "maker": 0.00000}, - 6: {"name": "VIP 6", "volume": 7_000_000_000, "taker": 0.00024, "maker": 0.00000}, + 0: {"name": "Tier 0", "volume": 0, "taker": 0.00045, "maker": 0.00015}, + 1: {"name": "Tier 1", "volume": 5_000_000, "taker": 0.00040, "maker": 0.00012}, + 2: {"name": "Tier 2", "volume": 25_000_000, "taker": 0.00035, "maker": 0.00008}, + 3: {"name": "Tier 3", "volume": 100_000_000,"taker": 0.00030, "maker": 0.00004}, + 4: {"name": "Tier 4", "volume": 500_000_000, "taker": 0.00028, "maker": 0.00000}, + 5: {"name": "Tier 5", "volume": 2_000_000_000, "taker": 0.00026, "maker": 0.00000}, + 6: {"name": "Tier 6", "volume": 7_000_000_000, "taker": 0.00024, "maker": 0.00000}, } # ── Spot fee tiers (base rates) ── SPOT_TIERS = { - 0: {"name": "VIP 0", "volume": 0, "taker": 0.00070, "maker": 0.00040}, - 1: {"name": "VIP 1", "volume": 100_000, "taker": 0.00060, "maker": 0.00030}, - 2: {"name": "VIP 2", "volume": 1_000_000, "taker": 0.00050, "maker": 0.00020}, - 3: {"name": "VIP 3", "volume": 10_000_000, "taker": 0.00040, "maker": 0.00010}, - 4: {"name": "VIP 4", "volume": 50_000_000, "taker": 0.00030, "maker": 0.00005}, - 5: {"name": "VIP 5", "volume": 200_000_000, "taker": 0.00020, "maker": 0.00000}, - 6: {"name": "VIP 6", "volume": 1_000_000_000,"taker":0.00010,"maker": -0.00005}, + 0: {"name": "Tier 0", "volume": 0, "taker": 0.00070, "maker": 0.00040}, + 1: {"name": "Tier 1", "volume": 100_000, "taker": 0.00060, "maker": 0.00030}, + 2: {"name": "Tier 2", "volume": 1_000_000, "taker": 0.00050, "maker": 0.00020}, + 3: {"name": "Tier 3", "volume": 10_000_000, "taker": 0.00040, "maker": 0.00010}, + 4: {"name": "Tier 4", "volume": 50_000_000, "taker": 0.00030, "maker": 0.00005}, + 5: {"name": "Tier 5", "volume": 200_000_000, "taker": 0.00020, "maker": 0.00000}, + 6: {"name": "Tier 6", "volume": 1_000_000_000,"taker":0.00010,"maker": -0.00005}, } # ── Staking discount multipliers ── diff --git a/dashboard/server.py b/dashboard/server.py index d0325c6..79f6ead 100644 --- a/dashboard/server.py +++ b/dashboard/server.py @@ -200,6 +200,34 @@ async def get_backtest(name: str): return JSONResponse({"error": "not found"}, status_code=404) + +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: + return equity_curve + fee_deltas = {} + cum_delta = 0.0 + for t in trades: + sz = t.get("size", 0) + px = t.get("price", 0) + old_fee = t.get("fee", 0) + new_fee = sz * px * new_fee_rate * 2 + delta = old_fee - new_fee + cum_delta += delta + fee_deltas[t.get("time", "")] = cum_delta + + new_curve = [] + delta_idx = 0 + trade_times = list(fee_deltas.keys()) + cum = 0.0 + for pt in equity_curve: + pt_time = pt.get("t", "") + while delta_idx < len(trade_times) and trade_times[delta_idx] <= pt_time: + cum = fee_deltas[trade_times[delta_idx]] + delta_idx += 1 + new_curve.append({"t": pt_time, "v": round(pt.get("v", 0) + cum, 6)}) + return new_curve + @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.""" @@ -251,7 +279,12 @@ async def recalc_backtest(name: str, fee_tier: int = 0, staking_tier: str = "non "pnl_net_pct": round(new_pnl_pct, 4), "fees_total": round(new_fees, 4), "total_trades": len(new_trades), - "equity_curve": data.get("equity_curve", []), + "equity_curve": recalc_equity_curve( + data.get("equity_curve", []), + data.get("trades", []), + new_fee_rate, + fee_model + ), "trades": new_trades[-100:], "sharpe": data.get("sharpe", 0), "sortino": data.get("sortino", 0), diff --git a/dashboard/static/index.html b/dashboard/static/index.html index b1f7842..9476c0c 100644 --- a/dashboard/static/index.html +++ b/dashboard/static/index.html @@ -162,13 +162,13 @@ footer a{color:#3f3f4a;text-decoration:none}footer a:hover{color:var(--tx)} Inc. fees