LabHub
배우기 러닝패스 코스

자본시장과 결제 · 체결 취소와 정정, 그리고 장부 상쇄 · 이론

장부는 지워서 고치지 않는다

LabHub 에서 이어서 보기

한 줄 요약

이미 나간 체결은 잘못됐다고 지우는 것이 아니라 되돌리는 기록을 더해 고친다. 자리는 더 먹지만, 몇 달 뒤에 누가 물어도 "왜 이 숫자가 이렇게 됐는가" 를 읽어 낼 수 있다.

왜 이게 필요했나

체결은 끝이 아니다. 가격을 잘못 넣었거나 상대방 확인이 안 되면 그 체결은 취소되고, 값이 조금 틀렸을 뿐이면 정정된다. 문제는 그 다음이다. 체결 원장은 이미 여러 곳으로 흘러 나갔다. 포지션 보고가 나갔고, 위험 한도 계산이 돌았고, 손익이 찍혔다. 그 상태에서 원장의 한 줄을 조용히 고쳐 쓰면 어제 나간 보고와 오늘 보는 원장이 어긋나는데, 어긋난 이유가 원장 어디에도 없다.

그래서 금융 장부는 뒤에 쓴다. 취소는 원 기록을 남겨 둔 채 부호가 반대인 상쇄 기록을 한 줄 더하는 것이고, 정정은 원 기록을 상쇄한 뒤 고쳐진 값으로 다시 한 줄 쓰는 것이다. 회계에서 오래 쓰던 방식이고, [FIX 프로토콜](https://www.fixtrading.org/standards/)의 체결 보고에도 취소·정정을 알리는 자리가 따로 있다. 필드의 뜻은 [Fiximate](https://fiximate.fixtrading.org/)에서 확인할 수 있다. 취소를 몇 분 안에 신청해야 하는지 같은 구체적 절차는 시장마다 다르므로, 이 실습에서는 자료로 주는 규칙 파일을 이 실습의 가정으로 삼는다.

어떻게 동작하나

먼저 통지가 무엇을 가리키는지 확인한다. 취소·정정 통지에는 원 체결의 식별자가 들어 있고, 그 식별자가 원장에 없으면 아무 일도 하면 안 된다. 여기서 "비슷한 체결을 찾아서 적용" 하는 구현이 가장 위험하다. 엉뚱한 체결을 되돌리면 잘못된 한 건이 두 건이 된다.

다음은 같은 통지를 두 번 받는 경우다. 재전송은 정상적인 일이다. 통지 식별자를 기억하지 않고 받은 대로 상쇄하면 한 번 취소된 체결이 두 번 상쇄되어 포지션이 반대로 넘어간다. 같은 입력을 몇 번 넣어도 결과가 같아야 한다는 것, 그것이 멱등이다.

세 번째는 이미 취소된 체결에 다시 오는 통지다. 재취소도, 취소된 체결에 대한 가격 정정도 받아들이면 안 된다. 그러려면 처리기가 "지금까지 무엇을 취소했는가" 를 들고 있어야 한다.

정정은 취소보다 손이 많이 간다. 가격만 바뀔 수도, 수량만 바뀔 수도 있어서 통지에 없는 값은 원래 값을 그대로 써야 하고, 바뀐 값에 따라 체결금액·평균단가·포지션이 함께 움직인다. 금액은 [decimal](https://docs.python.org/3/library/decimal.html)로 다루고 반올림 방식을 정해 둔다. 부동소수로 하면 정정 한 번에 마지막 자리가 흔들려, 고친 자리가 아닌 곳이 어긋난 것처럼 보인다.

마지막이 늦게 온 취소다. 포지션 보고가 나간 뒤에 도착한 통지는 장부만 고쳐서 끝나지 않는다. 이미 나간 보고가 틀린 것이 되므로 재보고 대상이 된다. 그런데 모든 늦은 통지가 포지션을 움직이는 것도 아니다. 가격만 바꾸는 정정은 수량을 건드리지 않아 포지션은 그대로고 금액만 달라진다. 이 둘을 갈라 놓지 않으면 재보고 목록이 쓸데없이 길어지고, 길어진 목록은 아무도 보지 않는다.

현장에서 만나는 모습

한 번은 야간에 도착한 취소 통지를 배치가 두 번 읽어 포지션이 하루 종일 반대 부호로 찍힌 적이 있다. 원인은 통지 식별자를 기록하지 않은 것 하나였다. 더 나빴던 것은 그 배치가 원 기록을 덮어쓰고 있어서, 무엇이 언제 사라졌는지 알아내는 데 이틀이 걸렸다는 점이다.

또 하나는 포지션 대사였다. 재계산한 포지션과 이미 나간 보고가 종목마다 조금씩 달랐는데, 차이의 대부분은 늦게 온 취소로 설명됐고 한 종목만 설명되지 않았다. 설명되지 않는 그 한 종목이 진짜 결함이었다. 차이를 뭉뚱그려 "대사 불일치" 라고 적었다면 그 한 건은 묻혔을 것이다.

세 번째로 자주 만나는 것은 추적할 수 없는 장부다. 상쇄와 재기장을 제대로 하고도, 그 줄이 어느 통지에서 나왔는지를 적어 두지 않으면 몇 주 뒤에 아무도 설명하지 못한다. 그때 나오는 질문은 늘 같다 — 이 종목의 수량이 왜 이렇게 됐는가. 장부의 줄마다 그 줄을 만든 통지의 식별자와 사유를 달아 두면 그 질문은 한 번의 조회로 끝난다. 사유를 달 때 한 가지 주의할 것이 있다. 재전송된 사본에도 사유가 붙어 있지만, 장부에 남겨야 하는 것은 실제로 적용된 통지의 사유다. 통지 스트림에서 마지막 것을 집어 오는 구현은 이 자리에서 조용히 틀린다.

그리고 이 모든 처리는 순서에 기대지 않아야 한다. 통지는 원래 순서대로 오지 않을 수 있고, 배치가 두 번 돌 수도 있다. 지금까지 무엇을 적용했는지를 상태로 들고 판정하면 같은 입력을 어떤 순서로 몇 번 넣어도 같은 장부가 나온다. 이 성질은 말로 주장할 것이 아니라 같은 입력을 두 번 흘려 넣어 결과 파일을 견주는 방식으로 시험해야 한다. 그 시험이 없으면 멱등하다는 문장은 희망일 뿐이다.

다음 실습에서 할 것

체결 원장과 취소·정정 통지 스트림, 그리고 이미 나간 포지션 보고를 만듭니다. 통지를 원 체결에 이어 붙여 적용할 것과 버릴 것을 가른 뒤, 원 기록을 그대로 둔 채 상쇄 기록을 더해 새 장부를 만듭니다. 정정을 반영해 체결금액과 평균단가를 다시 내고, 포지션을 재계산해 기존 보고와 대조합니다. 같은 통지 스트림을 두 번 흘려 넣어도 장부가 같은지 파일로 확인하고, 보고 시각 뒤에 도착한 통지를 모아 재보고 대상 목록과 정정 보고서를 냅니다.