Skip to main content

L2 Order Book (Level 2)

Stream aggregated order book data showing total size at each price level. The L2 book provides a consolidated view of market depth, with configurable levels and price aggregation for bandwidth optimization.

Code Examples Repository

How to Subscribe#

Send a subscription message to the WebSocket endpoint:

{
"method": "subscribe",
"subscription": {
"type": "l2Book",
"coin": "BTC",
"nSigFigs": 5,
"nLevels": 20
}
}

Subscription Parameters#

ParameterTypeRequiredDefaultDescription
typestringYes-Must be "l2Book"
coinstringYes-Trading pair symbol (e.g., "BTC", "ETH")
nSigFigsnumberNo5Price aggregation: 2-5 significant figures
nLevelsnumberNo20Number of levels per side: 1-100

Understanding nSigFigs (Price Aggregation)#

The nSigFigs parameter controls how prices are rounded for aggregation:

nSigFigsBTC ExampleEffect
5$106,217Most granular - individual price points
4$106,200Moderate aggregation
3$106,000Coarse aggregation
2$110,000Very coarse - major levels only

Lower nSigFigs values reduce message frequency and bandwidth but sacrifice price precision.

Sample Response#

{
"channel": "l2Book",
"data": {
"coin": "BTC",
"time": 1767878802704,
"levels": [
[
{"px": "90057", "sz": "5.34526", "n": 25},
{"px": "90056", "sz": "0.01336", "n": 4},
{"px": "90055", "sz": "0.33772", "n": 5},
{"px": "90054", "sz": "0.01859", "n": 2},
{"px": "90053", "sz": "0.08936", "n": 2}
],
[
{"px": "90058", "sz": "7.46316", "n": 18},
{"px": "90059", "sz": "0.44694", "n": 5},
{"px": "90060", "sz": "0.00013", "n": 1},
{"px": "90061", "sz": "0.05576", "n": 3},
{"px": "90062", "sz": "0.9716", "n": 3}
]
]
}
}

In this example:

  • Best bid: $90,057 with 5.34526 BTC across 25 orders
  • Best ask: $90,058 with 7.46316 BTC across 18 orders
  • Spread: $1 (approximately 1.1 basis points)

Response Field Reference#

FieldTypeDescription
channelstringAlways "l2Book" for this subscription type
data.coinstringTrading pair symbol
data.timenumberUnix timestamp in milliseconds
data.levelsarrayTwo-element array: [bids, asks]
levels[0]arrayBid levels (buyers), sorted best (highest) to worst
levels[1]arrayAsk levels (sellers), sorted best (lowest) to worst

Level Object Fields#

FieldTypeDescription
pxstringPrice level
szstringTotal size at this price level
nnumberNumber of orders aggregated at this level

Code Examples#

#!/usr/bin/env python3
"""
L2 Order Book Stream - Aggregated price levels with spread display
"""

import asyncio
import json
import websockets

def display_orderbook(data):
"""Display the order book with spread calculation"""
if data.get("channel") != "l2Book":
return

coin = data["data"]["coin"]
levels = data["data"]["levels"]
bids = levels[0]
asks = levels[1]

print(f"\n{'='*55}")
print(f"{coin} Order Book")
print(f"{'='*55}")

# Display asks (sellers) - reverse for visual display
print("\nASKS (Sellers)")
print(f"{'Price':<15} {'Size':<15} {'Orders':<10}")
print("-" * 55)
for ask in reversed(asks[:5]):
print(f"${ask['px']:<14} {ask['sz']:<15} {ask['n']:<10}")

# Calculate and display spread
if bids and asks:
best_bid = float(bids[0]['px'])
best_ask = float(asks[0]['px'])
spread = best_ask - best_bid
spread_bps = (spread / best_bid) * 10000

print(f"\n{'─'*55}")
print(f"Spread: ${spread:.2f} ({spread_bps:.1f} bps)")
print(f"{'─'*55}\n")

# Display bids (buyers)
print("BIDS (Buyers)")
print(f"{'Price':<15} {'Size':<15} {'Orders':<10}")
print("-" * 55)
for bid in bids[:5]:
print(f"${bid['px']:<14} {bid['sz']:<15} {bid['n']:<10}")


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

async with websockets.connect(ws_url) as websocket:
# Subscribe to ETH L2 order book
await websocket.send(json.dumps({
"method": "subscribe",
"subscription": {
"type": "l2Book",
"coin": "ETH",
"nLevels": 10,
"nSigFigs": 5
}
}))
print("Subscribed to ETH L2 order book")

async for message in websocket:
data = json.loads(message)
display_orderbook(data)


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

Liquidity Depth Calculator#

Calculate total liquidity within a price range:

class LiquidityAnalyzer:
def __init__(self):
self.current_book = None

def update(self, book_data):
self.current_book = book_data

def get_liquidity_within_percent(self, percent):
"""Calculate total liquidity within X% of mid price"""
if not self.current_book:
return None

levels = self.current_book["levels"]
bids, asks = levels[0], levels[1]

if not bids or not asks:
return None

best_bid = float(bids[0]['px'])
best_ask = float(asks[0]['px'])
mid_price = (best_bid + best_ask) / 2

threshold = mid_price * (percent / 100)
upper_bound = mid_price + threshold
lower_bound = mid_price - threshold

bid_liquidity = sum(
float(b['sz']) for b in bids
if float(b['px']) >= lower_bound
)

ask_liquidity = sum(
float(a['sz']) for a in asks
if float(a['px']) <= upper_bound
)

return {
'bid_liquidity': bid_liquidity,
'ask_liquidity': ask_liquidity,
'total_liquidity': bid_liquidity + ask_liquidity,
'imbalance': bid_liquidity / ask_liquidity if ask_liquidity > 0 else float('inf')
}

def get_depth_at_price(self, side, price_levels=5):
"""Get cumulative depth for top N price levels"""
if not self.current_book:
return []

levels = self.current_book["levels"]
book_side = levels[0] if side == 'bid' else levels[1]

cumulative = 0
depth = []
for level in book_side[:price_levels]:
cumulative += float(level['sz'])
depth.append({
'price': level['px'],
'size': level['sz'],
'cumulative': cumulative,
'orders': level['n']
})

return depth

Order Book Imbalance Signal#

Detect buying/selling pressure from book shape:

def calculate_imbalance(bids, asks, levels=5):
"""
Calculate order book imbalance.
Returns value from -1 (all asks) to +1 (all bids)
"""
bid_volume = sum(float(b['sz']) for b in bids[:levels])
ask_volume = sum(float(a['sz']) for a in asks[:levels])

total = bid_volume + ask_volume
if total == 0:
return 0

return (bid_volume - ask_volume) / total


def get_imbalance_signal(imbalance):
"""Convert imbalance to trading signal"""
if imbalance > 0.3:
return "STRONG_BUY"
elif imbalance > 0.1:
return "BUY"
elif imbalance < -0.3:
return "STRONG_SELL"
elif imbalance < -0.1:
return "SELL"
return "NEUTRAL"

Use Cases#

Market Making#

Monitor spread and depth to optimize quote placement:

  • Spread monitoring: Track bid-ask spread for quote width
  • Inventory management: Adjust quotes based on book imbalance
  • Competition analysis: Count orders at each level to gauge competition

Liquidity Analysis#

Assess market depth for large order execution:

  • Slippage estimation: Calculate expected price impact for order sizes
  • Best execution: Find optimal order sizes and timing
  • Market quality metrics: Track spread, depth, and resilience

Support/Resistance Detection#

Identify significant price levels from order concentration:

  • Order clusters: Large n values indicate many orders at a level
  • Size walls: High sz values suggest strong support/resistance
  • Level breaks: Monitor when significant levels get absorbed

Algorithmic Trading Signals#

Generate trading signals from book dynamics:

  • Imbalance signals: Buy when bids dominate, sell when asks dominate
  • Spread compression: Trade when spread narrows (high liquidity)
  • Depth changes: Alert on sudden liquidity additions or removals

Subscription Management#

Adjusting Parameters#

Change depth or aggregation by resubscribing:

# Switch from 20 levels to 50 levels
await websocket.send(json.dumps({
"method": "unsubscribe",
"subscription": {"type": "l2Book", "coin": "BTC", "nLevels": 20}
}))

await websocket.send(json.dumps({
"method": "subscribe",
"subscription": {"type": "l2Book", "coin": "BTC", "nLevels": 50}
}))

Bandwidth Optimization#

For high-frequency updates, use lower nSigFigs:

{
"method": "subscribe",
"subscription": {
"type": "l2Book",
"coin": "BTC",
"nSigFigs": 3,
"nLevels": 10
}
}

This reduces message size and frequency while maintaining key price levels.

Get Access#

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


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