Feed Message Parsing in C++
Read at your own pace. Code execution, quizzes, progress tracking, and the AI tutor are available in the interactive view with JavaScript enabled.
From Feed To Struct
Why this lesson matters
Now that we've covered Trading Lab Project Skeleton and Market Microstructure & Data Feeds, we know what quotes, trades, sequence numbers, and timestamps mean.
Now we make them executable.
A feed handler's first job is boring but critical:
raw line / bytes -> parser -> typed event -> validation -> downstream state
If the parser is wrong, everything after it can be fast and still be wrong.
In production HFT systems, feed formats are often binary, venue-specific, and heavily optimized. In this foundation lesson, we use a tiny readable format so you can practice the core skill: turn raw feed messages into trustworthy typed records.
We will not build a full order book yet. That comes later in Market Data Handling & Order Book Reconstruction.

Tiny Feed Format
A lab format we can parse
Real exchange feeds have official specifications. They define message types, field widths, encodings, sequence behavior, timestamps, and recovery rules.
For our lab, use this simplified text format:
Q,seq,timestamp_ns,symbol,bid,ask,bid_qty,ask_qty
T,seq,timestamp_ns,symbol,price,qty
Example messages:
Q,1001,1710000000123456789,AAPL,189.10,189.12,500,300
T,1002,1710000000123456795,AAPL,189.12,100
Meaning:
-
Qmeans quote update. -
Tmeans trade print. -
seqis the feed sequence number. -
timestamp_nsis the exchange/event timestamp in nanoseconds. -
symbolidentifies the instrument. - Quote fields update visible bid/ask information.
- Trade fields report an executed transaction.
The important habit: do not pass strings around forever. Parse once into a clear event shape.

Parse One Line
First parser: text to event
Even in a C++ trading stack, robust parsing starts with low-level habits:
- define the output type clearly
- check how many fields were actually read
- keep buffers bounded
- return success/failure explicitly
This C-compatible example parses one quote and one trade. The same idea ports directly into C++ structs and enums later.
```c
#include <stdio.h>
#include <string.h>
typedef enum {
MSG_UNKNOWN = 0,
MSG_QUOTE = 1,
MSG_TRADE = 2
} MsgType;
typedef struct {
MsgType type;
unsigned long long seq;
unsigned long long timestamp_ns;
char symbol[16];
double bid;
double ask;
double price;
int bid_qty;
int ask_qty;
int qty;
} FeedEvent;
int parse_line(const char *line, FeedEvent *out) {
memset(out, 0, sizeof(*out));
char kind = '\0';
if (line[0] == 'Q') {
int fields = sscanf(
line,
" %c,%llu,%llu,%15[^,],%lf,%lf,%d,%d",
&kind,
&out->seq,
&out->timestamp_ns,
out->symbol,
&out->bid,
&out->ask,
&out->bid_qty,
&out->ask_qty
);
if (fields != 8) return 0;
out->type = MSG_QUOTE;
return 1;
}
if (line[0] == 'T') {
int fields = sscanf(
line,
" %c,%llu,%llu,%15[^,],%lf,%d",
&kind,
&out->seq,
&out->timestamp_ns,
out->symbol,
&out->price,
&out->qty
);
if (fields != 6) return 0;
out->type = MSG_TRADE;
return 1;
}
return 0;
}
void print_event(const FeedEvent *e) {
if (e->type == MSG_QUOTE) {
printf("QUOTE seq=%llu symbol=%s bid=%.2f ask=%.2f bid_qty=%d ask_qty=%d\n",
e->seq, e->symbol, e->bid, e->ask, e->bid_qty, e->ask_qty);
} else if (e->type == MSG_TRADE) {
printf("TRADE seq=%llu symbol=%s price=%.2f qty=%d\n",
e->seq, e->symbol, e->price, e->qty);
}
}
int main(void) {
const char *lines[] = {
"Q,1001,1710000000123456789,AAPL,189.10,189.12,500,300",
"T,1002,1710000000123456795,AAPL,189.12,100",
"Q,broken,line"
};
for (int i = 0; i < 3; ++i) {
FeedEvent event;
if (parse_line(lines[i], &event)) {
print_event(&event);
} else {
printf("REJECT malformed line: %s\n", lines[i]);
}
}
return 0;
}
```
Parser Responsibility
A feed line is missing required fields. The parser cannot confidently build a complete quote or trade event.
What should the parser do first?
Validate Before Trust
Parsing is not enough
A message can be syntactically valid but still unsafe to use.
Examples:
- Sequence number jumps forward.
- Sequence number arrives late or duplicated.
- Quote has
bid >= ask. - Quantity is negative or zero when the feed spec requires positive size.
In the previous lesson, you learned why sequence numbers matter. Here, we attach that idea to the parser boundary: after parsing, validate before downstream code reacts.
```c
#include <stdio.h>
typedef enum {
MSG_QUOTE = 1,
MSG_TRADE = 2
} MsgType;
typedef struct {
MsgType type;
unsigned long long seq;
const char *symbol;
double bid;
double ask;
double price;
int bid_qty;
int ask_qty;
int qty;
} FeedEvent;
int validate_event(const FeedEvent *e, unsigned long long *expected_seq) {
if (e->seq != *expected_seq) {
if (e->seq > *expected_seq) {
printf("GAP expected=%llu received=%llu\n", *expected_seq, e->seq);
} else {
printf("LATE_OR_DUP expected=%llu received=%llu\n", *expected_seq, e->seq);
}
*expected_seq = e->seq + 1;
return 0;
}
*expected_seq = *expected_seq + 1;
if (e->type == MSG_QUOTE) {
if (e->bid <= 0.0 || e->ask <= 0.0 || e->bid >= e->ask) {
printf("BAD_QUOTE seq=%llu bid=%.2f ask=%.2f\n", e->seq, e->bid, e->ask);
return 0;
}
if (e->bid_qty <= 0 || e->ask_qty <= 0) {
printf("BAD_QUANTITY seq=%llu\n", e->seq);
return 0;
}
}
if (e->type == MSG_TRADE) {
if (e->price <= 0.0 || e->qty <= 0) {
printf("BAD_TRADE seq=%llu price=%.2f qty=%d\n", e->seq, e->price, e->qty);
return 0;
}
}
printf("OK seq=%llu symbol=%s\n", e->seq, e->symbol);
return 1;
}
int main(void) {
FeedEvent events[] = {
{MSG_QUOTE, 1001, "AAPL", 189.10, 189.12, 0.0, 500, 300, 0},
{MSG_QUOTE, 1002, "AAPL", 189.15, 189.14, 0.0, 200, 200, 0},
{MSG_TRADE, 1004, "AAPL", 0.0, 0.0, 189.12, 0, 0, 100},
{MSG_TRADE, 1003, "AAPL", 0.0, 0.0, 189.11, 0, 0, 50}
};
unsigned long long expected = 1001;
int n = (int)(sizeof(events) / sizeof(events[0]));
for (int i = 0; i < n; ++i) {
validate_event(&events[i], &expected);
}
return 0;
}
```
Parser Myth
True or false: if a text parser recognizes the message type character, it is safe to pass the message downstream even when some required fields are missing.
String Ownership Traps
Avoid dangling symbols
Parsing often creates a subtle ownership question:
Does the parsed event own the symbol string, or does it point into the input buffer?
Two common designs:
Copy symbol into event
safer for beginners
event survives after input buffer changes
costs a small fixed copy
Point into input buffer
avoids copy
dangerous if buffer is reused
requires very clear lifetime rules
For this course's early lab code, copying a bounded symbol like char symbol[16] is the right tradeoff. It is simple, deterministic, and easy to test.
Later, low-latency systems may use symbol IDs, interned strings, or numeric instrument IDs to avoid repeated string work. For now, correctness wins.

Fixed Width Bytes
Many feeds are bytes, not CSV
Text parsing is great for learning, tests, and replay files. Real low-latency market feeds are often binary.
A binary parser still follows the same idea:
known byte layout -> decode fields -> typed event -> validate
Beginner trap: do not blindly cast packet bytes to a struct. Padding, alignment, and byte order can surprise you.
This tiny example decodes a fixed-width quote packet:
byte 0 type: 'Q'
bytes 1-4 sequence number, big-endian uint32
bytes 5-8 bid price in cents, big-endian uint32
bytes 9-12 ask price in cents, big-endian uint32
```c
#include <stdio.h>
#include <stdint.h>
uint32_t read_be32(const unsigned char *p) {
return ((uint32_t)p[0] << 24) |
((uint32_t)p[1] << 16) |
((uint32_t)p[2] << 8) |
((uint32_t)p[3]);
}
int main(void) {
unsigned char packet[13] = {
'Q',
0x00, 0x00, 0x03, 0xE9,
0x00, 0x00, 0x49, 0xDE,
0x00, 0x00, 0x49, 0xE0
};
if (packet[0] != 'Q') {
printf("REJECT unknown packet type\n");
return 1;
}
uint32_t seq = read_be32(&packet[1]);
uint32_t bid_cents = read_be32(&packet[5]);
uint32_t ask_cents = read_be32(&packet[9]);
double bid = bid_cents / 100.0;
double ask = ask_cents / 100.0;
printf("QUOTE seq=%u bid=%.2f ask=%.2f\n", seq, bid, ask);
if (bid >= ask) {
printf("REJECT crossed quote\n");
} else {
printf("ACCEPT decoded quote\n");
}
return 0;
}
```
Typed Output Blank
Raw text or bytes should be converted into a typed ____ before book, strategy, or risk code consumes the message.
Parser Checklist
Your foundation parser boundary
At the end of Market Foundations, you now have the first useful shape of a feed handler:
read message
-> identify type
-> parse required fields
-> reject malformed input
-> validate sequence and basic market sanity
-> emit typed event
Keep this checklist close:
- Know the feed specification.
- Separate quote and trade formats.
- Check field counts.
- Bound string copies.
- Preserve sequence numbers and timestamps.
- Reject malformed or impossible messages.
- Emit one clean internal event type.
This closes the first section's loop: setup, project shape, market vocabulary, and now a runnable parser boundary. Next, Low-Latency C++ for HFT asks how to make this kind of path predictable and measurable.

One Pager Cheat Sheet
One Pager Cheat Sheet
- Feed parsing turns raw market data into typed events that later components can trust.
- A parser should convert a documented feed format into a stable internal event shape.
- A safe parser checks field counts, bounds string fields, and returns failure for malformed messages.
- Malformed feed messages belong on an error path, not in strategy logic.
- Parsing checks format; validation checks whether the parsed event is safe to trust.
- Message type alone does not prove a feed message is complete.
- Parsed events should have clear ownership rules for strings and buffers.
- Binary parsing should decode documented byte layouts explicitly instead of blindly casting bytes to structs.
- Downstream trading code should consume typed events, not raw feed strings.
- A feed parser is a boundary: raw input enters, validated typed events leave.