본문 바로가기

EP 빌드로그

EP.25 백테스트 미래참조 버그, 결과가 뒤집힌 날

들어가며: 그럴듯한 숫자를 의심하기

퀀트 백테스트를 만지다 보면 결과가 좋게 나올수록 오히려 조심해야 하는 순간이 있습니다. 이번 글은 매매 당일 자기 종가로 레짐(regime)을 라벨링하던 미래참조(look-ahead) 버그를 발견하고 고친 기록입니다. 고치고 나니 레짐 필터의 효과가 부풀려져 있었을 뿐 아니라, 최고와 최악의 순서까지 반대로 뒤바뀌었습니다.
이 글은 완결된 전략 소개가 아니라, 하나의 검증 실패와 수정 과정을 남기는 개발 기록입니다.


문제: 레짐과 수익률이 같은 하루를 봤다

build_backtest는 각 매매일에 regime_by_date[매매일] = 그 매매일의 지수 등락률로 레짐 라벨을 붙이고 있었습니다. 문제는 수익률도 같은 날 시가→종가로 측정했다는 점입니다. 즉 레짐과 수익률이 같은 하루를 측정한 것입니다.

지수가 -4%로 끝난 날을 RISK_OFF로 부르고 "그날 개별 종목이 빠졌다"고 말하는 것은 발견이 아니라 동어반복에 가깝습니다. "RISK_OFF를 빼면 흑자"라는 결론 역시 그날 장이 끝나야 알 수 있는 정보로 필터링한 것이라, 매매 시점에는 알 수 없는 정보를 쓴 셈입니다.


배경: 미래참조(look-ahead)란

이해를 돕기 위한 짧은 배경입니다. 미래참조는 백테스트가 그 시점에는 알 수 없었던 미래 정보를 의사결정에 사용하는 오류를 말합니다. 실전에서는 재현되지 않는 성과를 만들어내기 때문에, 백테스트의 대표적인 함정으로 꼽힙니다.
이번 사례에서 매매 시점(당일 시가)에 알 수 있는 정보는 전일 지수까지입니다. 당일 종가로 레짐을 붙이는 순간, 아직 오지 않은 정보를 미리 본 것이 됩니다.


해결: 전일 레짐을 붙이고 회귀 테스트

매매일에 직전 거래일의 레짐을 붙이도록 바꿨습니다. 규칙은 단순합니다.

  • regime_by_date[t] = regime(t-1) — 매매 시점에 알 수 있는 범위(전일 지수)로 제한

그리고 회귀 테스트 3건을 추가했습니다.

  1. 급락한 당일에는 RISK_OFF가 붙지 않을 것
  2. 급락한 다음 날에는 붙을 것
  3. 데이터 첫날은 레짐이 없을 것

결과: 순서까지 뒤바뀌었다

수정 후 결과가 뒤집혔습니다. 아래는 개발 기록에 남은 수정 전후 수치입니다.

RISK_ON+1.63% / 49.5%-1.11% / 36.5%
RISK_OFF-4.17% / 21.3%-2.30% / 32.6%

순서까지 반대가 됐습니다. 이제 RISK_ON이 최악, NEUTRAL이 최고로 나왔고, 승률 편차는 28.2%p에서 12.2%p로 줄었습니다. RISK_OFF vs 나머지 검정은 p=0.220으로 우연과 구분되지 않았습니다. 즉 "전일 레짐으로 매수를 거른다"는 실행 가능한 규칙에는 가치가 없다는 결론입니다.


파급: 같은 도구를 쓴 다른 규칙

ENABLE_RISK_OFF_BUY_BLOCK의 근거였던 이전 DEVLOG 기록("차단 시 기대값 -0.49% → +0.57% 흑자 전환")이 같은 도구에서 나왔다면, 동일한 미래참조를 안고 있을 가능성이 있습니다.

다만 주의할 점은, 라이브 차단은 당일 실시간 지수를 사용하므로 두 백테스트 어느 쪽도 측정하지 않은 규칙이라는 것입니다. 지금으로선 끄자고 할 근거도, 켜두자고 할 근거도 없습니다. 이 규칙을 제대로 재현·측정하려면 장중 지수 시계열이 필요합니다.


배운 점

가장 크게 남은 교훈은 하나입니다. 백테스트 결과를 받아들이기 전에 "이 정보를 그 시점에 알 수 있었나"부터 물어야 한다는 것입니다.

  • 미래참조는 결과를 좋게 만들 뿐 아니라 방향까지 바꾼다 — 고치고 나니 최고와 최악이 뒤바뀌었다.
  • 이런 버그는 조용하다. 숫자가 그럴듯하면 아무도 시점을 따지지 않는다.

라이브 차단 규칙을 실제 측정 가능한 형태로 재현하려면 장중 지수 시계열이 있어야 합니다.

반응형