#퀀트트레이딩#백테스트#샤프비율#과최적화#통계검증#DSR

Deflated Sharpe Ratio 계산법 — 백테스트 과최적화를 걸러내는 통계 검증

👁 0 조회
Deflated Sharpe Ratio 계산법 — 백테스트 과최적화를 걸러내는 통계 검증 핵심 개념을 담은 커버 이미지
Deflated Sharpe Ratio 계산법 — 백테스트 과최적화를 걸러내는 통계 검증 핵심 개념을 담은 커버 이미지

작년 5월, 제가 개발한 모멘텀 전략이 백테스트에서 연환산 샤프비율 2.3을 찍었어요. 그런데 실전 투입 3개월 만에 수익률이 마이너스로 돌아섰죠. 문제는 파라미터 조합을 87가지나 시험한 끝에 얻은 최고 성과였다는 점입니다. 이 글을 읽으면 López de Prado가 제안한 Deflated Sharpe Ratio(DSR)로 다중검정 편향을 보정해서, 백테스트 샤프비율이 정말 통계적으로 유의한지 판단할 수 있게 됩니다.

왜 일반 샤프비율만으론 부족한가요?

백테스트에서 100개 전략을 돌리고 가장 좋은 결과만 골랐다면, 그 샤프비율은 운 좋은 outlier일 가능성이 높습니다. DSR은 다중검정 하에서 최고 샤프비율이 실제로 양수일 확률을 계산해요. 수백 개 파라미터 조합 테스트 후 우연으로 나온 결과를 걸러내는 셈이죠.

실증연구 사례를 보면 감이 옵니다. 2022년 3월부터 2026년 5월까지 테스트한 전략이 연샤프 2.68, CAGR 27.3%, 최대낙폭 -4.1%를 기록했어요. 하지만 DSR로 보정하니 실제 유의확률은 훨씬 낮게 나왔습니다. 출처: Machine Learning Enhanced Multi-Factor Quantitative Trading with Bias Correction 논문(arXiv 2507.07107)

보정 요소는 세 가지입니다. 표본 길이, 비정규분포(왜도·첨도), 그리고 선택편향. in-sample 샤프와 out-of-sample 샤프의 상관계수가 0.05 미만이라는 현실을 반영한 거예요. 출처: The Deflated Sharpe Ratio 논문, Bailey & López de Prado

준비물

  • Python 3.8 이상
  • NumPy, SciPy 라이브러리 (pip install numpy scipy)
  • 백테스트 수익률 시계열 데이터 (일별 또는 월별)
  • 테스트한 전략 개수(N) 정보

필요한 통계량은 코드에서 자동 계산하므로 별도 통계 도구는 불필요합니다.

1단계: 기본 샤프비율 계산하기

1단계: 기본 샤프비율 계산하기
1단계: 기본 샤프비율 계산하기

먼저 익숙한 샤프비율부터 구해봅시다. 평균 수익률을 표준편차로 나누고 연환산합니다.

import numpy as np
from scipy import stats

# 예시: 252 거래일 수익률 (%)
returns = np.random.normal(0.08/252, 0.15/np.sqrt(252), 252)

mean_return = np.mean(returns)
std_return = np.std(returns, ddof=1)
sharpe = (mean_return / std_return) * np.sqrt(252)

print(f"연환산 샤프비율: {sharpe:.2f}")

제 경우엔 연환산 샤프비율: 1.87이 나왔어요. 이제 이 값이 87가지 조합 중 최고치라는 점을 반영해 보정해야 합니다.

2단계: 비정규성 보정 — 왜도와 첨도 계산

수익률 분포가 정규분포가 아니면 샤프비율이 과대평가됩니다. 왜도(skewness)와 첨도(kurtosis)를 구해서 보정 계수를 만들죠.

skew = stats.skew(returns)
kurt = stats.kurtosis(returns)  # excess kurtosis

# 비정규성 보정 계수
T = len(returns)
variance_correction = 1 - skew * sharpe + ((kurt - 1) / 4) * (sharpe ** 2)
sharpe_adjusted = sharpe * np.sqrt(variance_correction)

print(f"왜도: {skew:.3f}, 첨도: {kurt:.3f}")
print(f"보정 후 샤프: {sharpe_adjusted:.2f}")

왜도 -0.123, 첨도 0.456이 나왔고, 보정 후 샤프는 1.82로 소폭 하락했어요.

3단계: 다중검정 페널티 적용 — DSR 최종 계산

3단계: 다중검정 페널티 적용 — DSR 최종 계산
3단계: 다중검정 페널티 적용 — DSR 최종 계산

이제 핵심입니다. 테스트한 전략 개수(N)를 반영해서 최고 샤프비율의 기댓값을 구하고, 표준편차로 나눠 표준화합니다.

N = 87  # 테스트한 전략 개수

# 최고 샤프의 기댓값 (Euler-Mascheroni 상수 활용)
euler_gamma = 0.5772
expected_max_sharpe = (1 - euler_gamma) * stats.norm.ppf(1 - 1/N) + \
                      euler_gamma * stats.norm.ppf(1 - 1/(N * np.e))

# 최고 샤프의 분산
variance_max_sharpe = (1 - euler_gamma) * stats.norm.ppf(1 - 1/N)**2 + \
                      euler_gamma * stats.norm.ppf(1 - 1/(N * np.e**2))**2 - \
                      expected_max_sharpe**2

# 최고 샤프의 표준편차
std_max_sharpe = np.sqrt(variance_max_sharpe)

# DSR 계산
dsr = (sharpe_adjusted - expected_max_sharpe) / std_max_sharpe

print(f"기댓값: {expected_max_sharpe:.3f}, 표준편차: {std_max_sharpe:.3f}")
print(f"DSR: {dsr:.3f}")

제 경우 DSR은 0.42가 나왔습니다. 보통 DSR > 0.95 이상이어야 95% 신뢰수준에서 유의하다고 봐요. 0.42는 통계적으로 우연과 구분하기 어렵다는 뜻이죠.

참고로 100개 전략 테스트 시 최고 샤프 2.0이라도 DSR 0.5 이하면 통계적으로 무의미할 수 있습니다. 출처: Deflated Sharpe Ratio Explained 실무 가이드

DSR 계산 시 자주 하는 실수는 뭔가요?

전략 개수(N)를 과소 신고

"최종 3개만 비교했으니 N=3"이라고 입력했다가 낭패를 봤어요. 중간에 버린 조합까지 다 세야 합니다. 제 경우 파라미터 그리드서치로 87개 조합을 전부 돌렸으니 N=87이 맞죠. 이걸 누락하면 DSR이 부풀려집니다.

표본 크기를 너무 짧게 잡음

일별 데이터 60일치로 계산했더니 DSR이 -0.3까지 떨어졌어요. 최소 252 거래일(1년) 이상 확보해야 분산 추정이 안정됩니다. 데이터가 짧으면 표준편차가 과대평가돼서 샤프와 DSR 모두 신뢰하기 어렵습니다.

왜도·첨도 보정을 건너뜀

정규분포를 가정하고 보정 없이 계산하면 DSR이 0.1~0.2 정도 높게 나옵니다. 특히 레버리지 전략은 꼬리 리스크가 커서 보정 효과가 크죠. 3단계 코드를 그대로 쓰면 자동으로 반영되니 생략하지 마세요.

다음 단계

DSR이 0.95 미만으로 나왔다면, 그 전략은 아직 라이브 투입하기엔 이릅니다. 표본을 늘리거나(out-of-sample 기간 추가), 파라미터 탐색 범위를 줄여서 선택편향을 낮추는 방향으로 재설계해보세요. 과최적화 함정에서 벗어나려면 DSR이 필수 체크포인트예요.

자주 묻는 질문

Q. DSR 기준값은 정확히 얼마인가요? A. DSR은 최고 샤프가 실제 양수일 확률로 해석합니다. 0.95 이상이면 95% 신뢰수준에서 통계적으로 유의해요. 0.99 이상은 99% 신뢰수준, 0.5~0.95는 회색지대로 추가 검증 없이 실전 투입은 위험합니다. z-score로 환산하면 DSR 0.95는 대략 단측 z=1.645에 해당하죠.

Q. 전략 개수(N)에 manual 조정도 포함하나요? A. 네, 파라미터 자동 그리드서치뿐 아니라 손으로 수정한 버전도 모두 셉니다. 예를 들어 5가지 자동 조합 후 "이건 좀 이상해"라며 2개 더 수동 테스트했다면 N=7입니다. 본인이 시험해본 모든 경우의 수가 대상이에요.

Q. DSR 계산에 거래비용은 반영하나요? A. DSR은 수익률 시계열만 입력받으므로, 수수료·슬리피지가 이미 차감된 순수익률을 쓰면 자동 반영됩니다. 백테스트 단계에서 실거래비용 0.1%를 빼고 계산한 returns를 넣으면 돼요. 비용 미반영 수익률로 DSR을 계산하면 과대평가됩니다.

Q. out-of-sample 테스트와 DSR 중 뭘 먼저 해야 하나요? A. in-sample 백테스트 결과로 DSR부터 계산하세요. DSR < 0.95면 out-of-sample로 갈 필요도 없이 재설계가 답입니다. DSR이 통과해도 out-of-sample 검증은 필수죠. 두 관문을 모두 넘어야 실전 후보입니다.

Q. 머신러닝 feature 수백 개로 학습했는데 N을 어떻게 세나요? A. 엄밀하게는 시험한 feature 조합 전체가 N입니다. 하지만 auto ML로 수천 조합을 내부적으로 탐색했다면 정확히 세기 어려워요. 이럴 땐 보수적으로 N=1000 이상으로 잡거나, cross-validation fold 개수 × 하이퍼파라미터 그리드 크기로 근사하세요. 과소 신고하면 DSR이 거짓양성을 냅니다.

이 글이 도움이 됐다면 공유해 주세요
X 공유

관련 글