Backtrader 전략을 NautilusTrader로 옮기며 다시 짠 것들
라이브 배포를 염두에 두고 백테스트 프레임워크를 바꾸기로 했습니다. Backtrader로 짠 모멘텀 전략을 NautilusTrader로 옮기는 작업을 시작했죠. 결론부터 말하면 첫 전략이 돌아가기까지 사흘이 걸렸어요. CSV 파일 하나로 시작했던 Backtrader와 달리, 데이터 카탈로그·전략 이벤트 핸들러·주문 체결 모델을 전부 다시 짜야 했거든요. 이 글은 그 과정에서 실제로 뭘 바꿨는지, 어디서 막혔는지를 기록합니다.
데이터 파이프라인부터 다시 짜야 했다
Backtrader에선 pd.read_csv()로 읽어 넘기면 끝이었죠. 그런데 NautilusTrader는 데이터 카탈로그 개념을 강제합니다. Parquet 형식으로 변환한 뒤, 카탈로그에 등록하는 단계를 거쳐야 백테스트 엔진이 데이터를 읽어요.
제가 가진 건 야후 파이낸스 OHLCV 파일 1,500개였어요. 공식 로더를 쓰려면 스키마를 맞춰야 했는데, timestamp는 나노초 단위여야 하고 instrument_id는 거래소 접미사를 붙여야 했습니다. 원본엔 티커만 있었죠.
변환 스크립트를 따로 짰습니다. 파일당 3초씩 걸려 전체 두 시간 소요됐어요. 결측치도 dropna()로 직접 정리해야 했습니다. 자동으로 건너뛰던 방식과 달리, 결측 레코드가 있으면 에러가 났거든요.
타임존 처리도 명시적으로 해야 했어요. 데이터는 UTC인데 전략은 KST 기준이라, pd.to_datetime(utc=True) 후 KST 변환 헬퍼를 만들었습니다.
전략 코드는 이벤트 핸들러 구조로 전환했다
Backtrader는 next() 안에서 모든 로직을 순차 처리합니다. 지표 계산 → 매수/매도 판단 → 주문 제출 순서로 짤 수 있었죠.
NautilusTrader는 이벤트 드리븐입니다. on_bar()에서 새 바를 받고, on_order_filled()에서 체결 알림을 받는 식이에요. Backtrader의 next() 안에 있던 주문 로직을 on_bar()로 옮기고, 체결 확인은 on_order_filled() 콜백으로 분리해야 했습니다.
주문 상태 관리가 문제였어요. 제출과 체결이 분리돼 있어서 주문 ID를 직접 추적해야 했습니다. self._open_orders 딕셔너리로 ID를 관리하고 체결 시 제거하는 식으로 바꿨죠.
지표 계산은 수월했어요. 기존 라이브러리를 pandas 기반으로 바꾸고, 미리 계산 후 인덱싱하는 방식으로 전환했습니다.
주문 체결 모델이 훨씬 현실적이었다
백테스트 시 주문이 무조건 체결되는 가정이었죠. 실전의 호가 부족 같은 상황은 반영 안 됐어요.
체결 모델을 선택할 수 있어요. FillModel.default()는 호가창 시뮬레이션까지 하고, 주문이 일부 체결되거나 거부될 수 있습니다. 결과가 더 보수적으로 나왔죠.
청산도 체결 모델을 거쳐서 급락장에서 주문이 밀리는 시나리오가 반영됐어요. 전략에 타임아웃 로직을 추가했습니다. 2바 안에 미체결되면 취소 후 재제출하는 식으로요.
첫 전략이 돌아가기까지 사흘 걸렸다
전체 과정을 정리하면 이렇습니다. 첫날은 데이터 변환에 썼어요. CSV를 Parquet로 바꾸고 카탈로그에 넣는 데 반나절, 타임존과 결측치 처리에 반나절이 걸렸습니다. 둘째 날은 전략 코드 이식이었죠. next()를 on_bar()로 옮기고 주문 상태 관리를 다시 짜는 데 하루가 통째로 날아갔어요. 셋째 날엔 체결 모델 설정과 디버깅을 했습니다. 처음엔 주문이 전부 거부돼서 로그를 뒤져보니 instrument_id 형식이 틀렸더군요.
환경 설정도 까다로웠어요. Python 3.10으로 올리고 의존성 설치에 20분, Rust 바이너리 때문에 디스크 공간도 넉넉히 필요했습니다.
문서는 컨셉 위주라 실전 예제가 부족했고, 커뮤니티 답변도 하루씩 걸렸습니다. 한국어 자료는 거의 없었어요.
이관 후 달라진 점
라이브 배포 시 전략 코드를 다시 짜야 했던 게 사라졌어요. 같은 클래스를 백테스트와 라이브에서 공유하니까요. 엔진만 BacktestEngine에서 LiveEngine으로 바꾸면 됩니다.
배포 불일치 위험이 줄었습니다. 코드가 같으니 성과 차이가 없어졌죠. 대신 초기 진입장벽은 높았어요.
자주 묻는 질문
Q. 이관 중에 기존 Backtrader 전략을 계속 돌릴 수 있나요?
A. 네, 두 환경은 독립적이라 병행 운영이 가능합니다. 가상환경을 분리하면 되고, 같은 데이터 소스를 읽는 것도 문제없어요. 저는 이관 완료 전까지 기존 환경을 그대로 두고 검증용으로 썼습니다.
Q. 이관을 중단하고 원래대로 돌아갈 수 있나요?
A. 기존 코드를 건드리지 않았다면 언제든 돌아갈 수 있어요. 새 프레임워크용 코드는 별도 디렉토리에 두는 게 안전합니다. 다만 변환한 데이터나 설정 파일은 남게 되죠.
Q. 전략이 여러 개일 때 어떤 순서로 옮기는 게 좋나요?
A. 가장 단순한 전략부터 시작하는 게 낫습니다. 복잡한 주문 로직이 없는 것으로 파이프라인을 먼저 검증하고, 이후 어려운 전략을 차례로 이관하면 디버깅이 쉬워요.
Q. 체결 모델을 바꾸면 백테스트 결과가 많이 달라지나요?
A. 네, 꽤 차이 납니다. 기본 체결 모델은 호가창 시뮬레이션을 하기 때문에 무조건 체결 가정보다 보수적이에요. 청산 주문이 밀리는 시나리오도 반영돼서, 급락장에서 손실이 더 커지는 걸 미리 볼 수 있습니다.
Q. 라이브 배포는 어떤 브로커를 지원하나요?
A. Interactive Brokers, Binance, Bybit 어댑터가 공식 제공됩니다. 한국 증권사는 직접 어댑터를 만들어야 해요. IB 어댑터가 가장 안정적이고 문서도 잘 돼 있습니다.