자본시장과 결제 · 주문의 생애주기와 시계 · 실습
주문 이벤트로 최종 상태 재구성하기
목표
주문 이벤트 로그만 가지고 주문별 최종 상태를 재구성하고, OMS 상태 필드와 어긋난 주문을 찾아내고, 정렬 시계에 따라 결론이 뒤집히는 주문을 집어낸 뒤, 순서에 좌우되지 않는 규칙으로 그 흔들림을 없앨 수 있게 됩니다.
왜 중요한가
증권 시스템에서 "주문 상태" 는 저장된 값이 아니라 이벤트 여러 개를 접어 만든 결과입니다. 접는 규칙이 명시돼 있지 않으면 화면과 통보와 정산 배치가 각자 다른 규칙으로 접고, 그래서 서로 다른 답을 냅니다.
이 실습이 강제하는 네 가지는 전부 현장의 규칙입니다. 상태 필드를 믿지 않는다 — 처리기가 이벤트를 한 번 놓치면 그 주문의 상태 필드는 영원히 틀리고, 이벤트는 다시 흘러오지 않습니다. 취소 요청과 취소 확정을 구분한다 — 요청 시점에 상태를 바꾸면 요청 뒤에 붙은 체결이 통째로 사라집니다. 같은 체결이 두 번 오는 것을 정상으로 다룬다 — 재전송은 없어지지 않으므로 받는 쪽이 걸러야 하고, 거르지 않으면 수량이 조용히 부풉니다. 순서에 답이 좌우되지 않게 만든다 — 순서를 정확히 맞히는 것보다 순서와 무관한 규칙을 쓰는 편이 언제나 낫습니다.
이번 사건은 이렇습니다. 정산팀이 "체결 통보는 받았는데 OMS 화면에는 취소로 떠 있는 주문이 있다" 고 알려 왔습니다. 대상은 2026-08-27 정규장의 주문 900건이고, 우리가 가진 것은 이벤트 로그 한 파일과 OMS 상태 스냅샷 하나입니다.
재생 규칙은 이렇게 씁니다. 이벤트를 시각 오름차순(같으면 seq 오름차순)으로 읽으면서, new 는 상태를 received 로 하고 주문 수량을 정합니다. ack 는 open, reject 는 rejected, cancelled 는 cancelled 로 바꿉니다. replace 는 주문 수량만 바꾸고 상태는 건드리지 않습니다. cancel_req 는 아무것도 바꾸지 않습니다. fill 과 partial_fill 은 누적 체결에 자기 수량을 더한 뒤, 누적이 주문 수량 이상이면 filled, 아니면 partially_filled 로 합니다.
단계
1. /root/cap/order 를 만들고 정답지의 생성기를 그대로 돌려 /root/cap/order/events.jsonl 과 /root/cap/order/oms_status.csv 를 만드세요. 난수 씨앗을 바꾸면 채점 값과 어긋납니다.
2. /root/cap/order/shape.txt 에 열 줄을 적으세요. events=, orders=, 그리고 여덟 가지 이벤트 종류마다 type_new= 처럼 type_<종류>= 한 줄씩입니다.
3. 이벤트를 ts_exch 오름차순(같으면 seq)으로 재생해 /root/cap/order/final_exch.csv 를 만드세요. 머리글은 order_id,state,cum_qty,order_qty 이고 주문 하나에 한 줄씩 order_id 오름차순입니다.
4. final_exch.csv 와 oms_status.csv 를 대조하세요. /root/cap/order/omsdiff.txt 에 status_mismatch=, qty_mismatch=, any_mismatch=, qty_only_mismatch= 네 줄을, /root/cap/order/omsdiff.csv 에 어긋난 주문 목록을 order_id,replay_state,oms_status,replay_cum_qty,oms_filled_qty 머리글과 함께 적습니다.
5. 같은 로그를 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 머리글로 뒤집힌 주문을 적으세요.
6. 한 주문 안에서 같은 exec_id 가 두 번 나온 건을 찾아 /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= 로 적으세요.
7. 보정 규칙 두 가지를 넣어 /root/cap/order/final_fixed.csv 를 만드세요 — exec_id 로 중복 체결을 거르고, 재생이 끝난 뒤 누적이 주문 수량 이상이면 상태를 filled 로 못 박습니다. /root/cap/order/fixed.txt 에 flip_before=, flip_after=, final_filled=, final_cancelled= 를 적습니다.
8. /root/cap/order/report.md 에 보고서를 쓰세요. ## 무슨 일이 있었나, ## 근거, ## 돈으로 얼마인가, ## 왜 이런 일이 생기나, ## 무엇을 고쳐야 하나 다섯 절이 필요합니다.
참고
- 금액은 전부 정수 원 단위입니다. 실수로 다루면 합계가 미세하게 어긋나 채점에서 떨어집니다.
- 정렬 기준을 하나만 쓰면 같은 시각의 두 이벤트에서 매번 다른 답이 나옵니다. 항상
(시각, seq)두 개로 정렬하세요. - 흔한 실수 1: 4단계에서 상태 문자열만 비교하는 것. 그러면 27건에서 멈추고, 상태는 맞는데 수량이 빠진 40건을 통째로 놓칩니다.
- 흔한 실수 2: 6단계에서 누적이 주문 수량을 넘긴 것만 세는 것. 넘지 않은 중복이 더 위험합니다 — 아무 경보도 울리지 않기 때문입니다.
- 흔한 실수 3: 7단계에서 중복만 걸러 놓고 상태 판정 규칙을 그대로 두는 것. 그러면 뒤집히는 40건이 그대로 남습니다.
단계 8개
- 이벤트 로그와 OMS 스냅샷 만들기
- 이벤트 스트림의 모양 세기
- 거래소 시각으로 재생하기
- OMS 상태 필드와 대조하기
- 수신 시각으로 다시 재생해 비교하기
- 같은 체결이 두 번 들어온 건 찾기
- 순서에 좌우되지 않는 규칙 넣기
- 조사 보고서 쓰기