ブローカーと取引所が話す言語
取引システムがブローカーや取引所に注文を送るとき、非常に多くの場合 FIX(Financial Information eXchange プロトコル)を使います。1990年代初めから使われています。FIX メッセージはプレーンテキストで、タグ=値のフィールドが並び、各フィールドは目に見えない区切り文字 chr(1)(SOH)で終わります。各タグは意味の決まった番号で、35 はメッセージ種別、55 は銘柄、54 は売買、38 は数量、44 は価格です。古くて地味な形式ですが、業界全体が理解しています。
# 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]メッセージの構造
メッセージは3つの部分からなります。ヘッダーは 8=FIX.4.2(バージョン)で始まり、次に 9= の本文の長さ、続いてメッセージ種別 35= です。本文が内容を運びます。New Order Single(35=D)なら、クライアント注文ID、銘柄、売買(1 買い、2 売り)、数量、注文タイプ(40=2 指値)、価格です。トレーラーは 10= のチェックサムで、それより前のすべての文字コードを足し、256で割った余りを3桁で書きます。受信側は再計算して破損を検出し、本文の長さも確認します。セッションにはさらに、ログオン(35=A)、ハートビート(35=0)、前回追ったような連番が加わります。
# 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本番のエンジンを自作しないメッセージの解析と組み立ては FIX を理解するのに良い方法ですが、実際の接続にはセッションのロジックが必要です。ログオン、ハートビート、シーケンスのリセット、再送要求、市場ごとの方言などです。本番では QuickFIX のような実績あるエンジンを使い、自分のコードは注文ロジックに絞ります。不正な注文や重複した注文は実際のお金を失わせるので、送受信したすべてのメッセージを記録してください。
- FIX メッセージは
タグ=値のフィールドが並び、各々が区切り文字(実際はchr(1))で終わる。各フィールドは最初の=で分ける - ヘッダーは
8(バージョン)と9(本文の長さ)、本文は35(種別)から、トレーラーは10(チェックサム) - チェックサムは文字コードの合計を256で割った余りを3桁にしたもの。本番では実績あるエンジンを使い、すべてのメッセージを記録する
次はクリーンなコードです。命名、小さな関数、トレーディングのコードベースを読みやすく保つ習慣を学びます。
ラボ:採点される内容
parse_fixでメッセージを読む25点checksumでメッセージに封をする25点build_fixでフィールドを書く25点new_order_messageで注文を送る25点