QuantLabNote

주문 로그 16,547건을 열어보니 — 거절 코드 전수 분류와 하루가 삼킨 로그 98.9%

자동매매 봇이 브로커에 낸 주문 16,547건을 전수 해부했습니다. 거절 사유를 코드로 분류하니 상위 하나가 대부분을 차지했고, 2026-06-03 하루가 전체 로그의 98.9%(16,362건)를 만들었습니다. 덧붙여 이 주문 테이블로는 슬리피지·체결 지연을 계산할 수 없다는 '계측 공백'을 숫자로 확인했습니다.

최초 발행 2026-08-05 · 데이터 갱신 2026-08-10 · 데이터 2026-05-29 ~ 2026-08-07 · 자체 자동매매 시스템 orders·fills·signals (읽기 전용) · orders 전수 집계 + 오류 문자열 패턴 분류(코드), 폭주일은 '주문 최다일'을 데이터에서 선정해 분·시간 단위 재구성. 금액·수량 비공개

자동매매주문API트레이딩운영사고리스크관리

자동매매 글은 대개 ‘무엇을 샀는가’에서 시작합니다. 그런데 실제 운영에서 시간을 잡아먹는 쪽은 ‘주문이 왜 안 나갔는가’ 입니다. 이 글은 운용 중인 봇이 증권사 API에 낸 주문 16,547건(2026-05-29 ~ 2026-08-07)의 로그를 전수로 열어, 거절 사유를 코드로 분류하고 하루짜리 운영 사고를 분 단위로 재구성한 기록입니다. 모든 수치는 코드가 계산했고, 금액과 수량은 공개하지 않습니다(방법론).

한눈에 — 주문 16,547건의 최종 상태

상태건수비중
FAILED16,38899.0%
SUBMITTED1591.0%

첫 줄부터 이상합니다. 체결(FILLED)로 마감된 주문이 한 건도 없습니다. 이 봇은 실제로 체결을 79건 기록하고 있는데(별도 체결 테이블), 주문 테이블의 상태값은 ‘제출’까지만 전진하고 멈춰 있습니다. 뒤에서 다시 다루겠습니다 — 이게 이 글의 두 번째 주제입니다.

시장주문 건수
KR16,394
US153

주문이 한 건이라도 나간 날은 24일입니다(봇이 손익을 기록한 날은 74일). 대부분의 날에 봇은 아무것도 주문하지 않습니다 — 연구 0003에서 본 대로, 신호의 99%가 주문 이전 단계에서 잘리기 때문입니다. ‘자동매매 = 쉴 새 없이 사고파는 것’이라는 이미지와는 정반대입니다.

하루가 로그의 98.9%를 삼켰다 — 2026-06-03 폭주 사고

주문을 날짜별로 세면 하루가 튀어나옵니다. 2026-06-03, 주문 16,362건. 전체 로그의 98.9% 입니다. 같은 날 전략이 만든 신호는 16,362건이었습니다.

무슨 일이 있었나. 그날 봇은 국내 시장을 열려 있다고 판단했고, 브로커는 주문마다 “휴장·영업일 불일치” 계열의 오류를 돌려줬습니다(16,062건, 그날 오류의 98.2%). 휴장일 판정이 틀린 것입니다. 문제는 그다음입니다 — 봇은 거절을 받고도 같은 주문을 계속 다시 냈습니다.

사고일 거절 사유(코드 분류)건수비중
휴장·영업일 불일치16,06298.2%
초당 주문건수 초과(레이트리밋)2951.8%
사유 미기록40.0%
자전거래 의심 차단10.0%

분 단위로 펴 보면 사고의 성격이 분명해집니다. 그날 주문이 나간 종목은 8개인데, 그중 4종목이 반복 시도에 갇혀 16,356건을 만들었습니다 (그날 주문 16,362건 중 나머지 6건만 평범한 주문이었습니다). 이 반복 주문이 찍힌 시각은 00:00 ~ 06:29(UTC), 주문이 발생한 분(minute)은 388개, 그 분들의 평균 42.2건/분, 최대 44건/분입니다. 한 종목은 혼자 4,089번 시도됐습니다. 한 종목 기준으로 분당 약 10.5회 — 약 5.7초에 한 번씩 같은 실패를 반복한 셈입니다. 그 시장의 장 시간 내내 멈추지 않았고, 결국 브로커의 초당 주문 건수 제한까지 함께 밟았습니다 (레이트리밋 오류가 같은 날 표에 함께 잡힙니다).

여기서 손실은 돈이 아니라 신뢰와 관측력입니다. 체결이 없었으니 금전 손실은 없지만, 로그의 대부분이 한 사건으로 오염돼 이후 모든 집계가 왜곡됐습니다. 실제로 연구 0003은 이날을 빼고 계산해야 했습니다. 사고는 지나가지만 사고가 만든 데이터는 남아서 통계를 계속 오염시킵니다.

사고일을 빼면 무엇이 남나 — 진짜 운영 거절 사유

사고일 제외 거절 사유건수비중(실패 중)
호가단위 오류1446.7%
초당 주문건수 초과(레이트리밋)620.0%
종목정보 없음516.7%
주문가능금액 초과413.3%
네트워크 오류13.3%

사고일을 제외하면 주문은 185건, 그중 실패가 30건 (16.2%)입니다. 남은 실패는 화려하지 않습니다 — 호가단위 오류·초당 주문건수 초과(레이트리밋)·종목정보 없음처럼 전략과 아무 상관 없는 거래소·브로커 규칙들입니다. 호가 단위를 안 맞췄거나, 계좌 한도를 넘겼거나, 우리 유니버스엔 있는데 브로커 종목 마스터엔 없거나. 백테스트에는 이런 항목이 아예 존재하지 않습니다. 개인이 API 매매를 붙일 때 실제로 시간을 쓰는 지점이 바로 여기입니다.

이 로그로는 계산할 수 없는 것 — 계측 공백 3종

원래 이 연구는 ‘신호 → 주문 → 체결의 지연과 슬리피지 분해’로 시작했습니다. 데이터를 열어보고 주제를 바꿨습니다. 이 스키마로는 둘 다 계산할 수 없기 때문입니다. 정직하게 셌습니다.

계측 항목기록된 건수계산 가능?
체결 시각(filled_at)0불가
체결 가격(filled_price)0불가
제출 시각(submitted_at)159기록됨
└ 주문 생성과 시각이 다른 건0불가
체결↔주문 연결(order_id)0 / 체결 79불가
  • 슬리피지(주문가 대비 체결가 차이)를 구하려면 체결가가 주문 행에 있어야 하는데, 체결 가격이 채워진 주문이 0건입니다.
  • 체결 지연을 구하려면 주문 생성 시각과 제출·체결 시각이 서로 달라야 하는데, 제출 시각은 주문 생성 시각과 같은 값으로 찍혀 있어 측정 자체가 불가능합니다.
  • 주문과 체결의 대사(reconcile) 를 하려면 체결 행이 어느 주문에서 나왔는지 알아야 하는데, 체결 79건 중 주문 번호가 한 건도 연결돼 있지 않습니다.

즉 이 봇은 “얼마나 불리하게 체결됐나”를 말할 자격이 없습니다. 흔한 실수는 이 공백을 그럴듯한 추정으로 메우는 것입니다(주문가 = 체결가로 가정하기). 그러면 슬리피지는 항상 0으로 보고되고, 전략은 실제보다 좋아 보입니다. 계산할 수 없는 값을 0으로 적는 것은 측정이 아니라 분식(粉飾)입니다. 그래서 여기서는 빈칸으로 두고, 스키마를 고쳐야 한다고 적어 둡니다.

그래서 무엇을 고쳐야 하나 (같은 걸 만들 사람을 위한 체크리스트)

이 로그가 시킨 숙제는 다섯 줄로 요약됩니다.

  1. 재시도에 상한을 걸 것. 같은 주문의 반복 실패는 3~5회에서 멈춰야 합니다. 이번 사고는 상한이 없어서 16,362건이 됐습니다.
  2. 같은 오류가 연속되면 그 종목·시장을 그날 차단할 것. 브로커가 “영업일이 아니다”라고 말했으면, 그건 재시도로 해결될 성질의 오류가 아닙니다. 오류를 재시도 가능/불가능으로 분류하는 것이 재시도 로직의 절반입니다.
  3. 휴장일은 자체 달력이 아니라 원천에서 받을 것. 손으로 관리하는 휴일 목록은 반드시 틀립니다.
  4. 주문에 멱등키를 붙일 것. 같은 결정으로 두 번 나가는 주문을 서버 쪽에서 막을 수 있습니다.
  5. 체결 필드를 처음부터 설계할 것. 체결가·체결시각·주문 연결이 없으면, 나중에 아무리 분석해도 슬리피지는 영원히 계산되지 않습니다.

무엇을 배웠나

  • 자동매매의 실패는 대부분 전략이 아니라 배관에서 난다. 주문 16,547건 중 압도적 다수가 브로커 규칙·달력 문제로 거절됐다.
  • 재시도는 기능이 아니라 위험이다. 상한과 오류 분류가 없는 재시도 루프는 하루에 로그의 98.9%를 만들고, 그 데이터가 이후 모든 통계를 오염시킨다.
  • 측정할 수 없는 것을 추정으로 메우지 않는다. 슬리피지·지연은 지금 계산 불가이며, 그렇게 적는 것이 0으로 적는 것보다 정확하다.
  • 사고 기록은 자산이다. 지워버리면 다음 사람이 같은 값을 다시 치른다.

이 로그의 앞단(신호가 어떻게 걸러지는가)은 연구 0003에, 24시간 운영이 쌓는 데이터 규모는 연구 0011에, 체결된 결정의 채점 방식은 연구 0012에 있습니다.

다음 연구 예고: 위 체크리스트 중 재시도 상한·오류 분류를 실제로 넣은 뒤, 같은 로그가 어떻게 달라지는지 사고 전후로 비교합니다.

※ 본 글은 정보·교육 목적이며 특정 종목의 매수·매도 권유가 아닙니다. 모든 수치는 위에 명시된 데이터·기간 기준으로 코드로 계산한 값이며, 과거 성과는 미래 수익을 보장하지 않습니다.

📘 이 글, 책으로도 있어요 — 무료 샘플 읽기 · 전자책 보기