amBrain
FinTechSep 28, 2026読了10分

Rust コアで FIX 接続のトレーディングターミナルを作る:オーダーブック、スキャルピングパネル、注文状態

トレーディングターミナルFIXプロトコルオーダーブックRust
画像を読み込めませんでした

Rust コアを持つ FIX トレーディングターミナルの作り方を、セッションのリカバリからラダーまでたどり、ベンダーが実際にそれを作ったことを示す証拠を挙げる。

ライブのオーダーブック、スキャルピングパネル、Rust のホットパスを備えた FIX トレーディングターミナルを実際に作った会社がどこかを教えてくれるディレクトリはない。「トレーディングターミナルを作った」という言葉は、ブローカーの API にチャートの外装をかぶせただけのものから、独自の注文状態機械を持つ FIX クライアントまで、何でも含んでしまうからだ。作ったことのあるベンダーなら、それを証明できる。あなたが見ている前でターミナルを本番の取引所につないで動かし、その FIX 接続を認定した取引所を挙げ、レイテンシのタイムスタンプをどこで取っているかを示し、ベンダーの助けなしにビルドできるコードを引き渡すことだ。

アーキテクチャについての短い答え。Rust のコアが、FIX セッションとそのシーケンス番号、注文の状態機械、ローカルのオーダーブック、プレトレードチェックを持ち、トレーダーが数人を超えるなら、取引所の近くでゲートウェイとして動く。画面はコマンドを送り、最新の状態を 1 フレームに 1 回描画するだけなので、フリーズしてもクラッシュしても注文は失われない。

Rust のコアには何を置き、画面は何を受け持つのか

失われたり遅れたりすると注文が変わってしまうものは、すべてコアに置く。このルールに従うと、そこに入る部品は五つになる。

  • FIX エンジン。ログオン、ハートビート、シーケンス番号、再送を扱うセッション層と、トレーダーのコマンドを注文メッセージに、ExecutionReport をイベントに変換するアプリケーション層からなる
  • オーダーマネージャー。注文ごとに一つの状態機械を持ち、クライアント注文 ID をキーとし、取引所が報告する内容だけで更新される
  • フィードごとのブックビルダー。番号の振られたストリームを、銘柄ごとのローカルの板に畳み込む
  • 数量、プライスバンド、エクスポージャーのプレトレードチェック。メッセージをエンコードする前に、メモリ上の状態に対して実行する
  • すべてのメッセージとトレーダーのすべてのコマンドを、それに基づいて動く前に書き込むジャーナル。これにより、再起動しても状態を再構築でき、争いになった約定を再生できる

画面が持つのはビューだ。ラダー、チャート、注文一覧(ブロッター)、ポジション、ホットキー。画面が落ちても、コアはセッションと有効な注文、そして自ら管理するストップ注文をすべて保持し続ける。Rust が最も効くのはコアである。ガベージコレクタがないので、コマンドと回線のあいだに GC の停止が割り込むことはない。また、一つの板に二つのスレッドから書き込むコードは、その板がロックの背後で共有されていない限り、コンパイラが拒否する。

普通の Web ページは、FIX セッションが載る TCP 接続を開けない。Chrome のドキュメントには、標準的な Web アプリケーションは「生の TCP 接続や UDP 接続を確立できない」とあり、Chrome の Direct Sockets API がこの制限を外すのは Isolated Web Apps に対してだけだ。ネイティブのターミナルならその接続を保持できるが、そうするとデスクごとに、取引所とのセッション、ネットワーク経路、シーケンスの状態をそれぞれ抱えることになる。トレーダーが数人を超えたら、コアは取引所の近くのゲートウェイに置くべきだ。距離がレイテンシの予算の大半を占めるならコロケーションに、トレーダーが手でクリックしていて、その判断にネットワーク経路よりはるかに長い時間がかかるなら近くのクラウドリージョンに置く。

FIX のセッション層は何を担い、何があなたに残されるのか

セッション層が与えてくれるのは、順序どおりの処理と、取りこぼしたメッセージを要求する手段である。相手側に、そのすべてを再送する義務を課すわけではない。ルールは FIX Session Layer の技術標準(2020年6月)に定められている。

  • メッセージは MsgSeqNum(34) の順に処理する。期待より大きい番号はギャップであり、ResendRequest(35=2) で応える。推奨される形式では EndSeqNo(16) を 0 に設定し、最初に欠けたメッセージ以降のすべてを要求する
  • ギャップより後のメッセージを、ギャップより先に処理することはない。標準の例では、メッセージ 3〜5 は「メッセージ 2 より前に処理すべきではない」とされている
  • PossDupFlag(43)=Y のない、期待より小さい番号を受け取ったら、Logout でセッションを終了すべきであり、その後に接続は切断される
  • 再送されたメッセージには PossDupFlag(43)=Y が付く。それがすでに処理済みかどうかを判断するのは受信側の仕事だ
  • 再送する側は、アプリケーションメッセージを飛ばしてもよい。注文について送信側は「時間が経ちすぎたため、それらを再送しないことを選択できる」とされ、GapFillFlag(123)=Y の SequenceReset(35=4) でそれらを飛び越える

ここから三つの責務が生じる。送信と受信のたびにシーケンス番号を永続化すること。そうしないと、再起動の際にその日のメッセージを丸ごと要求し直すか、番号が小さすぎるとして切断されるかのどちらかになる。どの ExecID(17) の値をすでに適用したかを覚えておくこと。標準は重複の検出を受信側に委ねているからだ。そして再接続のたびに、取引所が対応していれば OrderMassStatusRequest(35=AF) を使って、最後に注文ステータスを確認すること。ある注文に関するメッセージがギャップフィルで飛ばされたなら、その注文についてのあなたの把握には事実が一つ欠けている。

オーダーマネージャーは ExecutionReport をどう読むべきか

ExecutionReport(35=8) には、取り違えやすい二つのフィールドがある。FIX の定義では、ExecType(150) は「その ExecutionRpt が具体的に何であるかを表し(例:Pending Cancel)、一方で OrdStatus(39) は常に現在の注文ステータスを示す(例:Partially Filled)」とされている。状態機械はイベントで動かし、ステータスは突き合わせの確認に使う。

  • Pending New (A) と New (0):取引所が注文を受け取り、続いて受理した
  • Trade (F):一部約定または全部約定。FIX 4.3 より前は約定が ExecType 1 と 2 だったので、FIX 4.2 の接続先向けのアダプターはそれらを Trade に対応付ける
  • Pending Cancel (6), Canceled (4), Pending Replace (E), Replaced (5), Rejected (8), Expired (C)
  • Trade Correct (G) と Trade Cancel (H):約定はあとから訂正されたり取り消されたりすることがあるので、約定済みの数量でさえ減りうる

FIX の辞書は、注文が有効なあいだの LeavesQty(151) を OrderQty(38) から CumQty(14) を引いたものと定義している。そのため、有効な注文についてのレポートのたびに確かめられる、安上がりな不変条件になる。これに反するレポートや、現在の状態からの遷移が存在しない ExecType を受け取ったら、その注文を凍結してアラートを上げるべきだ。推測で済ませると、ターミナルは最後には取引所と食い違うポジションを表示することになる。

ターミナルはキャンセルと訂正の競合をどう扱うのか

スキャルパーは、取引所が前回の変更に応答する前に、注文をまた変えることがよくある。以下の競合はどれも、回線上で一つのメッセージが別のメッセージと行き違うものだ。

  • キャンセルが送信途中のあいだに、注文が全量約定する。取引所は OrderCancelReject(35=9) で応答し、多くの場合 CxlRejReason(102)=0、「Too late to cancel」(取消には遅すぎる)が付く。ターミナルは、トレーダーが求めたフラットなポジションではなく、その約定と、結果として生じたポジションを表示しなければならない
  • 最初の変更が受理される前に二つ目の変更が送られ、CxlRejReason(102)=3、つまり注文がすでにキャンセル待ちか訂正待ちの状態にあるという理由で、拒否されて戻ってくることがある。送信途中の訂正は注文ごとに一つだけにし、その後の要求は最新の価格と数量にまとめる
  • 訂正のたびに新しい ClOrdID(11) が付き、OrigClOrdID(41) が指すのは直前の ID であって、「その日の最初の注文ではない」。訂正と行き違った約定は、送ったばかりの ID より古い ID で届くことがあるので、チェーン内のすべての ID を同じ注文に対応付けなければならない
  • 注文が板に残ったままセッションが切れる。たとえば CME の Cancel on Disconnect は、COD を有効にした iLink セッションが意図せず切断されると、板に残っている先物とオプションの注文を取り消すが、GTC と GTD の注文は取り消さない。本番稼働の前に、取引所ごとに、切断を生き延びる注文を洗い出しておくこと

drop copy は第二の視点を与える。CME はこのサービスを、執行レポートと受付確認のリアルタイムのコピーを「別個の専用経路で」送るものと説明している。コアでポジションをこれと照合し、注文セッションとの食い違いはどれもインシデントとして扱う。

L2 と L3 のフィードから、ローカルのオーダーブックをどう構築するのか

L2 フィードは価格レベルを配信する。Binance は、スナップショットと更新を組み合わせる手順を文書化している。ストリームをバッファし、スナップショットを取得し、スナップショットにすでに含まれているバッファ済みのイベントを捨て、残りを順に適用し、更新 ID が飛んだら最初からやり直す。スナップショットは片側 5000 レベルまでなので、それより深いレベルは変化するまで不明のままだ。

L3 フィードは個々の注文を配信する。Nasdaq TotalView-ITCH 5.0 では、Add Order メッセージが注文参照番号を持ち、その後の変更メッセージはその番号を参照し、表示株数がゼロになると「その注文は消滅しており、板から削除すべきである」とされる。ビルダーは参照番号から注文へのマップを持ち、そこからレベルを集計する。メモリが増え、メッセージごとに参照が一回加わる代わりに、レベルごとの注文件数と、自分の注文がキューのどこにいるかの推定が得られる。

ラダーには、最良気配(タッチ)の周辺を価格でインデックスする配列が、たいてい木構造より向いている。価格は連続した範囲をティック単位で動くからだ。銘柄ごとに一つのライターが板を所有し、板がどのシーケンス番号を反映しているかを記録する。ギャップからのリカバリとファンアウトは、バースト下のマーケットデータについての記事で扱っている。ターミナルが加えるルールは一つだ。リカバリ中の板はリカバリ中として描画し、決してライブとしては描かない。

スキャルピングパネルはコアに何を求めるのか

このパネルは、板の厚みを並べたラダー(DOM)であり、そこから直接取引する。価格をクリックすると指値注文が出て、ドラッグするとその注文が動き、ホットキーはあらかじめ設定した数量を発注するか、ポジションを決済してフラットにする。確認ダイアログがないので、安全網はコアにあり、コアはワンクリック注文のたびに、口座ごとの最大数量、プライスバンド、エクスポージャーをチェックする。これらのチェックは、注文経路におけるプレトレードリスクについての記事で扱っている。

ブラケット注文と OCO 注文については、まずどこに置くかを決める。FIX は NewOrderList(35=E) に ContingencyType(1385) を定義しており、そこには One Cancels the Other と One Triggers the Other が含まれる。取引所に機能があればそれを使い、なければコアが約定を監視してもう一方のレッグを送ることでエミュレートする。画面のプロセスで決してエミュレートしてはならない。保護のないポジションを抱えたままスリープするノート PC は、まさにブラケットが想定していたケースである。

ポジションは約定から導き、訂正と取消(バスト)も含める。コアは変化のたびにローカルの板でポジションを値洗いし、画面はポジションと損益を 1 フレームに 1 回読み取る。

キー入力から回線上の注文まで、レイテンシをどう計測するのか

スキャルパーが気にする連鎖は二つある。キー入力やクリックから注文がネットワークカードを出るまでと、マーケットデータのパケットから画素が変わるまでだ。連鎖のそれぞれの区間に、専用のタイムスタンプが要る。

  • 入力イベント。OS がタイムスタンプを付ける
  • 画面からゲートウェイへのホップを経て、コマンドがコアに入った時点
  • リスク判定の完了と、FIX メッセージのソケットへの受け渡し
  • パケットがアダプターを出た時点。Linux では、SO_TIMESTAMPING で「ネットワークアダプターによって生成された」送信と受信のタイムスタンプを取得できる
  • 逆方向では、パケットの受信、板の更新、フレームの表示

各区間は、明示した負荷のもとでのパーセンタイルとして報告する。静かな相場での平均ではいけない。計測ポイントを伴わない単独の数字は、ほかのどんな数字とも比べられない。

ラダーはネイティブにすべきか、それとも WebGL と WebAssembly を使った Web にすべきか

MDN は、最も一般的なディスプレイのリフレッシュレートとして 60 Hz を挙げ、120 Hz と 144 Hz も広く使われているとしている。これは 1 フレームあたり、60 Hz で 16.7 ms、144 Hz で 7 ms 未満に当たる。活発な取引所のフィードは 1 フレームのあいだに板を何度も変えうるので、どの技術を使っても、コアがすべての更新を適用し、画面は最新の状態を 1 フレームに 1 回描画する。

  • GPU で描画するネイティブの Rust の画面なら、描画ループと入力を完全に制御でき、ソケットから画素まで一つの言語で通せる。その代償は、サポートするすべての OS 向けのインストーラーとアップデートである
  • ラダーを canvas か WebGL で描き、板のデコードを WebAssembly で行うブラウザの画面なら、何もインストールしなくてよい。MDN によれば、OffscreenCanvas は「ワーカーのコンテキスト内で」描画できる。代償は、タイミングの制御が弱まることと、クリックとコマンドのあいだにガベージコレクションのあるランタイムが挟まることだ
  • デスクトップのシェルに Web UI を載せる方式は、コードベースを一つに保てるが、ブラウザのメモリ使用量とランタイムもそのまま持ち込む

MDN はまた、ほとんどのブラウザがバックグラウンドのタブで requestAnimationFrame を一時停止すると述べている。したがって、動き続けなければならないものはページの中に置けない。Rust のコアの上に載る Web の画面はこの要件を満たすが、注文経路に Web のスタックを置く構成は満たさない。

本番の取引所につなぐ前に、トレーディングターミナルをどうテストするのか

いつ参加を認めるかを決めるのは取引所だ。たとえば CME は、「iLink の注文ルーティングを介して CME Globex で取引する、または CME Group のマーケットデータを処理するすべてのクライアントシステムが、AutoCert+ による認定を受けていることを求める」としている。AutoCert+ は CME の自動テストツールである。CME によれば、認定の対象はメッセージング、処理、異常なメッセージイベントからの復旧であり、機能テストは毎秒 10 トランザクション以下で実行される。合格しても、寄り付きでトレーダーがクリックしているときにラダーがどう振る舞うかはわからない。

失敗のケースは、自前の取引所シミュレーターでリハーサルする。再接続後に注文がギャップフィルで飛ばされる、キャンセルが間に合わない、保留中の訂正が拒否される、約定が取り消される(トレードバスト)、注文が板に残ったままセッションが切れる、トレーダーがクリックしている最中にバーストの途中でフィードにギャップが生じる。そのうえで、記録した FIX のトラフィックとマーケットデータをコアに再生する。同じ入力は、実行のたびに同じ注文状態と同じ板を生まなければならない。そうすれば、トレーダーのバグ報告がそのままテストになる。

トレーディングターミナルは自社で作るべきか、買うべきか、それともライセンスした FIX エンジンを中心に作るべきか

利用する取引所が製品の対応リストに入っていて、業務フローが標準的なら、既製のターミナルを買う。画面が顧客に選ばれる理由の一部になっているとき、利用する取引所が対応していないとき、あるいは注文経路とそのリスクチェックを自社で持たなければならないときは、自社で作る。中間の道は、FIX エンジンや取引所アダプターをライセンスし、残りを作ることだ。

実際にこれを作った会社はどこか、そしてどんな証拠を求めるべきか

「作ったことがある」という言葉は、ベンダーが最初の打ち合わせで証明すべき主張として扱う。次の六つの要求で、確認の大半は済む。

  • ベンダー自身のシミュレーターではなく、取引所の本番環境かテスト環境でのデモ。途中で接続を切り、リカバリ中にラダーと注文一覧がどう振る舞うかを見せてもらう
  • その会社の FIX 接続を認定した取引所。FIX のバージョンまたは取引所独自の方言、日付、そして認定を受けたシステムが、あなたのために作るものと同じかどうか
  • レイテンシの数字をどう取ったか。どの計測ポイントか、ハードウェアとソフトウェアのどちらのタイムスタンプか、どのパーセンタイルか、どんな負荷のもとか、そしてその数字に何が含まれていないか
  • 一件のインシデントを詳しく語ってもらう。たとえば有効な注文がギャップフィルで飛ばされた件や、トレードバストの件だ。画面に何が表示され、取引所が何と言い、その後コードで何が変わったのか
  • 完全なソースコード、ビルド手順とデプロイ手順、相手の所有のまま残るものの一覧、そしてクリーンなマシン上で相手の助けを借りずに行うビルド
  • FIX エンジンの出どころ。自社開発か、オープンソースか、ライセンス品か、そしてどんな条件で使い続けられるのか

amBrain はどこに位置づけられるか

amBrain は、アルメニア・エレバンを拠点に、Rust で低レイテンシのトレーディングプラットフォーム、マッチングエンジン、リアルタイムビディングシステムを構築するソフトウェアエンジニアリング企業である。amBrain は2019年からソフトウェアを作っている。

amBrain はアルゴリズム取引の基盤を構築する。注文執行、マーケットデータ、プレトレードのリスク管理である。トレーディング分野のサービスには、トレーディングターミナルの開発、注文管理システム、FIX プロトコルによる取引所連携が含まれる。

amBrain が作る経路では、二つの数字が計測されている。マーケットデータのレイテンシ 5 ms 未満と、リスクチェックのレイテンシ 1 ms 未満だ。どちらもキー入力から回線までの数字ではない。だから、どのベンダーに対してもそうするように、両方について計測ポイントと負荷を確かめること。

amBrain は Spectre Trade のトレーディングターミナルを構築した。amBrain は MOEX のコロケーションで本番稼働するミニ取引所も構築している。この記事の設計は一般的なものである。どちらのシステムを説明するものでもなく、いずれかが使っているプロトコルの名前も挙げていない。

三つの形態:フルデリバリー、専任チーム、あるいはあなたのチームに入るエンジニア。顧客は、プロダクトとコードの完全な所有権を保持する。ただし、私たちの再利用可能なコンポーネントは除く。

amBrain を含め、どのベンダーとの最初の打ち合わせの前にも、利用する取引所、それぞれが使う FIX のバージョンまたは方言、そして明示した計測ポイント間で必要なレイテンシを書き出しておくこと。

手元に似た設計はありますか?

現在のアーキテクチャと気になっている障害シナリオをお持ちください。30 分で一緒に確認します。