Capital Markets and Settlement
The book on the screen had been quietly wrong all along
한국어 원문으로 표시합니다.
목표
스냅숏 한 장과 증분 갱신으로 호가창을 다시 세우고, 순번이 끊긴 자리와 늦게 도착한 줄이 만든 차이를 가격 단계 수로 셉니다. 마지막에는 재동기화 규칙을 넣은 구현이 기준 스냅숏과 한 단계도 어긋나지 않는 것을 확인합니다.
왜 중요한가
시세 화면은 틀려도 아무 소리를 내지 않습니다. 값이 비거나 예외가 나는 것이 아니라 조금 다른 값이 계속 돌기 때문에, 며칠 뒤 주문이 이상하게 나가고 나서야 거슬러 올라가게 됩니다. 스냅숏에 박힌 순번과 증분의 순번을 어떻게 잇느냐, 순번이 끊겼을 때 멈추느냐 계속 얹느냐 하는 두 줄짜리 규칙이 그 차이를 만듭니다. 그리고 이 실습의 계산은 전부 가격을 다룹니다. 가격을 이진 부동소수로 빼는 순간 한 호가 단위를 재는 판정이 조용히 0 이 되므로, 값이 아니라 자료형에서 먼저 지고 들어가는 경우가 무엇인지도 함께 봅니다.
단계
python3로/root/cap/book/data에 네 개 자료 파일을 만듭니다. 생성 스크립트를 그대로 쓰세요.- 증분 스트림의 모양을 세어
/root/cap/book/shape.txt에 아홉 줄로 적습니다. - 시작 스냅숏으로 호가창을 세우고 겹치는 증분을 세어
/root/cap/book/init.json에 적습니다. - 증분을 순번 순서로 얹어
/root/cap/book/naive.json에 호가창과 동작별 건수를 적습니다. - 재생 중 불변식이 깨진 자리를 찾아
/root/cap/book/invariant.txt에 일곱 줄로 적습니다. - 끊긴 구간과 다시 세운 결과의 차이를
/root/cap/book/gap.json에 적습니다. - 늦게 도착한 줄과 그것이 만든 차이를
/root/cap/book/late.json에 적습니다. - 재동기화 규칙을 넣은 최종 호가창을
/root/cap/book/final/book.json에, 품질 보고를/root/cap/book/final/report.json에 남깁니다.
참고
- 피드 형식은
/root/cap/book/data/feed_spec.txt에 적혀 있습니다. 실제 거래소 규격이 아니라 이 실습을 위한 합성 형식입니다. - 세션 상수는
/root/cap/book/data/session.json에 있습니다. 호가 단위, 스냅숏이 찍힌 순번, 시작 스냅숏, 기준 스냅숏이 모두 거기 있으니 숫자를 코드에 박지 마세요. - 가격은
decimal.Decimal로 다룹니다.float로 빼면 한 호가 단위가 정확히 나오지 않아 5단계의 스프레드 판정이 0 으로 떨어집니다. - 어긋난 가격 단계 수는 두 호가창을 각각
(종목, 방향, 가격, 수량)집합으로 만든 뒤 대칭 차집합의 크기로 셉니다. 한 가격의 수량만 다르면 양쪽에서 하나씩 나오므로 2 입니다. updates.jsonl의 줄 순서가 도착 순서입니다. 순번 순서와 다를 수 있습니다.- 흔한 실수 1: 수량 0 을 값으로 저장하는 것. 0 은 값이 아니라 그 가격 단계를 지우라는 동작입니다.
- 흔한 실수 2: 6단계에서 다시 세울 스냅숏을 마지막 것으로 고르는 것. 끊긴 자리를 메울 수 있는 것 중 가장 이른 검사점을 씁니다.
- 표준 문서: FIX Trading Community, FIXimate, decimal 모듈.
스냅숏과 증분 스트림 만들기
python3 로 /root/cap/book/data 에 snapshots.jsonl·updates.jsonl·session.json·feed_spec.txt 네 개를 만듭니다. 난수를 쓰지 않는 생성 스크립트를 그대로 쓰세요.
폐쇄망에는 내려받을 시세 캡처가 없으니 자료부터 직접 만듭니다. 난수를 쓰지 않아야 누가 몇 번을 돌려도 같은 자료가 나오고, 서로의 재구성 결과를 대조할 수 있습니다. 채점기는 자료를 표준형으로 바꿔 지문을 대조하므로 자료를 손으로 고치면 뒤 단계가 전부 막힙니다.
증분 스트림의 모양 세기
/root/cap/book/shape.txt 에 updates=, snapshots=, symbols=, seq_min=, seq_max=, seq_missing=, delete_ops=, upsert_ops=, out_of_order= 아홉 줄을 적으세요.
seq_missing 은 seq_min 부터 seq_max 까지의 정수 중 파일에 없는 것의 개수입니다. delete_ops 는 수량이 0 인 줄, upsert_ops 는 0 이 아닌 줄입니다. 추가와 변경을 여기서 가르려 하지 마세요 — 그 둘은 호가창을 들고 있어야 구분됩니다. out_of_order 는 앞선 줄보다 순번이 작은 줄의 수입니다.
스냅숏으로 호가창 세우고 겹치는 증분 버리기
session.json 의 start_checkpoint 검사점 스냅숏으로 종목마다 호가창을 세우고, /root/cap/book/init.json 에 종목별로 snap_id·last_seq·bid_levels·ask_levels·best_bid·best_ask·discarded·applicable·first_applied_seq 아홉 값을 적으세요.
스냅숏은 찍히는 동안에도 증분이 계속 나가기 때문에 last_seq 를 함께 싣습니다. 그 값 이하인 증분은 이미 스냅숏에 들어 있으니 버려야 합니다. discarded 는 그렇게 버릴 줄의 수, applicable 은 남는 줄의 수이고, 둘 다 그 종목 것만 셉니다. best_bid 는 가장 높은 매수가, best_ask 는 가장 낮은 매도가입니다.
증분을 얹어 호가창 재구성하기
3단계의 호가창에 순번이 start_checkpoint 보다 큰 증분을 순번 오름차순으로 전부 얹고, /root/cap/book/naive.json 에 book 과 ops 두 열쇠를 적으세요. ops 에는 add·change·delete·delete_missing·applied 다섯 수를 넣습니다.
동작은 메시지가 아니라 지금 들고 있는 호가창이 정합니다. 수량 0 이면 삭제인데, 지우려는 가격이 호가창에 없을 수도 있습니다. 그것을 오류로 처리하지 말고 delete_missing 으로 세어 두세요 — 그 숫자 자체가 못 받은 증분이 있다는 신호입니다. 끊긴 순번은 이 단계에서는 그냥 지나칩니다.
교차한 호가창이 나오는 순간 찾기
4단계와 같은 재생을 하면서 갱신 한 건마다 그 종목의 불변식을 보고, /root/cap/book/invariant.txt 에 first_cross_seq=, first_cross_symbol=, first_cross_bid=, first_cross_ask=, crossed_states=, tick_spread_states=, zero_qty_levels= 일곱 줄을 적으세요.
교차는 최우선 매수가가 최우선 매도가보다 크거나 같은 상태입니다. crossed_states 는 적용 직후 그 종목이 교차해 있던 횟수, tick_spread_states 는 교차하지 않았고 스프레드가 정확히 한 호가 단위였던 횟수입니다. 두 번째 값이 0 으로 나온다면 계산이 아니라 자료형을 의심하세요 — 십진 소수를 이진 부동소수로 빼면 한 호가 단위가 정확히 나오지 않습니다.
끊긴 자리에서 계속 얹은 것과 다시 세운 것 견주기
/root/cap/book/gap.json 에 gaps(끊긴 구간 목록), resync(다시 세운 자리 목록), level_mismatch(종목별 어긋난 가격 단계 수), level_mismatch_total 네 열쇠를 적으세요. gaps 항목은 from_seq·to_seq·count, resync 항목은 detected_after_seq·next_seq·checkpoint 입니다.
순번이 끊겼다는 것은 못 받은 변경이 있다는 뜻이고, 그것이 무엇이었는지는 알 방법이 없습니다. 그래서 증분을 더 얹지 말고 다음 스냅숏에서 다시 세웁니다. 다시 세울 검사점은 끊긴 직전까지 적용한 순번 이상인 것 중 가장 이른 것이고, 그 스냅숏의 last_seq 이하인 증분은 또 버려야 합니다. 두 목록은 순번이 작은 쪽부터 적습니다.
늦게 도착한 줄을 그대로 얹으면 어떻게 되는가
/root/cap/book/late.json 에 late_seqs(늦게 실린 줄의 순번 목록), harmful_seqs(그중 결과를 바꾸는 것), level_mismatch(종목별 어긋난 가격 단계 수), level_mismatch_total 네 열쇠를 적으세요. 비교는 4단계의 순번 순서 재생과, 같은 조건에서 파일에 실린 순서 그대로 얹은 재생 사이에서 합니다.
늦게 실린 줄이라고 전부 해로운 것은 아닙니다. 같은 종목·방향·가격을 더 큰 순번이 먼저 덮어쓴 경우에만 옛 값이 되살아납니다. 그렇지 않은 줄은 순서가 바뀌어도 결과가 같습니다. 두 재생 모두 끊긴 자리는 그냥 지나치고, 시작 검사점 이하인 줄은 버리는 것까지 같게 두세요 — 그래야 차이가 도착 순서 하나에서만 나옵니다.
재동기화 규칙을 넣은 최종 구현으로 기준과 대조하기
두 규칙(순번 순서로 얹기, 끊기면 다음 스냅숏에서 다시 세우기)을 모두 넣은 구현으로 전체 스트림을 돌려 /root/cap/book/final/book.json 에 최종 호가창을, /root/cap/book/final/report.json 에 applied·discarded·resync_count·resync_checkpoints·gap_count·lost_seqs·late_seqs·naive_vs_reference·final_vs_reference·crossed_symbols_final·reference_checkpoint 열한 값을 적으세요.
기준 호가창은 session.json 의 reference_checkpoint 검사점 스냅숏입니다. naive_vs_reference 는 4단계의 호가창과 기준 사이, final_vs_reference 는 이번 호가창과 기준 사이의 어긋난 가격 단계 수이고 뒤엣것은 0 이어야 합니다. discarded 는 순번이 이미 지나가서 버린 줄 전부입니다 — 처음 겹친 구간과 다시 세운 뒤 겹친 구간을 함께 셉니다. 앞 단계 산출물은 지우지 말고 새 디렉터리에 남기세요.