A modern, high-performance C++20 library for parsing NASDAQ TotalView-ITCH 5.0 protocol data feeds. This parser is designed for maximum speed, minimal memory overhead, and type safety, making it ideal for latency-sensitive financial applications, market data analysis, and quantitative research.
- Project Philosophy
- Key Features
- Architectural Overview
- Getting Started
- Dependency Management
- Comprehensive Usage Guide
- API Reference: ITCH 5.0 Message Types
- Performance
- Coding Standards
- Frequently Asked Questions (FAQ)
- Contributing
- License
- References
The design of this ITCH parser is guided by three principles:
-
Performance Above All: In market data processing, performance is not just a feature; it's a requirement. This library is architected to eliminate unnecessary overhead. It avoids dynamic memory allocations and stream I/O in its critical path, decoding each frame in a single forward pass. The goal is to convert raw binary data into a usable C++ struct as efficiently as possible.
-
Correctness and Safety: Financial data is unforgiving. A single misinterpreted byte can corrupt analysis. We prioritize correctness by adhering strictly to the ITCH 5.0 specification and ensuring data is represented in a type-safe manner.
-
Modern and Maintainable C++: The library leverages C++20 and above to provide a clean, maintainable, and efficient implementation. We use modern features to create a robust and developer-friendly API that is both powerful and easy to use correctly.
- Real Feed Ingestion: Consume ITCH as it actually arrives, MoldUDP64 UDP
multicast framing, SoupBinTCP (Glimpse/recovery) framing, and
.pcap/.pcapngcaptures, with per-session sequence tracking and gap detection. No libpcap dependency. See Feed Ingestion. - Full-Market Book Engine: Reconstruct every symbol on the feed in one pass with an allocation-light L3 order book (object pool, intrusive FIFO levels, flat ladders, open-addressed O(1) order lookup), with BBO change events, L2/L3 depth snapshots, and trade-tape extraction. See Book Engine.
- Zero-Copy Overlay API: Inspect raw frames through lazy typed views that convert only the fields you read, for hot paths that touch a few fields per message.
- Built-in Analytics: Header-only microstructure layer, OHLCV bar builders (time/tick/volume clocks), VWAP/TWAP, spread, queue imbalance, order-flow imbalance, NOII surfacing, and auction reconstruction. See Analytics.
- Interoperability: CSV and Arrow/Parquet export, a batteries-included
itch-toolCLI (inspect/filter/stats/convert), and native Python bindings (pybind11). See Interoperability. - Simulation & Ecosystem: Timestamp-paced replay engine, a full ITCH
encoder/writer (
parse(encode(msg)) == msg), multi-venue extension seams, and Conan/vcpkg packaging. See Simulation. - High Throughput: Multi-gigabyte-per-second parsing on modern hardware (see Benchmarks for measured numbers).
- Allocation-Free Core: The callback-based parsing loop performs zero dynamic memory allocations, minimizing latency and jitter.
- Type-Safe API: All ITCH messages are deserialized into a
std::variantof dedicated, packedstructs, ensuring compile-time safety. - Specification Compliant: Faithfully implements the message formats outlined in the Nasdaq TotalView-ITCH 5.0 specification.
- Flexible Parsing Strategies:
- Eagerly parse a buffer into a
std::vectorfor convenience. - Use a callback for efficient, low-memory stream processing of large files.
- Filter messages by type at the parsing stage to reduce processing load.
- Eagerly parse a buffer into a
- Cross-Platform: Compatible with any platform supporting a C++20 compiler and CMake.
- Minimal Dependencies: Zero runtime dependencies. Test and benchmark libraries are managed by
vcpkg.
The library is built on some key principles to deliver maximum performance and safety.
The parser is a fast, allocation-free, single-pass field decoder. Each message type is defined as a packed struct whose layout mirrors the ITCH wire format, and each frame is decoded in one forward pass: fields are copied out and converted from network byte order directly into the typed struct, with no intermediate buffers and no per-message heap allocation on the callback path.
Dispatch is driven by a compile-time table keyed on the one-byte message type, so selecting the right decoder is a single flat-array lookup followed by a direct, inlinable call. There is no red-black-tree lookup and no type-erased std::function indirection. The frame length declared on the wire is validated against the size the message type requires before any field is read, so a truncated or malformed frame can never cause the decoder to read into an adjacent message.
This is genuine single-pass decoding rather than a zero-copy overlay: the bytes are converted eagerly into host-order fields.
All possible ITCH messages are handled using a single, modern C++ object: itch::Message, which is a std::variant. This approach provides significant advantages over traditional designs:
- Compile-Time Safety: The compiler verifies that your code handles every possible message type. This prevents unexpected errors at runtime if a message type is overlooked.
- High Performance: This design allows the compiler to generate highly optimized code for processing different messages, which is typically faster than conventional virtual function lookups.
- Efficient Memory Layout: Messages are stored contiguously in memory, which improves CPU cache utilization and overall processing speed.
The ITCH protocol specifies that all multi-byte numbers use a big-endian (network) byte order. Most modern CPUs, however, use a little-endian format.
The parser handles this discrepancy automatically. After a message is read into a struct, the library transparently converts all multi-byte fields (such as prices and quantities) from the network order to the native order of the machine. This ensures all numerical data is immediately correct and ready for use without requiring any manual conversion.
- C++ Compiler: A compiler with full C++20 support (e.g., GCC 10+, Clang 12+, MSVC 19.29+).
- CMake: Version 3.25 or newer.
-
Clone the Repository
git clone https://github.com/bbalouki/itchcpp.git cd itchcpp -
Configure with CMake This step generates the build system and automatically downloads any developer dependencies if needed.
cmake -S . -B build -
Compile the Code Build the library and any example targets.
cmake --build build --config Release
The project includes unit tests and performance benchmarks that can be built by enabling the corresponding CMake options.
-
To build and run tests:
# Configure with tests enabled cmake -S . -B build -DITCH_BUILD_TESTS=ON # Build cmake --build build --config Release # Run tests from the build directory ctest --test-dir build --output-on-failure
-
To build and run benchmarks:
# Configure with benchmarks enabled cmake -S . -B build -DITCH_BUILD_BENCHMARKS=ON # Build cmake --build build --config Release # Run benchmarks ./build/benchmarks/parser_bench
This library has zero external runtime dependencies.
For development purposes, it uses two popular libraries:
- Google Test: For unit testing.
- Google Benchmark: For performance microbenchmarks.
These are handled automatically by Microsoft's vcpkg package manager. When you enable tests or benchmarks, You will need to install them on your system separately or install them in manifest mode.
Download some sample data here
This is the simplest approach. It reads an entire file into memory and returns a std::vector of parsed messages. This method is convenient for smaller files but may consume significant memory for large datasets.
#include "itch/parser.hpp"
#include <fstream>
#include <iostream>
#include <vector>
int main(int argc, char* argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <path_to_itch_file>\n";
return 1;
}
std::ifstream file(argv[1], std::ios::binary);
if (!file) {
std::cerr << "Error: Cannot open file " << argv[1] << "\n";
return 1;
}
itch::Parser parser;
try {
// Parse the entire file stream into a vector of messages.
std::vector<itch::Message> messages = parser.parse(file);
std::cout << "Successfully parsed " << messages.size() << " messages.\n";
// You can now iterate over the messages for analysis.
for (const auto& msg : messages) {
// ... process each message ...
}
} catch (const std::runtime_error& e) {
std::cerr << "Parsing failed: " << e.what() << "\n";
return 1;
}
return 0;
}This is the most memory-efficient way to process large files. The parser invokes your callback function for each message it finds, avoiding the need to store all messages in memory at once. For best performance, read the file into a buffer first.
#include "itch/parser.hpp"
#include <iostream>
#include <vector>
#include <fstream>
// A simple callback function that counts messages
void message_counter(const itch::Message& message) {
static long count = 0;
count++;
if (count % 1000000 == 0) {
std::cout << "Processed " << count << " messages...\n";
}
}
int main(int argc, char* argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <path_to_itch_file>\n";
return 1;
}
std::ifstream file(argv[1], std::ios::binary);
if (!file.is_open()) {
std::cerr << "Error opening file!\n";
return 1;
}
// Reading the entire file into a buffer minimizes I/O overhead during parsing.
std::vector<char> buffer(std::istreambuf_iterator<char>(file), {});
itch::Parser parser;
try {
// Parse the buffer and invoke `message_counter` for each message.
parser.parse(buffer.data(), buffer.size(), &message_counter);
std::cout << "Finished processing file.\n";
} catch (const std::runtime_error& e) {
std::cerr << "Parsing failed: " << e.what() << "\n";
return 1;
}
return 0;
}You can instruct the parser to only deserialize specific message types. This provides a significant performance boost if you are only interested in a subset of the data (e.g., just trades and quotes).
#include "itch/parser.hpp"
#include <fstream>
#include <iostream>
#include <vector>
int main(int argc, char* argv[]) {
if (argc != 2) { /*...*/ return 1; }
std::ifstream file(argv[1], std::ios::binary);
if (!file) { /*...*/ return 1; }
itch::Parser parser;
try {
// We only care about Add Order, Order Executed, and Broken Trade messages.
// The parser will skip all other message types.
std::vector<char> filter = {'A', 'F', 'E', 'C', 'B'};
std::vector<itch::Message> trade_events = parser.parse(file, filter);
std::cout << "Found " << trade_events.size() << " relevant order and trade messages.\n";
} catch (const std::runtime_error& e) {
std::cerr << "Parsing failed: " << e.what() << "\n";
return 1;
}
return 0;
}This standalone example demonstrates how to use std::visit with a visitor to process different message types and print their details correctly.
#include "itch/parser.hpp"
#include <iostream>
#include <vector>
#include <fstream>
#include <iomanip> // For std::setprecision
// A visitor object to handle different message types.
// Using `if constexpr` ensures only valid code is compiled for each message type.
auto message_visitor = [](const auto& msg) {
using T = std::decay_t<decltype(msg)>;
// Set fixed-point notation for prices
std::cout << std::fixed << std::setprecision(4);
if constexpr (std::is_same_v<T, itch::AddOrderMessage>) {
std::cout << "ADD ORDER: Ref #" << msg.order_reference_number
<< ", " << msg.shares << " shares of "
<< itch::to_string(msg.stock, sizeof(msg.stock)) // Helper to convert char array
<< " @ $" << static_cast<double>(msg.price) / itch::PRICE_DIVISOR << "\n";
} else if constexpr (std::is_same_v<T, itch::OrderExecutedMessage>) {
std::cout << "EXECUTION: Ref #" << msg.order_reference_number
<< ", executed " << msg.executed_shares << " shares.\n";
} else if constexpr (std::is_same_v<T, itch::SystemEventMessage>) {
std::cout << "SYSTEM EVENT: Code '" << msg.event_code
<< "' (O=Start, S=SysHours, Q=MktHours, M=EndMkt, E=EndSys, C=End)\n";
} else {
// This block will be visited for any other message types.
}
};
int main(int argc, char* argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <path_to_itch_file>\n";
return 1;
}
std::ifstream file(argv[1], std::ios::binary);
if (!file) {
std::cerr << "Error: Cannot open file " << argv[1] << "\n";
return 1;
}
itch::Parser parser;
try {
// Use a lambda to pass each message to our visitor
parser.parse(file, [](const itch::Message& msg) {
std::visit(message_visitor, msg);
});
} catch (const std::runtime_error& e) {
std::cerr << "Parsing failed: " << e.what() << "\n";
return 1;
}
return 0;
}Beyond simple parsing, this library provides a high-level LimitOrderBook utility that reconstructs the full order book state for a given financial instrument from an ITCH data stream. This is essential for applications requiring market depth analysis, such as algorithmic trading or market making.
The itch::LimitOrderBook class processes ITCH messages and internally manages the addition, removal, and modification of orders, giving you a complete, real-time view of the market. This example demonstrates how to parse an ITCH file and use the messages to build an order book for a specific stock (e.g., "AAPL"). After processing all messages, it prints a snapshot of the final book state.
#include <fstream>
#include <iostream>
#include "itch/parser.hpp"
#include "itch/order_book.hpp"
int main(int argc, char* argv[]) {
if (argc != 3) {
std::cerr << "Usage: " << argv[0] << " <itch_file> <symbol>\n";
return 1;
}
std::ifstream file(argv[1], std::ios::binary);
if (!file) {
std::cerr << "Error: Could not open file " << argv[1] << "\n";
return 1;
}
// Initialize the order book for the target stock symbol
itch::LimitOrderBook order_book(argv[2]);
// Create a parser and configure it to only process book-related messages
// This is a performance optimization.
itch::Parser parser;
std::vector<char> book_messages(order_book.book_messages.begin(),
order_book.book_messages.end());
try {
// Define a callback to process each relevant message
auto callback = [&](const itch::Message& msg) {
order_book.process(msg);
};
// Start parsing the file with the filter and callback
parser.parse(file, callback, book_messages);
// Print the final state of the order book
std::cout << "Final Order Book for " << argv[2] << ":\n";
order_book.print(std::cout);
} catch (const std::exception& e) {
std::cerr << "An error occurred: " << e.what() << "\n";
return 1;
}
return 0;
}The print() method provides a formatted snapshot of the top of the book. It displays the total volume at each price level for both bids and asks.
+-----------------------------------------------------+
| AAPL |
+--------------------------+--------------------------+
| ASKS (SELL) | BIDS (BUY) |
+------------+-------------+------------+-------------+
| PRICE | VOLUME | PRICE | VOLUME |
+------------+-------------+------------+-------------+
| 173.5000 | 15,000 | 173.4900 | 23,500 |
| 173.5100 | 12,300 | 173.4800 | 18,900 |
| 173.5200 | 8,000 | 173.4700 | 31,200 |
| 173.5300 | 21,100 | 173.4600 | 5,600 |
| 173.5400 | 4,500 | 173.4500 | 14,400 |
+------------+-------------+------------+-------------+
The raw parser assumes a concatenation of length-prefixed messages, but ITCH is
delivered inside transport framing. The itch::transport module decodes the
framing that actually arrives on the wire and on disk, then feeds the existing
parser. Everything is implemented in-house with no libpcap dependency.
| Decoder | Header | Purpose |
|---|---|---|
MoldUdp64Decoder |
itch/transport/moldudp64.hpp |
UDP multicast framing for live dissemination. |
SoupBinDecoder |
itch/transport/soupbintcp.hpp |
TCP framing for Glimpse snapshots and recovery/replay. |
PcapReader |
itch/transport/pcap.hpp |
Replay a feed from a .pcap/.pcapng capture file. |
SequenceTracker |
itch/transport/sequencing.hpp |
Per-session sequence tracking, gap detection, recovery hooks. |
#include "itch/transport/pcap.hpp"
itch::transport::PcapReader reader{[](const itch::Message& msg) {
// ... handle each decoded ITCH message ...
}};
// Surface any sequence gaps in the multicast stream.
reader.mold_decoder().tracker().set_gap_callback(
[](std::string_view session, std::uint64_t expected, std::uint64_t got) {
// ... drive recovery / log the gap ...
});
reader.read_file("capture.pcapng"); // walks Ethernet/IP/UDP/MoldUDP64
// reader.messages_decoded(), reader.udp_datagrams(), tracker().gap_count()For a live or captured MoldUDP64 datagram you already have in memory, call
MoldUdp64Decoder::decode_packet(span) directly; for a SoupBinTCP byte stream,
push segments through SoupBinDecoder::feed(span) as they arrive.
The original LimitOrderBook reconstructs a single, pre-selected symbol. The
Phase 2 itch::book::BookManager reconstructs every symbol on the feed in one
pass, routing each message to that security's book by stock locate code in O(1).
Each book (itch::book::L3Book) is allocation-light: orders live in a reusable
object pool linked into intrusive FIFO queues, price levels are kept in flat
sorted ladders, and order lookup by reference number uses a flat open-addressed
map, so there is no per-order heap allocation or atomic refcount on the hot path.
#include "itch/book/book_manager.hpp"
#include "itch/parser.hpp"
itch::book::BookManager manager;
// manager.track_symbol("AAPL"); // optional: restrict to a universe
manager.set_bbo_callback([](const itch::book::L3Book& book, const itch::book::Bbo& bbo) {
// best bid/offer for book.symbol() just changed
});
manager.set_trade_callback([](const itch::Trade& trade) {
// trade.printable distinguishes displayable prints from hidden ones
});
itch::Parser parser;
parser.parse(buffer.data(), buffer.size(), [&](const itch::Message& msg) {
manager.process(msg);
});
const itch::book::L3Book* book = manager.book_for_symbol("AAPL");
auto top = book->bbo(); // best bid/offer
auto l2 = book->depth(itch::book::Side::buy, 5); // top-5 aggregated bids
auto l3 = book->orders_at(itch::book::Side::buy, top.bid_price.raw()); // order-levelFor callers that touch only a few fields per message, itch/overlay.hpp provides
a zero-copy alternative to the eager parser: for_each_message(buffer, cb) yields
a MessageView (and typed views like AddOrderView) that decode each field
lazily on access.
The header-only itch::analytics layer computes the metrics quants ask for,
directly off the trade tape and book so every downstream team does not
reimplement them.
| Component | Header | Provides |
|---|---|---|
BarBuilder<Clock> |
analytics/bars.hpp |
OHLCV bars over TimeClock, TickClock, VolumeClock. |
Vwap, Twap |
analytics/vwap.hpp |
Running/interval volume- and time-weighted average price. |
| spread / mid / imbalance | analytics/microstructure.hpp |
Spread, mid, depth-at-level, queue imbalance, order-flow imbalance. |
ImbalanceInfo |
analytics/imbalance.hpp |
Decoded NOII (I) imbalance data. |
AuctionTracker |
analytics/auctions.hpp |
Opening/closing/halt/IPO cross reconstruction. |
#include "itch/analytics/vwap.hpp"
#include "itch/analytics/bars.hpp"
itch::analytics::Vwap vwap;
itch::analytics::BarBuilder bars{itch::analytics::TimeClock{60'000'000'000ULL}, // 1-minute bars
[](const itch::analytics::Bar& bar) { /* ... */ }};
manager.set_trade_callback([&](const itch::Trade& trade) {
vwap.add(trade.price, trade.shares);
bars.add(trade);
});
// ... parse the feed ...
bars.flush();
double session_vwap = vwap.value();Meet researchers in the tools they already use.
Output sinks. itch/io/csv_sink.hpp flattens any message stream into a wide
CSV table (dependency-free). With -DITCH_WITH_ARROW=ON (the arrow vcpkg
feature), itch/io/arrow_export.hpp writes Parquet for pandas/Polars/DuckDB/Spark.
itch-tool CLI (-DITCH_BUILD_TOOLS=ON). Inspect, filter, and convert feeds
without writing code; input may be a raw ITCH stream or a .pcap/.pcapng
capture (auto-detected):
itch-tool stats data.itch # per-type message histogram
itch-tool inspect data.pcapng --limit 50 # human-readable dump
itch-tool filter data.itch --types AEP --out trades.csv
itch-tool convert data.itch --out data.csv # ITCH -> CSV (-> Parquet w/ Arrow)Python bindings (-DITCH_BUILD_PYTHON=ON, pybind11). The itchcpp package is
a faster, drop-in backend for the pure-Python
itch package (PyPI:
itchfeed). It mirrors that package's layout
(itchcpp.messages, itchcpp.parser, itchcpp.indicators) and semantics, the same
message classes with the same raw bytes/int attributes, the same MessageParser
(type filter, lazy iteration, parse_file/parse_stream/parse_messages),
create_message, and decode/decode_price/to_bytes helpers, so migrating only
changes the import root. See itchcpp.
from itchcpp.parser import MessageParser
from itchcpp.messages import AddOrderNoMPIAttributionMessage
parser = MessageParser() # MessageParser(message_type=b"AFE") to filter types
with open("01302020.NASDAQ_ITCH50", "rb") as itch_file:
for message in parser.parse_file(itch_file):
if isinstance(message, AddOrderNoMPIAttributionMessage):
print(message.stock, message.decode_price("price"), message.shares)Replay engine (itch/replay.hpp). Drive a consumer at the feed's original
cadence (or a scaled speed) for realistic backtesting:
#include "itch/replay.hpp"
itch::ReplayEngine engine{10.0}; // 10x real time; <= 0 means as fast as possible
engine.replay(buffer, [](const itch::Message& msg) { /* paced by timestamps */ });Encoder / writer (itch/encoder.hpp). Serialize any message back to valid
wire bytes, with a guaranteed parse(encode(msg)) == msg round-trip, used to
synthesize streams and golden fixtures:
#include "itch/encoder.hpp"
std::vector<std::byte> frame = itch::encode_frame(message); // length-prefixedMulti-venue seam (itch/venue.hpp). NASDAQ TotalView-ITCH 5.0 is the only
implemented venue; itch::venue::VenuePolicy (modelled by itch::venue::Nasdaq50)
is the extension point for adding BX/PSX or older ITCH versions without rewriting
the dispatch machinery.
Packaging. The library is consumable through vcpkg (manifest with optional
python and arrow features) and Conan (conanfile.py). See
CONTRIBUTING for the build options and the versioning/ABI
compatibility policy.
This library supports all message types defined in the ITCH 5.0 specification. The char represents the message type on the wire.
| Type | Struct Name | Description |
|---|---|---|
S |
SystemEventMessage |
Signals a market or data feed handler event. |
| Type | Struct Name | Description |
|---|---|---|
R |
StockDirectoryMessage |
Conveys stock symbol directory information. |
H |
StockTradingActionMessage |
Indicates a change in trading status for a security (e.g., Halted, Trading). |
Y |
RegSHORestrictionMessage |
Regulation SHO short sale price test restricted indicator. |
L |
MarketParticipantPositionMessage |
Conveys a market participant's status (e.g., Primary Market Maker). |
V |
MWCBDeclineLevelMessage |
Informs of the Market-Wide Circuit Breaker (MWCB) breach points for the day. |
W |
MWCBStatusMessage |
Indicates that a MWCB level has been breached. |
h |
OperationalHaltMessage |
Indicates an operational halt for a security on a specific market center. |
| Type | Struct Name | Description |
|---|---|---|
A |
AddOrderMessage |
A new order has been accepted (no MPID attribution). |
F |
AddOrderMPIDMessage |
A new order has been accepted (with MPID attribution). |
E |
OrderExecutedMessage |
An order on the book was executed in part or in full. |
C |
OrderExecutedWithPriceMessage |
An order was executed at a price different from the display price. |
X |
OrderCancelMessage |
An order was partially canceled; this message contains the canceled quantity. |
D |
OrderDeleteMessage |
An order was canceled in its entirety and removed from the book. |
U |
OrderReplaceMessage |
An existing order has been replaced with a new order. |
| Type | Struct Name | Description |
|---|---|---|
P |
TradeMessage |
Reports a trade for a non-displayable order type. |
Q |
CrossTradeMessage |
Reports a cross trade (e.g., Opening, Closing, IPO Cross). |
B |
BrokenTradeMessage |
Reports that a previously disseminated execution has been broken. |
| Type | Struct Name | Description |
|---|---|---|
K |
IPOQuotingPeriodUpdateMessage |
Provides the anticipated IPO quotation release time. |
J |
LULDAuctionCollarMessage |
Indicates auction collar thresholds for a security in a LULD Trading Pause. |
I |
NOIIMessage |
Net Order Imbalance Indicator message, used for opening/closing crosses. |
N |
RetailPriceImprovementIndicator |
Indicates the presence of Retail Price Improvement (RPII) interest. |
O |
DirectListingCapitalRaisePriceDiscovery |
Disseminated for Direct Listing with Capital Raise (DLCR) securities. |
The parser is designed for high-throughput scenarios. Performance is heavily dependent on the underlying hardware (CPU speed, memory bandwidth, and I/O speed).
The numbers below were produced by benchmarks/parser_bench.cpp parsing from an in-memory buffer (file I/O excluded). They are reproducible with the setup described under How to reproduce.
| Configuration | Throughput |
|---|---|
parse(..., callback), allocation-free callback path |
2.24 GiB/s |
parse(...), collect all messages into a std::vector |
1.23 GiB/s |
parse(..., {'A','P','E','C','X'}), filtered into a std::vector |
1.15 GiB/s |
- Hardware: x86-64, 16 logical cores @ 1.70 GHz (L1d 32 KiB, L2 512 KiB, L3 4 MiB).
- Compiler: Clang 22,
-DCMAKE_BUILD_TYPE=Release, C++23. - Dataset: official NASDAQ TotalView-ITCH 5.0 sample (
01302020), a ~300 MB frame-aligned slice loaded fully into memory.
The collect/filter paths are slower than the callback path because they allocate and copy each parsed message into a std::vector; the callback path does neither, which is why it is the recommended interface for latency-sensitive processing.
These are produced by benchmarks/book_bench.cpp.
The throughput figures below were measured on a synthetic, churn-heavy stream
(adds and deletes around a moving mid across 100 symbols) on the same hardware,
Clang 22 -DCMAKE_BUILD_TYPE=Release, C++23; book-rebuild throughput on real
data depends heavily on the depth and churn profile of the feed.
| Configuration | Throughput |
|---|---|
BookManager::process, full multi-symbol L3 reconstruction |
~150 MiB/s |
Eager parse, touching one field per message |
~5.4 GiB/s |
Zero-copy overlay::for_each_message, touching one field per message |
~9.7 GiB/s |
The overlay is materially faster than the eager decoder when only a few fields are
read, because it never decodes the fields the caller does not touch. Reproduce
with ./build/benchmarks/book_bench <itch_file>.
# 1. Fetch an official sample (multi-GB; not committed to the repo).
scripts/fetch_sample.sh data
# 2. Configure and build with benchmarks enabled.
cmake -S . -B build -DITCH_BUILD_BENCHMARKS=ON -DITCH_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release
# 3. Run the benchmark suite against the sample.
./build/benchmarks/parser_bench data/01302020.NASDAQ_ITCH50The output reports throughput in GiB/s for each parsing strategy.
The project uses .clang-format to enforce a consistent code style. A .clang-tidy configuration is also provided to catch common programming errors and enforce best practices. Developers contributing to the project are expected to format their code using these tools.
-
What exactly is this NASDAQ ITCH data? It's the most detailed data feed available from NASDAQ. It's not just stock prices; it’s every single order placed, modified, cancelled, and executed. It's the complete "play-by-play" of the market.
-
Why can't I just use a simple program to read this data? The data is in a highly optimized, machine-only binary format, not human-readable text. More importantly, the volume and velocity are immense, a single day of trading can generate tens or hundreds of gigabytes of data. A standard program would be far too slow to keep up.
-
Is this a program I can just double-click and run? No, it’s a specialized component, a library, that software developers use as the "engine" inside a larger application (like a trading platform or an analysis tool). It does the heavy lifting of data processing so they can focus on building the features their users need.
Contributions are welcome! Whether it's a bug report, a feature request, or a pull request, your input is valuable. Please feel free to open an issue or submit a PR.
This project is licensed under the MIT License. See the LICENSE file for details.
- Nasdaq TotalView-ITCH 5.0 Specification: The official documentation is the definitive source for protocol details.