TT Lab
Get started
Learn Learning paths Courses

Capital Markets and Settlement

Reconstructing Final State From Order Events

Continue in TT Lab

Goal

You reconstruct each order's final state from the order event log alone, find the orders that disagree with the OMS status field, pick out the orders whose conclusion flips depending on the sort clock, and then remove that wobble with a rule that does not depend on order.

Why it matters

In a securities system, "order state" is not a stored value but the result of folding several events. If the folding rule is not stated explicitly, the screen, the notification, and the settlement batch each fold with a different rule and therefore give different answers.

The four things this lab enforces are all rules from the field. Do not trust the status field — once the processor misses an event, that order's status field is wrong forever, and the event does not flow in again. Distinguish a cancel request from a cancel confirmation — if you change the state at request time, a fill that arrived after the request vanishes entirely. Treat the same fill arriving twice as normal — retransmissions never go away, so the receiver has to filter them, and if it does not, the quantity inflates silently. Make the answer independent of order — using a rule that does not depend on order is always better than trying to get the order exactly right.

The incident is this. The settlement team reported that "there are orders for which we received the execution notice but the OMS screen shows them as canceled." The target is 900 orders from the regular session on 2026-08-27, and what we have is one event log file and one OMS status snapshot.

The replay rules are as follows. Read the events in ascending time order (ties broken by ascending seq). new sets the state to received and fixes the order quantity. ack changes it to open, reject to rejected, and cancelled to cancelled. replace changes only the order quantity and leaves the state alone. cancel_req changes nothing. fill and partial_fill add their own quantity to the cumulative fills, and then the state becomes filled if the cumulative is at least the order quantity, and partially_filled otherwise.

Steps

  1. Create /root/cap/order, then run the answer key's generator as is to produce /root/cap/order/events.jsonl and /root/cap/order/oms_status.csv. If you change the random seed, the values will not match the grading values.
  2. Write ten lines to /root/cap/order/shape.txt: events=, orders=, and then one line per each of the eight event types, like type_new=, in the form type_<종류>= (the placeholder is the event type).
  3. Replay the events in ascending ts_exch order (ties broken by seq) to produce /root/cap/order/final_exch.csv. The header is order_id,state,cum_qty,order_qty, with one line per order in ascending order_id order.
  4. Compare final_exch.csv with oms_status.csv. Write four lines, status_mismatch=, qty_mismatch=, any_mismatch=, qty_only_mismatch=, to /root/cap/order/omsdiff.txt, and write the list of mismatched orders to /root/cap/order/omsdiff.csv with the header order_id,replay_state,oms_status,replay_cum_qty,oms_filled_qty.
  5. Sort the same log by ts_recv to produce /root/cap/order/final_recv.csv. Write exch_filled=, recv_filled=, flipped= to /root/cap/order/clock.txt, and write the flipped orders to /root/cap/order/flipped.csv with the header order_id,exch_state,recv_state.
  6. Find the cases where the same exec_id appears twice within one order, and write them to /root/cap/order/dup.csv as order_id,exec_id,dup_qty,dup_amount_krw, and to /root/cap/order/dup.txt as dup_orders=, over_orders=, dup_qty_total=, dup_amount_total=.
  7. Add two correction rules to produce /root/cap/order/final_fixed.csv — filter duplicate fills by exec_id, and pin the state to filled when, after replay finishes, the cumulative is at least the order quantity. Write flip_before=, flip_after=, final_filled=, final_cancelled= to /root/cap/order/fixed.txt.
  8. Write a report in /root/cap/order/report.md. It needs five sections: ## 무슨 일이 있었나, ## 근거, ## 돈으로 얼마인가, ## 왜 이런 일이 생기나, ## 무엇을 고쳐야 하나 (the five Korean section titles mean: what happened, the evidence, what it amounts to in money, why this happens, and what to fix).

Notes

Generate the event log and the OMS snapshot

Create /root/cap/order, then run the answer key's generator as is to produce /root/cap/order/events.jsonl and /root/cap/order/oms_status.csv. If you change the random seed, the values will not match the grading values.

Just run the generator with python3 as it is. If you change the random seed, the values will not match the grading values, so leave it alone.

Count the shape of the event stream

Write ten lines to /root/cap/order/shape.txt: events=, orders=, and then one line per each of the eight event types, like type_new=, in the form type_<종류>= (the placeholder is the event type).

Count by type. If you can explain why ack is not 900, the next step gets easier.

Replay by exchange time

Replay the events in ascending ts_exch order (ties broken by seq) to produce /root/cap/order/final_exch.csv. The header is order_id,state,cum_qty,order_qty, with one line per order in ascending order_id order.

Sort the events by ts_exch ascending, ties broken by seq ascending, and fold them in order. Just remember that cancel_req does not change the state, and the rest is exactly as written in the instructions.

Compare with the OMS status field

Compare final_exch.csv with oms_status.csv. Write four lines, status_mismatch=, qty_mismatch=, any_mismatch=, qty_only_mismatch=, to /root/cap/order/omsdiff.txt, and write the list of mismatched orders to /root/cap/order/omsdiff.csv with the header order_id,replay_state,oms_status,replay_cum_qty,oms_filled_qty.

Compare twice. The numbers differ between comparing only the status strings and comparing the executed quantity as well. That difference is the point of this step.

Replay again by receive time and compare

Sort the same log by ts_recv to produce /root/cap/order/final_recv.csv. Write exch_filled=, recv_filled=, flipped= to /root/cap/order/clock.txt, and write the flipped orders to /root/cap/order/flipped.csv with the header order_id,exch_state,recv_state.

Feed the same function as in step 3 and change only the sort key to ts_recv. Compare the state of the two results order by order and collect only the ones that differ.

Find the fills that came in twice

Find the cases where the same exec_id appears twice within one order, and write them to /root/cap/order/dup.csv as order_id,exec_id,dup_qty,dup_amount_krw, and to /root/cap/order/dup.txt as dup_orders=, over_orders=, dup_qty_total=, dup_amount_total=.

Look at whether the same exec_id appears twice within one order. If you count only the orders whose cumulative exceeds the order quantity, you will not find even half of them.

Add a rule that does not depend on order

Add two correction rules to produce /root/cap/order/final_fixed.csv — filter duplicate fills by exec_id, and pin the state to filled when, after replay finishes, the cumulative is at least the order quantity. Write flip_before=, flip_after=, final_filled=, final_cancelled= to /root/cap/order/fixed.txt.

Add two things: filter duplicate fills by exec_id, and pin the state to filled when, after replay finishes, the cumulative is at least the order quantity. Then replay with each of the two clocks and count whether the results are the same.

Write the investigation report

Write a report in /root/cap/order/report.md. It needs five sections: ## 무슨 일이 있었나, ## 근거, ## 돈으로 얼마인가, ## 왜 이런 일이 생기나, ## 무엇을 고쳐야 하나 (the five Korean section titles mean: what happened, the evidence, what it amounts to in money, why this happens, and what to fix).

It needs five sections. The cause section must cover the clock and ordering, and the remediation section must cover duplicate removal. Write the numbers into the body as they are.