注文イベントから最終状態を再構成する
目標
注文イベントログだけを使って、注文ごとの最終状態を再構成し、OMSの状態フィールドとずれた注文を見つけ出し、並べ替えの時計によって結論が入れ替わる注文を拾い出したうえで、順序に左右されない規則で、その揺らぎをなくせるようになります。
なぜ重要なのか
証券システムでは、「注文の状態」は保存された値ではなく、複数のイベントを畳み込んで作った結果です。畳み込みの規則が明示されていなければ、画面と通知と清算バッチが、それぞれ異なる規則で畳み込み、そのため、互いに異なる答えを出します。
このラボが強制する4つは、すべて現場の規則です。状態フィールドを信じません。処理器がイベントを一度でも取りこぼせば、その注文の状態フィールドは永遠に間違い、イベントは再び流れてきません。取消要求と取消確定を区別します。要求の時点で状態を変えると、要求のあとに付いた約定がまるごと消えます。同じ約定が2回来ることを正常として扱います。再送はなくならないので、受け取る側が除外する必要があり、除外しなければ、数量が静かに膨らみます。順序に答えが左右されないようにします。順序を正確に合わせるより、順序と無関係な規則を使うほうが、常によいのです。
今回の事案は次のとおりです。清算チームから「約定通知は受け取ったのに、OMS画面で取消になっている注文がある」と連絡が来ました。対象は2026-08-27の通常取引時間の注文900件で、手元にあるのは、イベントログ1ファイルとOMSの状態スナップショット1つです。
再生の規則は次のように書きます。イベントを時刻の昇順(同じならseqの昇順)で読みながら、newは状態をreceivedにして注文数量を決めます。ackはopen、rejectはrejected、cancelledはcancelledに変えます。replaceは注文数量だけを変え、状態には触れません。cancel_reqは何も変えません。fillとpartial_fillは、累計約定に自分の数量を足したうえで、累計が注文数量以上ならfilled、そうでなければpartially_filledにします。
ステップ
/root/cap/orderを作成し、正解の生成スクリプトをそのまま実行して、/root/cap/order/events.jsonlと/root/cap/order/oms_status.csvを作ってください。乱数シードを変えると、採点値とずれます。/root/cap/order/shape.txtに10行書いてください。events=、orders=、そして8種類のイベントごとに、type_new=のように、type_<종류>=を1行ずつです(プレースホルダーはイベントの種類です)。- イベントを
ts_exchの昇順(同じならseq)で再生して、/root/cap/order/final_exch.csvを作ってください。ヘッダー行はorder_id,state,cum_qty,order_qtyで、注文1つにつき1行、order_idの昇順です。 final_exch.csvとoms_status.csvを突き合わせてください。/root/cap/order/omsdiff.txtにstatus_mismatch=、qty_mismatch=、any_mismatch=、qty_only_mismatch=の4行を、/root/cap/order/omsdiff.csvに、ずれた注文の一覧をorder_id,replay_state,oms_status,replay_cum_qty,oms_filled_qtyのヘッダー行とともに書いてください。- 同じログを
ts_recvで並べ替えて/root/cap/order/final_recv.csvを作り、/root/cap/order/clock.txtにexch_filled=、recv_filled=、flipped=を、/root/cap/order/flipped.csvにorder_id,exch_state,recv_stateのヘッダー行で、入れ替わった注文を書いてください。 - 1つの注文の中で同じ
exec_idが2回出た件を探して、/root/cap/order/dup.csvにorder_id,exec_id,dup_qty,dup_amount_krwで、/root/cap/order/dup.txtにdup_orders=、over_orders=、dup_qty_total=、dup_amount_total=で書いてください。 - 補正規則を2つ入れて、
/root/cap/order/final_fixed.csvを作ってください。exec_idで重複した約定を除外し、再生が終わったあとで累計が注文数量以上なら、状態をfilledに確定させます。/root/cap/order/fixed.txtにflip_before=、flip_after=、final_filled=、final_cancelled=を書いてください。 /root/cap/order/report.mdにレポートを書いてください。## 무슨 일이 있었나、## 근거、## 돈으로 얼마인가、## 왜 이런 일이 생기나、## 무엇을 고쳐야 하나の5つの節が必要です(見出しは韓国語で、順に「何があったか」「根拠」「金額でいくらか」「なぜこのようなことが起きるか」「何を直すべきか」という意味です)。
参考
- 金額はすべて整数のウォン単位です。実数で扱うと、合計が微妙にずれて採点で落ちます。
- 並べ替えの基準を1つしか使わないと、同じ時刻の2つのイベントで、毎回異なる答えが出ます。常に
(시각, seq)の2つで並べ替えてください(プレースホルダーは時刻です)。 - よくある間違い1: ステップ4で、状態の文字列だけを比較してしまいます。そうすると27件で止まり、状態は合っているのに数量が抜けている40件を、まるごと見逃します。
- よくある間違い2: ステップ6で、累計が注文数量を超えたものだけを数えてしまいます。超えていない重複のほうが危険です。何の警報も鳴らないからです。
- よくある間違い3: ステップ7で、重複だけを除外して、状態の判定規則をそのままにしてしまいます。そうすると、入れ替わる40件がそのまま残ります。
イベントログとOMSスナップショットを作る
/root/cap/orderを作成し、正解の生成スクリプトをそのまま実行して、/root/cap/order/events.jsonlと/root/cap/order/oms_status.csvを作ってください。乱数シードを変えると、採点値とずれます。
python3で生成スクリプトをそのまま実行すれば済みます。乱数シードを変えると採点値とずれるので、そのままにしてください。
イベントストリームの形を数える
/root/cap/order/shape.txtに10行書いてください。events=、orders=、そして8種類のイベントごとに、type_new=のように、type_<종류>=を1行ずつです(プレースホルダーはイベントの種類です)。
typeごとに数えれば済みます。ackが900ではない理由を説明できるようになれば、次のステップが楽になります。
取引所時刻で再生する
イベントをts_exchの昇順(同じならseq)で再生して、/root/cap/order/final_exch.csvを作ってください。ヘッダー行はorder_id,state,cum_qty,order_qtyで、注文1つにつき1行、order_idの昇順です。
イベントをts_exchの昇順、同じならseqの昇順に並べて、順に畳み込みます。cancel_reqは状態を変えないということだけ覚えておけば、残りは案内に書かれたとおりです。
OMSの状態フィールドと突き合わせる
final_exch.csvとoms_status.csvを突き合わせてください。/root/cap/order/omsdiff.txtにstatus_mismatch=、qty_mismatch=、any_mismatch=、qty_only_mismatch=の4行を、/root/cap/order/omsdiff.csvに、ずれた注文の一覧をorder_id,replay_state,oms_status,replay_cum_qty,oms_filled_qtyのヘッダー行とともに書いてください。
2回比較してください。状態の文字列だけを比較したときと、約定数量まで比較したときで、数字が違います。その差が、このステップの要点です。
受信時刻でもう一度再生して比較する
同じログをts_recvで並べ替えて/root/cap/order/final_recv.csvを作り、/root/cap/order/clock.txtにexch_filled=、recv_filled=、flipped=を、/root/cap/order/flipped.csvにorder_id,exch_state,recv_stateのヘッダー行で、入れ替わった注文を書いてください。
ステップ3と同じ関数に、並べ替えの基準だけをts_recvに変えて入れれば済みます。2つの結果のstateを注文ごとに比較して、違うものだけを集めてください。
同じ約定が2回入ってきた件を探す
1つの注文の中で同じexec_idが2回出た件を探して、/root/cap/order/dup.csvにorder_id,exec_id,dup_qty,dup_amount_krwで、/root/cap/order/dup.txtにdup_orders=、over_orders=、dup_qty_total=、dup_amount_total=で書いてください。
1つの注文の中で、同じexec_idが2回出ていないかを見てください。累計が注文数量を超えたものだけを数えると、半分も見つかりません。
順序に左右されない規則を入れる
補正規則を2つ入れて、/root/cap/order/final_fixed.csvを作ってください。exec_idで重複した約定を除外し、再生が終わったあとで累計が注文数量以上なら、状態をfilledに確定させます。/root/cap/order/fixed.txtにflip_before=、flip_after=、final_filled=、final_cancelled=を書いてください。
2つ入れます。exec_idで重複した約定を除外することと、再生が終わったあとで累計が注文数量以上なら、状態を約定に確定させること。そのあと、2つの時計でそれぞれ再生して、結果が同じかどうかを数えてください。
調査レポートを書く
/root/cap/order/report.mdにレポートを書いてください。## 무슨 일이 있었나、## 근거、## 돈으로 얼마인가、## 왜 이런 일이 생기나、## 무엇을 고쳐야 하나の5つの節が必要です(見出しは韓国語で、順に「何があったか」「根拠」「金額でいくらか」「なぜこのようなことが起きるか」「何を直すべきか」という意味です)。
5つの節が必要です。原因の節には時計と順序の話が、対処の節には重複除外の話が、入っている必要があります。数字は本文にそのまま書いてください。