Leka
A low-latency C++20 price-time-priority limit order book and matching engine
▶ Replay viewer
Loading...
Searching...
No Matches

Fixed-range, tick-indexed array of PriceLevel slots for one side of a book. More...

#include <price_ladder.hpp>

Public Member Functions

 PriceLadder (Price minPrice, Price tickSize, std::size_t levelCount, bool descending)
 Pre-allocates every level the ladder will ever hold.
 PriceLadder (const PriceLadder &)=delete
PriceLadderoperator= (const PriceLadder &)=delete
 PriceLadder (PriceLadder &&)=delete
PriceLadderoperator= (PriceLadder &&)=delete
PriceLevellevelAt (Price price)
 Returns the level slot for a price; the slot always exists.
void markOccupied (Price price)
 Marks a level occupied; call exactly once when its order count transitions from zero to nonzero.
void markEmpty (Price price)
 Marks a level empty; call exactly once when its order count transitions from nonzero to zero.
PriceLevelbest ()
 Returns the best occupied level, or nullptr when the side is empty.
const PriceLevelbest () const
 Returns the best occupied level for read-only inspection.
std::size_t occupiedCount () const
 Returns the number of currently occupied levels.
template<typename Fn>
requires std::invocable<Fn&, const PriceLevel&>
void forEachOccupied (std::size_t maxLevels, Fn &&fn) const
 Walks up to maxLevels occupied levels, best first.

Static Public Attributes

static constexpr std::size_t npos = static_cast<std::size_t>(-1)
 Sentinel returned when no level is occupied.

Detailed Description

Fixed-range, tick-indexed array of PriceLevel slots for one side of a book.

A price is not treated as an ordered-map key; it is treated as an index. Every level in [minPrice, minPrice + (levelCount-1)*tickSize] is constructed once, at PriceLadder construction, and never destroyed or moved for the ladder's lifetime. Adding an order at a price that already has resting orders, or that has never had one, is the same O(1) array access with no allocation and no tree rebalancing; the old std::map<Price, PriceLevel> design paid a node allocation for the former and a pointer-chasing tree descent for the latter on every best-of-book query.

Because PriceLevel addresses are stable for the ladder's lifetime, Order's cached PriceLevel* pointer stays valid across every insert and remove at any other price: nothing is ever reallocated out from under it.

"Best" is always the lowest occupied index. Index 0 is the worst price a resting order can have on that side, so the two sides differ only in which direction price increases with index: ascending for asks (index 0 is the lowest, hence best, ask) and descending for bids (index 0 is the highest, hence best, bid). This lets both sides share one implementation and one "lowest set bit" query instead of a min-heap on one side and a max-heap on the other.

Occupancy is tracked in a bitset rather than by asking each PriceLevel whether it is empty, so finding the best level after it empties is a hardware find-first-set over a handful of 64-bit words rather than a linear scan of PriceLevel objects. The best index is additionally cached, so the common case, a level away from the current best changing occupancy, costs one array access and one bit flip with no search at all.

Definition at line 46 of file price_ladder.hpp.

Constructor & Destructor Documentation

◆ PriceLadder()

lob::PriceLadder::PriceLadder ( Price minPrice,
Price tickSize,
std::size_t levelCount,
bool descending )

Pre-allocates every level the ladder will ever hold.

Parameters
minPriceLowest representable price on this side.
tickSizePrice increment between adjacent indices.
levelCountNumber of representable price levels.
descendingTrue for the bid side, where index 0 is the highest price rather than the lowest.
Exceptions
std::invalid_argumentif minPrice is zero, tickSize is zero, levelCount is zero, or the configured range overflows.

Definition at line 8 of file price_ladder.cpp.

Member Function Documentation

◆ levelAt()

PriceLevel & lob::PriceLadder::levelAt ( Price price)

Returns the level slot for a price; the slot always exists.

Exceptions
std::out_of_rangeif the price falls outside the configured range.
std::invalid_argumentif the price does not fall on a configured tick boundary.

Definition at line 60 of file price_ladder.cpp.

◆ markOccupied()

void lob::PriceLadder::markOccupied ( Price price)

Marks a level occupied; call exactly once when its order count transitions from zero to nonzero.

Setting bits and lowering a cached minimum are both O(1); nothing here ever searches.

Definition at line 68 of file price_ladder.cpp.

◆ markEmpty()

void lob::PriceLadder::markEmpty ( Price price)

Marks a level empty; call exactly once when its order count transitions from nonzero to zero.

Clearing a bit is O(1). Re-deriving the best index is only needed when the level that just emptied WAS the best index; every other call is O(1) as well. That one case scans forward from the vacated index for the next set bit, which is a handful of instructions per 64-bit word rather than a per-level check, and in practice terminates almost immediately because resting liquidity clusters near the touch.

Definition at line 85 of file price_ladder.cpp.

References npos.

Referenced by lob::OrderBook::removeOrder().

◆ best() [1/2]

PriceLevel * lob::PriceLadder::best ( )

Returns the best occupied level, or nullptr when the side is empty.

Definition at line 115 of file price_ladder.cpp.

References npos.

◆ best() [2/2]

const PriceLevel * lob::PriceLadder::best ( ) const

Returns the best occupied level for read-only inspection.

Definition at line 119 of file price_ladder.cpp.

References npos.

◆ occupiedCount()

std::size_t lob::PriceLadder::occupiedCount ( ) const
inline

Returns the number of currently occupied levels.

Definition at line 98 of file price_ladder.hpp.

◆ forEachOccupied()

template<typename Fn>
requires std::invocable<Fn&, const PriceLevel&>
void lob::PriceLadder::forEachOccupied ( std::size_t maxLevels,
Fn && fn ) const
inline

Walks up to maxLevels occupied levels, best first.

Read-only traversal for market-data snapshots. Because "best" is always the lowest occupied index on either side (see the class comment), walking outward from the touch is the same forward bit-scan on both bids and asks, and it visits levels in true price priority order without sorting anything.

This deliberately stops after maxLevels rather than walking the whole ladder: a snapshot consumer wants the top of book, and the ladder may span hundreds of thousands of mostly-empty levels.

Definition at line 115 of file price_ladder.hpp.

References npos.

Member Data Documentation

◆ npos

std::size_t lob::PriceLadder::npos = static_cast<std::size_t>(-1)
staticconstexpr

Sentinel returned when no level is occupied.

Definition at line 49 of file price_ladder.hpp.

Referenced by best(), best(), forEachOccupied(), and markEmpty().


The documentation for this class was generated from the following files: