Quant Trading Bot Devlog

Read in English

"[261005~261011] 주문 실행·안전장치 계층 확장과 사전등록 A/B 실험 틀 구축"

출력 상한·장부대조 가드·부분충전 매수·입출금 반영까지 실행 계층을 연일 손봤고, 성급히 반영하는 대신 결과를 보기 전에 기준을 못박는 실험 틀을 먼저 세운 한 주였습니다.

이번 주는 주문 실행·안전장치 계층(새 창)을 거의 매일 한 단계씩 넓힌 한 주였습니다.

그런데 그 밑으로는 다른 결의 규칙 하나가 계속 흘렀습니다. "일단 돌아가니 넘어가자", "일단 반영하고 눈으로 보자"로 처리하지 않는다는 것이었습니다.

통과 못 한 기준을 결과 보고 그 자리에서 낮추지 않고, 근거가 약한 아이디어는 참고 접고, 숫자가 안 맞으면 손대기 전에 원인부터 끝까지 따라갔습니다.

월요일, 통과 못 한 기준 하나를 어떻게 다룰 것인가

주 초의 출발점은 전날 발견한 문제였습니다. 모델 서빙에 출력 토큰 상한이 애초에 걸려 있지 않아, 모델이 멈추지 못하고 길게 생성하던 문제였습니다.

이걸 메인·백업 두 서빙 경로 모두에 새로 적당히 걸고, 도구 호출 경로의 결함도 같이 수선했습니다.

적용 전 스모크 테스트에서 지표 하나만 두 번 다 기준을 넘겼습니다. 들여다보니 그 기준 자체가 처음부터 근거 없이 "0이어야 한다"로 잡혀 있었고, 기존 로그에도 매일 비슷한 수준으로 나오던 값이었습니다.

그래서 넘어가긴 했지만, 결과를 보고 그 자리에서 기준을 낮추지는 않았습니다. 정식 기준값 재측정은 다음 휴장일로 따로 미뤄 못박아 뒀습니다.

같은 날 주문 체결 장부대조 가드도 두 단계 더 늘렸습니다. 체결 수량이 원래 주문량을 넘는 경우, 마감 시점에 원장 수량이 음수가 되는 경우를 각각 걸러내는 장치입니다.

이쪽은 반대로 AI 코드리뷰에서 나온 지적 세 건을 전부 반영한 뒤에야 병합했습니다. 그중 하나는 테스트가 실행 날짜에 의존해 휴장일에 일부가 조용히 건너뛰어지던 문제라, 날짜를 테스트 안에서 고정하도록 고쳤습니다.

화요일, "숫자가 안 맞는다"에서 시작한 두 사건

이튿날은 작은 신호 두 개를 각각 끝까지 따라간 날이었습니다.

하나는 평소보다 매수가 잘 안 들어가던 종목들이었습니다. 거래정지·상장폐지 종목을 걸러내는 분류기가 공시 문구를 잘못 읽어, 실제로는 정상 거래 중인 종목 몇 개를 계속 정지로 막고 있었습니다.

거래정지를 "취소"한다는 공시의 취소 문구를 못 알아보거나, 상장폐지 관련 의안 공시를 실제 폐지로 오인한 경우였습니다. 한 번 정지로 분류되면 해제 신호가 없어 영영 묶이는 구조적 결함도 같이 드러났습니다.

이게 정책인지 버그인지 애매해서 서로 다른 AI 둘에게 각각 자문을 구했습니다. 둘 다 "정책이 아니라 데이터 해석 버그"라는 같은 결론이었고, 그 판단을 받은 뒤에야 고쳤습니다.

다른 하나는 저녁에 터졌습니다. 모의 거래 계좌 하나의 장부대조 잔차가 경보 기준을 넘어, 다음 날 신규 매수가 자동 차단되는 상태가 됐습니다.

보유 수량과 평가금액은 정확히 맞았고 잔차는 거의 전부 현금에서만 났습니다. 거래가 많은 날일수록 커지는 패턴이라, 원장 수수료율이 실제보다 낮게 잡혀 조금씩 현금이 새던 것으로 추정했습니다.

모의서버 쪽 수수료 상세를 직접 볼 방법이 없어 완전히 확정하지는 못했습니다. 다만 추정 수수료율을 반영하고 그동안 쌓인 잔차를 한 번 정정한 뒤, 장부대조가 정상 범위로 돌아온 걸 확인하고 재시작했습니다.

수요일, 반영하고 싶은 걸 참고 틀부터 세웠다

수요일은 성격이 반대인 두 작업이 같은 결로 묶인 날이었습니다.

먼저, 현금이 주문 금액에 조금이라도 모자라면 주문을 통째로 거부하던 동작을 바꿨습니다. 이제는 가용 현금이 허용하는 수량으로 줄여 그만큼만 사도록 했습니다.

이건 "예수금을 놀리지 말자"는 평소 원칙과 맞닿은, 작고 구체적인 수선이었습니다. 캡은 속도를 조절하는 장치일 뿐 남는 현금을 일부러 쌓아두는 게 목표가 아니기 때문입니다.

반대로 오후에는 바로 반영하고 싶던 걸 참고 접었습니다. 특정 가격 정보를 판단에 반영하는 후보가 넷 있었는데, 그중 둘은 근거가 약했습니다.

하나는 여러 모델의 합의로 종목을 판단하는 층(새 창)을 건드리는 변경, 다른 하나는 주문 실행 쪽 변경이었습니다. AI 자문의 권고는 "둘은 지금 접고, 재개 조건을 명시한 가드로 남겨두라"였습니다.

그래서 둘은 재개 조건만 가드 문장으로 못박아 폐기하고, 넷 중 가장 위험 낮은 하나(동작은 안 바꾸고 진단용 수치 열만 추가)만 받아들였습니다.

진짜 효과가 궁금했던 프롬프트 변경 하나는 "적용하고 눈으로 보자" 대신 사전등록(pre-registration) 방식 A/B 실험으로 확인하기로 했습니다. 가설·측정지표·합격 기준을 결과를 보기 전에 미리 적어두는 방식입니다.

이렇게 해두면 나중에 결과를 보고 기준을 슬쩍 낮추는 일을 막을 수 있습니다. 월요일에 한 번 지킨 원칙을, 아예 절차로 박아두는 셈입니다.

판독 스크립트에는 순열검정을 넣어 관측된 차이가 우연으로 설명되는 수준인지 따지고, 지표 부호의 일관성과 무해성도 함께 보도록 했습니다. 설계는 아직 동결하지 않고, 남은 항목 몇 개와 공동 판정 날짜만 잡아뒀습니다.

목요일, 치명적 버그가 코드가 아니라 문장에 있었다

이번 주의 전환점은 목요일이었습니다. 원래 주말 작업 창으로 미뤄뒀던 입출금 장부 처리("머니패스")를, 오너 지시로 당겨 구현부터 배포까지 끝냈습니다.

이 경로의 핵심은 "같은 입출금은 정확히 한 번만 장부에 반영한다"입니다. 외부에서 들어온 현금을 트레이딩 수익으로, 빠져나간 돈을 손실로 착각하면 안 되기 때문입니다.

재시도가 일어나도, 중간에 프로세스가 죽었다 살아나도 두 번 기록되면 안 됩니다. 그래서 입출금마다 고유 식별자를 붙이고, 이미 처리된 식별자는 무시하도록 — 실행 계층의 기존 멱등성 패턴과 같은 결로 — 짰습니다.

구현을 끝내고 AI에게 델타 코드리뷰를 맡겼더니 치명 등급 하나가 나왔는데, 위치가 뜻밖이었습니다.

버그는 로직이 아니라, 장부 반영이 실패했을 때 사람에게 보여주는 안내 문구에 있었습니다. 그 문구가 "다시 실행해 보라"로 읽혀, 그대로 따라 하면 같은 입금이 두 번 들어갈 수 있었습니다.

멱등성은 코드로 막아놨는데, 정작 사람을 두 번 실행하게 유도하는 한 문장이 그 방어를 통째로 우회하는 셈이었습니다.

그래서 안내를 "무조건 재실행"이 아니라 "먼저 상태를 확인하고, 장부에 행이 없는 걸 재확인한 다음에만 재실행하라"는 순서로 바꾸고, 상태 확인 전용 명령도 따로 만들었습니다. 다시 리뷰에 넣었더니 치명 항목은 사라졌습니다.

배포에는 조건이 하나 붙었습니다. 관련 상주 프로세스를 전부 같이 재시작할 것. 하나라도 옛 코드를 든 채 살아 있으면 고치기 전 동작이 그대로 재발할 수 있기 때문입니다. 그래서 다섯 개를 한꺼번에 재시작하고 정상 상태를 확인한 뒤 마무리했습니다.

이번 주를 관통한 것

이번 주 작업은 거의 다 실행·안전장치 계층 위에 있었습니다. 출력 상한, 장부대조 가드, 부분충전 매수, 입출금 반영까지, 돈이 실제로 드나드는 경로를 연일 한 단계씩 단단하게 만든 셈입니다.

그 밑을 관통한 건 방어가 가장자리에서 뚫린다는 감각이었습니다. 근거 없이 잡혀 있던 기준, 사람에게 "다시 해보세요"라고 말하는 한 줄, 실제보다 낮게 잡힌 비용 가정 — 정작 구멍은 로직 한복판이 아니라 그 언저리에 있었습니다.

그래서 이번 주 규칙은 한 방향이었습니다. 통과 못 한 기준은 근거를 확인한 뒤에만 넘기고 재측정을 못박는다, 근거 약한 아이디어는 재개 조건만 남기고 접는다, 바꾸기 전에 제대로 잴 틀부터 세운다.

다음 주는 새로 건 출력 상한과 신규 장부대조 가드, 부분충전 매수, 입출금 장부 처리가 실전 라운드에서 오류 0건을 유지하는지 지켜보는 구간입니다. 사전등록 A/B 실험도 남은 항목을 채워 설계를 동결한 뒤, 한 번의 통제된 비교로 돌려 공동 판정으로 마무리할 계획입니다.