ITCHCPP 1.6.0
High-Performance NASDAQ TotalView-ITCH 5.0 Parser & Market-Data Platform
Loading...
Searching...
No Matches
book_manager.cpp
Go to the documentation of this file.
2
3#include <utility>
4#include <variant>
5
6namespace itch::book {
7
8namespace {
9
10// Maps an ITCH buy/sell indicator byte to the book Side enum.
11auto to_side(char buy_sell_indicator) noexcept -> Side {
12 return buy_sell_indicator == 'B' ? Side::buy : Side::sell;
13}
14
15} // namespace
16
17auto BookManager::in_universe(std::string_view symbol) const -> bool {
18 return m_universe.empty() || m_universe.contains(std::string {symbol});
19}
20
21auto BookManager::entry(std::uint16_t stock_locate) const -> BookEntry* {
22 if (stock_locate >= m_books_by_locate.size()) {
23 return nullptr;
24 }
25 return m_books_by_locate[stock_locate].get();
26}
27
28auto BookManager::ensure_entry(std::uint16_t stock_locate, std::string_view symbol) -> BookEntry* {
29 if (stock_locate >= m_books_by_locate.size()) {
30 m_books_by_locate.resize(static_cast<std::size_t>(stock_locate) + 1);
31 }
32 if (auto& slot = m_books_by_locate[stock_locate]) {
33 if (slot->book.symbol().empty() && !symbol.empty()) {
34 slot->book.set_symbol(std::string {symbol});
35 }
36 return slot.get();
37 }
38 if (!in_universe(symbol)) {
39 return nullptr;
40 }
41 m_books_by_locate[stock_locate] = std::make_unique<BookEntry>();
42 m_books_by_locate[stock_locate]->book.set_symbol(std::string {symbol});
43 ++m_book_count;
44 return m_books_by_locate[stock_locate].get();
45}
46
47auto BookManager::emit_bbo_if_changed(BookEntry& target) -> void {
48 const Bbo current = target.book.bbo();
49 if (current != target.last_bbo) {
50 target.last_bbo = current;
51 if (m_bbo_callback) {
52 m_bbo_callback(target.book, current);
53 }
54 }
55}
56
57template <typename AddMessage>
58auto BookManager::handle_add_order(const AddMessage& add) -> void {
59 BookEntry* target = ensure_entry(add.stock_locate, to_string(add.stock, STOCK_LEN));
60 if (target != nullptr) {
61 target->book.add_order(
62 add.order_reference_number, to_side(add.buy_sell_indicator), add.shares, add.price
63 );
64 emit_bbo_if_changed(*target);
65 }
66}
67
68auto BookManager::handle_order_executed(const OrderExecutedMessage& exec) -> void {
69 BookEntry* target = entry(exec.stock_locate);
70 if (target == nullptr) {
71 return;
72 }
73 if (m_trade_callback) {
74 const auto price = target->book.order_price(exec.order_reference_number);
75 const auto side = target->book.order_side(exec.order_reference_number);
76 if (price.has_value()) {
77 Trade trade {};
78 trade.timestamp = exec.timestamp;
79 trade.stock_locate = exec.stock_locate;
80 trade.symbol = target->book.symbol();
81 trade.price = StandardPrice {*price};
82 trade.shares = exec.executed_shares;
83 trade.match_number = exec.match_number;
84 trade.side = side.has_value() ? static_cast<char>(*side) : '\0';
85 trade.printable = true;
86 m_trade_callback(trade);
87 }
88 }
89 target->book.execute_order(exec.order_reference_number, exec.executed_shares);
90 emit_bbo_if_changed(*target);
91}
92
93auto BookManager::handle_order_executed_with_price(const OrderExecutedWithPriceMessage& exec
94) -> void {
95 BookEntry* target = entry(exec.stock_locate);
96 if (target == nullptr) {
97 return;
98 }
99 if (m_trade_callback) {
100 const auto side = target->book.order_side(exec.order_reference_number);
101 Trade trade {};
102 trade.timestamp = exec.timestamp;
103 trade.stock_locate = exec.stock_locate;
104 trade.symbol = target->book.symbol();
105 trade.price = StandardPrice {exec.execution_price};
106 trade.shares = exec.executed_shares;
107 trade.match_number = exec.match_number;
108 trade.side = side.has_value() ? static_cast<char>(*side) : '\0';
109 trade.printable = exec.printable == 'Y';
110 m_trade_callback(trade);
111 }
112 target->book.execute_order(exec.order_reference_number, exec.executed_shares);
113 emit_bbo_if_changed(*target);
114}
115
116auto BookManager::handle_order_cancel(const OrderCancelMessage& cancel) -> void {
117 if (BookEntry* target = entry(cancel.stock_locate)) {
118 target->book.reduce_order(cancel.order_reference_number, cancel.cancelled_shares);
119 emit_bbo_if_changed(*target);
120 }
121}
122
123auto BookManager::handle_order_delete(const OrderDeleteMessage& del) -> void {
124 if (BookEntry* target = entry(del.stock_locate)) {
125 target->book.delete_order(del.order_reference_number);
126 emit_bbo_if_changed(*target);
127 }
128}
129
130auto BookManager::handle_order_replace(const OrderReplaceMessage& replace) -> void {
131 if (BookEntry* target = entry(replace.stock_locate)) {
132 target->book.replace_order(
133 replace.original_order_reference_number,
134 replace.new_order_reference_number,
135 replace.shares,
136 replace.price
137 );
138 emit_bbo_if_changed(*target);
139 }
140}
141
142auto BookManager::handle_non_cross_trade(const NonCrossTradeMessage& trade) -> void {
143 // A non-displayable order print does not alter the visible book.
144 if (!m_trade_callback) {
145 return;
146 }
147 Trade tape_entry {};
148 tape_entry.timestamp = trade.timestamp;
149 tape_entry.stock_locate = trade.stock_locate;
150 tape_entry.symbol = to_string(trade.stock, STOCK_LEN);
151 tape_entry.price = StandardPrice {trade.price};
152 tape_entry.shares = trade.shares;
153 tape_entry.match_number = trade.match_number;
154 tape_entry.side = trade.buy_sell_indicator;
155 tape_entry.printable = true;
156 m_trade_callback(tape_entry);
157}
158
159auto BookManager::handle_cross_trade(const CrossTradeMessage& cross) -> void {
160 if (!m_trade_callback) {
161 return;
162 }
163 Trade tape_entry {};
164 tape_entry.timestamp = cross.timestamp;
165 tape_entry.stock_locate = cross.stock_locate;
166 tape_entry.symbol = to_string(cross.stock, STOCK_LEN);
167 tape_entry.price = StandardPrice {cross.cross_price};
168 tape_entry.shares = cross.shares;
169 tape_entry.match_number = cross.match_number;
170 tape_entry.printable = true;
171 tape_entry.is_cross = true;
172 tape_entry.cross_type = cross.cross_type;
173 m_trade_callback(tape_entry);
174}
175
176auto BookManager::handle_stock_directory(const StockDirectoryMessage& directory) -> void {
177 if (directory.stock_locate >= m_symbol_by_locate.size()) {
178 m_symbol_by_locate.resize(static_cast<std::size_t>(directory.stock_locate) + 1);
179 }
180 m_symbol_by_locate[directory.stock_locate] = to_string(directory.stock, STOCK_LEN);
181}
182
183auto BookManager::process(const Message& message) -> void {
184 if (const auto* add = std::get_if<AddOrderMessage>(&message)) {
185 handle_add_order(*add);
186 } else if (const auto* add_mpid = std::get_if<AddOrderMPIDAttributionMessage>(&message)) {
187 handle_add_order(*add_mpid);
188 } else if (const auto* executed = std::get_if<OrderExecutedMessage>(&message)) {
189 handle_order_executed(*executed);
190 } else if (const auto* executed_with_price =
191 std::get_if<OrderExecutedWithPriceMessage>(&message)) {
192 handle_order_executed_with_price(*executed_with_price);
193 } else if (const auto* cancel = std::get_if<OrderCancelMessage>(&message)) {
194 handle_order_cancel(*cancel);
195 } else if (const auto* del = std::get_if<OrderDeleteMessage>(&message)) {
196 handle_order_delete(*del);
197 } else if (const auto* replace = std::get_if<OrderReplaceMessage>(&message)) {
198 handle_order_replace(*replace);
199 } else if (const auto* trade = std::get_if<NonCrossTradeMessage>(&message)) {
200 handle_non_cross_trade(*trade);
201 } else if (const auto* cross = std::get_if<CrossTradeMessage>(&message)) {
202 handle_cross_trade(*cross);
203 } else if (const auto* directory = std::get_if<StockDirectoryMessage>(&message)) {
204 handle_stock_directory(*directory);
205 }
206}
207
208template auto BookManager::handle_add_order(const AddOrderMessage&) -> void;
209template auto BookManager::handle_add_order(const AddOrderMPIDAttributionMessage&) -> void;
210
211auto BookManager::book(std::uint16_t stock_locate) const -> const L3Book* {
212 const BookEntry* found = entry(stock_locate);
213 return found != nullptr ? &found->book : nullptr;
214}
215
216auto BookManager::book_for_symbol(std::string_view symbol) const -> const L3Book* {
217 for (const auto& slot : m_books_by_locate) {
218 if (slot && slot->book.symbol() == symbol) {
219 return &slot->book;
220 }
221 }
222 return nullptr;
223}
224
225auto BookManager::symbol_for_locate(std::uint16_t stock_locate) const -> std::string_view {
226 if (const BookEntry* found = entry(stock_locate);
227 found != nullptr && !found->book.symbol().empty()) {
228 return found->book.symbol();
229 }
230 if (stock_locate < m_symbol_by_locate.size()) {
231 return m_symbol_by_locate[stock_locate];
232 }
233 return {};
234}
235
236} // namespace itch::book
Full-market order book manager that fans out ITCH messages to per-symbol L3Book instances.
auto process(const Message &message) -> void
Processes one parsed ITCH message, updating the relevant book and emitting BBO/trade events as approp...
auto book_for_symbol(std::string_view symbol) const -> const L3Book *
The book for a symbol, or nullptr if none is tracked.
auto book(std::uint16_t stock_locate) const -> const L3Book *
The book for a locate code, or nullptr if none is tracked.
auto symbol_for_locate(std::uint16_t stock_locate) const -> std::string_view
The symbol associated with a locate code (empty if unknown).
A single-symbol, order-level (L3) limit order book with allocation-light internals.
Definition l3_book.hpp:72
auto symbol() const noexcept -> const std::string &
The stock symbol associated with this book (may be empty).
Definition l3_book.hpp:85
std::string symbol
Definition csv_sink.cpp:19
std::string price
Definition csv_sink.cpp:23
std::uint16_t stock_locate
Definition csv_sink.cpp:17
char side
Definition csv_sink.cpp:21
Side
Which side of the book an order rests on.
Definition l3_book.hpp:26
@ sell
An offer/ask.
constexpr int STOCK_LEN
Definition messages.hpp:485
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
BasicPrice< std::uint32_t, 4 > StandardPrice
Price scale for every ITCH price field except MWCB decline levels.
Definition price.hpp:102
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
Add Order, With MPID Attribution (F): like Add Order, but attributed to a market participant.
Definition messages.hpp:229
Add Order, No MPID Attribution (A): a new displayable order has been accepted and placed on the book.
Definition messages.hpp:211