저장해 둔 백테스트를 다시 실행했더니 자산 곡선이 달라졌습니다. 전략 코드는 같고, 날짜 범위도 같고, 심볼도 같았습니다. 최종 잔고는 1.8% 차이가 났고, 세 건의 거래가 한 봉만큼 이동했습니다.
이 정도 차이만으로도 리서치 결과를 신뢰하기 어려워집니다. 팀원이나 미래의 내가, 혹은 모의 거래 서비스가 실행 결과를 재현할 수 없다면 변경 사항이 전략을 개선한 건지 실험 조건만 바꾼 건지 알 수 없습니다. 차이가 생긴 원인을 찾기 위해 밟은 과정을 소개합니다.
1일 차: ‘같은 실행’의 의미를 기록했습니다
처음에는 전략 파일이 실험의 전부라고 생각했습니다. 하지만 아니었습니다. 실행 결과는 입력 데이터, 엔진 버전, 거래일 달력, 상품 메타데이터, 체결 설정에도 영향을 받았습니다. 코드는 계산의 한 부분만 설명할 뿐이었습니다.
변경에 앞서 실행 명세를 만들었습니다. 전략 커밋, 데이터 스냅샷 식별자, 날짜 범위, 거래소, 수수료 체계, 펀딩 출처, 체결 모델, 소프트웨어 버전을 기록했습니다. 결과 주문과 체결 내역도 저장했습니다. 자산 곡선만으로는 두 실행이 처음 갈라진 지점을 알 수 없기 때문입니다.
| 항목 | 기록할 내용 | 중요한 이유 |
|---|---|---|
| 시장 데이터 | 스냅샷 ID, 스키마 버전, 조정 내역 | 공급업체는 과거 데이터를 바로잡고 기업 활동 내역을 수정함 |
| 체결 | 수수료 등급, 펀딩 시계열, 체결 및 시장 충격 설정 | 기본값과 계정 가정이 결과를 바꿈 |
| 실행 환경 | 코드 커밋, 엔진 및 의존성 버전 | 라이브러리 변경으로 정렬 순서, 반올림 또는 지표가 달라질 수 있음 |
| 결과 | 주문, 체결, 포지션, 지표 | 실행 결과가 갈라지기 시작한 지점을 보여 줌 |
2일 차: Sharpe가 아니라 거래 내역을 비교했습니다
요약 지표는 오히려 시선을 흐렸습니다. 두 실행의 Sharpe는 거의 같았지만, 체결 로그를 보니 펀딩 정산에서 첫 불일치가 나타났습니다. 한 실행은 정산 시각에 열려 있던 포지션에 펀딩 비율을 적용했고, 다른 실행은 그 시각의 리밸런싱 이후 포지션을 사용했습니다.
전략 코드는 바뀌지 않았습니다. 엔진의 이벤트 처리 순서가 달라졌습니다. 작은 버전 업데이트로 이벤트 순서가 명시됐고, 그전에는 두 이벤트가 우연히 어떤 순서로 정렬되느냐에 따라 결과가 달라졌습니다.
실행 규약에 이벤트 순서를 명시했습니다. 정산 시점까지 보유한 포지션에 펀딩을 적용한 다음, 해당 시각의 전략 결정을 처리하도록 했습니다. 정확한 규칙은 거래소와 엔진에 따라 달라질 수 있습니다. 이를 암묵적으로 두는 것이 문제입니다.
3일 차: ‘같은’ 데이터 파일이 실제로는 달랐습니다
이벤트 순서를 고정한 뒤에도 남은 불일치는 일부 주식 거래에 집중됐습니다. 공급업체가 과거 주식 분할 조정 내역을 수정한 것이 원인이었습니다. 파일 이름과 행 수가 이전과 같아 변경되지 않은 것처럼 보였습니다.
이제 변경하지 않는 각 데이터 스냅샷의 지문을 만들고 조정 정책도 함께 보관합니다. 해시로 바이트가 바뀌었는지는 알 수 있지만, 왜 바뀌었는지는 알 수 없습니다. 그래서 명세에 출처, 가져온 시각, 변환 버전도 기록합니다. 수정될 수 있는 데이터에서는 이 정보도 결과의 일부입니다.
재현 가능한 백테스트라면 “과거의 어느 버전 데이터를 사용했는가?”에 답할 수 있어야 합니다.
4일 차: 눈에 잘 띄지 않던 기본값을 찾았습니다
마지막 차이는 maker 수수료였습니다. 전략 설정에서 해당 항목을 생략해 0으로 지정되어 있었습니다. 최신 엔진은 계정의 기본 수수료를 적용했습니다. 이 기본값 하나가 한계선에 있던 거래를 바꿔 최종 잔고 차이의 대부분을 설명했습니다.
경제적 의미가 있는 설정을 명시하고, 엔진이 실제로 적용한 설정을 실행 기록에 출력하도록 했습니다. 기본값은 탐색 단계에서는 편리하지만, 시간에 따른 결과를 비교할 때는 근거로 삼기 어렵습니다.
다음에는 생략할 일
첫 번째로 달라진 체결을 살펴보기 전에 요약 지표를 비교하느라 반나절을 썼습니다. 거기서 시작하지 마세요. 두 이벤트 로그를 타임스탬프순으로 정렬하고 처음 어긋나는 지점을 비교하세요. 그 이후의 차이는 대개 하나의 원인에서 비롯됩니다.
컨테이너 이미지 하나로 실행을 재현할 수 있다는 생각도 버리겠습니다. 컨테이너는 소프트웨어 환경 대부분을 고정하지만, 외부 데이터 파일이나 실행 중 가져온 수수료 체계, 공급업체가 수정한 과거 데이터까지 고정해 주지는 않습니다.
백테스트 결과가 바뀌면 두 실행의 명세와 로그를 모두 보존한 다음, 차이의 원인을 하나씩 해결하세요. 다시 실행할 수 있는 곡선만이 유용한 결과는 아닙니다. 어떤 데이터와 가정이 결과를 만들었는지, 다음 실행에서 왜 달라질 수 있는지 설명하는 기록도 필요합니다.
← 전체 글


