ITCHCPP 1.6.0
High-Performance NASDAQ TotalView-ITCH 5.0 Parser & Market-Data Platform
Loading...
Searching...
No Matches
csv_sink.cpp
Go to the documentation of this file.
2
3#include <format>
4#include <string>
5#include <variant>
6
7#include "itch/price.hpp"
8
9namespace itch::io {
10
11namespace {
12
14struct Row {
15 char message_type {'\0'};
16 std::uint64_t timestamp {0};
17 std::uint16_t stock_locate {0};
18 std::uint16_t tracking_number {0};
19 std::string symbol;
20 std::string reference_number;
21 char side {'\0'};
22 std::string shares;
23 std::string price;
24 std::string match_number;
25 char printable {'\0'};
26 std::string extra;
27};
28
29// Builds the common header fields shared by every message.
30template <typename MsgType>
31auto fill_common(Row& row, const MsgType& msg) -> void {
32 row.message_type = msg.message_type;
33 row.timestamp = msg.timestamp;
34 row.stock_locate = msg.stock_locate;
35 row.tracking_number = msg.tracking_number;
36}
37
38// Quotes a value only if it contains a comma; symbols never do, but be safe.
39auto field(const std::string& value) -> std::string {
40 // std::string::contains needs C++23, but this file must also build under
41 // this project's C++20 floor.
42 // NOLINTNEXTLINE(readability-container-contains)
43 if (value.find(',') != std::string::npos) {
44 return std::format("\"{}\"", value);
45 }
46 return value;
47}
48
49auto build_row(const Message& message) -> Row {
50 Row row {};
51 std::visit([&row](const auto& msg) { fill_common(row, msg); }, message);
52
53 if (const auto* add = std::get_if<AddOrderMessage>(&message)) {
54 row.symbol = to_string(add->stock, STOCK_LEN);
55 row.reference_number = std::to_string(add->order_reference_number);
56 row.side = add->buy_sell_indicator;
57 row.shares = std::to_string(add->shares);
58 row.price = StandardPrice {add->price}.to_string();
59 } else if (const auto* add_mpid = std::get_if<AddOrderMPIDAttributionMessage>(&message)) {
60 row.symbol = to_string(add_mpid->stock, STOCK_LEN);
61 row.reference_number = std::to_string(add_mpid->order_reference_number);
62 row.side = add_mpid->buy_sell_indicator;
63 row.shares = std::to_string(add_mpid->shares);
64 row.price = StandardPrice {add_mpid->price}.to_string();
65 row.extra = std::format("mpid={}", to_string(add_mpid->attribution, 4));
66 } else if (const auto* exec = std::get_if<OrderExecutedMessage>(&message)) {
67 row.reference_number = std::to_string(exec->order_reference_number);
68 row.shares = std::to_string(exec->executed_shares);
69 row.match_number = std::to_string(exec->match_number);
70 } else if (const auto* exec_px = std::get_if<OrderExecutedWithPriceMessage>(&message)) {
71 row.reference_number = std::to_string(exec_px->order_reference_number);
72 row.shares = std::to_string(exec_px->executed_shares);
73 row.price = StandardPrice {exec_px->execution_price}.to_string();
74 row.match_number = std::to_string(exec_px->match_number);
75 row.printable = exec_px->printable;
76 } else if (const auto* cancel = std::get_if<OrderCancelMessage>(&message)) {
77 row.reference_number = std::to_string(cancel->order_reference_number);
78 row.shares = std::to_string(cancel->cancelled_shares);
79 } else if (const auto* del = std::get_if<OrderDeleteMessage>(&message)) {
80 row.reference_number = std::to_string(del->order_reference_number);
81 } else if (const auto* replace = std::get_if<OrderReplaceMessage>(&message)) {
82 row.reference_number = std::to_string(replace->new_order_reference_number);
83 row.shares = std::to_string(replace->shares);
84 row.price = StandardPrice {replace->price}.to_string();
85 row.extra = std::format("orig={}", replace->original_order_reference_number);
86 } else if (const auto* trade = std::get_if<NonCrossTradeMessage>(&message)) {
87 row.symbol = to_string(trade->stock, STOCK_LEN);
88 row.reference_number = std::to_string(trade->order_reference_number);
89 row.side = trade->buy_sell_indicator;
90 row.shares = std::to_string(trade->shares);
91 row.price = StandardPrice {trade->price}.to_string();
92 row.match_number = std::to_string(trade->match_number);
93 } else if (const auto* cross = std::get_if<CrossTradeMessage>(&message)) {
94 row.symbol = to_string(cross->stock, STOCK_LEN);
95 row.shares = std::to_string(cross->shares);
96 row.price = StandardPrice {cross->cross_price}.to_string();
97 row.match_number = std::to_string(cross->match_number);
98 row.extra = std::format("cross={}", cross->cross_type);
99 } else if (const auto* event = std::get_if<SystemEventMessage>(&message)) {
100 row.extra = std::format("event={}", event->event_code);
101 } else if (const auto* directory = std::get_if<StockDirectoryMessage>(&message)) {
102 row.symbol = to_string(directory->stock, STOCK_LEN);
103 row.extra = std::format("category={}", directory->market_category);
104 }
105 return row;
106}
107
108// Renders a single character field, blank when unset.
109auto char_field(char value) -> std::string {
110 return value == '\0' ? std::string {} : std::string {value};
111}
112
113} // namespace
114
115CsvSink::CsvSink(std::ostream& out, bool write_header) : m_out {out} {
116 if (write_header) {
117 m_out << "message_type,timestamp,stock_locate,tracking_number,symbol,reference_number,"
118 "side,shares,price,match_number,printable,extra\n";
119 }
120}
121
122auto CsvSink::write(const Message& message) -> void {
123 const Row row = build_row(message);
124 m_out << std::format(
125 "{},{},{},{},{},{},{},{},{},{},{},{}\n",
126 row.message_type,
127 row.timestamp,
128 row.stock_locate,
129 row.tracking_number,
130 field(row.symbol),
131 row.reference_number,
132 char_field(row.side),
133 row.shares,
134 row.price,
135 row.match_number,
136 char_field(row.printable),
137 field(row.extra)
138 );
139 ++m_rows_written;
140}
141
142auto CsvSink::flush() -> void { m_out.flush(); }
143
144} // namespace itch::io
auto to_string() const -> std::string
The price as a fixed-precision decimal string.
Definition price.hpp:79
auto write(const Message &message) -> void override
Writes one message as a CSV row.
Definition csv_sink.cpp:122
auto flush() -> void override
Flushes the underlying stream.
Definition csv_sink.cpp:142
CsvSink(std::ostream &out, bool write_header=true)
Constructs a sink writing to out, emitting the header row unless write_header is false (useful when a...
Definition csv_sink.cpp:115
char printable
Definition csv_sink.cpp:25
std::string reference_number
Definition csv_sink.cpp:20
std::uint16_t tracking_number
Definition csv_sink.cpp:18
std::string symbol
Definition csv_sink.cpp:19
std::string price
Definition csv_sink.cpp:23
std::uint64_t timestamp
Definition csv_sink.cpp:16
std::uint16_t stock_locate
Definition csv_sink.cpp:17
char message_type
Definition csv_sink.cpp:15
std::string extra
Definition csv_sink.cpp:26
std::string match_number
Definition csv_sink.cpp:24
char side
Definition csv_sink.cpp:21
std::string shares
Definition csv_sink.cpp:22
CSV output sink for parsed ITCH messages.
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
Strongly typed, fixed-point price representation for ITCH price fields.