12#include <order_book/feed.hpp>
17enum class ItchKind : std::uint8_t {
60[[nodiscard]] std::string describe(
const ItchError& error);
62namespace itch_detail {
66constexpr std::size_t locate_offset = 1;
67constexpr std::size_t time_offset = 5;
68constexpr std::size_t order_offset = 11;
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;
76constexpr std::size_t shares_offset = 19;
78constexpr std::size_t new_order_offset = 19;
79constexpr std::size_t replace_shares_offset = 27;
80constexpr std::size_t replace_price_offset = 31;
83constexpr std::string_view skipped_types =
"SRHYLVWKJhBINO";
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},
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;
104constexpr std::size_t message_size(
char type)
106 return size_by_type[
static_cast<unsigned char>(type)];
110template <
typename T> T read(std::string_view message, std::size_t offset)
113 std::memcpy(&value, message.data() + offset,
sizeof(T));
114 if constexpr (std::endian::native == std::endian::little) {
115 value = std::byteswap(value);
121inline std::uint64_t read_time(std::string_view message)
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);
135template <
typename Handler>
decltype(
auto) visit_itch(std::string_view message, Handler&& handler)
137 using namespace itch_detail;
138 if (message.empty()) {
139 return handler.error(
142 const auto type = message.front();
143 const auto size = message_size(type);
145 if (skipped_types.contains(type)) {
146 return handler.skipped();
155 if (message.size() != size) {
159 .size = message.size(),
165 .time = read_time(message),
171 .locate = read<std::uint16_t>(message, locate_offset),
172 .kind = ItchKind::Trade,
175 if (type ==
'P' || type ==
'Q') {
177 return handler.trade(decoded);
179 decoded.
order = read<std::uint64_t>(message, order_offset);
181 decoded.replacement = read<std::uint64_t>(message, new_order_offset);
186 const auto side = message[side_offset];
187 if (side !=
'B' && side !=
'S') {
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);
203 decoded.kind = ItchKind::Delete;
204 return handler.remove(decoded);
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);
212 decoded.kind = ItchKind::Execute;
213 decoded.shares = read<std::uint32_t>(message, shares_offset);
214 return handler.execute(decoded);
224[[nodiscard]] std::expected<ItchMessage, ItchError> decode_itch(std::string_view message);
238inline ItchPeek peek_itch(std::string_view message)
240 using itch_detail::order_offset;
241 if (message.size() < order_offset +
sizeof(std::uint64_t) ||
242 itch_detail::message_size(message.front()) == 0) {
246 .order = itch_detail::read<std::uint64_t>(message, order_offset),
247 .locate = itch_detail::read<std::uint16_t>(message, itch_detail::locate_offset),
Why decode_itch() rejected a message.
char type
The message type, or the offending byte for UnknownType and InvalidSide.
friend bool operator==(const ItchError &, const ItchError &)=default
Memberwise equality.
std::size_t size
The message's size, for WrongSize.
Reason reason
The rule broken.
Reason
The rule the message broke.
@ WrongSize
A size other than the specification's for its type.
@ InvalidSide
A side other than B or S.
@ UnknownType
A type byte outside the specification.
std::uint16_t locate
Stock locate code, the day's index of the security.
Price price
Limit price of an add or a replacement.
std::uint64_t time
Nanoseconds since midnight.
std::uint64_t replacement
Reference number of the new order, for Replace.
std::string_view stock
Stock symbol, right-padded with spaces to 8 bytes, for Add.
friend bool operator==(const ItchMessage &, const ItchMessage &)=default
Memberwise equality.
Shares shares
Shares added, taken off, or of the replacement.
std::uint64_t order
Order reference number.
ItchKind kind
What the message does.
The stock locate and order reference number of a message, as peek_itch() reads them.
friend bool operator==(const ItchPeek &, const ItchPeek &)=default
Same fields.
std::uint64_t order
Order reference number.