10auto L3Book::side_levels(
Side side)
noexcept -> std::vector<Level>& {
14auto L3Book::side_levels(
Side side)
const noexcept ->
const std::vector<Level>& {
18auto L3Book::allocate_node() -> std::uint32_t {
19 if (m_free_head != NIL) {
20 const std::uint32_t index = m_free_head;
21 m_free_head = m_pool[index].next;
22 m_pool[index] = OrderNode {};
25 m_pool.push_back(OrderNode {});
26 return static_cast<std::uint32_t
>(m_pool.size() - 1);
29auto L3Book::free_node(std::uint32_t node_index) ->
void {
30 m_pool[node_index] = OrderNode {};
31 m_pool[node_index].next = m_free_head;
32 m_free_head = node_index;
35auto L3Book::find_level(
Side side, std::uint32_t
price)
const -> std::uint32_t {
36 const auto& levels = side_levels(
side);
43 const auto iter = std::lower_bound(
47 [](
const Level& level, std::uint32_t value) { return level.price > value; }
49 if (iter != levels.end() && iter->price ==
price) {
50 return static_cast<std::uint32_t
>(iter - levels.begin());
54 const auto iter = std::lower_bound(
58 [](
const Level& level, std::uint32_t value) { return level.price < value; }
60 if (iter != levels.end() && iter->price ==
price) {
61 return static_cast<std::uint32_t
>(iter - levels.begin());
67auto L3Book::find_or_create_level(
Side side, std::uint32_t
price) -> std::uint32_t {
68 auto& levels = side_levels(
side);
76 [](
const Level& level, std::uint32_t value) { return level.price > value; }
82 [](const Level& level, std::uint32_t value) {
return level.price < value; }
84 if (position != levels.end() && position->price ==
price) {
85 return static_cast<std::uint32_t
>(position - levels.begin());
88 new_level.price =
price;
89 const auto inserted = levels.insert(position, new_level);
90 return static_cast<std::uint32_t
>(inserted - levels.begin());
99 const std::uint32_t node_index = allocate_node();
100 OrderNode& node = m_pool[node_index];
106 const std::uint32_t level_index = find_or_create_level(
side,
price);
107 Level& level = side_levels(
side)[level_index];
110 node.prev = level.tail;
112 if (level.tail != NIL) {
113 m_pool[level.tail].next = node_index;
115 level.head = node_index;
117 level.tail = node_index;
118 level.total_shares +=
shares;
124auto L3Book::unlink_node(std::uint32_t node_index) ->
void {
125 OrderNode& node = m_pool[node_index];
126 const std::uint32_t level_index = find_level(node.side, node.price);
127 if (level_index == NIL) {
130 auto& levels = side_levels(node.side);
131 Level& level = levels[level_index];
133 if (node.prev != NIL) {
134 m_pool[node.prev].next = node.next;
136 level.head = node.next;
138 if (node.next != NIL) {
139 m_pool[node.next].prev = node.prev;
141 level.tail = node.prev;
143 level.total_shares -= node.shares;
145 if (level.order_count == 0) {
146 levels.erase(levels.begin() + level_index);
155 OrderNode& node = m_pool[node_index];
156 const std::uint32_t removed = std::min(
shares, node.shares);
158 if (removed == node.shares) {
159 unlink_node(node_index);
160 free_node(node_index);
165 node.shares -= removed;
166 const std::uint32_t level_index = find_level(node.side, node.price);
167 if (level_index != NIL) {
168 side_levels(node.side)[level_index].total_shares -= removed;
183 unlink_node(node_index);
184 free_node(node_index);
189 std::uint64_t old_reference_number,
190 std::uint64_t new_reference_number,
194 const std::uint32_t node_index = m_index.find(old_reference_number);
198 const Side side = m_pool[node_index].side;
199 delete_order(old_reference_number);
212 return m_pool[node_index].price;
220 return m_pool[node_index].side;
225 if (!m_bids.empty()) {
228 result.bid_shares = m_bids.front().total_shares;
230 if (!m_asks.empty()) {
231 result.has_ask =
true;
233 result.ask_shares = m_asks.front().total_shares;
239 const auto& levels = side_levels(
side);
240 const std::size_t count = max_levels == 0 ? levels.size() : std::min(max_levels, levels.size());
241 std::vector<DepthLevel> result;
242 result.reserve(count);
243 for (std::size_t index = 0; index < count; ++index) {
246 .shares = levels[index].total_shares,
247 .order_count = levels[index].order_count,
254 std::vector<OrderView> result;
255 const std::uint32_t level_index = find_level(
side,
price);
256 if (level_index == NIL) {
259 const Level& level = side_levels(
side)[level_index];
260 result.reserve(level.order_count);
261 for (std::uint32_t node_index = level.head; node_index != NIL;
262 node_index = m_pool[node_index].next) {
263 const OrderNode& node = m_pool[node_index];
266 .shares = node.shares,
274 return side_levels(
side).size();
auto execute_order(std::uint64_t reference_number, std::uint32_t shares) -> std::uint32_t
Removes shares from an order on execution (ITCH E/C), deleting it when fully filled.
auto replace_order(std::uint64_t old_reference_number, std::uint64_t new_reference_number, std::uint32_t shares, std::uint32_t price) -> void
Replaces an order with a new reference number, size, and price on the same side (ITCH U).
auto orders_at(Side side, std::uint32_t price) const -> std::vector< OrderView >
The resting orders at a given price on a side, in time priority.
auto delete_order(std::uint64_t reference_number) -> void
Deletes an order in its entirety (ITCH D).
auto level_count(Side side) const noexcept -> std::size_t
The number of active price levels on a side.
auto order_side(std::uint64_t reference_number) const -> std::optional< Side >
The side of a resting order, if present.
auto contains(std::uint64_t reference_number) const -> bool
Whether an order with the given reference number is resting.
L3Book(std::string symbol={})
Constructs a book, optionally tagged with its stock symbol.
auto order_price(std::uint64_t reference_number) const -> std::optional< std::uint32_t >
The raw limit price of a resting order, if present.
auto reduce_order(std::uint64_t reference_number, std::uint32_t shares) -> std::uint32_t
Removes shares from an order on a partial cancel (ITCH X), deleting it when it reaches zero.
auto add_order(std::uint64_t reference_number, Side side, std::uint32_t shares, std::uint32_t price) -> void
Adds a new resting order to the book (ITCH A/F).
auto depth(Side side, std::size_t max_levels=0) const -> std::vector< DepthLevel >
Aggregated L2 depth for a side, best level first.
auto bbo() const -> Bbo
The current best bid and offer.
static constexpr std::uint32_t NPOS
Sentinel returned by find when a key is absent.
std::string reference_number
Single-symbol, order-level (L3) limit order book with allocation-light internals.
Side
Which side of the book an order rests on.
Best bid and offer of a single book.
bool has_bid
Whether a bid side exists.
Aggregated state of one price level, for L2 depth snapshots.
StandardPrice price
The level's limit price.
A single resting order, for L3 order-level snapshots.
std::uint64_t reference_number
Exchange order reference number.