The language brokers and exchanges speak
When a trading system sends an order to a broker or an exchange it very often does so in FIX, the Financial Information eXchange protocol, in use since the early 1990s. A FIX message is plain text: a list of tag=value fields, each ended by an invisible separator character, chr(1) (SOH). Every tag is a number with an agreed meaning: 35 is the message type, 55 the symbol, 54 the side, 38 the quantity, 44 the price. The format is old and unglamorous, and the whole industry understands it.
# A FIX message is tag=value fields each ended by a separator (shown here as |)
message = "8=FIX.4.2|9=60|35=D|11=ord-1|55=AAPL|54=1|38=100|40=2|44=184.50|10=123|"
fields = {}
for part in message.split("|"):
if part: # the last separator leaves an empty piece
tag, value = part.split("=", 1) # split at the FIRST = only
fields[int(tag)] = value
print(fields[35], fields[55], fields[38], fields[44]) # D AAPL 100 184.50
print(sorted(fields)) # [8, 9, 10, 11, 35, 38, 40, 44, 54, 55]The shape of a message
A message has three parts. The header starts with 8=FIX.4.2 (the version), then 9= the body length, then the message type 35=. The body carries the content: for a New Order Single (35=D) the client order id, symbol, side (1 buy, 2 sell), quantity, order type (40=2 limit) and price. The trailer is 10=, a checksum: add up the character codes of everything before it and keep the remainder after dividing by 256, written with three digits. The receiver recomputes it to detect corruption, and it also checks the body length. Sessions add more: logon (35=A), heartbeats (35=0) and sequence numbers like the ones you tracked in the last topic.
# The checksum: add up the character codes, keep the remainder modulo 256, three digits
def checksum(text):
return f"{sum(ord(c) for c in text) % 256:03d}"
print(checksum("A")) # 065 the code of "A" is 65
print(checksum("aaa")) # 035 3 * 97 = 291 and 291 - 256 = 35
print(checksum("8=FIX.4.2|35=0|")) # a heartbeat header# Writing fields: tag=value and a separator after each one
def build_fix(fields, sep="|"):
return "".join(f"{tag}={value}{sep}" for tag, value in fields)
body = build_fix([(35, "D"), (11, "ord-1"), (55, "AAPL"), (54, 1), (38, 100)])
print(body) # 35=D|11=ord-1|55=AAPL|54=1|38=100|
print(len(body)) # 34 tag 9 holds this length# A complete order: header with the body length, the body, then the checksum trailer
def checksum(text):
return f"{sum(ord(c) for c in text) % 256:03d}"
def build_fix(fields, sep="|"):
return "".join(f"{tag}={value}{sep}" for tag, value in fields)
def new_order_message(cl_ord_id, symbol, side, quantity, price, sep="|"):
code = "1" if side == "buy" else "2"
body = build_fix([(35, "D"), (11, cl_ord_id), (55, symbol), (54, code), (38, quantity), (40, 2), (44, f"{price:.2f}")], sep)
head = build_fix([(8, "FIX.4.2"), (9, len(body))], sep) + body
return head + build_fix([(10, checksum(head))], sep)
message = new_order_message("ord-1", "AAPL", "buy", 100, 184.5)
print(message) # 8=FIX.4.2|9=...|35=D|11=ord-1|...|44=184.50|10=...|
print(message.split("|")[-2]) # the checksum field- A FIX message is
tag=valuefields each ended by a separator (chr(1)on the wire); split each field at its first=. - Header
8(version) and9(body length), body from35(type) onward, trailer10(checksum). - The checksum is the sum of the character codes modulo 256, as three digits; use a tested engine in production and log every message.
Next up: clean code, naming, small functions and the habits that keep a trading codebase readable.
Lab · what's graded
- Read a message with
parse_fix25 pts - Seal a message with
checksum25 pts - Write fields with
build_fix25 pts - Send an order with
new_order_message25 pts