order_book 0.3.0
Exchange feed order book that prints the best bid and offer
Loading...
Searching...
No Matches
itch.hpp
1#pragma once
2
3#include <array>
4#include <bit>
5#include <cstddef>
6#include <cstdint>
7#include <cstring>
8#include <expected>
9#include <string>
10#include <string_view>
11
12#include <order_book/feed.hpp>
13
14namespace order_book {
15
17enum class ItchKind : std::uint8_t {
18 Add,
19 Execute,
20 Delete,
21 Replace,
22 Trade,
23 Skipped,
24};
25
29 std::uint64_t time;
30 std::uint64_t order;
31 std::uint64_t replacement;
32 std::string_view stock;
33 Shares shares;
34 Price price;
35 std::uint16_t locate;
36 ItchKind kind;
37 Side side;
39 friend bool operator==(const ItchMessage&, const ItchMessage&) = default;
40};
41
43struct ItchError {
45 enum class Reason : std::uint8_t {
50 };
52 char type;
53 std::size_t size;
54 std::uint64_t value;
56 friend bool operator==(const ItchError&, const ItchError&) = default;
57};
58
60[[nodiscard]] std::string describe(const ItchError& error);
61
62namespace itch_detail {
63
64// Field offsets from the specification. Every message starts with its type, stock locate,
65// tracking number, and timestamp, and every type the book uses then names an order.
66constexpr std::size_t locate_offset = 1;
67constexpr std::size_t time_offset = 5;
68constexpr std::size_t order_offset = 11;
69// A and F
70constexpr std::size_t side_offset = 19;
71constexpr std::size_t add_shares_offset = 20;
72constexpr std::size_t stock_offset = 24;
73constexpr std::size_t stock_size = 8;
74constexpr std::size_t add_price_offset = 32;
75// E, C, and X
76constexpr std::size_t shares_offset = 19;
77// U
78constexpr std::size_t new_order_offset = 19;
79constexpr std::size_t replace_shares_offset = 27;
80constexpr std::size_t replace_price_offset = 31;
81
82// The specification's message types the book does not use
83constexpr std::string_view skipped_types = "SRHYLVWKJhBINO";
84
85struct Size {
86 char type;
87 std::uint8_t size;
88};
89// Size of each message type the book uses
90constexpr std::array sizes{
91 Size{.type = 'A', .size = 36}, Size{.type = 'F', .size = 40}, Size{.type = 'E', .size = 31},
92 Size{.type = 'C', .size = 36}, Size{.type = 'X', .size = 23}, Size{.type = 'D', .size = 19},
93 Size{.type = 'U', .size = 35}, Size{.type = 'P', .size = 44}, Size{.type = 'Q', .size = 40},
94};
95// Sizes by type byte, so each message costs one lookup: 0 for a type the book does not use
96constexpr auto size_by_type = [] {
97 std::array<std::uint8_t, 256> table{};
98 for (const auto& [type, size] : sizes) {
99 table[static_cast<unsigned char>(type)] = size;
100 }
101 return table;
102}();
103
104constexpr std::size_t message_size(char type)
105{
106 return size_by_type[static_cast<unsigned char>(type)];
107}
108
109// Big-endian unsigned integer at `offset`, which the caller has checked lies within `message`
110template <typename T> T read(std::string_view message, std::size_t offset)
111{
112 T value{};
113 std::memcpy(&value, message.data() + offset, sizeof(T));
114 if constexpr (std::endian::native == std::endian::little) {
115 value = std::byteswap(value);
116 }
117 return value;
118}
119
120// The 6-byte timestamp
121inline std::uint64_t read_time(std::string_view message)
122{
123 constexpr unsigned high_shift = 32;
124 return (std::uint64_t{read<std::uint16_t>(message, time_offset)} << high_shift) |
125 read<std::uint32_t>(message, time_offset + 2);
126}
127
128} // namespace itch_detail
129
135template <typename Handler> decltype(auto) visit_itch(std::string_view message, Handler&& handler)
136{
137 using namespace itch_detail;
138 if (message.empty()) {
139 return handler.error(
140 ItchError{.reason = ItchError::Reason::Empty, .type = 0, .size = 0, .value = 0});
141 }
142 const auto type = message.front();
143 const auto size = message_size(type);
144 if (size == 0) {
145 if (skipped_types.contains(type)) {
146 return handler.skipped();
147 }
148 return handler.error(ItchError{
150 .type = type,
151 .size = 0,
152 .value = 0,
153 });
154 }
155 if (message.size() != size) {
156 return handler.error(ItchError{
158 .type = type,
159 .size = message.size(),
160 .value = size,
161 });
162 }
163
164 ItchMessage decoded{
165 .time = read_time(message),
166 .order = 0,
167 .replacement = 0,
168 .stock = {},
169 .shares = 0,
170 .price = 0,
171 .locate = read<std::uint16_t>(message, locate_offset),
172 .kind = ItchKind::Trade,
173 .side = Side::Buy,
174 };
175 if (type == 'P' || type == 'Q') {
176 // Trades, whose order field, if any, names no order on the book
177 return handler.trade(decoded);
178 }
179 decoded.order = read<std::uint64_t>(message, order_offset);
180 if (type == 'U') {
181 decoded.replacement = read<std::uint64_t>(message, new_order_offset);
182 }
183 switch (type) {
184 case 'A':
185 case 'F': {
186 const auto side = message[side_offset];
187 if (side != 'B' && side != 'S') {
188 return handler.error(ItchError{
190 .type = side,
191 .size = 0,
192 .value = 0,
193 });
194 }
195 decoded.kind = ItchKind::Add;
196 decoded.stock = message.substr(stock_offset, stock_size);
197 decoded.shares = read<std::uint32_t>(message, add_shares_offset);
198 decoded.price = read<std::uint32_t>(message, add_price_offset);
199 decoded.side = side == 'B' ? Side::Buy : Side::Sell;
200 return handler.add(decoded);
201 }
202 case 'D':
203 decoded.kind = ItchKind::Delete;
204 return handler.remove(decoded);
205 case 'U':
206 decoded.kind = ItchKind::Replace;
207 decoded.shares = read<std::uint32_t>(message, replace_shares_offset);
208 decoded.price = read<std::uint32_t>(message, replace_price_offset);
209 return handler.replace(decoded);
210 default:
211 // E, C, and X
212 decoded.kind = ItchKind::Execute;
213 decoded.shares = read<std::uint32_t>(message, shares_offset);
214 return handler.execute(decoded);
215 }
216}
217
224[[nodiscard]] std::expected<ItchMessage, ItchError> decode_itch(std::string_view message);
225
227struct ItchPeek {
228 std::uint64_t order = 0;
229 std::uint16_t locate = 0;
231 friend bool operator==(const ItchPeek&, const ItchPeek&) = default;
232};
233
238inline ItchPeek peek_itch(std::string_view message)
239{
240 using itch_detail::order_offset;
241 if (message.size() < order_offset + sizeof(std::uint64_t) ||
242 itch_detail::message_size(message.front()) == 0) {
243 return {};
244 }
245 return {
246 .order = itch_detail::read<std::uint64_t>(message, order_offset),
247 .locate = itch_detail::read<std::uint16_t>(message, itch_detail::locate_offset),
248 };
249}
250
251} // namespace order_book
Why decode_itch() rejected a message.
Definition itch.hpp:43
std::uint64_t value
Definition itch.hpp:54
char type
The message type, or the offending byte for UnknownType and InvalidSide.
Definition itch.hpp:52
friend bool operator==(const ItchError &, const ItchError &)=default
Memberwise equality.
std::size_t size
The message's size, for WrongSize.
Definition itch.hpp:53
Reason reason
The rule broken.
Definition itch.hpp:51
Reason
The rule the message broke.
Definition itch.hpp:45
@ WrongSize
A size other than the specification's for its type.
Definition itch.hpp:48
@ InvalidSide
A side other than B or S.
Definition itch.hpp:49
@ UnknownType
A type byte outside the specification.
Definition itch.hpp:47
@ Empty
No bytes at all.
Definition itch.hpp:46
std::uint16_t locate
Stock locate code, the day's index of the security.
Definition itch.hpp:35
Price price
Limit price of an add or a replacement.
Definition itch.hpp:34
std::uint64_t time
Nanoseconds since midnight.
Definition itch.hpp:29
std::uint64_t replacement
Reference number of the new order, for Replace.
Definition itch.hpp:31
std::string_view stock
Stock symbol, right-padded with spaces to 8 bytes, for Add.
Definition itch.hpp:32
friend bool operator==(const ItchMessage &, const ItchMessage &)=default
Memberwise equality.
Shares shares
Shares added, taken off, or of the replacement.
Definition itch.hpp:33
std::uint64_t order
Order reference number.
Definition itch.hpp:30
ItchKind kind
What the message does.
Definition itch.hpp:36
The stock locate and order reference number of a message, as peek_itch() reads them.
Definition itch.hpp:227
friend bool operator==(const ItchPeek &, const ItchPeek &)=default
Same fields.
std::uint64_t order
Order reference number.
Definition itch.hpp:228
std::uint16_t locate
Definition itch.hpp:229