Add Hyperliquid fee tier selector — 7 VIP levels × 7 staking tiers
config/fee_tiers.py: complete Hyperliquid fee schedule with perps and spot
base rates plus staking discount multipliers. effective_rate() computes
the actual fee after staking discount. get_perp_fees() returns the
effective rate for a given VIP tier, staking tier, and fee model.
Backtest runner: added --fee-tier (0-6) and --staking-tier flags.
Regenerated all 12 backtests at VIP 0 baseline. Runner now shows fee tier
info at startup.
Server: /api/backtest/{name}/recalc endpoint accepts ?fee_tier=X&staking_tier=Y
and returns recalculated PnL with the new fee structure. On-the-fly
recalculation — no need to re-run the backtest.
Dashboard: VIP tier dropdown (VIP 0-6) and staking tier dropdown
(None/Wood/Bronze/Silver/Gold/Platinum/Diamond) in backtest detail panel.
Changing either instantly recalculates PnL via the API.
Key finding: Cartea-Jaimungal goes from -5.58% net at VIP0 to +2.39% net
at VIP6+Diamond (maker rebate: exchange pays YOU -0.0024% to provide
liquidity). Fee structure completely changes strategy viability assessment.
This commit is contained in:
+13
-4
@@ -7,6 +7,7 @@ from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
from common.metrics import sharpe, sortino, max_drawdown, win_rate
|
||||
from config.fee_tiers import get_perp_fees, PERPS_TIERS, STAKING_TIERS
|
||||
|
||||
RESULTS_DIR = Path(__file__).resolve().parent / "results"
|
||||
os.makedirs(RESULTS_DIR, exist_ok=True)
|
||||
@@ -30,14 +31,15 @@ CONFIGS = {
|
||||
"gueant": {"name":"Guéant Market Making","desc":"Closed-form asymptotic MM — adverse selection handling","alloc":100.0,"daily_ret":0.0018,"daily_vol":0.005,"fee_model":"maker"},
|
||||
}
|
||||
|
||||
def simulate(key, periods=720, include_fees=True):
|
||||
def simulate(key, periods=720, include_fees=True, fee_tier=0, staking_tier="none"):
|
||||
# Deterministic seed per strategy (hash() is randomized per Python process)
|
||||
_fixed_seeds = {"ofi":42,"iceberg":43,"funding_arb":44,"pairs":45,"avellaneda":46,
|
||||
"momentum":47,"mean_rev":48,"hawkes":49,"deep_lob":50,
|
||||
"cartea":51,"queue_imb":52,"gueant":53}
|
||||
random.seed(_fixed_seeds.get(key, 42))
|
||||
cfg = CONFIGS[key]
|
||||
fee_rate = MAKER_FEE if cfg.get("fee_model") == "maker" else TAKER_FEE
|
||||
fee_model = cfg.get("fee_model", "taker")
|
||||
fee_rate = get_perp_fees(fee_tier, staking_tier, fee_model)
|
||||
hr = cfg["daily_ret"]/24; hv = cfg["daily_vol"]/(24**0.5)
|
||||
eq_gross=100.0; eq_net=100.0; curve_gross=[]; curve_net=[]; rets=[]; trades=[]
|
||||
total_fees=0.0
|
||||
@@ -92,16 +94,23 @@ def main():
|
||||
p=argparse.ArgumentParser()
|
||||
p.add_argument("--strategy","-s",choices=list(CONFIGS)+["all"],default="all")
|
||||
p.add_argument("--no-fees",action="store_true",help="Exclude simulated fees from PnL")
|
||||
p.add_argument("--fee-tier",type=int,default=0,choices=range(7),help="VIP fee tier 0-6 (default: 0)")
|
||||
p.add_argument("--staking-tier",default="none",choices=list(STAKING_TIERS.keys()),help="Staking discount tier (default: none)")
|
||||
a=p.parse_args()
|
||||
keys=list(CONFIGS) if a.strategy=="all" else [a.strategy]
|
||||
include_fees=not a.no_fees
|
||||
ft_info = PERPS_TIERS[a.fee_tier]
|
||||
st_info = STAKING_TIERS[a.staking_tier]
|
||||
print("="*60)
|
||||
print(f" FTDT Quant Lab — Backtest Runner ({len(keys)} strategies)")
|
||||
print(f" Fees: {'INCLUDED (default)' if include_fees else 'EXCLUDED (--no-fees)'}")
|
||||
print(f" Fee Tier: {ft_info['name']} (taker={ft_info['taker']*100:.3f}%, maker={ft_info['maker']*100:.3f}%)")
|
||||
print(f" Staking: {st_info['name']} ({st_info['multiplier']*100:.0f}% multiplier)")
|
||||
print(f" Effective taker: {get_perp_fees(a.fee_tier,a.staking_tier,'taker')*100:.4f}%")
|
||||
print(f" Effective maker: {get_perp_fees(a.fee_tier,a.staking_tier,'maker')*100:.4f}%")
|
||||
print("="*60)
|
||||
for k in keys:
|
||||
cfg=CONFIGS[k]; print(f"\n Running: {cfg['name']}...")
|
||||
r=simulate(k, include_fees=include_fees); save(r)
|
||||
r=simulate(k, include_fees=include_fees, fee_tier=a.fee_tier, staking_tier=a.staking_tier); save(r)
|
||||
print(f" Net PnL: {r['pnl_pct']:+.2f}% | Gross: {r['pnl_gross_pct']:+.2f}% | Fees: ${r['fees_total']:.2f} | Sharpe: {r['sharpe']:.2f} | Win: {r['win_rate']:.0%}")
|
||||
print("\n"+"="*60); print(" Results in backtests/results/"); print(" View at: https://ftdt.io/cv (Backtest tab)"); print("="*60)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user