L2Book Structure
The L2Book gives you 20 levels of order book depth:
rust
pub struct L2Book {
pub bids: [Level; 20], // Best bid at index 0
pub asks: [Level; 20], // Best ask at index 0
pub bid_ct: u8, // Number of valid bid levels
pub ask_ct: u8, // Number of valid ask levels
pub symbol_id: u16, // Symbol identifier
pub recv_ns: u64, // When we received this update
}
pub struct Level {
pub px_1e9: u64, // Price × 10⁹
pub sz_1e8: u64, // Size × 10⁸
}Note
The book is sorted: bids[0] is the highest bid, asks[0] is the lowest ask.
Accessing Levels
rust
fn on_book(&mut self, book: &L2Book, state: &AlgoState, _features: &OnlineFeatures, actions: &mut Actions) {
// Best bid/ask
let best_bid = &book.bids[0];
let best_ask = &book.asks[0];
// Check if valid
if book.bid_ct > 0 && book.ask_ct > 0 {
println!("Bid: {} @ {}", best_bid.sz_1e8, best_bid.px_1e9);
println!("Ask: {} @ {}", best_ask.sz_1e8, best_ask.px_1e9);
}
// Iterate deeper levels
for i in 0..book.bid_ct as usize {
let level = &book.bids[i];
// ...
}
}Helper Methods
Prices
| Method | Returns | Description |
|---|---|---|
best_bid() | Option<&Level> | Best bid level |
best_ask() | Option<&Level> | Best ask level |
mid_px_1e9() | u64 | Mid price (avg of best bid/ask) |
spread_1e9() | u64 | Spread in price units |
spread_bps() | u32 | Spread in basis points |
rust
// Get mid price
let mid = book.mid_px_1e9();
// Check if spread is too tight
if book.spread_bps() < 5 {
return; // Don't trade
}Depth & Liquidity
| Method | Returns | Description |
|---|---|---|
bid_depth_1e8(n) | u64 | Total bid size for top N levels |
ask_depth_1e8(n) | u64 | Total ask size for top N levels |
imbalance_bps(n) | i32 | Order imbalance (-10000 to +10000) |
rust
// Total liquidity in top 5 levels
let bid_liq = book.bid_depth_1e8(5);
let ask_liq = book.ask_depth_1e8(5);
// Order imbalance (positive = more bids)
let imb = book.imbalance_bps(5);
if imb > 2000 {
// Strong bid-side pressure
}Example: VWAP Calculation
rust
fn vwap_1e9(book: &L2Book, side: i8, qty_1e8: i64) -> Option<u64> {
let levels = if side > 0 { &book.asks } else { &book.bids };
let count = if side > 0 { book.ask_ct } else { book.bid_ct } as usize;
let mut remaining = qty_1e8;
let mut total_value: u128 = 0;
let mut total_qty: i64 = 0;
for i in 0..count {
if remaining <= 0 { break; }
let lvl = &levels[i];
let fill = remaining.min(lvl.sz_1e8 as i64);
total_value += fill as u128 * lvl.px_1e9 as u128;
total_qty += fill;
remaining -= fill;
}
if total_qty == 0 { return None; }
Some((total_value / total_qty as u128) as u64)
}Example: Microprice
rust
fn microprice_1e9(book: &L2Book) -> u64 {
if book.bid_ct == 0 || book.ask_ct == 0 {
return 0;
}
let bid = &book.bids[0];
let ask = &book.asks[0];
let total = bid.sz_1e8 + ask.sz_1e8;
if total == 0 {
return book.mid_px_1e9();
}
// Size-weighted mid
((bid.px_1e9 as u128 * ask.sz_1e8 as u128
+ ask.px_1e9 as u128 * bid.sz_1e8 as u128)
/ total as u128) as u64
}Performance Tips
Tip
The L2Book struct is 688 bytes and fits in L1 cache. Direct array access is faster than method calls for hot loops.
rust
// Fast: direct access
let bid_px = book.bids[0].px_1e9;
// Slightly slower: method call
let bid_px = book.best_bid().map(|l| l.px_1e9).unwrap_or(0);