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:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+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