Appendix: CALF Protocol Reference¶
Learning objectives
After reading this appendix you will understand:
- what CALF is and how it differs from ALF and BALF
- the exact line-level wire format used by CALF over TCP
- channel, symbol, and subscription rules for
TOP,TRADE, andSTATE - sequencing, gap detection, replay, and resynchronization behavior
- required and optional fields for every CALF message type
- gateway and client behaviors required for CALF
1.0.0interoperability
What CALF is¶
CALF stands for Channel ALF.
CALF is EduMatcher's text market-data protocol. It is designed for educational clarity and bot usability: human-readable on the wire, easy to debug in a terminal, and strict enough to support deterministic clients.
CALF complements the other application protocols:
| Protocol | Purpose |
|---|---|
| ALF | Text order entry (interactive) |
| BALF | Binary order entry (low-latency programmatic) |
| CALF | Channelized text market data |
This appendix is the normative reference for CALF 1.0.0 semantics.
Scope and boundaries¶
CALF is the external market-data protocol exposed by pm-md-gwy. The gateway
subscribes to internal engine PUB topics and translates them into CALF lines
for TCP clients.
This appendix specifies the client-visible CALF protocol. It does not specify internal engine message schemas beyond what is needed to explain CALF behavior.
Supported in CALF 1.0.0¶
- top-of-book updates (
MD) by symbol - trade prints (
TRADE) by symbol - state transitions (
STATE) for session-wide and symbol-level changes - index level updates (
IDX) frompm-index - point-in-time stream baselines (
SNAP) forTOPandSTATE - per-stream sequence numbers on
(CH, SYM) - bounded replay on reconnect (
RESUME=1+LASTSEQ) - heartbeat and liveness signaling
Out of scope in CALF 1.0.0¶
- full depth-by-order market data
- multicast / UDP transport
- entitlement matrix per field
- durable historical replay from disk
- protocol-layer authentication token
Transport and session model¶
| Property | Value |
|---|---|
| Transport | TCP |
| Default port | 5570 |
| Encoding | UTF-8 line protocol |
| Delimiter | \n |
| Max line length | 4096 bytes including newline |
A CALF client connection is long-lived.
- Client must send
HELLOwithin 5 seconds of TCP connect. - Gateway replies with
WELCOMEon success. - Client may then send
SUB,UNSUB,PING, andEXIT. - Gateway streams
SNAP,MD,TRADE,STATE,HB, andERR.
If no HELLO is received within 5 seconds, the gateway closes the socket.
Core syntax rules¶
Line structure¶
Every CALF message is one line:
MSGTYPE is the first token and is always uppercase ASCII.
Examples:
HELLO|CLIENT=bot01|PROTO=CALF1
TRADE|CH=TRADE|SYM=AAPL|SEQ=809|TS=2026-06-07T10:16:00.141Z|PX=150.12|QTY=200|SIDE=BUY
Parsing behavior¶
- Messages are delimited by newline (
\n). \r\nfrom clients is accepted for robustness.- Field order after
MSGTYPEis not significant. - Unknown keys are ignored unless needed for validating a specific message.
- Duplicate keys: last occurrence wins.
- Empty lines are invalid and may result in
ERR|CODE=BAD_MESSAGE.
TCP stream requirement¶
TCP is a byte stream, not a message queue.
A receiver must buffer bytes and split by newline. A single recv() may
contain half a line, one full line, or many lines.
Field conventions¶
Reserved keys¶
| Key | Meaning |
|---|---|
CH |
Logical channel (TOP, TRADE, STATE) |
SYM |
Symbol or * where allowed |
SEQ |
Sequence number for one (CH, SYM) stream |
TS |
UTC ISO-8601 timestamp with milliseconds |
Wire value types¶
| Type | Wire representation | Example |
|---|---|---|
| Decimal price | Text decimal | 150.25 |
| Integer | Base-10 text integer | 1200 |
| Boolean flag | 0 or 1 |
RESUME=1 |
| Timestamp | UTC ISO-8601 ms | 2026-06-07T10:15:23.411Z |
Optional fields are omitted when not present. Empty required values are invalid.
Channel model¶
CALF groups market data into logical channels.
| Channel | Description | SYM=* allowed? |
|---|---|---|
TOP |
Best bid/ask updates and snapshots | No |
TRADE |
Trade prints | No |
STATE |
Session or symbol state transitions | Yes |
INDEX |
Index level updates | No |
SNAP is a message type, not a channel.
A client does not subscribe to SNAP directly. The gateway auto-sends SNAP
for new SUB requests on TOP and STATE.
Subscription rules¶
SUBmay include multiple channels and symbols separated by commas.- A multi-value
SUBapplies to the Cartesian product of channels and symbols. SYM=*is valid only when channel set is exactlySTATE.- Re-subscribing an already active pair is idempotent.
- Maximum symbols per client are enforced by gateway config.
- If any requested
(CH,SYM)pair is invalid, the gateway rejects theSUBrequest withERRand leaves existing subscriptions unchanged.
Sequence and recovery semantics¶
Stream identity¶
Sequence numbers are maintained per (CH, SYM) stream.
Examples:
(TOP, AAPL)has its own counter(TRADE, AAPL)has a different counter(STATE, *)and(STATE, AAPL)are distinct counters
Sequence rules¶
- Start value is
1for each stream. - Increment by
1per emitted message in that stream. - Sequence appears in
SNAP,MD,TRADE, andSTATE. - A client-detected gap means one or more missed messages.
First connect behavior¶
On first subscribe to a TOP or STATE stream:
- Gateway sends
SNAPwith current streamSEQ. - Client stores
last_seq[(CH,SYM)] = SNAP.SEQ. - Next incremental event for that stream must be
SEQ + 1.
Reconnect behavior (RESUME=1)¶
RESUME=1 applies to one stream per HELLO.
- Client supplies
CH,SYM, andLASTSEQ. CHandSYMmust each contain exactly one value whenRESUME=1.LASTSEQmust be a positive base-10 integer.- If missing events are inside replay window, gateway replays in order then continues live.
- If missing range is outside window, gateway sends
ERR|CODE=REPLAY_MISSfollowed by a freshSNAP.
sequenceDiagram
participant C as Client
participant G as pm-md-gwy
Note over C,G: Last seen on (TOP,AAPL): SEQ=1042
C->>G: HELLO|CLIENT=bot01|PROTO=CALF1|RESUME=1|CH=TOP|SYM=AAPL|LASTSEQ=1042
G-->>C: WELCOME|PROTO=CALF1|GW=md-gwy01|HBINT=1|REPLAY=30
alt Replay hit
G-->>C: MD|CH=TOP|SYM=AAPL|SEQ=1043|...
G-->>C: MD|CH=TOP|SYM=AAPL|SEQ=1044|...
G-->>C: ...
G-->>C: MD|CH=TOP|SYM=AAPL|SEQ=1050|...
G-->>C: MD|CH=TOP|SYM=AAPL|SEQ=1051|... (live)
else Replay miss
G-->>C: ERR|CODE=REPLAY_MISS|CH=TOP|SYM=AAPL
G-->>C: SNAP|CH=TOP|SYM=AAPL|SEQ=1105|...
end
Message catalog¶
Session control messages¶
| Message | Direction | Purpose |
|---|---|---|
HELLO |
Client -> Gateway | Start session; optional single-stream resume |
WELCOME |
Gateway -> Client | Confirm session and advertise parameters |
SUB |
Client -> Gateway | Add subscriptions |
UNSUB |
Client -> Gateway | Remove subscriptions |
PING |
Client -> Gateway | Liveness probe |
PONG |
Gateway -> Client | Probe reply |
HB |
Gateway -> Client | Heartbeat when quiet |
ERR |
Gateway -> Client | Protocol or flow error |
EXIT |
Client -> Gateway | Clean disconnect |
Market-data messages¶
| Message | Direction | Purpose |
|---|---|---|
SNAP |
Gateway -> Client | Point-in-time baseline for one stream |
MD |
Gateway -> Client | Incremental top-of-book update |
TRADE |
Gateway -> Client | Trade print |
STATE |
Gateway -> Client | Session/symbol state transition |
IDX |
Gateway -> Client | Index level update |
Message definitions¶
HELLO¶
Direction: Client -> Gateway
Purpose: Session handshake. Optional replay request for one stream.
Response: WELCOME or ERR.
| Field | Req | Description |
|---|---|---|
CLIENT |
Yes | Client ID (ASCII, max 32 chars) |
PROTO |
Yes | Must be CALF1 |
RESUME |
No | 1 enables replay request |
CH |
If RESUME=1 |
One channel to resume |
SYM |
If RESUME=1 |
One symbol for resumed stream |
LASTSEQ |
If RESUME=1 |
Last received sequence for that stream |
Validation rules:
- Messages other than
HELLOsent before successful handshake receiveERR|CODE=AUTH_REQUIRED. RESUME=1with missingCH,SYM, orLASTSEQis invalid.RESUME=1with multi-valueCHorSYMis invalid.
WELCOME¶
Direction: Gateway -> Client
| Field | Req | Description |
|---|---|---|
PROTO |
Yes | Echoes negotiated protocol |
GW |
Yes | Gateway instance name |
HBINT |
Yes | Heartbeat interval in seconds |
REPLAY |
Yes | Replay window in seconds |
SYMBOLS |
No | Optional comma-separated symbol list |
SUB¶
Direction: Client -> Gateway
| Field | Req | Description |
|---|---|---|
CH |
Yes | Comma-separated channels |
SYM |
Yes | Comma-separated symbols (* only with STATE) |
Response semantics:
- No explicit ACK.
- New
TOP/STATEsubscriptions triggerSNAP. TRADEsubscriptions do not have a baselineSNAP; only futureTRADEevents are sent.- Invalid requests return
ERR. - Existing successful subscriptions remain active when a later
SUBrequest is invalid.
UNSUB¶
Direction: Client -> Gateway
| Field | Req | Description |
|---|---|---|
CH |
Yes | Comma-separated channels |
SYM |
Yes | Comma-separated symbols |
UNSUB is idempotent. Removing a non-existent (CH,SYM) pair has no effect.
SNAP¶
Direction: Gateway -> Client
Purpose: Baseline for one stream.
SNAP uses channel-specific payload fields.
Common fields:
| Field | Req | Description |
|---|---|---|
CH |
Yes | TOP or STATE |
SYM |
Yes | Symbol or * for session state |
SEQ |
Yes | Current stream sequence |
TS |
Yes | Snapshot timestamp |
CH=TOP fields:
| Field | Req | Description |
|---|---|---|
BID |
No | Best bid price |
BIDSZ |
No | Best bid size |
ASK |
No | Best ask price |
ASKSZ |
No | Best ask size |
LAST |
No | Last trade price |
LASTSZ |
No | Last trade size |
CH=STATE fields:
| Field | Req | Description |
|---|---|---|
SESSION |
Yes | Current state value |
TRADE stream note:
- There is no
SNAPvariant forCH=TRADEin CALF1.0.0. TRADEdelivery starts from events that occur after the subscription is active.
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
SNAP|CH=STATE|SYM=*|SEQ=5|TS=2026-06-07T10:16:00.000Z|SESSION=CONTINUOUS
MD¶
Direction: Gateway -> Client
Purpose: Incremental TOP update. Unchanged sides may be omitted.
| Field | Req | Description |
|---|---|---|
CH |
Yes | TOP |
SYM |
Yes | Symbol |
SEQ |
Yes | Stream sequence |
TS |
Yes | Event timestamp |
BID |
No | Updated bid |
BIDSZ |
No | Updated bid size |
ASK |
No | Updated ask |
ASKSZ |
No | Updated ask size |
LAST |
No | Updated last trade price |
LASTSZ |
No | Updated last trade size |
TRADE¶
Direction: Gateway -> Client
| Field | Req | Description |
|---|---|---|
CH |
Yes | TRADE |
SYM |
Yes | Symbol |
SEQ |
Yes | Stream sequence |
TS |
Yes | Trade timestamp |
PX |
Yes | Trade price |
QTY |
Yes | Trade quantity |
SIDE |
Yes | Aggressor side (BUY or SELL) |
STATE¶
Direction: Gateway -> Client
| Field | Req | Description |
|---|---|---|
CH |
Yes | STATE |
SYM |
Yes | Symbol or * |
SEQ |
Yes | Stream sequence |
TS |
Yes | Transition timestamp |
SESSION |
Yes | New state value |
PREV |
No | Previous state when known |
Valid SESSION values:
PRE_OPENOPENING_AUCTIONCONTINUOUSCLOSING_AUCTIONCLOSEDHALTED(symbol-level)
STATE|CH=STATE|SYM=*|SEQ=14|TS=2026-06-07T10:30:00.000Z|SESSION=CONTINUOUS|PREV=OPENING_AUCTION
STATE|CH=STATE|SYM=AAPL|SEQ=3|TS=2026-06-07T11:02:17.330Z|SESSION=HALTED|PREV=CONTINUOUS
IDX¶
Direction: Gateway -> Client
Purpose: Index level update for one INDEX stream.
| Field | Req | Description |
|---|---|---|
CH |
Yes | INDEX |
SYM |
Yes | Index identifier (e.g. EDU50) |
SEQ |
Yes | Stream-local sequence number |
TS |
Yes | Event timestamp |
LEVEL |
Yes | Current index level, decimal string |
SESSION |
Yes | Index session state |
OPEN |
No | Day open level, decimal string |
HIGH |
No | Day high, decimal string |
LOW |
No | Day low, decimal string |
CHG |
No | Change from open, signed decimal e.g. +1.23 |
PCTCHG |
No | Percent change from open, signed e.g. +0.45 |
AGGCAP |
No | Aggregate market cap, integer string |
IDX has no baseline SNAP variant in CALF 1.0.0. Delivery starts from
events that occur after the subscription becomes active.
IDX|CH=INDEX|SYM=EDU50|SEQ=12|TS=2026-06-07T10:16:00.000Z|LEVEL=5123.45|SESSION=CONTINUOUS|OPEN=5100.00|CHG=+23.45|PCTCHG=+0.46
HB¶
Direction: Gateway -> Client
Sent when no outbound market-data line was emitted during the heartbeat interval.
| Field | Req | Description |
|---|---|---|
TS |
Yes | Gateway timestamp |
PING / PONG¶
PING is client-initiated liveness check. PONG is immediate reply.
ERR¶
Direction: Gateway -> Client
| Field | Req | Description |
|---|---|---|
CODE |
Yes | Machine-readable error code |
MSG |
No | Human-readable context |
CH |
No | Channel context when relevant |
SYM |
No | Symbol context when relevant |
Normative error codes:
| Code | Meaning |
|---|---|
PROTO_MISMATCH |
HELLO missing CLIENT or PROTO != CALF1 |
AUTH_REQUIRED |
Non-HELLO message sent before successful handshake |
INVALID_CHANNEL |
CH not in {TOP, TRADE, STATE, INDEX} |
INVALID_SYMBOL |
Symbol not in known list, or wildcard used on wrong channel |
SUB_LIMIT |
Subscription would exceed max_symbols_per_client |
REPLAY_MISS |
LASTSEQ is older than the replay window; gateway sends a SNAP instead |
SLOW_CLIENT |
Outbound queue exceeded max_client_queue; connection closed |
BAD_MESSAGE |
Parse failure, oversized line (> 4096 bytes), or unsupported message type |
Terminal behavior:
SLOW_CLIENTis terminal for the current TCP session; gateway disconnects.BAD_MESSAGEmay be terminal when parsing cannot continue safely.
EXIT¶
Direction: Client -> Gateway
Requests clean disconnect.
Liveness and timeout rules¶
- Gateway emits
HBeveryheartbeat_interval_secwhen no outbound market-data line has been sent in that interval. - Client may issue
PINGanytime; gateway must respond withPONG. - If no inbound or outbound traffic occurs for
idle_timeout_sec, gateway closes the connection. HB,PING, andPONGare liveness messages and do not participate in(CH,SYM)sequence counters.
Session lifecycle¶
sequenceDiagram
participant C as CALF Client
participant G as pm-md-gwy
C->>G: TCP connect
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|...
G-->>C: HB|TS=...
C->>G: PING
G-->>C: PONG
C->>G: EXIT
Note over G: Close TCP cleanly
Gateway behavior requirements¶
For CALF 1.0.0 interoperability, pm-md-gwy must:
- Accept TCP clients and enforce HELLO-before-use semantics.
- Normalize internal engine events into CALF lines.
- Maintain independent sequence counters per
(CH, SYM). - Keep bounded replay buffers per
(CH, SYM)stream. - Auto-send
SNAPon newTOP/STATEsubscriptions. - Enforce channel and symbol rules deterministically.
- Disconnect slow clients when queue limits are exceeded.
Configuration reference¶
CALF gateway settings are part of the main engine configuration file
(engine_config.yaml) as a top-level market_data_gateway block.
Path location:
engine_config.yaml->market_data_gateway
All supported CALF 1.0.0 configuration fields are listed below.
| Field | Type / allowed range | Default | Description |
|---|---|---|---|
market_data_gateway.enabled |
Boolean (true/false) |
true (recommended when CALF is used) |
Enables/disables the CALF gateway process configuration. |
market_data_gateway.name |
Non-empty string | Implementation-defined | Gateway instance name advertised in WELCOME|GW=.... |
market_data_gateway.bind_address |
IP/host bind string | 0.0.0.0 (common) |
Local interface address to bind for incoming TCP clients. |
market_data_gateway.port |
Integer, 1..65535 |
5570 |
TCP listen port for CALF clients. |
market_data_gateway.heartbeat_interval_sec |
Integer, > 0 |
1 |
Interval used to emit HB when no outbound market-data line was sent. |
market_data_gateway.idle_timeout_sec |
Integer, > 0 |
5 |
Maximum silent period (no inbound and no outbound traffic) before disconnect. |
market_data_gateway.replay_window_sec |
Integer, > 0 |
30 |
Time-bounded replay retention per (CH,SYM) stream for resume/gap recovery. |
market_data_gateway.max_symbols_per_client |
Integer, > 0 |
200 |
Per-client subscription symbol limit across active subscriptions. |
market_data_gateway.max_client_queue |
Integer, > 0 |
10000 |
Per-client outbound queue cap; overflow triggers ERR|CODE=SLOW_CLIENT and disconnect. |
Operational notes:
HBINTinWELCOMEmust reflectheartbeat_interval_sec.REPLAYinWELCOMEmust reflectreplay_window_sec.max_symbols_per_clientaffectsSUBvalidation andERR|CODE=SUB_LIMIT.max_client_queuecontrols slow-client backpressure behavior.
Example:
market_data_gateway:
enabled: true
name: "md-gwy01"
bind_address: "0.0.0.0"
port: 5570
heartbeat_interval_sec: 1
idle_timeout_sec: 5
replay_window_sec: 30
max_symbols_per_client: 200
max_client_queue: 10000
What to watch out for during implementation¶
- Implement line buffering correctly for TCP streams (
recv()may return partial lines or multiple lines at once). - Enforce HELLO-before-use strictly; all non-HELLO pre-auth messages must
receive
ERR|CODE=AUTH_REQUIRED. - Keep
SNAPsemantics explicit: it is a message type, not a subscribable channel; onlyTOPandSTATEsubscriptions auto-triggerSNAP. - Enforce
SYM=*constraints exactly (STATEonly) and validate multi-valueSUBas Cartesian stream requests. - Track sequence numbers independently per
(CH,SYM)stream; never use a single global counter. - Treat
RESUME=1as single-stream only and validateCH,SYM, andLASTSEQstrictly. - Bound replay by configured window and emit deterministic
REPLAY_MISS+ freshSNAPbehavior when outside window. - Apply slow-client backpressure deterministically: queue overflow must produce
SLOW_CLIENTand disconnect. - Keep liveness signals (
HB,PING,PONG) outside market-data sequencing; they do not consume(CH,SYM)sequence numbers.
Normative implementation truths¶
If you are implementing a CALF client, the most important protocol truths are:
- CALF is line-based text over TCP, not message-framed datagrams.
HELLOis mandatory before any subscription command.SNAPis a message type, not a subscribable channel.- Sequence tracking is per
(CH, SYM)stream. SYM=*is valid only forSTATEsubscriptions.- Replay resume is single-stream per
HELLO|RESUME=1. - On replay miss, client must accept fresh
SNAPand reset local baseline. - Heartbeats and ping/pong are separate liveness mechanisms.
- A
SLOW_CLIENTerror indicates disconnect and reconnect is required. - Protocol values and keys are uppercase by convention and should be emitted uppercase for interoperability.