7#define SLICK_OB_INLINE inline
27 : symbol_(other.symbol_),
28 sides_(std::move(other.sides_)),
29 observers_(std::move(other.observers_)),
30 cached_tob_(other.cached_tob_),
31 cached_best_bid_(other.cached_best_bid_),
32 cached_best_ask_(other.cached_best_ask_),
33 tob_seq_(other.tob_seq_.load(std::memory_order_relaxed)),
34 last_seq_num_(other.last_seq_num_) {
41 sides_ = std::move(other.sides_);
42 observers_ = std::move(other.observers_);
43 cached_tob_ = other.cached_tob_;
44 cached_best_bid_ = other.cached_best_bid_;
45 cached_best_ask_ = other.cached_best_ask_;
46 tob_seq_.store(other.tob_seq_.load(std::memory_order_relaxed), std::memory_order_relaxed);
47 last_seq_num_ = other.last_seq_num_;
53 uint64_t seq_num,
bool is_last_in_batch) {
67 auto it =
sides_[side].find(price);
68 if (it !=
sides_[side].end()) {
69 uint16_t level_idx =
static_cast<uint16_t
>(std::distance(
sides_[side].begin(), it));
79 if (is_last_in_batch) {
93 auto [it, inserted] =
sides_[side].insertOrUpdate(price, quantity, timestamp);
96 uint16_t level_idx =
static_cast<uint16_t
>(std::distance(
sides_[side].begin(), it));
102 uint8_t change_flags = 0;
112 if (is_last_in_batch) {
128 if (!
sides_[side].erase(price)) {
153 seq1 =
tob_seq_.load(std::memory_order_acquire);
156 seq1 =
tob_seq_.load(std::memory_order_acquire);
163 seq2 =
tob_seq_.load(std::memory_order_acquire);
164 }
while (seq1 != seq2);
175 seq1 =
tob_seq_.load(std::memory_order_acquire);
178 seq1 =
tob_seq_.load(std::memory_order_acquire);
185 seq2 =
tob_seq_.load(std::memory_order_acquire);
186 }
while (seq1 != seq2);
197 seq1 =
tob_seq_.load(std::memory_order_acquire);
200 seq1 =
tob_seq_.load(std::memory_order_acquire);
205 seq2 =
tob_seq_.load(std::memory_order_acquire);
206 }
while (seq1 != seq2);
212 return sides_[side].getLevels(depth);
217 return sides_[side].getLevel(price);
222 const auto& container = sides_[side];
223 if (index >= container.size()) {
226 return &container[index];
231 return sides_[side].size();
236 return sides_[side].empty();
248 Price new_best_bid = bid ? bid->price : 0;
249 Quantity new_bid_qty = bid ? bid->quantity : 0;
250 Price new_best_ask = ask ? ask->price : 0;
251 Quantity new_ask_qty = ask ? ask->quantity : 0;
254 uint8_t bid_change_flags = 0;
255 uint8_t ask_change_flags = 0;
261 if (bid_change_flags || ask_change_flags) {
263 uint64_t seq =
tob_seq_.load(std::memory_order_relaxed);
264 tob_seq_.store(seq + 1, std::memory_order_release);
287 tob_seq_.store(seq + 2, std::memory_order_release);
299 uint16_t level_idx = 0;
void notifyPriceLevelUpdate(const PriceLevelUpdate &update) const
Notify all observers of price level update.
void notifyTopOfBookUpdate(const TopOfBook &tob) const
Notify all observers of top-of-book update.
void notifySnapshotEnd(SymbolId symbol, uint64_t seq_num, Timestamp timestamp) const
Notify all observers that snapshot processing is complete.
void notifySnapshotBegin(SymbolId symbol, uint64_t seq_num, Timestamp timestamp) const
Notify all observers that snapshot processing is beginning.
Level 2 OrderBook - aggregated price levels.
std::vector< detail::PriceLevelL2 > getLevels(Side side, std::size_t depth=0) const
Get price levels for a side.
ObserverManager observers_
detail::PriceLevelL2 cached_best_ask_
std::atomic< uint64_t > tob_seq_
std::size_t levelCount(Side side) const noexcept
Get number of levels on a side.
void notifyTopOfBookIfChanged(Timestamp timestamp)
Notify observers of top-of-book update if best changed Updates cached_tob_ after notification.
uint16_t change_starting_index_
const detail::PriceLevelL2 * getBestBid() const noexcept
Get best bid (highest buy price)
const detail::PriceLevelL2 * getLevel(Side side, Price price) const noexcept
Get a specific price level by price.
OrderBookL2 & operator=(const OrderBookL2 &)=delete
TopOfBook getTopOfBook() const noexcept
Get top-of-book snapshot.
void emitSnapshot(Timestamp timestamp)
Emit complete orderbook snapshot to observers Useful for replaying full book state to a new observer ...
bool isEmpty() const noexcept
Check if book is empty (both sides)
void updateLevel(Side side, Price price, Quantity quantity, Timestamp timestamp, uint64_t seq_num=0, bool is_last_in_batch=true)
Add or update a price level If quantity is 0, the level is deleted.
const detail::PriceLevelL2 * getLevelByIndex(Side side, uint16_t index) const noexcept
Get a price level by index (0 = best)
bool deleteLevel(Side side, Price price, Timestamp timestamp=0)
Delete a price level Does not emit a PriceLevelUpdate, but refreshes the cached top-of-book (and noti...
void clearSide(Side side, Timestamp timestamp=0)
Clear all levels on one side Refreshes the cached top-of-book (and notifies onTopOfBookUpdate if it c...
detail::PriceLevelL2 cached_best_bid_
const detail::PriceLevelL2 * getBestAsk() const noexcept
Get best ask (lowest sell price)
OrderBookL2(SymbolId symbol, std::size_t initial_capacity=32)
Constructor.
void clear(Timestamp timestamp=0)
Clear all levels (both sides) Refreshes the cached top-of-book (and notifies onTopOfBookUpdate if it ...
std::array< detail::LevelContainer, SideCount > sides_
Container for storing sorted price levels Uses a sorted vector for cache-friendly storage (optimal fo...
#define SLICK_NAMESPACE_END
#define SLICK_NAMESPACE_BEGIN
#define SLICK_ASSERT(expr)
int64_t Quantity
Quantity type - volume or size.
int64_t Price
Price type - fixed-point representation Example: For 4 decimal places, price = actual_price * 10000 T...
Side
Order side - using enum for easy array indexing.
constexpr uint16_t INVALID_INDEX
uint16_t SymbolId
Symbol identifier - can be hash or sequential ID.
uint64_t Timestamp
Timestamp in nanoseconds since epoch.
Level 2 price level update event Note: quantity = 0 implies the price level should be deleted.
Top-of-book snapshot - best bid and ask.
std::array< uint8_t, 2 > change_flags
Price level for Level 2 orderbook (aggregated) Stores total quantity at a specific price point.