Market Data Feed (CALF)¶
Learning objectives
After reading this page you will understand:
- What CALF is and why a market-data protocol is separate from order entry
- How CALF channels expose different levels of market information
- How
TOP,TRADE, andSTATEcomplement each other - How participants subscribe and stay synchronized using a generic text protocol
- How sequence numbers and replay make reconnect behavior deterministic
- How the same CALF model can disseminate derived index calculations
Why CALF Exists¶
EduMatcher already provides order-entry protocols:
ALF: simple text order entryBALF: binary order entry
CALF adds the missing piece: market data distribution.
Order entry answers: "How do I submit intent to trade?" Market data answers: "What is happening in the market right now?"
CALF is designed as a readable, line-based TCP protocol so learners can inspect
it in a terminal while still following exchange-grade ideas:
- channelized subscriptions
- snapshots plus incrementals
- sequence-based gap detection
- bounded replay recovery
Information Levels by Channel¶
CALF separates data into channels so clients can subscribe only to what they need.
| Channel | What it tells you | Typical client use |
|---|---|---|
TOP |
Best bid/ask, top sizes, last-trade fields (SNAP + MD) |
UI book widgets, spread monitoring, execution bots |
TRADE |
Executed prints (TRADE) |
Tape display, strategy triggers, volume analytics |
STATE |
Session/instrument state transitions (STATE) |
Session-aware bots, operational monitors |
Think of the three channels as answering increasingly specific questions about the market:
| Question | Channel |
|---|---|
| Is trading open, auction, halted, or closed? | STATE |
| What is the current best bid, best ask, and spread? | TOP |
| What price and size just executed, and who was the aggressor? | TRADE |
flowchart LR
subgraph Channels
direction TB
S[STATE\nMarket phase context]
T[TOP\nBest bid and ask]
R[TRADE\nExecuted prints]
end
The channels are independent subscriptions — you can take any combination depending on what your client needs.
Core Message Types¶
CALF uses one UTF-8 line per message (\n terminated):
The messages you will encounter first as a client:
| Message | Who sends it | Purpose |
|---|---|---|
HELLO |
Client | Start the session |
WELCOME |
Gateway | Confirm session, advertise parameters |
SUB |
Client | Subscribe to channels and symbols |
SNAP |
Gateway | Point-in-time baseline for a stream |
MD |
Gateway | Incremental top-of-book change |
TRADE |
Gateway | Executed trade print |
STATE |
Gateway | Session or instrument state transition |
HB |
Gateway | Heartbeat when no data is flowing |
ERR |
Gateway | Error notification |
SNAP is a message type, not a channel
You subscribe to a channel (TOP or STATE). The gateway then automatically
sends a SNAP baseline for that channel. Clients never subscribe to SNAP directly.
How Participants Subscribe¶
A participant opens a TCP connection and follows a small, generic flow:
- Send
HELLO - Receive
WELCOME - Send one or more
SUBcommands - Process
SNAPbaseline(s) - Process live incrementals (
MD,TRADE,STATE)
Annotated example — each line is one complete CALF message:
# Client identifies itself and requests CALF version 1
HELLO|CLIENT=bot01|PROTO=CALF1
# Gateway confirms; HB every 1 s, replay window 30 s, known symbols AAPL and MSFT
WELCOME|PROTO=CALF1|GW=md-gwy01|HBINT=1|REPLAY=30|SYMBOLS=AAPL,MSFT
# Client subscribes to TOP (best bid/ask) and TRADE (fills) for AAPL
SUB|CH=TOP,TRADE|SYM=AAPL
# Gateway sends snapshot: current top-of-book at SEQ=100
# BID=150.10 x 1200, ASK=150.12 x 900, last trade was 150.11 x 300
SNAP|CH=TOP|SYM=AAPL|SEQ=100|TS=2026-06-07T10:16:00.000Z|BID=150.10|BIDSZ=1200|ASK=150.12|ASKSZ=900|LAST=150.11|LASTSZ=300
# The best bid just moved to 150.11 x 1400 — only changed fields are sent
MD|CH=TOP|SYM=AAPL|SEQ=101|TS=2026-06-07T10:16:00.115Z|BID=150.11|BIDSZ=1400
# A trade executed: 200 shares at 150.12, buy-side was the aggressor
TRADE|CH=TRADE|SYM=AAPL|SEQ=44|TS=2026-06-07T10:16:00.141Z|PX=150.12|QTY=200|SIDE=BUY
sequenceDiagram
participant C as CALF Client
participant G as pm-md-gwy
C->>G: HELLO|CLIENT=bot01|PROTO=CALF1
G-->>C: WELCOME|PROTO=CALF1|GW=md-gwy01|HBINT=1|REPLAY=30
C->>G: SUB|CH=TOP,TRADE|SYM=AAPL
G-->>C: SNAP|CH=TOP|SYM=AAPL|SEQ=100|...
G-->>C: MD|CH=TOP|SYM=AAPL|SEQ=101|...
G-->>C: TRADE|CH=TRADE|SYM=AAPL|SEQ=44|...
Staying Up To Date Reliably¶
CALF uses monotonic SEQ numbers per (CH, SYM) stream.
That means sequence tracking is per stream, not global:
(TOP, AAPL)has one independent counter(TRADE, AAPL)has another(STATE, *)has its own
flowchart LR
A[(TOP, AAPL)] --> A1[SEQ 100] --> A2[SEQ 101] --> A3[SEQ 102]
B[(TRADE, AAPL)] --> B1[SEQ 44] --> B2[SEQ 45] --> B3[SEQ 46]
C[(STATE, *)] --> C1[SEQ 5] --> C2[SEQ 6] --> C3[SEQ 7]
Client rule:
- If current
SEQ != previous + 1, you detected a gap - Attempt replay when possible
- If replay misses the window, accept fresh
SNAPand continue
This gives deterministic behavior on reconnect without requiring full historical storage.
First Connect vs Reconnect¶
First connect¶
When you connect for the first time, the gateway has no history to replay for you.
Instead it sends a SNAP — a complete point-in-time picture of the current state
for each (CH, SYM) pair you subscribed to. Record the SEQ from that SNAP.
Every incremental message (MD, TRADE, STATE) that follows will have
SEQ = last_seen + 1. As long as the sequence is gapless you are fully in sync.
Reconnect¶
- Send
HELLOwithRESUME=1andLASTSEQfor one stream - If gap is within replay window, gateway replays missing messages
- If not, gateway sends
ERR|CODE=REPLAY_MISSand then a freshSNAP
flowchart TD
D[Client detects sequence gap] --> E{Reconnect with RESUME and LASTSEQ}
E --> F{Replay window contains gap?}
F -->|Yes| G[Gateway replays missing messages]
G --> H[Resume live stream]
F -->|No| I[Gateway sends ERR CODE=REPLAY_MISS]
I --> J[Gateway sends fresh SNAP baseline]
J --> H
This keeps client logic straightforward: always restore a known baseline, then continue incrementally.
Why This Matters For Different Participants¶
Different participant types consume different CALF slices:
- Viewer:
TOP+STATEfor spread, best levels, and market phase - Recorder:
TRADE(and oftenTOP) for post-trade analysis - Execution bot:
TOP+TRADE+STATEfor session-aware strategy decisions - Risk monitor:
STATE+ selectedTRADEstreams for halt/session supervision
flowchart LR
V[Viewer] --> VS[TOP + STATE]
R[Recorder] --> RS[TRADE + optional TOP]
B[Execution Bot] --> BS[TOP + TRADE + STATE]
M[Risk Monitor] --> MS[STATE + selected TRADE]
The same protocol works for all, with different subscription sets.
CALF and Index Dissemination¶
The CALF channel model is also suitable for disseminating index calculations.
Conceptually, an index feed is just another ordered stream of derived market facts:
- index value
- timestamp
- optional contribution metadata
- sequence for gap detection/recovery
In EduMatcher, this can be integrated in the same operational pattern as other CALF
streams, so clients can consume index updates with the same subscription, sequence,
and replay semantics they already use for TOP/TRADE/STATE.
Practical options include:
- publishing index values as a dedicated CALF stream in the gateway
- mapping index updates into
STATE-style or dedicated symbol streams for consumers
A client subscribing to index data would follow the same handshake and sequence rules as any other CALF stream. For example, an index update might look like:
SUB|CH=TOP|SYM=EDU-INDEX
SNAP|CH=TOP|SYM=EDU-INDEX|SEQ=1|TS=2026-06-07T10:00:00.000Z|LAST=1042.30
MD|CH=TOP|SYM=EDU-INDEX|SEQ=2|TS=2026-06-07T10:00:05.000Z|LAST=1043.10
The key idea: one generic subscriber model for both raw market events and derived index events.
flowchart LR
E[Engine market events] --> X[Index calculator]
X --> G[pm-md-gwy normalisation and fanout]
G --> C1[Index dashboard]
G --> C2[Strategy bot]
G --> C3[Recorder]
G --> S[Per-stream SEQ + replay semantics]
Minimal Client Checklist¶
For a robust CALF client implementation:
- Parse line-by-line — TCP is a byte stream; a single
recv()can contain part of a line, one full line, or several lines at once. Buffer and split on\n. - Track
last_seqper stream — keep a dict keyed by(CH, SYM)and check every incomingSEQ. - Treat missing fields in
MDas unchanged — if anMDmessage has noASKfield, the ask price has not moved since the lastSNAPorMDthat carried it. Do not reset it to zero. - Handle
HB— a heartbeat means the gateway is alive but quiet. If neither data norHBarrives, the connection may be dead. - On gap, attempt replay; on replay miss, reset from
SNAP— sendHELLO|RESUME=1|LASTSEQ=<n>; if you getERR CODE=REPLAY_MISSinstead of replayed messages, accept the freshSNAPthat follows. - Keep subscriptions narrow — only subscribe to
(CH, SYM)pairs your process actually uses.
Summary¶
CALF gives EduMatcher a readable but production-shaped market-data model:
- channelized information levels (
TOP,TRADE,STATE) - snapshot baseline + incremental updates
- deterministic sync via per-stream sequences
- bounded replay for short disconnect recovery
- reusable dissemination pattern for derived outputs such as index calculations
This makes CALF a practical bridge between educational clarity and realistic feed behavior.