Files
ftdt-quant-lab/config/fee_tiers.py
T
ramseshk 0c0d2124ad 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.
2026-08-04 07:26:40 +00:00

92 lines
3.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Hyperliquid fee tiers — perps and spot, base rates + staking discounts.
Source: https://hyperliquid.gitbook.io/hyperliquid-docs/trading/fees
Fee = base_rate × staking_multiplier
Staking tiers are based on staked HYPE tokens.
"""
from dataclasses import dataclass, field
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": 250_000_000,"taker": 0.00025, "maker": 0.00000},
5: {"name": "VIP 5", "volume": 750_000_000,"taker": 0.00020, "maker": -0.00002},
6: {"name": "VIP 6", "volume": 2_500_000_000,"taker":0.00015,"maker": -0.00004},
}
# ── 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},
}
# ── Staking discount multipliers ──
STAKING_TIERS = {
"none": {"name": "No Stake", "multiplier": 1.00},
"wood": {"name": "Wood", "multiplier": 0.95},
"bronze": {"name": "Bronze", "multiplier": 0.90},
"silver": {"name": "Silver", "multiplier": 0.85},
"gold": {"name": "Gold", "multiplier": 0.80},
"platinum": {"name": "Platinum", "multiplier": 0.70},
"diamond": {"name": "Diamond", "multiplier": 0.60},
}
def effective_rate(base_rate: float, staking_tier: str = "none") -> float:
"""Calculate effective fee rate after staking discount."""
mult = STAKING_TIERS.get(staking_tier, STAKING_TIERS["none"])["multiplier"]
return base_rate * mult
def get_perp_fees(vip_tier: int, staking_tier: str = "none", fee_model: str = "taker") -> float:
"""Get effective perp fee for a given VIP tier and staking tier."""
tier = PERPS_TIERS.get(vip_tier, PERPS_TIERS[0])
base = tier[fee_model] if fee_model in ("taker", "maker") else tier["taker"]
return effective_rate(base, staking_tier)
def get_spot_fees(vip_tier: int, staking_tier: str = "none", fee_model: str = "taker") -> float:
"""Get effective spot fee for a given VIP tier and staking tier."""
tier = SPOT_TIERS.get(vip_tier, SPOT_TIERS[0])
base = tier[fee_model] if fee_model in ("taker", "maker") else tier["taker"]
return effective_rate(base, staking_tier)
def fee_tier_from_volume(volume_14d: float, market: str = "perps") -> int:
"""Determine fee tier from 14-day rolling volume."""
tiers = PERPS_TIERS if market == "perps" else SPOT_TIERS
current = 0
for t in sorted(tiers.keys()):
if volume_14d >= tiers[t]["volume"]:
current = t
return current
# ── Strategy-specific defaults (matching existing classification) ──
STRATEGY_FEE_MODELS = {
"Order Book Imbalance": "taker",
"Iceberg Detection": "taker",
"Funding Rate Arb": "taker",
"Pairs Trading": "taker",
"Avellaneda-Stoikov": "maker",
"Momentum Breakout": "taker",
"Mean Reversion": "taker",
"Hawkes OFI": "taker",
"Deep LOB": "maker",
"Cartea-Jaimungal": "maker",
"Queue Imbalance": "taker",
"Guéant Market Making": "maker",
}