Order Book Reconstruction
Server-side and client-side reconstruction for Hyperliquid, HIP-3, HIP-4, and Lighter depth feeds.
Official clients for the core API in Python, TypeScript, and Rust.
Order book reconstruction
Reconstruct Hyperliquid, HIP-3, and HIP-4 L4/L2 books server-side or client-side. Lighter uses checkpoints and deltas.
Server-side reconstruction (Hyperliquid / HIP-3 / HIP-4)
- Tier
- Pro+ (L4) / Build+ (L2)
- For
- Point-in-time L4 or L2 snapshots
- Recommended
- One API call, server handles matching engine
# Server-side L4 reconstruction (Pro+ tier)# The server handles the matching engine — just pass a timestamp.from oxarchive import Client
client = Client(api_key="0xa_your_api_key")
# Hyperliquid: get L4 orderbook at a specific point in timel4 = client.hyperliquid.l4_orderbook.get("BTC", timestamp=1711900800000)print(f"BTC L4: {l4['bid_count']} bids, {l4['ask_count']} asks")print(f"Best bid: {l4['bids'][0]['price']}, Best ask: {l4['asks'][0]['price']}")
# HIP-3: same method, same response formathip3_l4 = client.hyperliquid.hip3.l4_orderbook.get("xyz:CL", timestamp=1711900800000)print(f"Crude Oil L4: {hip3_l4['bid_count']} bids, {hip3_l4['ask_count']} asks")
# Omit timestamp for current live state (from Redis, sub-second)live = client.hyperliquid.l4_orderbook.get("BTC")
# L2 full-depth (aggregated from L4, Build+ tier)l2 = client.hyperliquid.l2_orderbook.get("BTC", timestamp=1711900800000)print(f"BTC L2: {l2['bid_levels']} bid levels, {l2['ask_levels']} ask levels")print(f"Best bid: {l2['bids'][0]['px']} ({l2['bids'][0]['n']} orders)")| Endpoint | Tier | Returns |
|---|---|---|
/orderbook/:symbol/l4 | Pro+ | L4 order-level book: individual orders with OID, wallet, price, size. |
/orderbook/:symbol/l2 | Build+ | L2 full-depth book: aggregated price levels with total size and order count. |
Client-side L4/L2 reconstruction (Hyperliquid / HIP-3 / HIP-4)
- Tier
- Pro+
- For
- Time-series iteration, backtesting, custom analysis
- Exchanges
- Same method for Hyperliquid, HIP-3, and HIP-4
# Client-side L4 reconstruction with matching engine (Pro+ tier)# Same approach for Hyperliquid and HIP-3 — identical diff format.from oxarchive import Client, L4OrderBookReconstructorfrom collections import defaultdict
client = Client(api_key="0xa_your_api_key")start, end = 1711900800000, 1711901100000 # 5-minute window
# 1. Fetch nearest L4 checkpointcheckpoints = client.hyperliquid.l4_orderbook.history("BTC", start=start-1800000, end=start)checkpoint = checkpoints.data[-1] # nearest before target
# 2. Fetch L4 diffs from checkpoint to targetdiffs = client.hyperliquid.l4_orderbook.diffs("BTC", start=start, end=end)
# 3. Fetch order statuses for non-resting filter (optional, improves accuracy)statuses = client.hyperliquid.orders.history("BTC", start=start, end=end)non_resting = defaultdict(set)for s in statuses.data: if s["status"] != "open": non_resting[s["block_number"]].add(s["oid"])
# 4. Reconstruct with matching enginebook = L4OrderBookReconstructor()book.load_checkpoint(checkpoint)
# Group diffs by block, apply in orderblocks = defaultdict(list)for d in diffs.data: blocks[d["block_number"]].append(d)
for bn in sorted(blocks): nr = non_resting.get(bn) for diff in blocks[bn]: book.apply_diff(diff, nr)
# 5. Result: non-crossed L4 orderbookassert not book.is_crossed(), "Book should not be crossed"print(f"Best bid: {book.best_bid()}, Best ask: {book.best_ask()}")print(f"Orders: {len(book.bids())} bids, {len(book.asks())} asks")
# 6. Derive L2 from L4 (aggregate by price level)l2_bids, l2_asks = book.derive_l2()print(f"L2 levels: {len(l2_bids)} bid, {len(l2_asks)} ask")| Method | Best use | Notes |
|---|---|---|
L4OrderBookReconstructor | Core reconstruction class | Matching engine built in. Handles crossing orders and non-resting filter. |
loadCheckpoint() / applyDiff() | Step through time | Initialize from checkpoint, then apply diffs block-by-block. |
deriveL2() | L2 from L4 | Aggregate L4 orders by price level (sum sizes, count orders per level). |
isCrossed() | Validation | Should always return false after correct reconstruction. |
Lighter tick reconstruction
- Tier
- Enterprise
- For
- Tick-level Lighter order book history
- Preferred path
- Auto-paginating iterator
# Orderbook Reconstruction (Enterprise tier only)from oxarchive import Client, OrderBookReconstructor
client = Client(api_key="0xa_your_api_key")
# Option 1: Auto-paginating iterator (recommended for large time ranges)# Automatically handles pagination, fetching up to 1,000 deltas per requestfor snapshot in client.lighter.orderbook.iterate_tick_history( "BTC", start="2026-01-01T00:00:00Z", end="2026-01-01T12:00:00Z" # 12 hours of data): print(f"{snapshot.timestamp}: mid={snapshot.mid_price}") if some_condition: break # Early exit supported
# Option 2: Get fully reconstructed snapshots (single page)snapshots = client.lighter.orderbook.history_reconstructed( "BTC", start="2026-01-01T00:00:00Z", end="2026-01-01T01:00:00Z")for ob in snapshots: print(f"{ob.timestamp}: bid={ob.bids[0].px} ask={ob.asks[0].px}")
# Option 3: Get raw tick data for custom reconstructiontick_data = client.lighter.orderbook.history_tick("BTC", start=start, end=end)print(f"Checkpoint: {len(tick_data.checkpoint.bids)} bids")print(f"Deltas: {len(tick_data.deltas)} updates")
# Check for sequence gapsgaps = OrderBookReconstructor.detect_gaps(tick_data.deltas)if gaps: print("Gaps detected:", gaps)| Method | Best use | Notes |
|---|---|---|
iterateTickHistory() | Long windows and production replay | Auto-paginates and handles large delta ranges cleanly. |
historyTick() | Raw checkpoints and deltas | Single page, maximum 1,000 deltas. |
historyReconstructed() | Direct reconstructed snapshots | Single page when you need the finished book immediately. |
detectGaps() | Continuity checks | Use before backtests or downstream reconstruction pipelines. |