tickfoundry
capture livesign inget the data →
§ guide

How to backtest on Hyperliquid
against the order book that was actually there.

python · pandasfree sample dayTickFoundry · 2026-09-24 · runs on the free Hyperliquid sample day

A Hyperliquid backtest built on candles fills every order at a price, not against a book, and it never pays a fee. How wrong that is depends on the coin. On the sample day BTC's book was deep and one tick wide, so a $100k market order cost half a tick over mid, while the taker fee on the tape cost fifty times that. HYPE held about $0.2M in its 25 visible levels: the same $100k order paid 2.2 bps over mid at a typical moment, and a $1M order did not fit in the visible book 99% of the day. This guide loads the historical order book in pandas, prices every simulated order by walking the levels resting at that block, and charges the fee from the tape. Every figure comes from the free sample day and the script shown.

Follow along with the sample: Hyperliquid · 2026-08-12 · BTC + HYPE · hl_l1 / hl_l2 / hl_trades · about 250 MB.
pull it free →↓ hl_slippage.py

Why candles and snapshots are not enough

Hyperliquid produces a block about every 71.5 ms. On the sample day BTC's book changed in 68.7% of them, 824,716 book states, against the 15,999 book snapshots the chain's archive published, one every 5.4 seconds. A snapshot series sees about one BTC book in fifty. The info API only returns the book as it is now.

And the orders that matter are exactly the ones that walk the book. Rebuilding each taker order from its fills, 5.3% of BTC taker orders (11.9% on HYPE) filled at more than one price, but they carried 61% of the day's taker notional on both coins. A backtest that prices those at the mid or the last trade is wrong on most of the volume that moves anything.

Step 1 · Get the historical order book

Hyperliquid data is sold per day with a coin filter: pick a range, the tables and the coins, and the bundle keeps one parquet file per table per coin per day. Three ways in:

Free sample day

2026-08-12, any coins up to 1.0 GB, on a free account. Costs no market·day claims.

Pull it →
Any coin, any day

Every perp, spot pair and HIP-3 market since 2025-01-25, on a Hyperliquid plan or add-on.

What's in the archive →
REST API

Explorer and up: request a range of days for a set of coins, poll, stream the bundle. Snippet below.

API reference →
request a month of HYPE books and fills over the APIpython
import requests, time
API = "https://tickfoundry.com/api/v1" # Explorer and up
H = {"Authorization": "Bearer tf_live_…"} # minted in the dashboard
job = requests.post(f"{API}/hyperliquid/downloads", headers=H, json={
"start": "2026-08-01", "end": "2026-08-31",
"datasets": ["hl_l2", "hl_trades"],
"coins": ["HYPE"],
}).json()
while (s := requests.get(f"{API}/downloads/{job['id']}", headers=H).json())["status"] == "staging":
time.sleep(10)
open("hyperliquid.zip", "wb").write(requests.get(s["url"], headers=H).content)

Building this yourself — pulling the chain's order-event archive out of requester-pays S3 and replaying it into books — runs to around $10,000 in egress and compute.

Step 2 · Load it in pandas

A bundle is table-first: hl_l2/date=D/<COIN>/l2.parquet, the same for hl_l1 and hl_trades, plus quality/date=D/<COIN>.json. Rows key on msg_seq, the block height, which is the ordering to trust; ts_src_ms is the block's own time. The book table has one row per block in which anything in the 25 levels changed, as wide columns bid_px_1 … ask_sz_25 with the resting-order count per level in bid_n_* / ask_n_*. Sizes are in coin units.

load.pypython
import pandas as pd
# the free sample day, pulled from /catalog/hyperliquid and unzipped
book = pd.read_parquet("hl_l2/date=2026-08-12/BTC/l2.parquet") # 25-level book, a row per block it changed
trades = pd.read_parquet("hl_trades/date=2026-08-12/BTC/trades.parquet") # every fill: taker side, wallet, fee
top = pd.read_parquet("hl_l1/date=2026-08-12/BTC/l1.parquet") # top of book, every change
# msg_seq is the block height and orders everything; ts_src_ms is block time
book = book.sort_values("msg_seq")
book["ts"] = pd.to_datetime(book.ts_src_ms, unit="ms", utc=True)
book["mid"] = (book.best_bid + book.best_ask) / 2
# or every coin and day in the pull at once; date comes from the path
books = pd.read_parquet("hl_l2", columns=["coin", "msg_seq", "ts_src_ms", "best_bid", "best_ask"])

Full column reference and coverage caveats in the Hyperliquid schema docs.

Step 3 · Measure slippage by walking the book

Pricing a market order is then arithmetic on each row: take dollars from each level until the order is filled, and divide. Because rows are irregular (a busy second has dozens, a quiet one none), weight each book state by how long it was live, so the statistics answer "what would this order have cost at a random moment of the day". Where the 25 levels hold less than the order, the result is NaN rather than a guess.

hl_slippage.py · the script behind the tablespython
# hl_slippage.py (core) — the full script is linked above
import numpy as np
def walk(px, sz, usd):
"""Average fill price of a market order for `usd` notional, taking levels
best-first (px/sz: rows x 25). NaN where the 25 levels hold less."""
cost = px * sz # $ resting at each level
cum = np.nancumsum(cost, axis=1)
take = np.minimum(cost, np.maximum(usd - (cum - cost), 0)) # $ taken per level
qty = np.nansum(take / px, axis=1)
with np.errstate(divide="ignore", invalid="ignore"):
return np.where(cum[:, -1] >= usd, usd / qty, np.nan)
levels = range(1, 26)
ask_px = book[[f"ask_px_{i}" for i in levels]].to_numpy()
ask_sz = book[[f"ask_sz_{i}" for i in levels]].to_numpy() # size in coin units
mid = book.mid.to_numpy()
live_ms = book.ts_src_ms.shift(-1).sub(book.ts_src_ms).fillna(0).to_numpy() # time-weight
vwap = walk(ask_px, ask_sz, 1_000_000)
slip_bps = (vwap / mid - 1) * 1e4 # worse than mid, per book state
print("fits in 25 levels:", live_ms[~np.isnan(vwap)].sum() / live_ms.sum())
BTC
824,716 book changes · 215,511 fills · $1,697M traded · spread 0.16 bps (one tick) · $10.2M bid / $8.7M ask in the 25 levels
market orderfits in 25 levelsvs mid · medianvs mid · p90vs touch · median
buy $10,000100.0%0.080.080.00
sell $10,000100.0%0.080.080.00
buy $100,000100.0%0.080.480.00
sell $100,000100.0%0.080.460.00
buy $1,000,000100.0%0.351.310.27
sell $1,000,000100.0%0.291.260.21
HYPE
667,808 book changes · 154,728 fills · $170M traded · spread 0.18 bps median, 0.23 mean · $0.19M bid / $0.20M ask in the 25 levels
market orderfits in 25 levelsvs mid · medianvs mid · p90vs touch · median
buy $10,000100.0%0.331.370.21
sell $10,000100.0%0.321.410.19
buy $100,00096.4%2.203.322.08
sell $100,00097.4%2.373.442.26
buy $1,000,0000.9%
sell $1,000,0000.4%

2026-08-12 UTC · slippage in bps, average fill price worse than the reference · time-weighted over the 23.8 hours in the day's folder · "fits" is the share of the day the 25 levels could absorb the whole order; a $1M HYPE order fit too rarely for its cost to mean anything, so it is left blank.

123450$1k$10k$100k$1M$10Mmedian taker fee on BTC's tape, 4.3 bpsBTCHYPEbps over mid · market buy
Median cost of a market buy over mid by order size, time-weighted, 2026-08-12. Each line stops where the 25 visible levels could absorb the order less than 85% of the day: BTC at $5M, HYPE at $100k.

Read it against the assumptions most backtests make. On BTC, filling at the mid is off by half a tick up to about $200k, and a $1M order costs 0.35 bps at the median and 1.3 bps one moment in ten. The dashed line is the bigger number: the median taker fee on the same day's tape was 4.3 bps, so on BTC the fee, not the book, decides whether a short-horizon strategy survives. On HYPE the book is the problem: a $100k order walks deep into the visible 25 levels and pays 2.2 bps at the median, and by $200k the visible book is too thin for more than half the day.

Step 4 · Rules that make the backtest honest

Price each order off the block before it
Order everything by msg_seq. For a decision at block N, walk the last book row at or before N−1; a merge_asof on msg_seq does it for a whole signal table.
Charge the fee from the tape
hl_trades carries fee, fee_token and builder_fee on every fill. The median taker fee was 4.3 bps on BTC and 3.9 on HYPE that day; use your own tier, but never zero.
Know where the 25 levels end
They spanned about 3.8 bps on BTC and 5 bps on HYPE. If the order is bigger than the visible book, the remainder is a model: size down, slice it, or say so in the result.
A day is a block range, not a calendar day
Each date= folder is a range of blocks: 2026-08-12 runs from 00:09:56 to 00:00:47 the next day, UTC. Select by ts_src_ms and pull the neighbouring day for windows that cross midnight.
Calibrate against real taker orders
Group hl_trades by (oid, msg_seq) to rebuild every real taker order, then check that your simulated fill for the same size and block lands where the real one did.
Read the quality file and the caveats
quality/date=D/<COIN>.json scores the day against Hyperliquid's own snapshots (BTC matched the best bid at 99.73% of them on the sample day). The README lists the known holes and synthesized-fill periods in your range.
rebuild taker orders from the tapepython
# every taker order of the day, rebuilt from its fills: one order id, one block
t = trades.assign(usd=trades.price * trades["size"])
orders = t.groupby(["oid", "msg_seq"]).agg(side=("side", "first"), usd=("usd", "sum"),
prices=("price", "nunique"), fee=("fee", "sum"))
walked = orders[orders.prices > 1]
print(f"{len(walked) / len(orders):.1%} of taker orders filled at more than one price,")
print(f"{walked.usd.sum() / orders.usd.sum():.1%} of taker notional")

Where the mid-fill assumption breaks hardest

The medians above describe an ordinary moment. The expensive moments are the fast ones, and they are where a candle backtest books its best trades. At 12:30 UTC on the sample day BTC fell 34.8 bps in a minute, with a 52.6 bps high-to-low range inside it. The ask side's 25 levels, which held $8.7M at the median that day, dropped as low as $196k; the spread widened from one tick to 5.45 bps; and a $1M market buy that costs 0.35 bps at a typical moment cost 0.76 bps at that minute's median and 9.41 bps at its worst instant, and did not fit in the visible book for 3.8% of the minute.

HYPE's fastest minute came at 14:36 UTC, up 52.2 bps. Its 25 levels thinned to about $71k a side from a median near $195k, and a $100k buy that costs 2.2 bps at the median cost up to 34.9 bps at the worst instant of that minute. A backtest priced off candles never sees either number.

Hyperliquid books in a lead-lag study →The same guide for Polymarket

FAQ

Does Hyperliquid provide historical order book data?
Not in a form you can simulate fills against. The info API returns the book as it is now, not as it was. The chain's public archive holds periodic book snapshots (15,999 of them for 2026-08-12, one every ~5.4 seconds) and the raw order-event stream, which has to be replayed block by block to recover every book change. BTC's book changed 824,716 times that day. TickFoundry replays that stream into a 25-level book, top of book and the fill tape for every coin since 2025-01-25.
Can I backtest Hyperliquid for free?
Yes. Any free account can pull the fixed sample day, 2026-08-12, for any coins up to 1.0 GB per pull, without spending a market·day claim. BTC and HYPE with all three tables is about 250 MB. No card.
Is a 25-level book deep enough to backtest?
It depends on the coin, and the file tells you. On the sample day BTC's 25 levels spanned about 3.8 bps and held about $10M a side, enough for a $1M market order essentially all day and a $5M one 89% of the day. HYPE's spanned about 5 bps and held about $0.2M a side: a $100k order fit 96% of the day, $1M under 1%. Past the visible levels the cost of the remainder is a model, not data.
How do fees show up in the data?
Every fill in hl_trades carries fee and fee_token (USDC on perps), a separate builder_fee, the taker's wallet, closed_pnl and start_position. The median taker fee on the sample day was 4.3 bps on BTC and 3.9 bps on HYPE. On BTC that is about twelve times what walking the book cost a $1M order, so a backtest that models slippage and skips the fee has the error the wrong way round.
How accurate is the rebuilt book?
Every coin-day ships a quality file comparing our replayed book with each of Hyperliquid's own published snapshots. On 2026-08-12 BTC's best bid matched at 99.73% of 15,999 snapshots and best ask at 99.67%; HYPE 99.51% and 99.41%. The known coverage caveats for your date range are in the bundle README and the docs.
Which Python tools read it?
The tables are parquet, one file per table per coin per day, so pandas, polars, DuckDB and Arrow read them directly; pd.read_parquet("hl_l2") reads a whole pull at once. There is no CSV twin for Hyperliquid. The walkthrough uses pandas and numpy only.
Run it on the coins you actually trade.

The sample day covers every coin on the venue, not just these two. Pull the ones you trade for 2026-08-12 on a free account, and the script runs on them unchanged: python hl_slippage.py . SOL ETH.

Pull the free sample day →Every coin since 2025-01-25