-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path02_market_data.cpp
More file actions
132 lines (113 loc) · 6.11 KB
/
Copy path02_market_data.cpp
File metadata and controls
132 lines (113 loc) · 6.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// ============================================================================
// LESSON 02 — Market data: structs, parsing, containers, time
// ============================================================================
// Build & run (from the folder root, so it can find data/ticks.csv):
// make 02 && ./bin/02_market_data
//
// The first real job of most junior trading developers: take a feed of raw
// market data and turn it into clean, typed structs the rest of the system
// can use. Here the "feed" is a CSV file of trades (ticks); in production
// it's a binary protocol over UDP multicast (e.g. Nasdaq ITCH) — but the
// shape of the code is identical: bytes in → validated structs out.
// ============================================================================
#include <cstdint>
#include <fstream> // std::ifstream — file input, an RAII object (lesson 01!)
#include <iostream>
#include <sstream> // std::istringstream — parse a line like a stream
#include <string>
#include <vector>
#include <algorithm> // std::minmax_element
using Price = std::int64_t; // scaled by 10'000, as established in lesson 01
using Qty = std::int64_t;
// ----------------------------------------------------------------------------
// A struct groups related data. In trading code these are kept "plain":
// public data, no behaviour — they're messages, not objects with logic.
// ----------------------------------------------------------------------------
struct Tick {
std::int64_t ts_ns; // nanoseconds since midnight. Time is ALWAYS an
// integer nanosecond count in trading systems —
// never a string, never a double.
std::string symbol;
Price price; // scaled integer, e.g. 1'012'500 == $101.25
Qty qty;
char side; // 'B' = aggressor bought, 'S' = aggressor sold
};
// Parse "34200000001000,MARQ,1012500,100,B" into a Tick.
// Returns true on success. Returning bool + out-parameter is the simple
// classic pattern; modern codebases often use std::optional<Tick> instead.
bool parse_tick(const std::string& line, Tick& out) {
std::istringstream ss(line); // treat the string as a stream
std::string field;
if (!std::getline(ss, field, ',')) return false; // read up to next comma
out.ts_ns = std::stoll(field); // string -> long long
if (!std::getline(ss, out.symbol, ',')) return false;
if (!std::getline(ss, field, ',')) return false;
out.price = std::stoll(field);
if (!std::getline(ss, field, ',')) return false;
out.qty = std::stoll(field);
if (!std::getline(ss, field, ',')) return false;
if (field.empty() || (field[0] != 'B' && field[0] != 'S')) return false;
out.side = field[0];
// GOLDEN RULE OF FEED HANDLERS: never trust the wire. Validate everything;
// one bad message must not take down the system or poison the book.
return out.price > 0 && out.qty > 0;
}
int main() {
std::cout << "=== 02 Market data ===\n";
// --- read the file -------------------------------------------------------
std::ifstream file("data/ticks.csv"); // opens in constructor (RAII);
if (!file) { // closes itself in destructor
std::cerr << "ERROR: run from the folder root: ./bin/02_market_data\n";
return 1; // non-zero exit = failure, so scripts/make can detect it
}
std::vector<Tick> ticks; // std::vector: THE default container. A dynamic
ticks.reserve(1024); // array, contiguous in memory (fast — lesson 05).
// reserve() pre-allocates so the hot loop below
// never has to stop and reallocate.
std::string line;
std::getline(file, line); // skip the CSV header row
std::size_t bad = 0;
while (std::getline(file, line)) {
Tick t;
if (parse_tick(line, t)) ticks.push_back(t);
else ++bad; // count, don't crash
}
std::cout << "parsed " << ticks.size() << " ticks (" << bad << " bad rows)\n";
if (ticks.empty()) return 1;
// --- basic analytics over the container ---------------------------------
Qty volume = 0;
std::int64_t notional = 0; // price*qty summed, still scaled
Qty buys = 0;
for (const Tick& t : ticks) { // const ref: no copy per element
volume += t.qty;
notional += t.price * t.qty;
if (t.side == 'B') buys += t.qty;
}
const double vwap = static_cast<double>(notional) / volume / 10'000.0;
// std::minmax_element returns iterators to the smallest/largest element.
// The lambda tells it to compare ticks BY PRICE. Lambdas — inline,
// unnamed functions — are everywhere in modern C++.
auto [lo, hi] = std::minmax_element(
ticks.begin(), ticks.end(),
[](const Tick& a, const Tick& b) { return a.price < b.price; });
const double session_secs =
static_cast<double>(ticks.back().ts_ns - ticks.front().ts_ns) / 1e9;
std::cout << "symbol " << ticks.front().symbol << "\n"
<< "session " << session_secs << " s of data\n"
<< "volume " << volume << " (buy-side aggression "
<< 100.0 * static_cast<double>(buys) / volume << "%)\n"
<< "low/high $" << lo->price / 10'000.0
<< " / $" << hi->price / 10'000.0 << "\n"
<< "vwap $" << vwap << "\n";
return 0;
}
// ----------------------------------------------------------------------------
// EXERCISES
// 1. Add a field `venue` to Tick and to the parser; make one row in the CSV
// carry it and watch the parser reject the other rows. Fix gracefully.
// 2. Compute the largest single-tick price jump (in ticks) — feed handlers
// use exactly this to detect bad prints / fat fingers.
// 3. parse_tick uses std::stoll, which THROWS on garbage like "abc".
// Wrap it in try/catch so a malformed row counts as bad instead of
// terminating the program. (Look up: try { } catch (const std::exception&))
// ----------------------------------------------------------------------------