자동매매 봇 킬스위치 설계법 — MiFID II 규정으로 배우는 프리트레이드 리스크 체크리스트
킬스위치가 없으면 실제로 무슨 일이 벌어지나요?
새벽 3시, 알림이 울렸다. 제가 돌리던 자동매매 봇이 같은 매수 주문을 47번 연속으로 넣고 있었다. API 응답 지연 때문에 체결 확인 로직이 타임아웃마다 재주문을 트리거하던 단순한 버그였는데, 알아차리기까지 3분 15초가 걸렸다. 프로세스를 강제 종료했지만 이미 나간 주문은 취소되지 않았고, 거래소에는 미체결 주문이 그대로 쌓여 있었다. 그날 배운 건 하나다 — 전략 로직이 아무리 정교해도 "지금 당장 멈추는 기능"이 없으면 계좌는 무방비 상태라는 것.
이 글을 끝까지 읽으면 개인 규모 자동매매 시스템에도 적용할 수 있는 3계층 킬스위치 구조를 직접 설계할 수 있게 된다. 유럽 금융당국이 대형 알고리즘 트레이딩 회사에 요구하는 규정을 개인 프로젝트 체크리스트로 바꿔서 정리했다.
킬스위치 설계, 무엇을 준비해야 하나요?
거창한 인프라는 필요 없다. 제가 실제로 쓰는 구성은 파이썬 3.11 기반 봇, 국내 증권사 REST API(키움증권 OpenAPI 또는 한국투자증권 KIS Developers), 별도 프로세스로 돌아가는 감시 스크립트 하나가 전부다. 여기에 텔레그램 봇 알림 정도만 추가하면 충분하다.
핵심은 도구가 아니라 구조다. MiFID II RTS 6 제12조는 알고리즘 트레이딩 회사에 비정상 동작 시 모든 거래소 주문을 즉시 전량 회수하는 킬기능을 요구한다(출처: ESMA Supervisory Briefing on Algorithmic Trading in the EU). 이 킬기능이 반드시 하나의 소프트웨어 버튼일 필요는 없다는 점이 흥미롭다. 절차와 스위치를 조합해도 되고, 개인 프로젝트라면 이 유연함을 그대로 활용하면 된다.
1단계: 게이트웨이 계층에서 속도 제한부터 건다
주문이 거래소로 나가는 첫 관문에 메시지 속도 제한을 건다. "1초에 주문 3건 초과 시 자동 차단" 규칙을 넣으면 → 루프에 걸린 봇이 초당 수십 건씩 주문을 쏟아내는 사고를 원천 차단한다. 초당 주문 카운터를 두고 임계치를 넘으면 즉시 예외를 던져 이후 요청을 거부하도록 짜면 된다.
같은 종목·수량·방향 주문이 5초 이내 재발생하면 차단하는 중복 탐지 규칙도 이 계층에 넣는다. 네트워크 재시도가 중복 체결로 이어지는 사고를 막을 수 있다.
2단계: EMS 계층에서 주문 크기 상한을 검증한다
주문 실행 관리(EMS) 단계에서는 개별 주문의 크기 자체를 검증한다. "단일 주문이 계좌 평가액의 10퍼센트를 넘으면 거부" 규칙을 넣으면 → 전략 코드에 소수점 버그가 생겨 수량이 100배로 부풀어도 실제 체결까지 가지 않고 걸러진다. 이 계층을 추가한 뒤로 실수로 인한 초과 주문이 실거래 계좌에서 한 번도 나가지 않았다.
3단계: OMS 계층에서 포지션과 손실 한도를 지킨다
주문 관리(OMS) 계층은 개별 주문이 아니라 계좌 전체 상태를 본다. "일일 손실이 계좌 잔고의 3퍼센트를 넘으면 당일 신규 진입을 전면 중단" 규칙을 넣으면, 전략이 계속 손실 방향 신호를 내더라도 계좌가 그날 더 깎이는 걸 막을 수 있다. 특정 종목 보유 비중이 상한을 넘으면 매수 신호 자체를 무시하는 규칙도 같은 계층에 둔다.
4단계: 세 계층을 하나의 킬스위치로 묶는다
세 계층을 하나의 상태 파일(킬플래그)로 묶는 게 핵심이다. 어느 계층에서든 이상 신호가 감지되면 이 플래그가 켜지고, 봇의 모든 주문 함수는 실행 전에 이 플래그부터 확인한다. 플래그가 켜지면 신규 주문 차단은 물론 미체결 주문 전량 취소 요청까지 자동으로 나간다. 제 감시 스크립트는 이 변화를 5초 주기로 확인해서 → 텔레그램으로 즉시 알림을 보낸다.
킬스위치를 만들면서 겪은 흔한 실수 세 가지
첫째, 만들어놓고 테스트를 안 했다. 모의투자 계좌에서만 돌려보고 실전 배포를 했는데, 정작 API 응답 형식이 달라 취소 요청 함수가 조용히 실패했다. 배포 11일째 우연히 로그를 훑어보다가 알아챘다. 그 뒤로는 매주 금요일 장 마감 후 킬스위치를 강제로 트리거해보는 루틴을 만들었다.
둘째, 알림과 실제 차단을 혼동했다. 텔레그램 메시지는 오는데 주문은 계속 나가는 걸 사흘 동안 몰랐다. 알림 함수와 차단 함수를 같은 조건문에 묶지 않은 게 원인이었다. 이후로는 알림과 차단을 같은 트랜잭션에서 처리하도록 고쳤다.
셋째, 해제 절차를 안 만들었다. 플래그가 켜지면 사람이 로그를 보고 원인을 확인한 뒤 수동으로 풀어야 하는데, 이 절차를 문서화하지 않아 재가동까지 40분이 걸린 적이 있다. 지금은 해제 체크리스트를 코드 주석에 그대로 박아뒀다.
지금 당장 무엇부터 시작해야 할까요?
오늘 당장 전략 코드를 건드릴 필요는 없다. 대신 봇의 주문 함수 맨 앞줄에 킬플래그를 확인하는 조건문 한 줄만 추가해보길 권한다. 그 한 줄의 유무가, 사고가 났을 때 3분 15초짜리 손실로 끝나느냐 계좌 전체가 흔들리느냐를 가른다.
자주 묻는 질문
Q. 개인 자동매매도 MiFID II 규정을 지켜야 하나요? A. 아니다. 유럽에서 영업하는 알고리즘 트레이딩 회사 대상 규정이라 국내 개인에게 법적 구속력은 없다. 다만 "즉시 전량 회수" 설계 원칙은 자동매매를 쓰는 누구에게나 유효한 안전장치다.
Q. 키움증권이나 한국투자증권 API에도 같은 구조를 적용할 수 있나요? A. 가능하다. 3계층 구분은 특정 API 스펙에 매이지 않는 설계 패턴이라 국내 REST API에도 그대로 옮길 수 있다.
Q. 킬스위치 트리거 임계치는 어떻게 정해야 하나요? A. 처음엔 보수적으로 좁게 잡고 실거래 로그를 보며 넓히는 방식을 권한다. 저는 첫 2주 단일 주문 상한을 계좌의 5퍼센트로 시작했다가 오탐이 잦아 10퍼센트로 조정했다.
Q. 킬스위치 자체가 오작동하면 어떻게 하나요? A. 감시 스크립트를 봇 본체와 별도 프로세스로 분리해서, 봇이 죽어도 감시는 살아있게 짜는 게 원칙이다. 저는 systemd로 두 프로세스를 독립 관리한다.