백테스트 수익률이 과장되는 7가지 함정
결론부터 말하면, 백테스트 수익률은 전략이 미래를 잘 예측해서가 아니라 데이터에 미래가 섞였거나 실패 종목·비용·체결 불가능한 가격이 빠져서 높아질 수 있다.
검증 순서는 수익률 숫자를 보는 것보다 먼저 데이터 시점, 종목 모집단, 기업행동, 주문 시점과 비용을 확인하는 것이다. 그 뒤에 학습·선택에 쓴 구간과 한 번도 보지 않은 평가 구간을 분리한다.
2026-08-22에 일곱 함정을 작은 결정적 합성 데이터로 계산했다. 예를 들어 양의 수익률인 날만 미리 고르면 같은 일별 자료의 누적값이 2.32%에서 9.86%로 바뀌고, 살아남은 종목만 보면 평균 23.2%지만 상장폐지 -100% 종목을 포함하면 -38.4%가 된다. 이는 실제 시장 성과가 아니라 왜곡 원리를 드러내는 교육용 계산이다.
이 글로 해결할 수 있는 것
- 높은 수익률보다 먼저 검사할 데이터 계약을 안다.
- 일곱 함정이 결과를 어느 방향으로 왜곡하는지 숫자로 본다.
- 재현 가능한 백테스트 보고서에 필요한 항목을 정리한다.
실행 환경과 계산 기준
Python 표준 라이브러리만 사용하는 content/evidence/backtest_traps_demo.py를 실행했다. 난수 예제는 seed 42로 고정했고 결과는 content/evidence/backtest_traps_20260822.json에 저장했다. 모든 값은 합성 데이터다.
1. 미래정보 사용
오늘 장이 끝난 뒤 알 수 있는 당일 수익률의 부호로 오늘 포지션을 정하면 미래를 미리 본 것이다. 같은 8일 자료를 그대로 보유하면 2.32%지만 양의 날만 골라 담는 불가능한 규칙은 9.86%로 계산됐다.
2. 생존편향
현재 남아 있는 종목 목록으로 과거를 시험하면 상장폐지·합병·거래정지 종목이 사라진다. 합성 예에서 살아남은 종목만 보면 23.2%였지만 -100% 종목을 같은 비중으로 포함하면 평균은 -38.4%였다.
3. 거래비용과 체결오차 누락
여섯 거래의 총수익이 2.20%여도 매번 20bp 비용을 차감하면 0.98%로 줄었다. 실제 계산에는 수수료, 세금, bid-ask spread, 시장 충격과 부분 체결을 전략의 회전율에 맞게 넣어야 한다.
4. 신호를 본 같은 종가에 체결
100원 종가를 보고 매수 신호를 만든 뒤 같은 100원에 체결했다고 가정해 다음 105원까지 5%를 기록하면 시간 순서가 맞지 않을 수 있다. 다음 거래 가능 시점의 103원에 체결하면 같은 청산가의 수익은 1.94%다.
5. 액면분할·배당 같은 기업행동
100원이 2대1 분할 뒤 50원으로 보인 것을 원시 가격으로 계산하면 -50%다. 보유 가치가 유지된 조정 가격에서는 0%다. 수정주가를 썼다는 이름만 믿지 말고 배당, 분할, 합병과 상장폐지 처리 방식을 확인한다.
6. 과최적화와 여러 번의 선택
평균이 0인 무작위 후보 100개 중 학습 구간 합이 가장 큰 93번을 선택하자 학습 합은 20.57%처럼 보였지만 같은 후보의 독립 평가 구간 합은 -7.17%였다. 후보를 많이 시험할수록 우연히 좋은 결과를 고를 가능성이 커진다.
7. 결측치와 거래 불가 구간 삭제
문제가 있는 -10% 구간을 결측으로 보고 삭제하면 누적값은 6.11%였고 해당 구간을 명시하면 -4.50%였다. 결측을 0으로 채우거나 행을 지우기 전에 왜 비었는지, 그 시점에 실제 거래가 가능했는지 확인해야 한다.
게시할 백테스트의 최소 체크리스트
- 각 필드가 실제로 알 수 있었던 시각
- 해당 날짜의 전체 종목 모집단과 상장폐지 처리
- 기업행동·배당·통화 조정 규칙
- 신호 계산과 주문 체결의 시간 순서
- 수수료·세금·spread·slippage 가정
- 학습·선택·최종 평가 구간 분리
- 결측·거래정지·가격제한 처리와 코드 버전
한계와 투자정보 고지
이 계산은 실제 종목이나 시장을 사용하지 않은 교육용 예제이며 어떤 전략의 예상 수익률도 아니다. 실전에서는 데이터 공급자별 수정 방식, 주문 유형, 유동성과 시장 제도를 추가로 반영해야 한다. 이 글은 특정 금융상품의 매수·매도를 권유하지 않는다.
출처와 변경 이력
- 실행 코드:
content/evidence/backtest_traps_demo.py - 실행 결과:
content/evidence/backtest_traps_20260822.json - 최종 검토: 2026-08-22
- 2026-08-22: 일곱 합성 계산과 한계 최초 기록