FIX 接続はセッション層で決まる── シーケンス番号と再送をどう扱うか
注文の項目より先に、セッション層の作りが接続の成否を決めます。シーケンス番号・再送・接続検証で実際に問題になる箇所を整理します。

FIX の接続が平常時に安定して動いていても、本当に試されるのは回線が切れた直後です。切断の瞬間に送っていた注文は相手に届いたのか、板に残っている注文は取り消されたのか、再接続したら何を送り直せばよいのか。ここで判断を誤ると、同じ注文を二重に出すか、取り消したつもりの注文が板に残ります。
本稿では、セッション層の基本を扱った前稿の続きとして、流量の上限・切断時の自動取消・再接続での番号の合わせ方・重複の見分け方を整理します。これらの多くは FIX の規格ではなく、対向ごとの接続仕様で決まります。具体例には、公開されている Cboe と Coinbase の FIX 仕様を使います。
FIX のセッション層には、毎秒何件まで送ってよいかという決まりはありません。上限は取引所や証券会社が、それぞれの仕様で定めています。
Cboe Futures Exchange の FIX 仕様では、接続口(ポート)ごとの上限と銘柄ごとの上限が、どちらも既定で毎秒 3,000 件です。上限を超えたときの扱いも書かれており、その時間枠の新規注文はすべて拒否され、訂正は取消として扱われ、取消はそのまま処理されます。
この扱いには、設計上の示唆が 2 つあります。
同じ仕様には、検証用の銘柄では上限が毎秒 1 件になるとも書かれています。本番と同じ速度で負荷試験をしようとすると、検証環境で拒否が続きます。検証の計画を立てる前に、対向の検証環境の上限を確認しておきます。
回線が切れたとき、板に残っている注文をどうするかも対向ごとに違います。多くの取引所は、切断を検知して注文を自動で取り消す機能(Cancel on Disconnect)を持っていますが、設定の場所と範囲がそろっていません。
CancelOrdersOnDisconnect(8013) で接続ごとに指定する。S ならそのセッションの注文、Y ならプロファイル全体の注文が対象Coinbase の仕様には、取引所がまだ受付を返していない注文は、取り消される保証がないとも明記されています。切断の直前に送った注文は、届いて残っているかもしれないし、届いていないかもしれません。自動取消があっても「切断したら板は空」とは言い切れないということです。再接続したあとは、次の節以降で述べる番号の照合と注文状態の照会を、必ず行う前提で組みます。

FIX のシーケンス番号は接続をまたいで続きます。再接続のときに、その番号をどう合わせるかには主に 2 つのやり方があります。
1 つは、Logon で ResetSeqNumFlag(141) を立てて、双方の番号を 1 に戻すやり方です。単純ですが、切断の前後に送られたメッセージの再送は受けられません。Coinbase Exchange はこちらを既定にしており、クライアントから取引所への番号は切断のたびに必ず戻ります。取引所からクライアントへの番号を引き継ぎたい場合は、141=N で Logon し、取引所が返す番号まで再送を要求します。抜けが大きいときは 1,000 件ずつに分けて要求します。
もう 1 つは、FIX 4.4 から Logon に加わった NextExpectedMsgSeqNum(789) を使うやり方です。各自が「相手から次に受け取るはずの番号」を Logon に入れて送り、受け取った側は自分が次に振る番号と比べます。
このやり方では、Logon に付いた番号を見て再送要求(Resend Request)を出してはいけない決まりになっています。再送要求の往復を待たずに抜けを埋められるのが利点ですが、使うかどうかは対向との合意が前提です。片方だけが対応していると、Logon の直後に番号がずれます。
どちらのやり方でも、再接続を即座に繰り返す実装は避けます。番号の不整合で打ち切られた接続は、何度つなぎ直しても同じ理由で切られます。待ち時間を少しずつ延ばし、一定回数で運用担当へ知らせる形にしておきます。
送り直したメッセージには、重複かもしれないことを示す印を付けます。FIX にはこの印が 2 つあり、意味が違います。
PossDupFlag(43) — 同じシーケンス番号のまま送り直したことを示す。再送要求に応えるときなど、セッション層の都合で使う。受け取った側は番号で重複を判定できるPossResend(97) — 以前に別の番号で送った内容を、新しい番号で送り直したことを示す。番号が違うので、セッション層では重複と分からない。判定は業務層が注文 ID などで行う切断の直前に送った注文に応答が無かったとき、業務として送り直すなら使うのは後者です。そのとき注文 ID の ClOrdID(11) は元と同じ値を使います。新しい ID で送れば、取引所から見れば別の注文で、届いていた場合は二重の発注になります。FIX 4.1 以降には、重複した注文を拒否する理由として OrdRejReason(103)=6(Duplicate Order)も用意されています。
ただし、PossResend への対応も対向ごとに違います。Cboe Futures Exchange は PossResend=Y の新規注文に対応しておらず、受け取っても黙って無視します。ClOrdID の重複を判定するのは、その時点で板に残っている注文の範囲だけです。Coinbase Exchange は、ClOrdID を UUID 形式で付け、未約定の注文と重ならないことを求めています。送り直しの印に頼れない対向では、送り直す前に次の節の照会で、届いていたかを確かめるしかありません。
番号が合っても、それはメッセージの抜けが埋まったというだけです。手元の記録と取引所の板が一致しているかは、別に確かめる必要があります。FIX 4.4 には、そのためのメッセージがあります。
35=H) — 注文を 1 件ずつ照会する35=AF) — 条件に合う注文をまとめて照会する。MassStatusReqType(585)=7 ならその接続で出した注文すべて。結果は注文ごとに、ExecType(150) が Order Status の Execution Report で返る再接続後の手順は、番号を合わせる → 応答待ちだった注文と板に残っているはずの注文を照会する → 手元の記録と突き合わせる → 食い違いを解消する → 新規注文を再開する、の順にします。突き合わせが終わるまで新規注文を止めておくのが要点です。先に新規を流すと、食い違いの原因が切断前のものか再開後のものか区別できなくなります。
照会用のメッセージに対応していない対向もあります。その場合は、約定の写しを別の接続で配信するドロップコピーや、取引所の管理画面での確認を手順に組み込みます。どの手段が使えるかは、接続仕様書で事前に確認しておきます。
ここまでの項目は、どれも対向ごとに値が違います。複数の取引所や証券会社に接続する基盤では、対向ごとに次の項目を台帳にまとめ、実装の分岐ではなく設定として持たせます。
NextExpectedMsgSeqNum に対応しているかPossResend に対応しているか、ClOrdID の形式と一意性の範囲35=H・35=AF・ドロップコピーのどれが使えるかこの台帳は、接続検証の確認項目にもなります。対向の検証環境で、上限を超えたとき・切断したとき・送り直したときに、仕様書のとおりの応答が返るかを 1 項目ずつ確かめます。
FIX 接続の流量の上限、切断時の自動取消、再接続での番号の合わせ方、送り直した注文の重複の扱いは、規格ではなく対向の仕様で決まります。Cboe Futures Exchange は毎秒 3,000 件を超えた新規を拒否して取消は通し、PossResend の新規注文は無視します。Coinbase Exchange は切断のたびに番号を戻し、受付前の注文の取消は保証しません。送り直すときは同じ ClOrdID を使い、再接続後は注文の状態を照会して突き合わせるまで新規を止めます。対向ごとの差は台帳にまとめ、設定として扱います。
金融テクノロジー総合研究所では、FIX 接続の設計・実装・接続検証の支援を受託しています。複数の対向をまとめる発注基盤の設計や、既存接続の切断時の挙動の点検からご相談いただけます。FIX 対応の詳細もあわせてご覧ください。ご相談はお問い合わせよりご連絡ください。
NextExpectedMsgSeqNum による番号の合わせ方と、双方の合意が前提であること)MassStatusReqType の値と応答)PossResend の扱いと ClOrdID の一意性の範囲)CancelOrdersOnDisconnect(8013)、番号のリセットと再開、ClOrdID の形式)OrdRejReason=6 による重複注文の拒否)FTL の技術スタックと受託開発の進め方は FTLの技術とは? にまとめています。