18template <
typename ValueType>
19auto unpack(
const char* buffer, std::size_t& offset) -> ValueType {
21 std::memcpy(&value, buffer + offset,
sizeof(ValueType));
22 offset +=
sizeof(ValueType);
24 if constexpr (std::is_integral_v<ValueType> &&
sizeof(ValueType) > 1) {
25 return from_big_endian(value);
31inline auto unpack_string(
const char* buffer, std::size_t& offset,
char* dest, std::size_t size)
33 std::memcpy(dest, buffer + offset, size);
37inline auto unpack_timestamp(
const char* buffer, std::size_t& offset) -> std::uint64_t {
38 std::uint16_t high {};
40 constexpr int LOWER_SHIFT = 32;
41 std::memcpy(&high, buffer + offset,
sizeof(high));
42 offset +=
sizeof(high);
43 std::memcpy(&low, buffer + offset,
sizeof(low));
44 offset +=
sizeof(low);
45 high = from_big_endian(high);
46 low = from_big_endian(low);
47 return (
static_cast<std::uint64_t
>(high) << LOWER_SHIFT) | low;
52 msg.event_code = utils::unpack<char>(buffer, offset);
56 utils::unpack_string(buffer, offset, msg.stock,
STOCK_LEN);
57 msg.market_category = utils::unpack<char>(buffer, offset);
58 msg.financial_status_indicator = utils::unpack<char>(buffer, offset);
59 msg.round_lot_size = utils::unpack<uint32_t>(buffer, offset);
60 msg.round_lots_only = utils::unpack<char>(buffer, offset);
61 msg.issue_classification = utils::unpack<char>(buffer, offset);
63 utils::unpack_string(buffer, offset, msg.issue_sub_type, 2);
64 msg.authenticity = utils::unpack<char>(buffer, offset);
65 msg.short_sale_threshold_indicator = utils::unpack<char>(buffer, offset);
66 msg.ipo_flag = utils::unpack<char>(buffer, offset);
67 msg.luld_ref = utils::unpack<char>(buffer, offset);
68 msg.etp_flag = utils::unpack<char>(buffer, offset);
69 msg.etp_leverage_factor = utils::unpack<uint32_t>(buffer, offset);
70 msg.inverse_indicator = utils::unpack<char>(buffer, offset);
74 utils::unpack_string(buffer, offset, msg.stock,
STOCK_LEN);
75 msg.trading_state = utils::unpack<char>(buffer, offset);
76 msg.reserved = utils::unpack<char>(buffer, offset);
77 utils::unpack_string(buffer, offset, msg.reason, 4);
81 utils::unpack_string(buffer, offset, msg.stock,
STOCK_LEN);
82 msg.reg_sho_action = utils::unpack<char>(buffer, offset);
87 utils::unpack_string(buffer, offset, msg.mpid, 4);
88 utils::unpack_string(buffer, offset, msg.stock,
STOCK_LEN);
89 msg.primary_market_maker = utils::unpack<char>(buffer, offset);
90 msg.market_maker_mode = utils::unpack<char>(buffer, offset);
91 msg.market_participant_state = utils::unpack<char>(buffer, offset);
95 msg.level1 = utils::unpack<uint64_t>(buffer, offset);
96 msg.level2 = utils::unpack<uint64_t>(buffer, offset);
97 msg.level3 = utils::unpack<uint64_t>(buffer, offset);
101 msg.breached_level = utils::unpack<char>(buffer, offset);
106 utils::unpack_string(buffer, offset, msg.stock,
STOCK_LEN);
107 msg.ipo_quotation_release_time = utils::unpack<uint32_t>(buffer, offset);
108 msg.ipo_quotation_release_qualifier = utils::unpack<char>(buffer, offset);
109 msg.ipo_price = utils::unpack<uint32_t>(buffer, offset);
113 utils::unpack_string(buffer, offset, msg.stock,
STOCK_LEN);
114 msg.auction_collar_reference_price = utils::unpack<uint32_t>(buffer, offset);
115 msg.upper_auction_collar_price = utils::unpack<uint32_t>(buffer, offset);
116 msg.lower_auction_collar_price = utils::unpack<uint32_t>(buffer, offset);
117 msg.auction_collar_extension = utils::unpack<uint32_t>(buffer, offset);
121 utils::unpack_string(buffer, offset, msg.stock,
STOCK_LEN);
122 msg.market_code = utils::unpack<char>(buffer, offset);
123 msg.operational_halt_action = utils::unpack<char>(buffer, offset);
127 msg.order_reference_number = utils::unpack<uint64_t>(buffer, offset);
128 msg.buy_sell_indicator = utils::unpack<char>(buffer, offset);
129 msg.shares = utils::unpack<uint32_t>(buffer, offset);
130 utils::unpack_string(buffer, offset, msg.stock,
STOCK_LEN);
131 msg.price = utils::unpack<uint32_t>(buffer, offset);
136 msg.order_reference_number = utils::unpack<uint64_t>(buffer, offset);
137 msg.buy_sell_indicator = utils::unpack<char>(buffer, offset);
138 msg.shares = utils::unpack<uint32_t>(buffer, offset);
140 utils::unpack_string(buffer, offset, msg.stock,
STOCK_LEN);
141 msg.price = utils::unpack<uint32_t>(buffer, offset);
142 utils::unpack_string(buffer, offset, msg.attribution, 4);
146 msg.order_reference_number = utils::unpack<uint64_t>(buffer, offset);
147 msg.executed_shares = utils::unpack<uint32_t>(buffer, offset);
148 msg.match_number = utils::unpack<uint64_t>(buffer, offset);
153 msg.order_reference_number = utils::unpack<uint64_t>(buffer, offset);
154 msg.executed_shares = utils::unpack<uint32_t>(buffer, offset);
155 msg.match_number = utils::unpack<uint64_t>(buffer, offset);
156 msg.printable = utils::unpack<char>(buffer, offset);
157 msg.execution_price = utils::unpack<uint32_t>(buffer, offset);
161 msg.order_reference_number = utils::unpack<uint64_t>(buffer, offset);
162 msg.cancelled_shares = utils::unpack<uint32_t>(buffer, offset);
166 msg.order_reference_number = utils::unpack<uint64_t>(buffer, offset);
170 msg.original_order_reference_number = utils::unpack<uint64_t>(buffer, offset);
171 msg.new_order_reference_number = utils::unpack<uint64_t>(buffer, offset);
172 msg.shares = utils::unpack<uint32_t>(buffer, offset);
173 msg.price = utils::unpack<uint32_t>(buffer, offset);
177 msg.order_reference_number = utils::unpack<uint64_t>(buffer, offset);
178 msg.buy_sell_indicator = utils::unpack<char>(buffer, offset);
179 msg.shares = utils::unpack<uint32_t>(buffer, offset);
181 utils::unpack_string(buffer, offset, msg.stock,
STOCK_LEN);
183 msg.price = utils::unpack<uint32_t>(buffer, offset);
184 msg.match_number = utils::unpack<uint64_t>(buffer, offset);
188 msg.shares = utils::unpack<uint64_t>(buffer, offset);
189 utils::unpack_string(buffer, offset, msg.stock,
STOCK_LEN);
191 msg.cross_price = utils::unpack<uint32_t>(buffer, offset);
192 msg.match_number = utils::unpack<uint64_t>(buffer, offset);
193 msg.cross_type = utils::unpack<char>(buffer, offset);
197 msg.match_number = utils::unpack<uint64_t>(buffer, offset);
201 msg.paired_shares = utils::unpack<uint64_t>(buffer, offset);
202 msg.imbalance_shares = utils::unpack<uint64_t>(buffer, offset);
203 msg.imbalance_direction = utils::unpack<char>(buffer, offset);
205 utils::unpack_string(buffer, offset, msg.stock,
STOCK_LEN);
207 msg.far_price = utils::unpack<uint32_t>(buffer, offset);
208 msg.near_price = utils::unpack<uint32_t>(buffer, offset);
209 msg.current_reference_price = utils::unpack<uint32_t>(buffer, offset);
210 msg.cross_type = utils::unpack<char>(buffer, offset);
211 msg.price_variation_indicator = utils::unpack<char>(buffer, offset);
216 utils::unpack_string(buffer, offset, msg.stock,
STOCK_LEN);
217 msg.interest_flag = utils::unpack<char>(buffer, offset);
221 utils::unpack_string(buffer, offset, msg.stock,
STOCK_LEN);
222 msg.open_eligibility_status = utils::unpack<char>(buffer, offset);
223 msg.minimum_allowable_price = utils::unpack<uint32_t>(buffer, offset);
224 msg.maximum_allowable_price = utils::unpack<uint32_t>(buffer, offset);
225 msg.near_execution_price = utils::unpack<uint32_t>(buffer, offset);
226 msg.near_execution_time = utils::unpack<uint64_t>(buffer, offset);
227 msg.lower_price_range_collar = utils::unpack<uint32_t>(buffer, offset);
228 msg.upper_price_range_collar = utils::unpack<uint32_t>(buffer, offset);
237template <
typename MsgType>
238auto decode_typed(
const char* buffer) ->
Message {
240 std::size_t offset = 1;
242 msg.stock_locate = utils::unpack<std::uint16_t>(buffer, offset);
243 msg.tracking_number = utils::unpack<std::uint16_t>(buffer, offset);
244 msg.timestamp = utils::unpack_timestamp(buffer, offset);
252struct DispatchEntry {
257constexpr std::size_t DISPATCH_TABLE_SIZE = 256;
262consteval auto build_dispatch_table() -> std::array<DispatchEntry, DISPATCH_TABLE_SIZE> {
263 std::array<DispatchEntry, DISPATCH_TABLE_SIZE> table {};
265 auto add = [&table]<
typename MsgType>(
char type) {
266 table[
static_cast<unsigned char>(type)] =
267 DispatchEntry {
static_cast<std::uint16_t
>(WIRE_SIZE<MsgType>), &decode_typed<MsgType>};
277constexpr auto DISPATCH_TABLE = build_dispatch_table();
284 ++m_unknown_message_count;
288 ++m_malformed_message_count;
291 if (m_error_callback) {
296auto Parser::parse_impl(
const char* data, std::size_t size,
const MessageCallback& callback)
297 -> std::optional<ParseError> {
298 std::size_t offset = 0;
299 while (offset < size) {
301 if (offset +
sizeof(std::uint16_t) > size) {
305 std::uint16_t length {};
306 std::memcpy(&length, data + offset,
sizeof(length));
307 length = utils::from_big_endian(length);
308 offset +=
sizeof(std::uint16_t);
315 if (offset + length > size) {
320 const char* message = data + offset;
324 const DispatchEntry& entry = DISPATCH_TABLE[
static_cast<unsigned char>(
message_type)];
325 if (entry.decode ==
nullptr) {
333 if (length < entry.wire_size) {
338 callback(entry.decode(message));
344 if (parse_impl(data, size, callback).has_value()) {
345 throw std::runtime_error(
"Incomplete message at end of buffer.");
352 std::vector<Message> messages;
354 parse(data, size, [&](
const Message& msg) { messages.push_back(msg); });
358auto Parser::parse(
const char* data,
size_t size,
const std::vector<char>& messages)
359 -> std::vector<Message> {
360 std::vector<Message> results;
361 std::set<char> filter(messages.begin(), messages.end());
363 if (filter.empty()) {
368 auto callback = [&](
const Message& msg) {
369 char message_type = std::visit([](
auto&& arg) {
return arg.message_type; }, msg);
371 results.push_back(msg);
374 parse(data, size, callback);
381auto as_char_ptr(std::span<const std::byte> data) ->
const char* {
384 const void* raw = data.data();
385 return static_cast<const char*
>(raw);
390 parse(as_char_ptr(data), data.size(), callback);
393auto Parser::parse(std::span<const std::byte> data) -> std::vector<Message> {
394 return parse(as_char_ptr(data), data.size());
397auto Parser::parse(std::span<const std::byte> data,
const std::vector<char>& messages)
398 -> std::vector<Message> {
399 return parse(as_char_ptr(data), data.size(), messages);
402#ifdef __cpp_lib_expected
403auto Parser::try_parse(std::span<const std::byte> data,
const MessageCallback& callback)
404 -> std::expected<void, ParseError> {
405 if (
auto error = parse_impl(as_char_ptr(data), data.size(), callback)) {
406 return std::unexpected(*error);
411auto Parser::try_parse(std::span<const std::byte> data
412) -> std::expected<std::vector<Message>,
ParseError> {
413 std::vector<Message> messages;
415 if (
auto error = parse_impl(as_char_ptr(data), data.size(), [&](
const Message& msg) {
416 messages.push_back(msg);
418 return std::unexpected(*error);
425 m_error_callback = std::move(callback);
430 data.seekg(0, std::ios::end);
431 auto size = data.tellg();
432 data.seekg(0, std::ios::beg);
435 throw std::runtime_error(
"Failed to determine stream size.");
438 std::vector<char> buffer(
static_cast<size_t>(size));
439 data.read(buffer.data(), size);
445 parse(buffer.data(), buffer.size(), callback);
450 return parse(buffer.data(), buffer.size());
453auto Parser::parse(std::istream& data,
const std::vector<char>& messages) -> std::vector<Message> {
455 return parse(buffer.data(), buffer.size(), messages);
auto parse(const char *data, size_t size, const MessageCallback &callback) -> void
Parses messages from a memory buffer and invokes a callback for each.
auto set_error_callback(ErrorCallback callback) -> void
Registers a callback invoked for each recoverable framing problem.
constexpr std::size_t WIRE_SIZE
The exact on-wire size, in bytes, of a fully formed message of type T.
constexpr auto for_each_message_type(Visitor &&visitor) -> void
Invokes visitor.template operator()<MsgType>(type_byte) once for each ITCH 5.0 message type,...
ParseError
Categories of recoverable problems encountered while framing a feed.
@ truncated
The buffer ended in the middle of a frame.
@ size_mismatch
The declared length is shorter than the message type requires.
@ unknown_type
The message type byte does not correspond to a known message.
std::function< void(ParseError, char)> ErrorCallback
The signature for the optional diagnostics callback.
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.
constexpr size_t average_message_size
std::function< void(const Message &)> MessageCallback
The signature for the callback function used in streaming parse methods.
static auto read_stream_into_buffer(std::istream &data) -> std::vector< char >
auto unpack_message(SystemEventMessage &msg, const char *buffer, size_t &offset) -> void
std::uint16_t wire_size
Expected on-wire frame size (0 == unknown type).
Public parsing interface for NASDAQ TotalView-ITCH 5.0 feeds, plus the low-level byte-unpacking utili...
Add Order, With MPID Attribution (F): like Add Order, but attributed to a market participant.
Add Order, No MPID Attribution (A): a new displayable order has been accepted and placed on the book.
Broken Trade (B): a previously disseminated execution has been broken and should be removed from any ...
Cross Trade (Q): the result of a cross (opening, closing, halt/IPO cross) for a security.
Direct Listing with Capital Raise Price Discovery (O): price-discovery data for a Direct Listing with...
IPO Quoting Period Update (K): the anticipated IPO quotation release time and price for a security.
LULD Auction Collar (J): the auction collar thresholds for a security in a Limit-Up Limit-Down tradin...
MWCB Decline Level (V): the Market-Wide Circuit Breaker breach points for the day.
MWCB Status (W): notification that a Market-Wide Circuit Breaker level has been breached.
Market Participant Position (L): a market participant's (market maker's) status in a security.
Net Order Imbalance Indicator (I): the imbalance and price-discovery data disseminated during the ope...
Trade, Non-Cross (P): an execution of a non-displayable order.
Operational Halt (h): an operational halt or resumption for a security on a specific market center.
Order Cancel (X): a partial cancellation reduced the shares of a resting order.
Order Delete (D): a resting order was cancelled in its entirety and removed from the book.
Order Executed (E): a resting order was executed in whole or in part at its display price.
Order Executed With Price (C): a resting order was executed at a price different from its display pri...
Order Replace (U): a resting order was replaced with a new order at a new reference number,...
Reg SHO Short Sale Price Test Restriction (Y): the Reg SHO short-sale restriction state for a securit...
Retail Price Improvement Indicator (N): the presence of retail price improvement interest on the bid,...
Stock Directory (R): the trading and listing reference data for a security, disseminated at the start...
Stock Trading Action (H): a change in the trading status of a security (halted, paused,...
System Event (S): signals a market or data-feed handler event.
Single source of truth for the ITCH 5.0 wire layout metadata shared by the eager parser,...