Die Sprache von Brokern und Börsen
Wenn ein Handelssystem eine Order an einen Broker oder eine Börse sendet, geschieht das sehr oft in FIX, dem Financial Information eXchange Protocol, das seit den frühen 1990er-Jahren verwendet wird. Eine FIX-Nachricht ist Klartext: eine Liste von Tag=Wert-Feldern, jedes durch ein unsichtbares Trennzeichen, chr(1) (SOH), beendet. Jeder Tag ist eine Zahl mit vereinbarter Bedeutung: 35 ist der Nachrichtentyp, 55 das Symbol, 54 die Seite, 38 die Menge, 44 der Preis. Das Format ist alt und unglamourös, und die ganze Branche versteht es.
# 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]Der Aufbau einer Nachricht
Eine Nachricht hat drei Teile. Der Header beginnt mit 8=FIX.4.2 (die Version), dann 9= die Länge des Rumpfs, dann der Nachrichtentyp 35=. Der Rumpf trägt den Inhalt: bei einer New Order Single (35=D) die Kunden-Order-ID, das Symbol, die Seite (1 Kauf, 2 Verkauf), die Menge, den Ordertyp (40=2 Limit) und den Preis. Der Trailer ist 10=, eine Prüfsumme: Summieren Sie die Zeichencodes von allem davor und behalten Sie den Rest der Division durch 256, dreistellig geschrieben. Der Empfänger berechnet sie neu, um Beschädigung zu erkennen, und prüft auch die Rumpflänge. Sitzungen fügen mehr hinzu: Logon (35=A), Heartbeats (35=0) und Sequenznummern wie die aus dem letzten Thema.
# 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- Eine FIX-Nachricht besteht aus
Tag=Wert-Feldern, jedes durch ein Trennzeichen (chr(1)auf der Leitung) beendet; trennen Sie jedes Feld am ersten=. - Header
8(Version) und9(Rumpflänge), Rumpf ab35(Typ), Trailer10(Prüfsumme). - Die Prüfsumme ist die Summe der Zeichencodes modulo 256, dreistellig; nutzen Sie produktiv eine erprobte Engine und protokollieren Sie jede Nachricht.
Als Nächstes: sauberer Code, Namen, kleine Funktionen und die Gewohnheiten, die eine Trading-Codebasis lesbar halten.
Lab: Was bewertet wird
- Mit
parse_fixeine Nachricht lesen25 Pkt. - Mit
checksumeine Nachricht versiegeln25 Pkt. - Mit
build_fixFelder schreiben25 Pkt. - Mit
new_order_messageeine Order senden25 Pkt.