
⚠️ 이 글은 투자 권유나 종목 추천이 아니라 순수하게 "개발 후기"입니다.
백테스트 수치와 매매 로직은 학습·개발 과정을 설명하기 위한 예시일 뿐이며, 실제 수익을 보장하지 않습니다. 실전 투자 판단은 반드시 본인 책임 하에 신중하게 하시길 바랍니다.
DB/ETL 업무를 하면서 취미 삼아 키움증권 Open API로 자동매매봇을 만들어본 지 어느 정도 시간이 지났습니다. 검색해보면 "키움 Open API 연동 방법" 같은 튜토리얼성 글은 꽤 있는데, 실제로 봇을 돌리면서 만난 에러, 백테스트와 실전의 괴리, 그리고 그걸 해결해나간 과정을 솔직하게 기록한 글은 의외로 드물었습니다. 그래서 제가 겪은 삽질을 개발 로그 형태로 정리해보려고 합니다. 이 프로젝트를 시작한 계기는 단순했습니다. 평소 오라클 ETL 배치를 짜면서 "정해진 조건에 따라 반복적으로 데이터를 처리하는 로직"에는 익숙했는데, 이걸 증권 데이터에 적용해보면 어떨까 하는 궁금증이었습니다. 처음엔 "그냥 API 붙이고 조건문 몇 개 넣으면 되겠지"라고 가볍게 생각했는데, 실제로 붙잡고 보니 API 자체의 특성부터 시장 데이터의 변덕스러움까지, 예상보다 훨씬 다루기 까다로운 영역이었습니다. 왜 만들게 됐는지, 그리고 키움 Open API의 첫인상 가장 먼저 놀랐던 건 키움 Open API가 여전히 32비트 COM 기반이라는 점이었습니다. 요즘 개발 환경에 익숙하다면 이 구조 자체가 낯설게 느껴질 수 있습니다. 파이썬에서 쓰려면 pywin32로 COM 객체를 붙잡아야 하고, 실행 환경도 32비트 파이썬으로 맞춰야 정상 동작한다는 걸 처음엔 모르고 계속 원인 모를 오류를 마주했습니다. 두 번째로 적응이 필요했던 부분은 철저히 이벤트 기반 비동기 구조라는 점이었습니다. 로그인 요청을 보내면 즉시 결과가 오는 게 아니라 OnEventConnect 이벤트가 발생할 때까지 기다려야 하고, 종목 조회(TR) 요청도 OnReceiveTrData 이벤트로 결과가 넘어옵니다. 일반적인 동기 방식 API 호출에 익숙하다 보니, 초반에는 요청을 보내고 바로 다음 줄에서 결과값을 쓰려다가 계속 빈 값을 받는 실수를 반복했습니다. 결국 이벤트 콜백과 조건 변수(또는 큐)를 이용해서 "응답이 올 때까지 대기"하는 구조로 짜야 한다는 걸 체득했습니다. 세 번째는 조회 횟수 제한이었습니다. 초당 요청 가능 횟수와 하루 요청 가능 횟수가 정해져 있는데, 이 제한을 넘기면 일정 시간 동안 조회가 아예 막혀버립니다. 처음엔 이 제한을 몰라서 여러 종목을 반복문으로 빠르게 조회하다가 중간에 봇이 먹통이 되는 일이 잦았습니다. 이후로는 요청 사이에 의도적으로 딜레이를 넣고, 요청 큐를 만들어서 초당 처리 건수를 스스로 제한하는 방식으로 바꿨습니다. 네 번째로는 UI 스레드와 관련된 문제도 있었습니다. COM 기반 API 특성상 반드시 메인(STA) 스레드에서 API를 초기화하고 이벤트를 처리해야 하는데, 별도 스레드에서 무심코 API 객체를 건드렸다가 원인을 알 수 없는 예외가 터진 적이 여러 번 있었습니다. 결국 API 관련 호출은 전부 하나의 스레드에서만 처리하고, 매매 로직이나 데이터 가공처럼 무거운 연산은 별도 스레드로 분리해서 메시지 큐로 결과만 주고받는 구조로 정리했습니다. 백테스트로 확인한 것들과 실전에서 다르게 흘러간 것들 로직을 어느 정도 완성한 뒤에는 과거 데이터로 백테스트를 돌려봤습니다. 여기서 배운 게 생각보다 많았습니다. 1) 백테스트 성과가 좋다고 실전 성과가 좋은 게 절대 아니었습니다. 특정 기간 데이터로 파라미터를 계속 조정하다 보니, 그 구간에만 잘 맞는 형태로 로직이 최적화되는 과적합(오버피팅) 문제를 겪었습니다. 백테스트 승률이 높게 나온다고 좋아했다가, 기간을 조금만 바꿔서 돌려보니 성과가 뚝 떨어지는 경험을 몇 번 하고 나서야 "이 결과를 얼마나 믿어야 하는가"에 대해 훨씬 보수적으로 접근하게 됐습니다. 2) 슬리피지와 호가 갭을 처음엔 아예 고려하지 않았습니다. 백테스트에서는 "이 가격에 체결됐다"고 가정하고 계산하지만, 실제 시장에서는 주문을 넣는 시점과 체결되는 시점 사이에 가격이 이미 움직여 있는 경우가 많습니다. 특히 거래량이 적은 종목이나 급등락 구간에서는 원하는 가격에서 한참 벗어난 가격에 체결되는 일도 있었습니다. 이후 백테스트 로직에 슬리피지 가정치를 강제로 넣고 나서야, 실제 성과와의 괴리가 조금씩 줄어들었습니다. 3) 장중 데이터와 백테스트용 봉 데이터의 타이밍 차이도 문제였습니다. 분봉 데이터를 기준으로 신호를 계산했는데, 실제 봇은 해당 봉이 완전히 마감된 시점에야 신호를 인식하는 반면, 초기 백테스트 코드에서는 이 시차를 정확히 반영하지 못해서 실제로는 불가능한 "미래를 살짝 엿본" 듯한 결과가 나온 적도 있었습니다. 이걸 발견했을 때 꽤 당황스러웠는데, 봉 마감 시점과 신호 계산 시점을 명확히 분리하고 나서야 신뢰할 수 있는 백테스트 구조가 됐습니다. 이 세 가지를 겪고 나서부터는 백테스트 결과를 볼 때 습관이 완전히 바뀌었습니다. 이제는 특정 구간의 성과 수치 하나에 집중하기보다, 여러 기간으로 나눠서 돌려봤을 때 결과가 얼마나 들쭉날쭉한지, 즉 일관성이 있는지를 먼저 확인합니다. 한 번의 그럴듯한 결과보다 여러 번의 밋밋하지만 꾸준한 결과가 훨씬 신뢰할 만하다는 걸 몸으로 배운 셈입니다. 실전에서 만난 에러 로그와 삽질 (그리고 극복기) 실제로 봇을 며칠씩 돌려보면서 마주친 에러들을 로그와 함께 정리해봅니다.
가장 자주 만난 에러: 조회 및 주문 과부하 관련 에러코드
연속 조회나 짧은 시간 내 반복 주문 시도 시 발생하는 제한 관련 에러가 로그에 계속 찍혔습니다. 원인은 앞서 말한 요청 큐 관리가 미흡했던 탓이었고, 요청 간격 제어 로직을 추가하고 나서야 빈도가 크게 줄었습니다.
1) 자정 부근 시간 처리 버그. 특정 시간 이후 데이터 집계 로직을 짜면서 "다음 날 날짜로 넘어가는" 경계 처리를 놓쳐서, 자정을 넘기는 순간 그날 데이터가 이상하게 꼬이는 문제가 있었습니다. 로그를 새벽에 확인하다가 날짜 필드가 하루 전으로 찍혀 있는 걸 보고 원인을 찾았는데, 단순히 날짜 비교 로직에 하드코딩된 가정이 있었던 게 문제였습니다. 2) 장 마감 직후 미체결 주문 처리 문제. 장이 끝나는 시점에 미체결 주문이 남아있는 상태를 제대로 처리하지 않아서, 다음 거래일 시작 시점에 봇이 이 주문을 새 주문으로 착각하고 중복 처리를 시도한 적이 있었습니다. 이 문제는 실제 돈이 걸린 부분이라 가장 긴장했던 버그였습니다. 이후로는 장 마감 시점에 미체결 주문을 전량 취소하거나, 봇 상태를 완전히 초기화하고 다음 날 새로 시작하는 방식으로 안전장치를 만들었습니다. 3) 연결 끊김 후 재접속이 자동으로 안 되는 문제. 네트워크가 잠깐 끊기거나 시스템이 절전 모드로 들어가면서 API 연결이 끊어졌는데, 봇은 이걸 감지하지 못하고 마치 정상 작동 중인 것처럼 계속 루프를 돌고 있었습니다. 결국 별도의 헬스체크 스레드를 만들어서 일정 주기로 연결 상태를 확인하고, 끊어졌을 경우 재로그인 절차를 자동으로 수행하도록 개선했습니다. 4) 이벤트 콜백 순서가 꼬이는 문제. 여러 종목을 동시에 조회하다 보니, 요청을 보낸 순서와 콜백이 돌아오는 순서가 항상 일치하지 않는다는 걸 뒤늦게 알았습니다. 요청과 응답을 순서에만 의존해서 매칭시키던 초기 코드에서는 가끔 엉뚱한 종목 데이터가 다른 종목 처리 로직으로 흘러들어가는 사고가 있었고, 이후 요청 시점에 고유 식별자를 함께 관리하는 방식으로 바꿔서 해결했습니다.
정리하자면, 자동매매봇 개발에서 가장 어려웠던 부분은 매매 전략 자체보다 "API의 비동기·이벤트 구조를 안전하게 다루는 것"과 "백테스트와 실전 사이의 괴리를 줄이는 것"이었습니다. 화려한 전략보다 이런 기초 체력이 훨씬 중요하다는 걸 절실히 느낀 프로젝트였습니다.
돌아보면 처음 몇 주는 매매 전략을 더 정교하게 다듬는 데 시간을 쓰고 싶었는데, 실제로는 API 연결을 안정적으로 유지하고 에러를 로그로 남겨서 추적 가능하게 만드는 기반 작업에 훨씬 많은 시간을 썼습니다. 지나고 보니 이런 기반 작업이 부실하면 아무리 좋은 전략을 짜도 실전에서는 엉뚱한 곳에서 터지기 마련이라, 결코 시간 낭비가 아니었다는 생각이 듭니다. 다음 글에서는 요청 큐와 헬스체크 스레드를 실제로 어떻게 구현했는지, 코드 구조를 좀 더 구체적으로 다뤄보겠습니다. 다시 한번 말씀드리지만 이 글은 투자 조언이 아니라 순수한 개발 경험 공유이니, 실제 투자 판단은 본인의 충분한 검토와 책임 하에 이루어지시길 바랍니다.