Leka
A low-latency C++20 price-time-priority limit order book and matching engine
▶ Replay viewer
Loading...
Searching...
No Matches
order_book.hpp
1// include/lob/book/order_book.hpp
2
3#ifndef ORDER_BOOK_HPP
4#define ORDER_BOOK_HPP
5
7#include "lob/book/order_pool.hpp"
8#include "lob/book/price_ladder.hpp"
9#include "lob/index/order_index.hpp"
10#include "lob/book/price_level.hpp"
11
12#include <cstddef>
13
14namespace lob {
15
16/**
17 * @brief Owns resting limit orders and maintains their book indexes.
18 *
19 * OrderBook owns Order storage through OrderPool. OrderIndex and PriceLevel
20 * objects hold non-owning references to those orders. Matching decisions are
21 * made by MatchingEngine; this class maintains synchronized book state.
22 *
23 * Bid and ask levels are tick-indexed arrays (PriceLadder), not ordered maps:
24 * a price is an index, not a search key, so both adding a level and finding
25 * the best one are array operations rather than a tree allocation and a tree
26 * descent. That requires a bounded, pre-configured price range; see the
27 * two-argument constructor.
28 */
29class OrderBook {
30 public:
31 /** Number of representable price levels used by the default constructor. */
32 static constexpr std::size_t DefaultLevelCount = 65536;
33
34 /**
35 * @brief Creates an order book with a small default price range.
36 *
37 * The default range and tick (integer prices 1 through 65536, tick 1)
38 * exist for tests and examples that do not care about the ladder's
39 * configuration. A real instrument should use the explicit
40 * constructor, sized to its actual tick size and trading range, and
41 * call it once at startup: the ladder never grows after construction.
42 */
43 OrderBook();
44
45 /**
46 * @brief Creates an order book with an explicitly sized price ladder.
47 * @param minPrice Lowest representable price on either side.
48 * @param tickSize Price increment between adjacent levels.
49 * @param levelCount Number of representable price levels.
50 * @throws std::invalid_argument for an invalid or overflowing range.
51 */
52 OrderBook(Price minPrice, Price tickSize, std::size_t levelCount);
53
54 OrderBook(const OrderBook&) = delete;
55 OrderBook& operator=(const OrderBook&) = delete;
56 OrderBook(OrderBook&&) = delete;
57 OrderBook& operator=(OrderBook&&) = delete;
58 ~OrderBook() = default;
59
60 /**
61 * @brief Adds a valid resting limit order to the appropriate side.
62 *
63 * The book allocates the order, links it into the side's PriceLevel FIFO,
64 * and registers it in OrderIndex. If any step fails, all earlier state is
65 * rolled back before the pool slot is released.
66 * @return The stable address of the newly stored order.
67 * @throws std::invalid_argument if the order is invalid or not a limit.
68 * @throws std::logic_error if the order ID already exists.
69 */
70 Order* addOrder(OrderId orderId, Price price, Quantity quantity,
71 Timestamp timestamp, OrderSide orderSide,
72 OrderType orderType, SequenceNumber sequenceNumber);
73
74 /**
75 * @brief Adds a partially filled limit order while preserving its history.
76 *
77 * This is used when MatchingEngine has executed part of an incoming order.
78 * The stored order retains the submitted quantity as originalQuantity and
79 * enters the book with only remainingQuantity available.
80 * @param originalQuantity Quantity submitted before any executions.
81 * @param remainingQuantity Quantity still available to execute.
82 * @throws std::invalid_argument if remainingQuantity is zero, exceeds the
83 * original quantity, or the order is otherwise invalid.
84 */
86 OrderId orderId, Price price, Quantity originalQuantity,
87 Quantity remainingQuantity, Timestamp timestamp, OrderSide orderSide,
88 OrderType orderType, SequenceNumber sequenceNumber);
89
90 /**
91 * @brief Removes a resting order by ID.
92 * @return false when no order with the ID is present; true after removal.
93 *
94 * Removal is delegated to removeOrder(Order*) so the PriceLevel, OrderIndex,
95 * and OrderPool remain synchronized.
96 */
97 bool cancelOrder(const OrderId& orderId);
98
99 /**
100 * @brief Shrinks a resting order in place, preserving its priority.
101 *
102 * The order keeps its price, its FIFO position, and its sequence
103 * number; only its remaining quantity and the level aggregate change.
104 * This is the sole modification that does not forfeit time priority,
105 * which is why repricing and size increases are expressed as a cancel
106 * followed by a new order rather than handled here.
107 *
108 * A missing order is reported rather than thrown: a replayed feed may
109 * reference an order that was resting before the captured window began.
110 *
111 * @return false when the order is absent; true after the reduction.
112 * @throws std::invalid_argument if newQuantity is zero or above the
113 * order's current remaining quantity.
114 */
115 bool reduceOrder(const OrderId& orderId, Quantity newQuantity);
116
117 /**
118 * @brief Removes a currently resting order from every book structure.
119 *
120 * The order is unlinked from its FIFO and its empty PriceLevel is erased
121 * before its index entry and pool storage are released. The caller must not
122 * use the pointer after this function returns.
123 */
124 void removeOrder(Order* order);
125
126 /** Finds an order by ID, or returns nullptr when absent. */
127 Order* findOrder(const OrderId& orderId);
128 /** Finds an order by ID without allowing mutation. */
129 const Order* findOrder(const OrderId& orderId) const;
130
131 /**
132 * @brief Returns the mutable highest-priced bid level.
133 * @return The best bid level, or nullptr when no bids are resting.
134 */
136 /**
137 * @brief Returns the mutable lowest-priced ask level.
138 * @return The best ask level, or nullptr when no asks are resting.
139 */
141 /** @brief Returns the best bid level for read-only inspection. */
142 const PriceLevel* getBestBid() const;
143 /** @brief Returns the best ask level for read-only inspection. */
144 const PriceLevel* getBestAsk() const;
145
146 /** Returns the number of occupied bid price levels. */
147 std::size_t getBidLevelCount() const { return bids.occupiedCount(); }
148 /** Returns the number of occupied ask price levels. */
149 std::size_t getAskLevelCount() const { return asks.occupiedCount(); }
150
151 /**
152 * @brief Pre-allocates order storage and index capacity for up to
153 * @p orderCount live orders. See OrderPool::reserve(),
154 * OrderIndex::reserve(), and ARCH_DECISIONS.md ADR-008. Call once,
155 * before trading begins.
156 */
157 void reserveOrderCapacity(std::size_t orderCount);
158
159 /**
160 * @brief Fills @p out with the top @p depth levels of each side.
161 *
162 * Read-only, and deliberately not called from anywhere inside the
163 * matching path: a caller captures this *between* events so that
164 * nothing a snapshot consumer needs can ever execute inside
165 * processEvent(). @p out is reused rather than returned by value, so
166 * a caller snapshotting repeatedly stops allocating once its vectors
167 * reach steady-state capacity — the same reasoning as the execution
168 * buffer in ADR-005.
169 */
170 void captureSnapshot(BookSnapshot& out, std::size_t depth) const;
171
172 private:
173 OrderPool orderPool;
174 OrderIndex orderIndex;
175 // Bids are indexed with descending=true, so "best" is the highest
176 // price; asks are ascending, so "best" is the lowest.
177 PriceLadder bids;
178 PriceLadder asks;
179
180 Order* addOrderWithQuantities(
181 OrderId orderId, Price price, Quantity originalQuantity,
182 Quantity remainingQuantity, Timestamp timestamp, OrderSide orderSide,
183 OrderType orderType, SequenceNumber sequenceNumber);
184};
185
186} // namespace lob
187
188#endif // ORDER_BOOK_HPP
static constexpr std::size_t DefaultLevelCount
Number of representable price levels used by the default constructor.
std::size_t getBidLevelCount() const
Returns the number of occupied bid price levels.
void reserveOrderCapacity(std::size_t orderCount)
Pre-allocates order storage and index capacity for up to orderCount live orders.
PriceLevel * getBestBid()
Returns the mutable highest-priced bid level.
Order * addRestingRemainder(OrderId orderId, Price price, Quantity originalQuantity, Quantity remainingQuantity, Timestamp timestamp, OrderSide orderSide, OrderType orderType, SequenceNumber sequenceNumber)
Adds a partially filled limit order while preserving its history.
bool reduceOrder(const OrderId &orderId, Quantity newQuantity)
Shrinks a resting order in place, preserving its priority.
Order * addOrder(OrderId orderId, Price price, Quantity quantity, Timestamp timestamp, OrderSide orderSide, OrderType orderType, SequenceNumber sequenceNumber)
Adds a valid resting limit order to the appropriate side.
void captureSnapshot(BookSnapshot &out, std::size_t depth) const
Fills out with the top depth levels of each side.
std::size_t getAskLevelCount() const
Returns the number of occupied ask price levels.
OrderBook()
Creates an order book with a small default price range.
Definition order_book.cpp:8
bool cancelOrder(const OrderId &orderId)
Removes a resting order by ID.
Order * findOrder(const OrderId &orderId)
Finds an order by ID, or returns nullptr when absent.
void removeOrder(Order *order)
Removes a currently resting order from every book structure.
PriceLevel * getBestAsk()
Returns the mutable lowest-priced ask level.
Type-safe identifier for an order; zero is reserved as invalid.
Definition order_id.hpp:10
Open-addressed OrderId -> Order* index with average O(1) lookup.
Provides stable, pooled storage for Order objects.
Represents a resting or incoming order and its lifecycle state.
Definition order.hpp:17
Fixed-range, tick-indexed array of PriceLevel slots for one side of a book.
Maintains FIFO resting orders at one price.
Type-safe nonzero price value used for price ordering.
Definition price.hpp:9
Type-safe unsigned order quantity; zero represents a filled state.
Definition quantity.hpp:8
Monotonic engine sequence used for deterministic processing order.
Nanoseconds since the Unix epoch; zero is reserved as invalid.
Definition timestamp.hpp:10
Point-in-time view of book state for consumers outside the engine.
Top-of-book depth plus the counters a viewer needs for context.