Skip to main content

L4 Order Book (Level 4)

The most detailed market data available - individual order visibility with user wallet addresses, order IDs, timestamps, and full order parameters. L4 data enables queue position tracking, whale watching, and advanced market microstructure analysis.

Code Examples Repository

How to Subscribe#

Send a subscription message to the WebSocket endpoint:

{
"method": "subscribe",
"subscription": {
"type": "l4Book",
"coin": "BTC"
}
}

Subscription Parameters#

ParameterTypeRequiredDescription
typestringYesMust be "l4Book"
coinstringYesTrading pair symbol (e.g., "BTC", "ETH")

Unlike L2, L4 subscriptions do not support nLevels or nSigFigs - you receive the complete order book with all individual orders.

Message Types#

L4 subscriptions produce two types of messages:

  1. Initial Snapshot - Complete order book state when you subscribe
  2. Incremental Updates - Changes as orders are placed, modified, or filled

Initial Snapshot Response#

When you first subscribe, you receive a complete snapshot of the order book wrapped in a Snapshot object:

{
"channel": "l4Book",
"data": {
"Snapshot": {
"coin": "BTC",
"height": 854890775,
"levels": [
[
{
"user": "0xf9109ada2f73c62e9889b45453065f0d99260a2d",
"coin": "BTC",
"side": "B",
"limitPx": "90057",
"sz": "0.33289",
"oid": 289682065711,
"timestamp": 1767878782721,
"triggerCondition": "N/A",
"isTrigger": false,
"triggerPx": "0.0",
"isPositionTpsl": false,
"reduceOnly": false,
"orderType": "Limit",
"tif": "Alo",
"cloid": "0x4c4617dbd8b94d358285c5c6d5a43df3"
}
],
[
{
"user": "0x13558be785661958932ceac35ba20de187275a42",
"coin": "BTC",
"side": "A",
"limitPx": "90058",
"sz": "0.37634",
"oid": 289682176026,
"timestamp": 1767878800615,
"triggerCondition": "N/A",
"isTrigger": false,
"triggerPx": "0.0",
"isPositionTpsl": false,
"reduceOnly": false,
"orderType": "Limit",
"tif": "Alo",
"cloid": "0x000000000814768000001999b6671c90"
}
]
]
}
}
}
Snapshot Size

A full BTC order book snapshot typically contains 20,000-40,000+ orders. The example above shows one order per side for brevity.

Incremental Update Response#

After the initial snapshot, you receive incremental updates wrapped in an Updates object containing order_statuses and book_diffs:

{
"channel": "l4Book",
"data": {
"Updates": {
"time": 1767878802703,
"height": 854890776,
"order_statuses": [
{
"time": "2026-01-08T13:26:42.703377851",
"user": "0xbc927e87d072dfac3693846a83fa6922cc6c5f2a",
"status": "open",
"order": {
"user": null,
"coin": "BTC",
"side": "B",
"limitPx": "90056.0",
"sz": "0.00014",
"oid": 289682192129,
"timestamp": 1767878802703,
"triggerCondition": "N/A",
"isTrigger": false,
"triggerPx": "0.0",
"isPositionTpsl": false,
"reduceOnly": false,
"orderType": "Limit",
"tif": "Alo",
"cloid": "0xa097c34ee13a42a1afeed2a5ce96b413"
}
}
],
"book_diffs": [
{
"user": "0xbc927e87d072dfac3693846a83fa6922cc6c5f2a",
"oid": 289682192129,
"px": "90056.0",
"coin": "BTC",
"raw_book_diff": {
"new": {
"sz": "0.00014"
}
}
}
]
}
}
}

Order Status Values#

The status field in order_statuses indicates the result of order processing:

StatusDescription
openOrder successfully placed and resting on the book
filledOrder fully executed
canceledOrder canceled by user
badAloPxRejectedALO (Add Liquidity Only) order rejected - would have crossed the spread
marginCanceledOrder canceled due to insufficient margin

Response Field Reference#

Snapshot Fields#

FieldTypeDescription
channelstringAlways "l4Book" for this subscription type
data.SnapshotobjectWrapper object for snapshot data
data.Snapshot.coinstringTrading pair symbol
data.Snapshot.heightnumberHyperliquid block height
data.Snapshot.levelsarrayTwo-element array: [bids, asks]

Individual Order Fields#

FieldTypeDescription
userstringWallet address of order owner
coinstringTrading pair
sidestring"B" = Bid (buy), "A" = Ask (sell)
limitPxstringLimit price
szstringRemaining order size
oidnumberUnique order ID
timestampnumberOrder placement time (Unix ms)
orderTypestring"Limit", "Market", etc.
tifstringTime-in-force: "Gtc" (Good til Cancel), "Ioc" (Immediate or Cancel), "Alo" (Add Liquidity Only)
cloidstringClient order ID (hex string, user-provided)
reduceOnlybooleanReduce-only order flag
isTriggerbooleanTrigger/stop order flag
triggerPxstringTrigger price ("0.0" if not a trigger order)
triggerConditionstringTrigger condition type ("N/A" if not a trigger order)
isPositionTpslbooleanPosition take-profit/stop-loss flag

Incremental Update Fields#

FieldTypeDescription
data.UpdatesobjectWrapper object for update data
data.Updates.timenumberUnix timestamp in milliseconds
data.Updates.heightnumberHyperliquid block height
data.Updates.order_statusesarrayOrder status changes (new orders, fills, rejections)
data.Updates.book_diffsarrayOrder book modifications

Order Status Fields#

FieldTypeDescription
timestringISO-8601 timestamp with nanosecond precision
userstringWallet address of order owner
statusstringOrder status (see Order Status Values above)
orderobjectOrder details

Book Diff Fields#

FieldTypeDescription
userstringWallet address of order owner
oidnumberOrder ID
pxstringPrice level
coinstringTrading pair
raw_book_diffobjectThe modification details
raw_book_diff.newobjectNew order added (contains sz)
raw_book_diff.modifiedobjectOrder size changed (contains sz)
raw_book_diff.removedobjectOrder removed from book

Code Examples#

#!/usr/bin/env python3
"""
L4 Order Book Stream - Individual order visibility
Track whale orders, queue position, and market microstructure
"""

import asyncio
import json
import websockets
from collections import defaultdict

class L4OrderBook:
def __init__(self):
self.bids = {} # oid -> order
self.asks = {} # oid -> order
self.orders_by_user = defaultdict(list)

def apply_snapshot(self, snapshot_data):
"""Initialize from snapshot (data.Snapshot object)"""
self.bids.clear()
self.asks.clear()
self.orders_by_user.clear()

levels = snapshot_data.get("levels", [[], []])
bid_orders, ask_orders = levels

for order in bid_orders:
self.bids[order['oid']] = order
self.orders_by_user[order['user']].append(order['oid'])

for order in ask_orders:
self.asks[order['oid']] = order
self.orders_by_user[order['user']].append(order['oid'])

print(f"Snapshot loaded: {len(self.bids)} bids, {len(self.asks)} asks")

def apply_update(self, update_data):
"""Apply incremental update (data.Updates object)"""
# Process book_diffs for order book changes
for diff in update_data.get("book_diffs", []):
oid = diff["oid"]
user = diff["user"]
raw_diff = diff.get("raw_book_diff", {})

if "new" in raw_diff:
# New order added
order = {
"oid": oid,
"user": user,
"limitPx": diff["px"],
"sz": raw_diff["new"]["sz"],
"coin": diff["coin"]
}
# Determine side from order_statuses if available
self.bids[oid] = order # You may need to track side separately
self.orders_by_user[user].append(oid)

elif "modified" in raw_diff:
# Order size changed
new_sz = raw_diff["modified"]["sz"]
if oid in self.bids:
self.bids[oid]["sz"] = new_sz
elif oid in self.asks:
self.asks[oid]["sz"] = new_sz

elif "removed" in raw_diff:
# Order removed
if oid in self.bids:
del self.bids[oid]
elif oid in self.asks:
del self.asks[oid]

def get_best_bid(self):
if not self.bids:
return None
return max(self.bids.values(), key=lambda o: float(o['limitPx']))

def get_best_ask(self):
if not self.asks:
return None
return min(self.asks.values(), key=lambda o: float(o['limitPx']))

def get_orders_by_user(self, user_address):
"""Get all orders for a specific wallet"""
oids = self.orders_by_user.get(user_address, [])
orders = []
for oid in oids:
if oid in self.bids:
orders.append(self.bids[oid])
elif oid in self.asks:
orders.append(self.asks[oid])
return orders

def get_large_orders(self, min_size):
"""Find orders above a size threshold"""
large = []
for order in list(self.bids.values()) + list(self.asks.values()):
if float(order['sz']) >= min_size:
large.append(order)
return sorted(large, key=lambda o: float(o['sz']), reverse=True)


async def stream_l4_book():
ws_url = "wss://your-instance.dwellir.com/ws"
book = L4OrderBook()

async with websockets.connect(ws_url) as websocket:
await websocket.send(json.dumps({
"method": "subscribe",
"subscription": {"type": "l4Book", "coin": "BTC"}
}))
print("Subscribed to BTC L4 order book\n")

message_count = 0
async for message in websocket:
msg = json.loads(message)

if msg.get("channel") != "l4Book":
continue

data = msg["data"]

# Detect snapshot vs update by checking wrapper key
if "Snapshot" in data:
book.apply_snapshot(data["Snapshot"])
elif "Updates" in data:
book.apply_update(data["Updates"])

message_count += 1

# Display stats every 10 messages
if message_count % 10 == 0:
best_bid = book.get_best_bid()
best_ask = book.get_best_ask()

if best_bid and best_ask:
spread = float(best_ask['limitPx']) - float(best_bid['limitPx'])
print(f"\nBids: {len(book.bids)} | Asks: {len(book.asks)} | "
f"Spread: ${spread:.2f}")

# Show largest orders
large = book.get_large_orders(0.1)[:3]
if large:
print("Top orders:")
for o in large:
side = "BID" if o.get('side') == 'B' else "ASK"
print(f" {side} {o['sz']} @ ${o['limitPx']} "
f"by {o['user'][:10]}...")


if __name__ == "__main__":
asyncio.run(stream_l4_book())

Queue Position Tracker#

Track your order's position in the queue at a price level:

class QueuePositionTracker:
def __init__(self, book, your_address):
self.book = book
self.your_address = your_address.lower()

def get_queue_position(self, oid):
"""
Get position in queue for an order.
Returns (position, total_ahead) where:
- position: 1-indexed position in queue
- total_ahead: total size ahead of your order
"""
# Find the order
order = self.book.bids.get(oid) or self.book.asks.get(oid)
if not order:
return None, None

price = order['limitPx']
side = order['side']
timestamp = order['timestamp']

# Get all orders at same price, same side
if side == 'B':
same_price = [o for o in self.book.bids.values()
if o['limitPx'] == price]
else:
same_price = [o for o in self.book.asks.values()
if o['limitPx'] == price]

# Sort by timestamp (FIFO)
same_price.sort(key=lambda o: o['timestamp'])

# Find position
position = 0
size_ahead = 0
for o in same_price:
position += 1
if o['oid'] == oid:
return position, size_ahead
size_ahead += float(o['sz'])

return None, None

def get_your_orders(self):
"""Get all your orders in the book"""
return self.book.get_orders_by_user(self.your_address)

Whale Watcher#

Alert on large orders from specific wallets:

class WhaleWatcher:
def __init__(self, whale_addresses=None, size_threshold=1.0):
self.whale_addresses = set(a.lower() for a in (whale_addresses or []))
self.size_threshold = size_threshold
self.alerts = []

def check_order(self, order):
"""Check if order triggers whale alert"""
user = order['user'].lower()
size = float(order['sz'])

# Check if known whale
if user in self.whale_addresses:
self.alerts.append({
'type': 'KNOWN_WHALE',
'user': user,
'side': 'BUY' if order['side'] == 'B' else 'SELL',
'size': size,
'price': order['limitPx']
})
return True

# Check if large order
if size >= self.size_threshold:
self.alerts.append({
'type': 'LARGE_ORDER',
'user': user,
'side': 'BUY' if order['side'] == 'B' else 'SELL',
'size': size,
'price': order['limitPx']
})
return True

return False

def process_book_update(self, book_diffs):
"""Process incremental updates for whale activity"""
for diff in book_diffs:
if diff['action'] == 'add':
self.check_order(diff['order'])

def get_recent_alerts(self, limit=10):
return self.alerts[-limit:]

Use Cases#

Queue Position Optimization#

Understand your place in the order queue:

  • Time priority: See exactly where your order sits at a price level
  • Fill probability: Estimate likelihood of execution based on orders ahead
  • Repositioning: Decide when to cancel and replace for better position

Whale Wallet Tracking#

Monitor large or notable traders:

  • Address tracking: Follow specific wallet addresses
  • Size alerts: Trigger on orders above threshold
  • Pattern detection: Identify accumulation or distribution

Market Microstructure Research#

Analyze order flow dynamics:

  • Order arrival rates: Study how orders enter the book
  • Cancellation patterns: Track order lifetime and modification frequency
  • Toxicity analysis: Measure adverse selection from order flow

Smart Order Routing#

Optimize order execution strategy:

  • Liquidity mapping: Know exactly what size exists at each level
  • Hidden liquidity: Detect when large orders are being worked
  • Impact estimation: Model expected slippage from current book state

Bandwidth Considerations#

L4 data is significantly higher bandwidth than L2:

AspectL2L4
Data per level3 fields (px, sz, n)15+ fields per order
Orders visibleAggregated count onlyEvery individual order
Update frequencyPer price levelPer order change
Typical message size1-5 KB10-100+ KB

Consider subscribing to L4 only for coins where you need individual order visibility.

Get Access#

Ready to integrate real-time Hyperliquid L4 order book data?


Stream institutional-grade Hyperliquid order book data with Dwellir's ultra-low latency infrastructure.