ITCHCPP 1.6.0
High-Performance NASDAQ TotalView-ITCH 5.0 Parser & Market-Data Platform
Loading...
Searching...
No Matches
order_book.cpp
Go to the documentation of this file.
1#include "itch/order_book.hpp"
2
3#include <chrono>
4#include <format>
5#include <ranges>
6#include <string_view>
7#include <thread>
8#include <type_traits>
9
10#include "itch/price.hpp"
11
12namespace itch {
13
14auto PriceLevel::add_order(const std::shared_ptr<Order>& order) -> void {
15 total_shares += order->shares;
16 orders.push_back(order);
17 order->level = this;
18}
19
20auto PriceLevel::remove_order(const OrderIt& order_it) -> void { orders.erase(order_it); }
21
22auto LimitOrderBook::process(const Message& message) -> void {
23 std::visit(
24 [this](auto&& msg) {
25 if (!book_messages.contains(msg.message_type)) {
26 return;
27 }
28 this->handle_message(msg);
29 },
30 message
31 );
32}
33
34auto LimitOrderBook::print(std::ostream& out, unsigned int delay_ms) const -> void {
35 constexpr std::string_view BORDER = "==========================================\n";
36 constexpr std::string_view MID_RULE = "-----------+--------------+--------------\n";
37
38 out << BORDER;
39 out << " SHARES | PRICE | SIDE \n";
40 out << BORDER;
41
42 const auto print_level =
43 [&](std::uint32_t total_shares, std::uint32_t raw_price, std::string_view side) {
44 if (delay_ms > 0) {
45 std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms));
46 }
47 out << std::format(
48 "{:>10} | {:>12.4f} | {}\n", total_shares, make_price(raw_price).to_double(), side
49 );
50 if (delay_ms > 0) {
51 out << std::flush;
52 }
53 };
54
55 // Asks are stored low-to-high; print them high-to-low so the best ask sits
56 // just above the spread.
57 for (const auto& [raw_price, level] : std::ranges::reverse_view(m_asks)) {
58 print_level(level.total_shares, raw_price, "Ask");
59 }
60
61 out << MID_RULE;
62
63 // Bids are stored high-to-low, which is already the desired print order.
64 for (const auto& [raw_price, level] : m_bids) {
65 print_level(level.total_shares, raw_price, "Bid");
66 }
67
68 out << BORDER;
69}
70
71auto LimitOrderBook::handle_message(const AddOrderMessage& msg) -> void {
72 if (to_string(msg.stock, sizeof(msg.stock)) != m_stock_symbol) {
73 return;
74 }
75 add_order(
76 msg.order_reference_number,
77 msg.buy_sell_indicator,
78 msg.shares,
79 msg.price,
80 to_string(msg.stock, sizeof(msg.stock))
81 );
82}
83
84auto LimitOrderBook::handle_message(const AddOrderMPIDAttributionMessage& msg) -> void {
85 if (to_string(msg.stock, sizeof(msg.stock)) != m_stock_symbol) {
86 return;
87 }
88 add_order(
89 msg.order_reference_number,
90 msg.buy_sell_indicator,
91 msg.shares,
92 msg.price,
93 to_string(msg.stock, sizeof(msg.stock))
94 );
95}
96
97auto LimitOrderBook::handle_message(const OrderExecutedMessage& msg) -> void {
98 auto order_iter = m_orders.find(msg.order_reference_number);
99 if (order_iter == m_orders.end() || (*order_iter->second)->stock != m_stock_symbol) {
100 return;
101 }
102 remove_order(msg.order_reference_number, msg.executed_shares);
103}
104
105auto LimitOrderBook::handle_message(const OrderExecutedWithPriceMessage& msg) -> void {
106 auto order_iter = m_orders.find(msg.order_reference_number);
107 if (order_iter == m_orders.end() || (*order_iter->second)->stock != m_stock_symbol) {
108 return;
109 }
110 remove_order(msg.order_reference_number, msg.executed_shares);
111}
112
113auto LimitOrderBook::handle_message(const OrderCancelMessage& msg) -> void {
114 auto order_iter = m_orders.find(msg.order_reference_number);
115 if (order_iter == m_orders.end() || (*order_iter->second)->stock != m_stock_symbol) {
116 return;
117 }
118 remove_order(msg.order_reference_number, msg.cancelled_shares);
119}
120
121auto LimitOrderBook::handle_message(const OrderDeleteMessage& msg) -> void {
122 auto iter = m_orders.find(msg.order_reference_number);
123 if (iter != m_orders.end() && (*iter->second)->stock == m_stock_symbol) {
124 auto order = *iter->second;
125 remove_order(msg.order_reference_number, order->shares);
126 }
127}
128
129auto LimitOrderBook::handle_message(const OrderReplaceMessage& msg) -> void {
130 auto iter = m_orders.find(msg.original_order_reference_number);
131 if (iter != m_orders.end() && (*iter->second)->stock == m_stock_symbol) {
132 auto old_order = *iter->second;
133 char side = old_order->buy_sell_indicator;
134 const std::string& stock = old_order->stock;
135 remove_order(msg.original_order_reference_number, old_order->shares);
136 add_order(msg.new_order_reference_number, side, msg.shares, msg.price, stock);
137 }
138}
139
140auto LimitOrderBook::add_order(
141 uint64_t order_ref, char side, uint32_t shares, uint32_t price, const std::string& stock
142) -> void {
143 auto order = std::make_shared<Order>(order_ref, side, shares, price, stock);
144
145 PriceLevel* level = nullptr;
146
147 if (side == 'B') {
148 level = &m_bids[price];
149 } else {
150 level = &m_asks[price];
151 }
152 order->level = level;
153 level->add_order(order);
154 m_orders[order_ref] = std::prev(level->orders.end());
155}
156
157auto LimitOrderBook::remove_order(uint64_t order_ref, uint32_t canceled_shares) -> void {
158 auto iter = m_orders.find(order_ref);
159 if (iter == m_orders.end()) {
160 return;
161 }
162
163 auto order_iterator = iter->second;
164 // This copy is deliberate: it keeps the Order alive after the list node is
165 // erased below, since the order's price and side are read afterwards.
166 auto order_ptr = *order_iterator; // NOLINT(performance-unnecessary-copy-initialization)
167 PriceLevel* level = order_ptr->level;
168
169 level->total_shares -= canceled_shares;
170 order_ptr->shares -= canceled_shares;
171
172 if (order_ptr->shares == 0) {
173 level->remove_order(order_iterator);
174 if (level->orders.empty()) {
175 if (order_ptr->buy_sell_indicator == 'B') {
176 m_bids.erase(order_ptr->price);
177 } else {
178 m_asks.erase(order_ptr->price);
179 }
180 }
181 m_orders.erase(iter);
182 }
183}
184
185} // namespace itch
auto print(std::ostream &out, unsigned int delay_ms=0) const -> void
Visualizes the current state of the order book to an output stream.
auto process(const Message &message) -> void
Dispatches and processes a generic ITCH message.
std::string price
Definition csv_sink.cpp:23
char side
Definition csv_sink.cpp:21
std::string shares
Definition csv_sink.cpp:22
std::list< std::shared_ptr< Order > >::iterator OrderIt
Iterator type for navigating the list of orders within a price level.
std::variant< SystemEventMessage, StockDirectoryMessage, StockTradingActionMessage, RegSHOMessage, MarketParticipantPositionMessage, MWCBDeclineLevelMessage, MWCBStatusMessage, IPOQuotingPeriodUpdateMessage, LULDAuctionCollarMessage, OperationalHaltMessage, AddOrderMessage, AddOrderMPIDAttributionMessage, OrderExecutedMessage, OrderExecutedWithPriceMessage, OrderCancelMessage, OrderDeleteMessage, OrderReplaceMessage, NonCrossTradeMessage, CrossTradeMessage, BrokenTradeMessage, NOIIMessage, RetailPriceImprovementIndicatorMessage, DLCRMessage > Message
A variant able to hold any one of the ITCH 5.0 message structs.
Definition messages.hpp:483
constexpr auto make_price(std::uint32_t raw_value) noexcept -> StandardPrice
Wraps a raw 4-decimal price value in the typed StandardPrice.
Definition price.hpp:111
auto to_string(const char *source, size_t size) -> std::string
Converts a fixed-width character array to a string, trimming trailing spaces and NUL characters.
Definition messages.hpp:495
An in-memory limit order book reconstructed from a stream of parsed ITCH 5.0 messages.
Strongly typed, fixed-point price representation for ITCH price fields.
Add Order, No MPID Attribution (A): a new displayable order has been accepted and placed on the book.
Definition messages.hpp:211
auto add_order(const std::shared_ptr< Order > &order) -> void
Appends an order to the end of the queue (Time priority).
auto remove_order(const OrderIt &order_it) -> void
Removes a specific order from the queue.