Capital Markets and Settlement
Reconstructing Final State From Order Events
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
- Create
/root/cap/order, then run the answer key's generator as is to produce/root/cap/order/events.jsonland/root/cap/order/oms_status.csv. If you change the random seed, the values will not match the grading values. - Write ten lines to
/root/cap/order/shape.txt:events=,orders=, and then one line per each of the eight event types, liketype_new=, in the formtype_<종류>=(the placeholder is the event type). - Replay the events in ascending
ts_exchorder (ties broken byseq) to produce/root/cap/order/final_exch.csv. The header isorder_id,state,cum_qty,order_qty, with one line per order in ascendingorder_idorder. - Compare
final_exch.csvwithoms_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.csvwith the headerorder_id,replay_state,oms_status,replay_cum_qty,oms_filled_qty. - Sort the same log by
ts_recvto produce/root/cap/order/final_recv.csv. Writeexch_filled=,recv_filled=,flipped=to/root/cap/order/clock.txt, and write the flipped orders to/root/cap/order/flipped.csvwith the headerorder_id,exch_state,recv_state. - Find the cases where the same
exec_idappears twice within one order, and write them to/root/cap/order/dup.csvasorder_id,exec_id,dup_qty,dup_amount_krw, and to/root/cap/order/dup.txtasdup_orders=,over_orders=,dup_qty_total=,dup_amount_total=. - Add two correction rules to produce
/root/cap/order/final_fixed.csv— filter duplicate fills byexec_id, and pin the state tofilledwhen, after replay finishes, the cumulative is at least the order quantity. Writeflip_before=,flip_after=,final_filled=,final_cancelled=to/root/cap/order/fixed.txt. - 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
- All amounts are integers in won. If you treat them as floating-point numbers, the totals drift slightly and fail grading.
- If you use only one sort key, two events at the same time give a different answer each time. Always sort by the two keys
(시각, seq)(the placeholder is the timestamp). - Common mistake 1: comparing only the status strings in step 4. Then you stop at 27 and miss the 40 orders whose state is right but whose quantity is missing.
- Common mistake 2: counting only the cases where the cumulative exceeds the order quantity in step 6. Duplicates that do not exceed it are more dangerous — because they set off no alert at all.
- Common mistake 3: filtering only the duplicates in step 7 and leaving the state rule as it was. Then the 40 flipping orders remain.
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.