멀티에셋 백테스트가 수익률을 뻥튀기하는 이유 — 바 인덱스 정렬 함정
코인+주식 합산 백테스트가 +13.84%로 나왔는데 실제로는 +7.71%였다. 서로 다른 자산군을 바 인덱스로 정렬하면 날짜가 어긋난다는 조용한 버그의 정체와 해결.
문제
자동매매 봇 백테스트에 코인 5종과 주식 24종을 합쳐 돌렸더니 합산 수익률 +13.84%가 나왔다. 코인 단독은 +2.81%, 주식 단독은 +1.78%였는데 합치니 갑자기 두 자릿수가 됐다. 분산 효과라기엔 과했다.
1
2
3
코인만 +2.81%
주식만 +1.78%
합산 +13.84% ← 왜 합치면 4배가 되지?
“너무 좋은 숫자는 축하가 아니라 조사 대상”이라, 백테스트 루프를 뜯어봤다.
원인
백테스트 엔진이 모든 종목을 바 인덱스 t로 동시에 처리하고 있었다.
1
2
3
for t in range(len(bars)):
for symbol in universe:
price = series[symbol][t] # 모든 종목의 t번째 바
여기에 함정이 있다. 코인은 24시간 거래라 연 365바, 주식은 평일만 거래라 연 250바다. 즉 같은 인덱스 t=100이:
- BTC에게는 상장 후 100일째 (달력상 4월 초)
- 삼성전자에게는 상장 후 100거래일째 (달력상 5월 말)
서로 다른 날짜를 같은 시점으로 취급하고 있었다. 포트폴리오 자본을 공유하는데 종목마다 시계가 어긋나 있으니, 손익 합산이 뒤죽박죽이 되면서 우연히 부풀려진 것이다. 단일 자산군만 돌릴 때는 모든 종목이 같은 거래일이라 문제가 안 드러났고, 코인+주식을 섞는 순간에만 터지는 버그였다.
해결 과정
시뮬레이션의 시계를 “바 인덱스”가 아니라 실제 거래일 날짜의 합집합으로 바꿨다.
- 모든 종목의 거래일을 모아 정렬된 날짜 축을 만든다.
- 각 날짜에서, 그 날 거래가 있는 종목만 처리한다.
- 그 날 바가 없는 종목(예: 주말의 주식)은 직전 종가로 평가만 하고 매매는 건너뛴다.
1
2
3
4
5
all_dates = sorted(union_of_all_trading_dates(series))
for d in all_dates:
for symbol in universe:
if d in series[symbol].index: # 그 날 거래가 있는 종목만
...
날짜 정렬로 고치자 합산 +13.84% → +7.71%로 내려왔다. 4배 수익의 절반이 버그였던 것이다. (이후 다음날 시가 체결·실거래소 데이터까지 반영하니 코인 단독은 +2.81%까지 더 떨어졌지만, 그건 별개의 검증 단계다.)
사용한 기술
- 바 인덱스 vs 날짜 인덱스: 시계열 백테스트에서 여러 자산을 다룰 땐
iloc(위치)이 아니라date(라벨) 기준으로 정렬해야 한다. - 거래일 합집합(union of trading days): 자산군마다 거래 캘린더가 다르면(코인 24/7 vs 주식 평일) 공통 시계를 날짜 합집합으로 만들고, 결측 종목은 직전값으로 forward-fill 평가.
- sanity check 습관: 단일 케이스는 정상인데 합칠 때만 이상하면 “합치는 로직”을 의심하라.
정리
- 자산군을 섞은 백테스트는 반드시 날짜로 정렬하라 — 바 인덱스 정렬은 코인(365바)과 주식(250바)의 같은 t를 다른 날짜로 매칭한다.
- 단일 자산군에선 안 보이는 버그가 있다 — 합산에서만 터지므로, 합치기 전후 숫자를 비교하라.
- 너무 좋은 숫자는 조사 대상이다 — +13.84% 중 절반이 정렬 버그였다.
이 글은 AI 자동매매 봇 3주 검증 회고의 트러블슈팅 서브클러스터 중 하나입니다. 전체 맥락(무엇을 검증했고 어떤 의사결정을 했는지)은 메인 글에서 볼 수 있습니다.
