#!/usr/bin/env python3 """ hl_funding_arb.py — Hyperliquid vs Binance funding rate monitor Compares 1-hour funding rates across matching perpetual pairs on Hyperliquid and Binance USDT-margined futures. Ranks arb opportunities by annualized spread. Separately analyzes HIP-3 equity perps that have no Binance equivalent. Usage: python hl_funding_arb.py python hl_funding_arb.py --top 30 python hl_funding_arb.py --min-oi 10 # only pairs with OI > $10M """ import argparse import json import sys from datetime import datetime, timezone import requests HL_API = "https://api.hyperliquid.xyz/info" BN_PERP_API = "https://fapi.binance.com/fapi/v1/premiumIndex" # Taker fee assumptions — standard retail tier, no volume discounts. # HL: 0.035% taker (drops to 0.010% at higher volume tiers; maker is rebated at -0.002%) # Binance USDT perp: 0.050% taker (drops to 0.017% at VIP 6+) # Adjust these constants to match your actual fee tier. HL_TAKER_FEE = 0.00035 BN_TAKER_FEE = 0.0005 ROUNDTRIP_COST = (HL_TAKER_FEE + BN_TAKER_FEE) * 2 # open + close both legs # Known HIP-3 equity perps on Hyperliquid (tokenized stock/index perps) EQUITY_TICKERS = { "TSLA", "AAPL", "NVDA", "MSFT", "AMZN", "GOOGL", "GOOG", "META", "NFLX", "AMD", "INTC", "COIN", "MSTR", "BABA", "SPX", "QQQ", "GLD", "SLV", "PLTR", "UBER", "SNAP", "ARKK", "PYPL", "CRM", "AVGO", "TSM", "ASML", "SHOP", "SQ", "RBLX", "NET", "CRWD", "DDOG", "ZS", "PANW", "SMCI", "ARM", "HOOD", "RIVN", "LCID", } def fetch_hl_data() -> list[dict]: """Return all HL perp markets with funding, OI, and mark price.""" resp = requests.post( HL_API, json={"type": "metaAndAssetCtxs"}, timeout=15, ) resp.raise_for_status() meta, ctxs = resp.json() results = [] for coin_meta, ctx in zip(meta["universe"], ctxs): name = coin_meta["name"] try: funding_1h = float(ctx["funding"]) mark_px = float(ctx["markPx"]) oi_contracts = float(ctx["openInterest"]) except (KeyError, ValueError, TypeError): continue oi_usd = oi_contracts * mark_px funding_annual = funding_1h * 24 * 365 # 1h rate × 8760 results.append( { "symbol": name, "funding_1h": funding_1h, "funding_annual": funding_annual, "oi_usd": oi_usd, "mark_px": mark_px, "asset_type": "equity" if name in EQUITY_TICKERS else "crypto", } ) return results def fetch_binance_funding() -> dict[str, dict]: """Return mapping of base asset → Binance 1h equivalent funding rate.""" resp = requests.get(BN_PERP_API, timeout=15) resp.raise_for_status() results = {} for item in resp.json(): symbol: str = item["symbol"] if not symbol.endswith("USDT"): continue base = symbol[:-4] # Binance lastFundingRate is for the 8-hour settlement window rate_8h = float(item["lastFundingRate"]) results[base] = { "funding_1h": rate_8h / 8, "funding_annual": rate_8h * 3 * 365, # 3 settlements/day × 365 "next_funding_time": item.get("nextFundingTime"), } return results def breakeven_holding_days(spread_annual: float) -> float: """Minimum days the spread must persist to cover round-trip fees.""" if spread_annual == 0: return float("inf") return ROUNDTRIP_COST / abs(spread_annual) * 365 def _pct(rate: float) -> str: return f"{rate * 100:+.4f}%" def _pct2(rate: float) -> str: return f"{rate * 100:+.2f}%" def print_table(rows: list[dict], columns: list[tuple], title: str): header = " ".join(f"{name:>{width}}" for name, width, _ in columns) sep = " ".join("-" * width for _, width, _ in columns) print(f"\n── {title} ──\n") print(header) print(sep) for row in rows: cells = [] for name, width, key in columns: val = row.get(key, "") if callable(key): val = key(row) cells.append(f"{str(val):>{width}}") print(" ".join(cells)) def run(top: int = 25, min_oi_m: float = 0.0): ts = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC") print(f"\n{'='*76}") print(f" HYPERLIQUID vs BINANCE FUNDING RATE MONITOR — {ts}") print(f"{'='*76}") print("\nFetching Hyperliquid data...", end=" ", flush=True) hl_data = fetch_hl_data() print(f"{len(hl_data)} markets") print("Fetching Binance data...", end=" ", flush=True) bn_data = fetch_binance_funding() print(f"{len(bn_data)} markets") min_oi = min_oi_m * 1e6 cross_venue = [] equity_perps = [] hl_only = [] for item in hl_data: if item["oi_usd"] < min_oi: continue sym = item["symbol"] if item["asset_type"] == "equity": equity_perps.append(item) elif sym in bn_data: bn = bn_data[sym] spread = item["funding_annual"] - bn["funding_annual"] breakeven = breakeven_holding_days(spread) cross_venue.append( { **item, "bn_annual": bn["funding_annual"], "spread_annual": spread, "breakeven_days": breakeven, } ) else: hl_only.append(item) cross_venue.sort(key=lambda x: abs(x["spread_annual"]), reverse=True) equity_perps.sort(key=lambda x: x["oi_usd"], reverse=True) # ── SECTION 1: Cross-venue arb ────────────────────────────────────────── print(f"\n\n{'─'*76}") print(f" SECTION 1 — FUNDING ARB: HL vs BINANCE (top {top} by |spread|)") print(f" Round-trip fee cost: {ROUNDTRIP_COST*100:.3f}% | min OI filter: ${min_oi_m:.0f}M") print(f"{'─'*76}") header = ( f" {'Symbol':<10}" f"{'HL annual':>11}" f"{'BN annual':>11}" f"{'Spread':>10}" f"{'OI $M':>9}" f"{'B/E days':>9}" f" Direction" ) print(header) print(" " + "-" * 72) shown = 0 for row in cross_venue: if shown >= top: break spread = row["spread_annual"] direction = "BN short / HL long" if spread > 0 else "HL short / BN long" covers_fees = "✓" if abs(spread) > ROUNDTRIP_COST * 2 else " " print( f" {row['symbol']:<10}" f"{_pct2(row['funding_annual']):>11}" f"{_pct2(row['bn_annual']):>11}" f"{_pct2(spread):>10}" f"{row['oi_usd']/1e6:>8.1f}M" f"{row['breakeven_days']:>9.1f}" f" {covers_fees} {direction}" ) shown += 1 # ── SECTION 2: Equity perps ───────────────────────────────────────────── print(f"\n\n{'─'*76}") print(f" SECTION 2 — HIP-3 EQUITY PERPS on HYPERLIQUID (no Binance equivalent)") print(f"{'─'*76}") eq_header = ( f" {'Symbol':<10}" f"{'1h rate':>10}" f"{'Annual':>10}" f"{'OI $M':>9}" f" Signal" ) print(eq_header) print(" " + "-" * 55) for row in equity_perps[:20]: ann = row["funding_annual"] if ann > 0.50: signal = "↑ heavily long-biased" elif ann < -0.50: signal = "↓ heavily short-biased" elif abs(ann) < 0.05: signal = " neutral" else: signal = "" print( f" {row['symbol']:<10}" f"{_pct(row['funding_1h']):>10}" f"{_pct2(ann):>10}" f"{row['oi_usd']/1e6:>8.1f}M" f" {signal}" ) # ── SECTION 3: Platform summary ───────────────────────────────────────── total_oi = sum(x["oi_usd"] for x in hl_data if x["oi_usd"] >= min_oi) equity_oi = sum(x["oi_usd"] for x in equity_perps) crypto_oi = sum(x["oi_usd"] for x in cross_venue) + sum(x["oi_usd"] for x in hl_only) positive_spread = [x for x in cross_venue if x["spread_annual"] > ROUNDTRIP_COST * 2] negative_spread = [x for x in cross_venue if x["spread_annual"] < -ROUNDTRIP_COST * 2] print(f"\n\n{'─'*76}") print(f" SECTION 3 — PLATFORM SUMMARY") print(f"{'─'*76}") print(f" Total HL OI tracked: ${total_oi/1e9:>7.2f}B") print(f" Crypto perp OI: ${crypto_oi/1e9:>7.2f}B ({crypto_oi/total_oi*100:.1f}%)") print(f" HIP-3 equity perp OI: ${equity_oi/1e9:>7.2f}B ({equity_oi/total_oi*100:.1f}%)") print(f" Cross-venue pairs (HL+BN): {len(cross_venue):>4}") print(f" HL-only crypto pairs: {len(hl_only):>4}") print(f" Equity-only pairs: {len(equity_perps):>4}") print(f" Pairs with +spread > fees: {len(positive_spread):>4} (BN short / HL long bias)") print(f" Pairs with -spread > fees: {len(negative_spread):>4} (HL short / BN long bias)") avg_eq_funding = ( sum(abs(x["funding_annual"]) for x in equity_perps) / len(equity_perps) if equity_perps else 0 ) avg_crypto_funding = ( sum(abs(x["funding_annual"]) for x in cross_venue) / len(cross_venue) if cross_venue else 0 ) print(f"\n Avg |annual funding| — equity perps: {avg_eq_funding*100:.2f}%") print(f" Avg |annual funding| — crypto perps: {avg_crypto_funding*100:.2f}%") print() if __name__ == "__main__": parser = argparse.ArgumentParser(description="HL vs Binance funding rate monitor") parser.add_argument("--top", type=int, default=25, help="Top N pairs to show (default: 25)") parser.add_argument( "--min-oi", type=float, default=0.0, metavar="M", help="Minimum OI in millions USD (default: 0)", ) args = parser.parse_args() run(top=args.top, min_oi_m=args.min_oi)