Skip to content

Protocol Support Library Examples

Learning objectives

After reading this chapter you will understand:

  • where protocol support-library source code lives
  • the Python and C APIs exposed by each parser library
  • how to run the example clients for each gateway
  • how to validate gateway connectivity quickly using reusable example code
  • how to navigate parser-library source files when integrating external clients

Scope

This chapter covers source-code examples for all external protocol gateways and the REST API:

Protocol Gateway Port Directory
ALF pm-alf-gwy 5565 docs/examples/alf
CALF pm-md-gwy 5570 docs/examples/calf
RALF pm-ralf-gwy 5580 docs/examples/ralf
BALF pm-balf-gwy 5560 docs/examples/balf
REST pm-api-gwy 8080 docs/examples/REST

This chapter does not cover the ready-to-use engine configuration templates under docs/examples/ref_data — see Example Engine Configs for those.

See also: Protocol Overview


ALF support library and examples

ALF (pm-alf-gwy) is the primary order-entry gateway. External clients connect over TCP and exchange text lines using the ALF wire format.

Source files

File Description
alf/README.md Directory overview and quick-start
alf/python/alf_parser.py Python parser, builder, and session helper
alf/python/alf_client.py Interactive Python client with tab-completion
alf/c/alf_parser.h C library header
alf/c/alf_parser.c C library implementation
alf/c/alf_client.c Interactive C client with readline
alf/c/Makefile Build helper

Wire format

VERB|KEY=VALUE|KEY=VALUE\n

Command verbs and field keys are case-insensitive (normalized to uppercase). Segments without = are silently skipped. Duplicate keys: last value wins.

Python API — alf_parser.py

parse_alf_line(line: str) → AlfMessage : Parse one ALF line. Returns an AlfMessage(msg_type, fields) frozen dataclass. Raises AlfParseError on malformed input.

build_alf_line(msg_type: str, fields: dict | None) → str : Build one \n-terminated ALF line. Raises AlfParseError if the message type contains invalid characters or any field contains |.

AlfSession.connect(host, port, gateway_id, client_name, timeout) → AlfSession : Open a TCP connection and complete the HELLO/WELCOME handshake. Returns an authenticated session object.

AlfSession.send(msg_type, fields) : Build and send one ALF line over the established connection.

AlfSession.recv_msg() → AlfMessage : Block until one complete \n-terminated line arrives and parse it.

AlfSession.close() : Send EXIT and close the socket.

Python usage pattern

from alf_parser import parse_alf_line, build_alf_line, AlfSession, AlfMessage

# Parse one line received from the gateway
msg: AlfMessage = parse_alf_line("ACK|ORDER_ID=abc|ACCEPTED=TRUE|SYMBOL=AAPL")
print(msg.msg_type)   # "ACK"
print(msg.fields)     # {"ORDER_ID": "abc", "ACCEPTED": "TRUE", "SYMBOL": "AAPL"}

# Build a line to send
line: str = build_alf_line("NEW", {
    "SYM": "AAPL", "SIDE": "BUY", "TYPE": "LIMIT",
    "QTY": "100", "PRICE": "150.00",
})
# → "NEW|SYM=AAPL|SIDE=BUY|TYPE=LIMIT|QTY=100|PRICE=150.00\n"

# Full session: connect, perform HELLO/WELCOME handshake, send and receive
session = AlfSession.connect("127.0.0.1", 5565, "TRADER01")
print(session.welcome.gw_name)          # "alf-gwy01"
session.send("SYMBOLS")
msg = session.recv_msg()                # first line of SYMBOLS response
session.close()

Run Python interactive client

cd docs/examples/alf/python

# Connect to local gateway as TRADER01
python3 alf_client.py --id TRADER01

# Connect to remote gateway with a custom client name in logs
python3 alf_client.py --host 10.0.0.5 --port 5565 --id MM01 --client "my-mm-bot"

At the prompt, tab-completion is available for commands, field names, and enum values. Command history is persisted to ~/.alf_client_history.

[TRADER01]> NEW|SYM=AAPL|SIDE=BUY|TYPE=LIMIT|QTY=100|PRICE=150.00
[TRADER01]> AMEND|ID=<order-id>|PRICE=151.00
[TRADER01]> CANCEL|ID=<order-id>
[TRADER01]> KILL
[TRADER01]> SYMBOLS
[TRADER01]> ORDERS
[TRADER01]> POS
[TRADER01]> STATUS
[TRADER01]> HELP
[TRADER01]> EXIT

C API — alf_parser.h

int alf_parse_line(char *line, alf_message_t *out) : Parse one mutable ALF line in-place. Returns 0 on success, negative on error (-1 null args, -2 empty line, -3 invalid verb, -4 too many fields).

const char *alf_get_field(const alf_message_t *msg, const char *key) : Return the value for key (case-insensitive), or NULL if absent.

int alf_build_line(char *out, size_t cap, const char *msg_type, const char * const *kv) : Write one ALF line into out. kv is a NULL-terminated array of alternating key/value strings. Returns bytes written or -1 on overflow.

C usage pattern

#include "alf_parser.h"

/* Parse */
char line[] = "ACK|ORDER_ID=abc|ACCEPTED=TRUE";
alf_message_t msg;
alf_parse_line(line, &msg);
printf("%s\n", msg.msg_type);                    /* "ACK" */
printf("%s\n", alf_get_field(&msg, "ACCEPTED")); /* "TRUE" */

/* Build */
const char *kv[] = {
    "SYM", "AAPL", "SIDE", "BUY",
    "TYPE", "LIMIT", "QTY", "100", "PRICE", "150.00", NULL,
};
char buf[4096];
alf_build_line(buf, sizeof(buf), "NEW", kv);
/* → "NEW|SYM=AAPL|SIDE=BUY|TYPE=LIMIT|QTY=100|PRICE=150.00\n" */

Build and run C client

cd docs/examples/alf/c
make

# Connect to local gateway
./alf_client --id TRADER01

# Connect to remote gateway; disable ANSI colours
./alf_client --host 10.0.0.5 --port 5565 --id TRADER01 --no-color

The C client supports the same commands and tab-completion as the Python client.

Manual gateway sanity check

nc 127.0.0.1 5565

Then type:

HELLO|CLIENT=manual01|PROTO=ALF1|ID=TRADER01
NEW|SYM=AAPL|SIDE=BUY|TYPE=MARKET|QTY=10
SYMBOLS
ORDERS

See also:


CALF support library and examples

CALF (pm-md-gwy) is the market data feed gateway. Subscribers connect over TCP and receive a stream of top-of-book, trade, and other market-data events.

The example subscribers are more than a single trivial subscription: they combine TOP, TRADE, STATE (including the session-wide SYM=* wildcard), and Level 2 DEPTH in one client, maintain a small top-of-book cache (incremental MD updates omit unchanged sides), pretty-print the DEPTH ladder, detect gaps in the per-(CH,SYM) SEQ counters, and check WELCOME|CH_SUPPORTED= before relying on channels that may not exist on an older gateway build.

Source files

File Description
calf/README.md Directory overview and quick-start
calf/calf_parser.py Python parser and builder
calf/calf_subscriber.py Python subscriber example
calf/calf_parser.h C library header
calf/calf_parser.c C library implementation
calf/calf_subscriber.c C subscriber example
calf/Makefile Build helper

Wire format

MSGTYPE|KEY=VALUE|KEY=VALUE\n

Fields missing = raise CalfParseError. Duplicate keys: last value wins (same as ALF), the parser never raises for this case.

Python API — calf_parser.py

parse_calf_line(line: str) → CalfMessage : Parse one CALF line. Returns a CalfMessage(msg_type, fields) frozen dataclass. Raises CalfParseError on malformed input.

build_calf_line(msg_type: str, fields: dict | None) → str : Build one \n-terminated CALF line. Raises CalfParseError for invalid message types or fields containing |.

Python usage pattern

from calf_parser import build_calf_line, parse_calf_line

# Build a HELLO line
line = build_calf_line("HELLO", {"CLIENT": "demo01", "PROTO": "CALF1"})
# → "HELLO|CLIENT=demo01|PROTO=CALF1\n"

# Parse a WELCOME response
msg = parse_calf_line("WELCOME|PROTO=CALF1|GW=md-gwy01|HBINT=1|REPLAY=30")
print(msg.msg_type)          # "WELCOME"
print(msg.fields["HBINT"])   # "1"

# Parse a streaming TOP-OF-BOOK update
top = parse_calf_line("TOP|SYM=AAPL|BID=149.50|ASK=150.00|SEQ=1042")
print(top.fields["BID"])     # "149.50"

Run Python example

cd docs/examples/calf

# Subscribe to TOP/TRADE/STATE/DEPTH for AAPL and MSFT
python3 calf_subscriber.py --host 127.0.0.1 --port 5570 \
    --symbols AAPL,MSFT

# Also subscribe to an index feed (skipped if the gateway doesn't advertise
# INDEX support in WELCOME|CH_SUPPORTED=)
python3 calf_subscriber.py --host 127.0.0.1 --port 5570 \
    --symbols AAPL --index EDU100

# Resume from a known sequence number (single-stream resume)
python3 calf_subscriber.py --host 127.0.0.1 --port 5570 \
    --resume --resume-ch TOP --resume-sym AAPL --lastseq 1042

Channels are not selected with a flag — the client always requests every channel the connected gateway advertises via WELCOME|CH_SUPPORTED= (TOP, TRADE, STATE, DEPTH), plus a separate session-wide SUB|CH=STATE|SYM=* subscription and, if --index is given, SUB|CH=INDEX. Run python3 calf_subscriber.py --help for the full flag list, including --client and --no-state-wildcard (skip the session-wide STATE subscription).

C API — calf_parser.h

int calf_parse_line(char *line, calf_message_t *out) : Parse one mutable CALF line in-place. Returns 0 on success, negative on error.

const char *calf_get_field(const calf_message_t *msg, const char *key) : Return the value for key, or NULL if absent.

C usage pattern

#include "calf_parser.h"

char line[] = "TOP|SYM=AAPL|BID=149.50|ASK=150.00|SEQ=1042";
calf_message_t msg;
calf_parse_line(line, &msg);
printf("%s\n", calf_get_field(&msg, "BID"));  /* "149.50" */

Build and run C example

cd docs/examples/calf
make
./calf_subscriber 127.0.0.1 5570 AAPL,MSFT EDU100

Arguments are positional: host [port [symbols [index_id]]]. symbols is a comma-separated list (default AAPL); index_id is optional (default: skip the INDEX subscription).

Manual gateway sanity check

nc 127.0.0.1 5570

Then type:

HELLO|CLIENT=manual01|PROTO=CALF1
SUB|CH=TOP,TRADE|SYM=AAPL

See also:


RALF support library and examples

RALF (pm-ralf-gwy) is the post-trade dissemination gateway. Clients connect over TCP and receive execution reports, drop-copy events, and audit records.

The example subscribers subscribe to every channel a role is actually entitled to (CLEARING/DROP_COPY → that channel only, AUDIT → all three) in one combined SUB, then put the received EXEC/EOD data to use: a running per-symbol executed-volume tally that de-duplicates EXEC lines by EXEC_ID (a single executed trade is delivered once per subscribed channel, so an AUDIT client receives the same trade up to three times and naively counting raw lines would overstate volume). Both examples also detect gaps in the per-channel SEQ counters, cross-check the tally against each EOD's TRADE_COUNT, and print a final tally on Ctrl-C shutdown.

Source files

File Description
ralf/README.md Directory overview and quick-start
ralf/ralf_parser.py Python parser and builder
ralf/ralf_subscriber.py Python subscriber example
ralf/ralf_parser.h C library header
ralf/ralf_parser.c C library implementation
ralf/ralf_subscriber.c C subscriber example
ralf/Makefile Build helper

Wire format

MSGTYPE|KEY=VALUE|KEY=VALUE\n

Identical text-line structure to CALF; field parsing rules are the same.

Python API — ralf_parser.py

parse_ralf_line(line: str) → RalfMessage : Parse one RALF line. Returns a RalfMessage(msg_type, fields) frozen dataclass. Raises RalfParseError on malformed input.

build_ralf_line(msg_type: str, fields: dict | None) → str : Build one \n-terminated RALF line. Raises RalfParseError for invalid message types or fields containing |.

Python usage pattern

from ralf_parser import build_ralf_line, parse_ralf_line

# Build a HELLO line
line = build_ralf_line("HELLO", {
    "CLIENT": "demo01",
    "PROTO": "RALF1",
    "ROLE": "CLEARING",
    "LASTSEQ": "0",
})
# → "HELLO|CLIENT=demo01|PROTO=RALF1|ROLE=CLEARING|LASTSEQ=0\n"

# Parse a WELCOME response
msg = parse_ralf_line("WELCOME|PROTO=RALF1|GW=ralf-gwy01|ROLE=CLEARING")
print(msg.msg_type)         # "WELCOME"
print(msg.fields["ROLE"])   # "CLEARING"

# Parse an inbound execution report
fill = parse_ralf_line("FILL|SYM=AAPL|SIDE=BUY|QTY=100|PRICE=150.00|SEQ=77")
print(fill.fields["PRICE"]) # "150.00"

Run Python example

cd docs/examples/ralf

# Subscribe as CLEARING role — --role defaults the subscribed channel(s)
# to exactly what that role may access (here: just the CLEARING channel)
python3 ralf_subscriber.py --host 127.0.0.1 --port 5580 --role CLEARING

# Resume from a known sequence number
python3 ralf_subscriber.py --host 127.0.0.1 --port 5580 \
    --role CLEARING --lastseq 77

Pass --channels explicitly to override a role's default channel set — this is also how to trigger ERR|CODE=ENTITLEMENT_DENIED on purpose, by requesting a channel outside the role's entitlement:

# CLEARING is only entitled to the CLEARING channel, so requesting AUDIT
# gets rejected
python3 ralf_subscriber.py --role CLEARING --channels AUDIT

Run python3 ralf_subscriber.py --help for the full flag list, including --client and --symbols (defaults to *).

C API — ralf_parser.h

int ralf_parse_line(char *line, ralf_message_t *out) : Parse one mutable RALF line in-place. Returns 0 on success, negative on error.

const char *ralf_get_field(const ralf_message_t *msg, const char *key) : Return the value for key, or NULL if absent.

C usage pattern

#include "ralf_parser.h"

char line[] = "FILL|SYM=AAPL|SIDE=BUY|QTY=100|PRICE=150.00|SEQ=77";
ralf_message_t msg;
ralf_parse_line(line, &msg);
printf("%s\n", ralf_get_field(&msg, "PRICE")); /* "150.00" */

Build and run C example

cd docs/examples/ralf
make
./ralf_subscriber 127.0.0.1 5580 AUDIT

Arguments are positional: host [port [role]] (role one of CLEARING, DROP_COPY, AUDIT; default CLEARING).

Manual gateway sanity check

nc 127.0.0.1 5580

Then type:

HELLO|CLIENT=manual01|PROTO=RALF1|ROLE=CLEARING|LASTSEQ=0
SUB|CH=CLEARING|SYM=*

See also:


BALF support library and examples

BALF (pm-balf-gwy) is the binary order-entry gateway, intended for ultra-low-latency clients. Frames are fixed-length little-endian structs rather than text lines.

Source files

File Description
balf/balf_parser.py Python binary parser
balf/balf_parser.c C binary parser

Binary frame structure

Every BALF frame begins with an 8-byte header:

Offset Size Field
0 1 Magic (0xBA)
1 1 Version (0x01)
2 1 Message type
3 1 Flags
4 4 Sequence number (LE uint32)

Prices are encoded as fixed-point integers scaled by 100_000_000 (8 decimal places).

Supported message types

Constant Value Description
MSG_LOGON 0x01 Session logon
MSG_LOGON_ACK 0x02 Logon acknowledgement
MSG_NEW_ORDER 0x10 New order
MSG_ORDER_ACK 0x11 Order acknowledgement
MSG_CANCEL_ORDER 0x12 Cancel request
MSG_CANCEL_ACK 0x13 Cancel acknowledgement
MSG_AMEND_ORDER 0x14 Amend request
MSG_AMEND_ACK 0x15 Amend acknowledgement
MSG_EXECUTION_REPORT 0x20 Fill / execution report
MSG_HEARTBEAT 0x30 Heartbeat
MSG_HEARTBEAT_ACK 0x31 Heartbeat acknowledgement
MSG_LOGOUT 0x40 Session logout

Python API — balf_parser.py

parse_header(frame: bytes) → Header : Parse the 8-byte BALF header. Returns a Header(magic, version, msg_type, flags, seq_no) frozen dataclass. Raises ValueError on bad magic or unsupported version.

split_frame(frame: bytes) → tuple[Header, bytes] : Validate frame length against the expected size for the message type, then return (header, body_bytes). Raises ValueError for unknown message types or wrong frame sizes.

decode_price(raw: int) → float : Convert a raw fixed-point integer to a float price value.

parse_logon_ack(body: bytes) → dict : Decode a LOGON_ACK body. Returns a dict with keys gateway_id, accepted, reject_code, message.

parse_order_ack(body: bytes) → dict : Decode an ORDER_ACK body. Returns a dict with keys client_order_id, order_id, timestamp_ns, accepted, reject_code, reason.

parse_execution_report(body: bytes) → dict : Decode an EXECUTION_REPORT body. Returns a dict with keys client_order_id, order_id, fill_price, fill_qty, remaining_qty, timestamp_ns, symbol, side, status. fill_price is already converted to a float via decode_price() — no further scaling is needed.

Python usage pattern

from balf_parser import (
    parse_header, split_frame, decode_price,
    parse_logon_ack, parse_order_ack, parse_execution_report,
    MSG_LOGON_ACK, MSG_ORDER_ACK, MSG_EXECUTION_REPORT,
    FRAME_SIZES,
)

# Decode a raw frame received from TCP
hdr, body = split_frame(raw_bytes)

if hdr.msg_type == MSG_EXECUTION_REPORT:
    report = parse_execution_report(body)
    price = report["fill_price"]  # already decoded to a float
    print(f"fill: {report['symbol']} qty={report['fill_qty']} price={price}")
elif hdr.msg_type == MSG_ORDER_ACK:
    ack = parse_order_ack(body)
    print(f"order_id={ack['order_id']} accepted={ack['accepted']}")

C reference decoder — balf_parser.c

Unlike the ALF and CALF C libraries, balf_parser.c has no header file and its functions are all static — it is a single self-contained reference program, not a linkable library. It builds a synthetic LOGON_ACK frame in memory, decodes it with the same split_frame/parse_header logic a real client would use, and prints the result as a self-test:

cd docs/examples/balf
cc -std=c11 -Wall -Wextra -pedantic -O2 balf_parser.c -o balf_parser
./balf_parser
# → LOGON_ACK gateway_id=TRADER01 accepted=1 reject_code=0 msg=ok
#   balf_parser.c self-test: OK

There is no BALF C (or Python) network client example — only the parsing logic is provided. To exercise BALF end-to-end, connect a TCP client to pm-balf-gwy and feed received frames through split_frame() (Python: balf_parser.py) or the C reference decoder's parsing functions.

Run the gateway

pm-balf-gwy

# Override bind port
pm-balf-gwy --port 5560

# Override engine host
pm-balf-gwy --engine-host 10.0.0.5

The gateway binds on port 5560 by default. BALF uses fixed-length binary frames so there is no text-mode manual sanity check; use the Python parser library or a binary client tool to verify connectivity.

See also:


REST API support library and examples

pm-api-gwy exposes a JSON REST API for order submission and session queries. The support library (api_gateway_client) uses only the Python standard library and the C standard socket API — no third-party HTTP libraries are required.

Source files

File Description
REST/python/api_gateway_client.py Python REST client library
REST/python/demo_info.py Print gateway status, symbols, and session info
REST/python/submit_market_order.py Submit a MARKET order from the command line
REST/c/api_gateway_client.h C library header
REST/c/api_gateway_client.c C library implementation
REST/c/demo_info.c C equivalent of demo_info.py
REST/c/Makefile Build helper

Authentication

All requests require a Bearer token in the Authorization header. The default development key is key-trader-demo.

Configure via environment variables:

export EDUMATCHER_API_URL="http://127.0.0.1:8080"
export EDUMATCHER_API_KEY="key-trader-demo"

Python API — api_gateway_client.py

ApiGatewayClient(base_url, api_key, timeout=5.0) : Construct a client for the given base URL and API key.

client.get_json(path: str) → dict : Issue a GET request and return the parsed JSON body. Raises RuntimeError on HTTP error responses.

client.post_json(path: str, payload: dict) → dict : Issue a POST request with a JSON body and return the parsed JSON response. Raises RuntimeError on HTTP error responses.

Python usage pattern

from api_gateway_client import ApiGatewayClient

client = ApiGatewayClient("http://127.0.0.1:8080", "key-trader-demo")

# Query endpoints
status  = client.get_json("/api/v1/status")
symbols = client.get_json("/api/v1/symbols")
session = client.get_json("/api/v1/session")

# Submit a MARKET order
result = client.post_json("/api/v1/orders", {
    "symbol":     "AAPL",
    "side":       "BUY",
    "order_type": "MARKET",
    "quantity":   100,
})
print(result["order_id"], result["status"])

# Submit and wait for the engine ACK
result = client.post_json("/api/v1/orders?wait=ack", {
    "symbol":     "MSFT",
    "side":       "SELL",
    "order_type": "MARKET",
    "quantity":   50,
})
print(result.get("event"))   # engine acknowledgement event

Run Python examples

cd docs/examples/REST/python

# Print gateway status, symbols, and session information
python3 demo_info.py

# Submit a BUY MARKET order for 100 AAPL
python3 submit_market_order.py --side BUY --symbol AAPL --qty 100

# Submit and block until the engine ACKs
python3 submit_market_order.py --side SELL --symbol MSFT --qty 50 --wait-ack

# Override URL or key on the command line
python3 submit_market_order.py --url http://10.0.0.5:8080 --key mykey \
    --side BUY --symbol TSLA --qty 10

C API — api_gateway_client.h

ApiGatewayClient api_gateway_client(host, port, api_key) : Construct a client struct (stack-allocated).

char *api_gateway_get(const ApiGatewayClient *client, const char *path) : Issue a GET request and return the raw HTTP response body as a heap-allocated string. The caller must free() the result. Returns NULL on network or allocation failure.

C usage pattern

#include "api_gateway_client.h"
#include <stdlib.h>
#include <stdio.h>

const char *key = getenv("EDUMATCHER_API_KEY") ?: "key-trader-demo";
ApiGatewayClient client = api_gateway_client("127.0.0.1", 8080, key);

char *body = api_gateway_get(&client, "/api/v1/symbols");
if (body) {
    printf("%s\n", body);
    free(body);
}

Build and run C example

cd docs/examples/REST/c
make

# Print status, symbols, and session info
./demo_info

Manual API sanity check

curl -s -H "Authorization: Bearer key-trader-demo" \
     http://127.0.0.1:8080/api/v1/status | python3 -m json.tool

curl -s -X POST \
     -H "Authorization: Bearer key-trader-demo" \
     -H "Content-Type: application/json" \
     -d '{"symbol":"AAPL","side":"BUY","order_type":"MARKET","quantity":10}' \
     http://127.0.0.1:8080/api/v1/orders | python3 -m json.tool

See also:


Integration guidance

Use the parser libraries as the protocol boundary in your client code:

  1. Read bytes from TCP (or HTTP for REST).
  2. Split by newline into protocol lines (text protocols) or read fixed-size frames (BALF).
  3. Parse lines or frames using the appropriate parser library.
  4. Route by msg_type and fields.
  5. Track SEQ checkpoints per protocol rules to enable gap detection and replay on reconnect.

This keeps socket handling separate from protocol semantics and makes unit testing straightforward — pass raw strings or byte buffers directly into the parser functions without a network connection.