ALF TCP Gateway Protocol Training¶
Objective¶
Train on end-to-end usage of the ALF protocol through pm-alf-gwy using OS
CLI tools, the example parser libraries, and the interactive example clients.
You will practise:
- generating an
alf_gateway:config withpm-config-gen - starting
pm-alf-gwyand verifying the port is listening using OS tools - testing connectivity and the full session lifecycle manually with
ncandtelnet - submitting, amending, and cancelling orders over a raw TCP session
- using the Python
alf_parser.py/alf_client.pyexamples for scripted and interactive access - building and running the C
alf_clientexample - operating control commands (
PING,ORDERS,QBOOT,KILL) - diagnosing error conditions from the
ERR|CODE=...vocabulary
Prerequisites¶
- Chapters 01-25 completed.
- Engine running and configured with at least two gateway IDs.
pm-alf-gwyavailable in the environment (poetry run pm-alf-gwyor installed viapipx).- Support libraries and clients available in
docs/examples/alf: python/alf_parser.py,python/alf_client.pyc/alf_parser.h,c/alf_parser.c,c/alf_client.c,c/Makefile
Recommended startup terminals:
- Engine:
pm-engine --verbose - ALF gateway:
pm-alf-gwy - One or more client terminals for connection exercises
Background¶
pm-alf-gwy is the TCP gateway for the ALF (ALmost Fix) protocol.
It accepts multiple simultaneous connections from external bots and scripts
written in any language, and translates ALF commands into the same engine ZMQ
messages used by the interactive pm-alf-console terminal.
In this chapter, the most important operational ideas are:
- Session establishment:
HELLO→ auth round-trip →WELCOMEwith automaticSYMBOLSbootstrap - Authenticated order entry and management:
NEW,AMEND,CANCEL,QUOTE - Gateway kill-switch:
KILL(with optionalSYM=scope) - Bootstrap state recovery:
ORDERSandQBOOT - Liveness:
PING/PONGand unsolicitedHBheartbeat lines - Error escalation: per-error
ERR|CODE=...and sliding-window forced disconnect
Exercise 1: Generate a Config That Enables pm-alf-gwy¶
Generate engine_config.yaml with the alf_gateway: section enabled:
pm-config-gen \
--symbols AAPL MSFT \
--gateways TRADER01 TRADER02 MM01:MARKET_MAKER \
--alf-gateway \
--alf-gateway-port 5565 \
--alf-gateway-bind-address 127.0.0.1 \
--output engine_config.yaml
Inspect the generated section:
Expected output includes:
alf_gateway:
enabled: true
port: 5565
bind_address: 127.0.0.1
heartbeat_interval_sec: 5
idle_timeout_sec: 30
Start processes with that config:
Checkpoint: pm-alf-gwy starts, logs that it is listening on port 5565, and reports a successful engine ZMQ connection.
Exercise 2: Verify the Port Is Listening with CLI Tools¶
Before writing any client code, confirm the gateway is actually bound to the configured port using OS tools. This is the fastest way to rule out startup failures, bind-address mismatches, and firewall issues.
macOS:
# lsof — shows the process name and PID holding the port
sudo lsof -iTCP:5565 -sTCP:LISTEN
# BSD netstat (ships with macOS)
netstat -an | grep LISTEN | grep 5565
Expected: a line with pm-alf-gwy (or Python if running under Poetry) and LISTEN.
Linux:
# ss — preferred on modern Linux
ss -tlnp 'sport = :5565'
# lsof
sudo lsof -iTCP:5565 -sTCP:LISTEN
# netstat (older distributions)
netstat -tlnp | grep 5565
Both commands should show a process bound to *:5565 or 127.0.0.1:5565.
If no output appears:
- the gateway is not running or failed to start — check logs
alf_gateway.enabledmay befalsein the config- the port may differ from what you expect — re-run
grep -A5 'alf_gateway:' engine_config.yaml pm-enginemust be running beforepm-alf-gwycan complete its ZMQ handshake
Checkpoint: at least one CLI command shows a process listening on port 5565.
Exercise 3: Manual Handshake Probe with nc¶
Use nc (netcat) to open a raw TCP connection and type ALF lines by hand.
This verifies the full session lifecycle — HELLO, auth, WELCOME, SYMBOLS — with
no code.
Type these lines, pressing Enter after each:
Expected response pattern:
WELCOME|PROTO=ALF1|GW=alf-gwy01|ID=TRADER01|HBINT=5|IDLE=30- Automatic
SYMBOLS|COUNT=Nblock with oneSYMBOL|SYM=...|TICK=...per instrument END|TYPE=SYMBOLSHB|TS=...every 5 seconds when quiet- Connection closes after
EXIT
Also try ORDERS to see the empty bootstrap snapshot:
Expected: ORDERS|COUNT=0|GW=TRADER01 and END|TYPE=ORDERS.
Checkpoint: nc session reaches WELCOME and receives a valid SYMBOLS block and an END|TYPE=ORDERS line.
Exercise 3B: telnet and Non-Interactive One-Liner¶
telnet (macOS / Linux):
Type HELLO|CLIENT=test|PROTO=ALF1|ID=TRADER01 and press Enter. telnet
echoes characters locally so the line looks doubled in the terminal — the
WELCOME response confirms the gateway accepted it. Press Ctrl-], then
type quit to close.
Non-interactive probe — useful in scripts or CI pipelines:
Expected: the full session including WELCOME, SYMBOLS block, and
END|TYPE=SYMBOLS is printed to stdout and the command exits cleanly.
Checkpoint: the one-liner prints WELCOME and END|TYPE=SYMBOLS without interaction.
Exercise 4: Submit and Cancel an Order over nc¶
Open a new nc session and type all commands (press Enter after each line):
HELLO|CLIENT=manual|PROTO=ALF1|ID=TRADER01
NEW|SYM=AAPL|SIDE=BUY|TYPE=LIMIT|QTY=100|PRICE=150.00
ORDERS
Expected behavior:
ACK|ORDER_ID=<uuid>|ACCEPTED=TRUE|...arrives immediately afterNEWORDERSshows the resting order withSTATUS=NEWandREMAINING=100- If
MM01is quoting, the order may fill immediately andORDERSreturnsCOUNT=0
Cancel the resting order using the UUID from the ACK:
Expected: CANCELLED|ORDER_ID=<uuid>.
Try amending before cancelling to see priority rules in action:
Expected: AMENDED|ORDER_ID=...|PRICE=149.50|QTY=150|REMAINING=150|PRIORITY_RESET=TRUE
Checkpoint: you can submit, confirm, amend, and cancel an order using only raw TCP and a terminal.
Exercise 5: Use the Python Interactive Client¶
alf_client.py provides an pm-alf-console-style interactive session over
TCP, with tab-completion, background event display, and local P&L tracking.
At the prompt:
[TRADER01]> SYMBOLS
[TRADER01]> NEW|SYM=AAPL|SIDE=BUY|TYPE=LIMIT|QTY=100|PRICE=150.00
[TRADER01]> ORDERS
[TRADER01]> POS
[TRADER01]> STATUS
[TRADER01]> HELP
[TRADER01]> EXIT
Expected behavior:
POSshows accumulated positions tracked locally by the clientSTATUSshows session info: gateway name, heartbeat interval, idle timeout- fills,
HB, and broadcastSESSION/TRADEevents appear in real time while you type other commands - Tab completes command verbs and field names
- Command history is saved across sessions to
~/.alf_client_history
Connect to a remote gateway:
Checkpoint: interactive Python client connects, shows WELCOME, and ORDERS and POS respond correctly.
Exercise 6: Use the Python Library Directly¶
alf_parser.py exposes parse_alf_line, build_alf_line, and the
AlfSession high-level class for use in scripts.
cd docs/examples/alf/python
python3 - <<'EOF'
from alf_parser import parse_alf_line, build_alf_line, AlfSession, AlfMessage
# Parse a 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", ...}
# Build a line to send
line = build_alf_line("NEW", {
"SYM": "AAPL", "SIDE": "BUY",
"TYPE": "LIMIT", "QTY": "100", "PRICE": "150.00",
})
print(repr(line)) # 'NEW|SYM=AAPL|SIDE=BUY|TYPE=LIMIT|QTY=100|PRICE=150.00\n'
# High-level session: connect, HELLO/WELCOME, send/recv
session = AlfSession.connect("127.0.0.1", 5565, "TRADER01")
print(session.welcome.gw_name) # alf-gwy01
session.send("ORDERS")
msg = session.recv_msg() # header line of ORDERS response
print(msg.msg_type, msg.fields)
session.close()
EOF
Expected output: ACK, the parsed fields dict, the built line string, the
gateway name from WELCOME, and the ORDERS response header.
Checkpoint: AlfSession.connect completes the HELLO/WELCOME handshake and recv_msg returns a parsed message.
Exercise 7: Build and Run the C Client¶
macOS — install Homebrew readline for full callback support if not already present:
Linux:
Build:
Run:
At the prompt:
[TRADER01]> SYMBOLS
[TRADER01]> NEW|SYM=AAPL|SIDE=BUY|TYPE=LIMIT|QTY=100|PRICE=150.00
[TRADER01]> ORDERS
[TRADER01]> EXIT
Expected behavior:
- the C client uses
select()to multiplex the socket and stdin, so gateway events (fills, heartbeats, broadcasts) appear in real time while you type - readline provides tab completion and persistent command history
--no-colordisables ANSI colour output for plain terminals
Try connecting to a remote host:
Checkpoint: C client connects, shows WELCOME, and a NEW order returns an ACK.
Exercise 8: Control Commands and the Kill-Switch¶
PING / PONG¶
Expected: PONG|TS=<ISO-8601-timestamp>.
QBOOT — quote bootstrap state (MARKET_MAKER role)¶
Open a second terminal and connect with the MM01 identity:
Expected: QBOOT|COUNT=0 and END|TYPE=QBOOT initially.
Submit a quote then re-check:
Expected: QBOOT|COUNT=1 with one active QUOTE|... line, then END|TYPE=QBOOT.
KILL — cancel all resting orders and active quotes¶
Expected: KILL_ACK|ACCEPTED=TRUE|ORDERS=N|QUOTES=N.
Confirm everything was cleared:
Both responses should show COUNT=0.
Scope the kill to one symbol:
NEW|SYM=AAPL|SIDE=BUY|TYPE=LIMIT|QTY=100|PRICE=149.00
NEW|SYM=MSFT|SIDE=BUY|TYPE=LIMIT|QTY=50|PRICE=400.00
KILL|SYM=AAPL
ORDERS
Expected: only the MSFT order remains.
Checkpoint: you can use PING, QBOOT, and KILL (with and without SYM=) and interpret the responses.
Exercise 9: Error Conditions and Operational Interpretation¶
Test typical protocol errors. Start a fresh nc session and send each line before
completing the HELLO handshake, or with invalid content:
NEW|SYM=AAPL|SIDE=BUY|TYPE=LIMIT|QTY=100|PRICE=150.00 (before HELLO)
HELLO|CLIENT=test|PROTO=WRONG|ID=TRADER01
HELLO|CLIENT=test|PROTO=ALF1|ID=NONEXISTENT
After a successful HELLO, try:
NEW|SYM=AAPL|SIDE=BUY|TYPE=LIMIT|QTY=100 (missing PRICE for LIMIT)
NEW|SYM=UNKNOWN|SIDE=BUY|TYPE=LIMIT|QTY=100|PRICE=150.00
QUOTE|SYM=AAPL|BID=150.00|ASK=150.10|BID_QTY=500|ASK_QTY=500 (TRADER role)
Map each observed ERR|CODE=... to its operator remediation:
| Code | Practical interpretation |
|---|---|
AUTH_REQUIRED |
Client sent a non-HELLO first line; fix client code ordering |
PROTO_MISMATCH |
Wrong PROTO= value; must be exactly ALF1 |
AUTH_FAILED |
Gateway ID not in gateways.alf; add to config and restart engine |
GATEWAY_ALREADY_CONNECTED |
Same ID connected from another session; disconnect it first |
MISSING_FIELD |
Required field absent for this order type; check command reference |
INVALID_VALUE |
Field value fails validation (e.g. PRICE=NaN, QTY=0); fix client |
SYMBOL_NOT_CONFIGURED |
Unknown symbol; run SYMBOLS to see the configured list |
ROLE_DENIED |
Command not allowed for this gateway's role (QUOTE requires MARKET_MAKER) |
RATE_LIMITED |
Commands arriving faster than max_commands_per_second; throttle client |
Checkpoint: you can map each ERR|CODE to the correct config or client-code fix.
Support Libraries and Example Clients¶
Reference implementations used in this training chapter:
docs/examples/alf/python/alf_parser.py— protocol library:parse_alf_line,build_alf_line,AlfSessiondocs/examples/alf/python/alf_client.py— interactive client with tab-completion, event display, and P&L trackingdocs/examples/alf/c/alf_parser.h/alf_parser.c— C protocol library:alf_parse_line,alf_build_line,alf_get_fielddocs/examples/alf/c/alf_client.c— interactive C client usingselect()+ readline
Use these to bootstrap ALF clients in any language that can open a TCP socket and read newline-delimited text.
Summary¶
You can now:
- Generate an
alf_gateway:config withpm-config-genand startpm-alf-gwy. - Verify a TCP port is listening using
lsof,ss, andnetstaton macOS and Linux. - Test the complete ALF session lifecycle manually with
ncandtelnetbefore writing any code. - Submit, amend, and cancel orders over a raw TCP session and read
ACK,AMENDED, andCANCELLEDresponses. - Use
python3 alf_client.pyfor interactive sessions with tab-completion and live event display. - Script against the gateway using
AlfSessionfromalf_parser.py. - Build and run the C
alf_clientfor a readline-based interactive session withselect()multiplexing. - Operate
PING,ORDERS,QBOOT, andKILLand interpret their responses. - Map
ERR|CODE=...values to the correct config or client-side fix.
Reflection¶
pm-alf-gwy (this chapter) and pm-api-gwy (Chapter 24) both allow
external clients to submit orders to the same matching engine. Answer the
following:
-
A Python bot connecting to
pm-alf-gwyreceives aFILLevent pushed directly over its persistent TCP connection. A Python bot connecting topm-api-gwylearns about the same fill through a WebSocketeventsstream. Both need to act on fills within milliseconds. Which transport has lower latency overhead and why? -
A compliance team needs read-only access to every fill for all gateway IDs — not just their own. Can they use
pm-alf-gwyfor this? If not, which gateway process should they consume instead and with what role? -
After a network interruption, an ALF bot reconnects and sends
ORDERSto recover resting state. The API gateway equivalent isGET /api/v1/orders. At the protocol level, which reconnect handler is simpler to implement and why?
Handoff for Chapter 27¶
This completes the external gateway protocol series. pm-engine,
pm-alf-gwy, and any other running processes can remain up if you want to
continue a live session, or you can stop them and start fresh for the next
chapter.
Further Reading¶
- ALF TCP Gateway — configuration, session lifecycle, command reference, and troubleshooting
- ALF Protocol Reference — formal wire syntax and full field/enum definitions
- Gateway Commands — interactive command reference for
pm-alf-console - Protocol Support Library Examples
- Processes