본문 바로가기
파이썬(Python)으로 데이터 기반 주식 퀀트 투자하기/chapter4 백테스팅 알고리즘 트레이딩 정복하기 전략

chapter4 알고리즘 트레이딩 정복하기-Sharpe Ratio 와 MMD 접목 정량적 백테스팅 정의 및 성과지표 실습

by ohsungjun 2026. 1. 29.
################ 백테스팅 파라미터 ################ 필수
holding_cash = 1_000_000 # 보유 현금
position = 0 # 현재 보유 포지션
avg_price = 0 # 평단가
slippage = 0.004 # 슬리피지
daily_total_value = [] # 일별 총 포트폴리오 가치
################ 백테스팅 파라미터 ################

################ 전략 파라미터 ################
holding_time_passed = 0 # 마지막 매수 후 경과 일수
################ 전략 파라미터 ################

# for 문으로 하루씩 백테스팅 진행
for idx,data in d.iterrows():
    daily_total_value.append(0)

    if (data['close'] < data['20d_mean']) and (data['close'] == data['5d_min']):
        if holding_cash > 1*data['close']:
            position += 1
            holding_cash -= 1 * data['close']
            avg_price = data['close']
            holding_time_passed = 0

    # 마지막 매수 3일 후 매도
    if position > 0 and holding_time_passed == 3:
        holding_cash += position * data['close'] * (1-slippage)
        position = 0
        avg_price = 0

    # 오늘의 마무리
    if position > 0:
        holding_time_passed += 1
   
    daily_total_value[-1] = holding_cash + position * data['close']

plt.figure(figsize=(15,8))
plt.plot(daily_total_value)

매도에 슬리피지까지 적용한 백태스팅을 시각화

daily_total_value #일 평균가치를 슬리피지 적용한 백태스킹

 

다음단계:성과 분석실습

 

전략성과 측정

# 전략 총 수익률 계산
total_return_pct = daily_total_value[-1]/daily_total_value[0]
# 총수익률=daily_total_value[-1]는 최종포트폴리오 평가가치 / daily_total_value[0] 처음가지고 있던 100만원
 
print('총 수익률: {:.2f}%'.format((total_return_pct-1)*100)) #2f문자열은 정수만자리 -1은 원가를 빼기위해서 100은 퍼센트

총 수익률: 9.99% 나의 목표는 퀀트를 했을 때, 국민연금 20%보다 높도록 만들기

# 1년을 250일로 가정, 연 복리 수익률 계산
total_years = len(daily_total_value)/250 #실제 1년에 트레이딩 시간은 보통 250~252일임 주식 한국시장 기준
print('총 백테스팅 기간: {:.2f}년'.format(total_years))

총 백테스팅 기간: 14.14년

daily_total_value #일 평균가치를 슬리피지 적용한 백태스킹의 길이/1년 으로 나눔= 총 백테스팅 기간


복리(Compounding)**는 선택이 아닌 **'필수 전략이자 목표'**에 가깝습니다. 사실 퀀트 투자가 일반적인 주관적 투자보다 복리 효과를 누리기에 훨씬 유리한 구조를 가지고 있거든요.

왜 퀀트가 복리 투자를 많이 하는지, 그 핵심 이유를 3가지로 정리해 드릴게요.

1. 수학적 '기댓값'의 반복 (Edge)

퀀트는 감이 아니라 **통계적 우위(Edge)**가 확인된 전략을 사용합니다. 예를 들어 "이 조건에서 사면 55% 확률로 오른다"는 데이터가 있다면, 이를 수천 번 반복합니다. 이때 수익금을 다시 원금에 합쳐서 다음 매매에 투입하는 자금 관리(Position Sizing) 기법을 쓰는데, 이것이 바로 복리의 핵심입니다.

2. MDD(최대 낙폭) 관리의 마법

복리 투자의 최대 적은 '큰 손실'입니다. 1억 원이 -50%가 되어 5천만 원이 되면, 다시 1억이 되기 위해선 +100%의 수익이 필요하죠. 퀀트는 **MDD(Maximum Drawdown)**를 철저히 통제하기 때문에, 복리 그래프가 꺾이지 않고 우상향할 수 있도록 방어력을 높이는 데 집중합니다.

3. 감정이 배제된 재투자

사람은 돈을 벌면 "이건 좀 쓰고 싶다"거나, 잃으면 "무서워서 투자를 쉬고 싶다"는 유혹에 빠집니다. 하지만 퀀트 시스템은 수익을 기계적으로 재투자합니다. 복리의 마법이 중간에 끊기지 않도록(Never interrupt compounding) 강제로 시스템화하는 것이죠.

 

POW거듭제곱 설명

#복리 수익율을 할려면
import math #수학적 라이브러리를 불러옴
# 전략 총 수익률 계산total_return_pct 
annaul_return = math.pow(total_return_pct,1/total_years)

print('연 수익률: {:.2f}%'.format((annaul_return-1)*100))

연 수익률: 0.68%


# Sharpe Ratio
daily_return = math.pow(total_return_pct,1/len(daily_total_value))#일별 수익률을 복리 투자했을때 총수익률/일 갯수 =앒평균 수익률 POW거듭제곱이고,1은복리구조 역산 기하평균 법칙때문에
daily_std = pd.DataFrame(daily_total_value).pct_change().std()[0] #단리

 

total_return_pct은전략 총 수익률 계산

total_return_pct = daily_total_value[-1]/daily_total_value[0]# 총수익률=daily_total_value[-1]는

최종포트폴리오 평가가치 / daily_total_value[0] 처음가지고 있던 100만원

daily_total_value 일 평균가치를 슬리피지 적용한 백태스킹  포트폴리오

1. 포트폴리오 수익률 (R_p)

  • 의미: 내가 실행한 매매 전략을 통해 실제로 얻은 수익률입니다.
  • 역할: 분자 자리에 위치하여, 이 값이 높을수록 샤프 지수가 올라갑니다. 즉, 수익이 좋을수록 평가는 좋아집니다.

2. 리스크 프리 수익률 (R_f) — 무위험 수익률= 이건 외부에서 주어짐

  • 의미: 위험을 전혀 감수하지 않고도 얻을 수 있는 수익률을 말하며, 보통 **'국채 금리'**나 **'은행 예금 금리'**를 사용합니다.
  • 계산의 이유: 내가 주식이라는 위험 자산에 투자했다면, 최소한 "가만히 은행에 넣어뒀을 때 버는 돈($R_f$)"보다는 더 벌어야 의미가 있기 때문입니다.
  • 초과 수익률: 따라서 분자인 $R_p - R_f$는 내가 위험을 감수해서 얻은 순수한 '실력' 수익률을 뜻합니다.

3. 포트폴리오 변동성 (sigma_p) — 표준편차

  • 의미: 수익률이 얼마나 들쭉날쭉했는지를 나타내는 **'리스크'**의 척도입니다.
  • 역할: 분모 자리에 위치합니다.
    • 그래프가 완만하게 우상향하면 변동성이 낮아 샤프 지수가 높아집니다.
    • 그래프가 요동치며 불안하게 올라가면 변동성이 높아 샤프 지수가 낮아집니다.

 

  • daily_return은 공식의 분자(R_p)에 해당합니다.
    • 엄밀히 말하면 $R_p$는 포트폴리오 수익률입니다. 코드에서 math.pow를 통해 구한 값은 전체 기간의 총 수익을 일일 단위로 쪼갠 **'일일 복리 수익률'**이므로, 공식의 상단에 들어갈 수익률 데이터를 준비하는 과정입니다.
    • 이미지 설명에 있듯이 보통 $R_f$(무위험 수익률, 예: 채권 금리)를 빼주지만, 코딩 시에는 보통 0으로 간주하고 생략하기도 합니다.
  • daily_std는 공식의 분모(sigma_p)에 해당합니다.
    • $\sigma_p$는 포트폴리오의 **변동성(표준편차)**을 의미합니다.
    • 작성하신 std()[0]가 바로 이 변동성을 계산하는 핵심 코드입니다.
  • 1. 단리와 표준편차의 관계
    • 단리(pct_change): 어제 대비 오늘 자산이 몇 % 변했는지만 단순하게 계산합니다. (예: 100원에서 110원 되면 +10%)
    • 표준편차(std): 위에서 구한 '일별 수익률들'이 평균적으로 얼마나 변동이 심했는지를 계산합니다.
    즉, **"단리로 계산된 일일 수익률들의 변동성"**을 구하기 위해 std()를 사용하는 것입니다.
  •  

RF는 외부에서 주어짐

 

 

 

 

daily_return = math.pow(total_return_pct,1/len(daily_total_value))#일별 수익률을 복리 투자했을때 총수익률/일 갯수 =앒평균 수익률 POW거듭제곱이고,1은복리구조 역산 기하평균 법칙때문에   -1은 원금을 제외시키기 위해 리스크 프리 수익률 (R_f)를 뺌 /나누기

daily_std = pd.DataFrame(daily_total_value).pct_change().std()[0] #단리 *곱하기 250일 주식시장이 여느날 표준편차

 

이게 규칙임


 

 

1. 일 수익률 (Daily Return)

무위험 수익률($R_f$)을 적용하느냐에 따라 '내가 인정받는 수익률' 숫자가 달라집니다.

  • 반영 안 함 (daily_return - 1): 내 계좌에 찍힌 순수 수익률 그대로를 의미합니다.
  • 반영 함 (daily_return - 1 - rf_daily): 내 수익에서 "가만히 있어도 벌었을 돈($R_f$)"을 뺀 초과 수익률입니다.
  • 결과: 이미지를 보면 무위험 수익률을 반영했을 때 샤프 지수가 -0.96으로 마이너스가 나왔죠? 이는 내 전략의 일 수익률이 은행 이자($R_f\_daily$)보다도 낮았다는 뜻입니다.

2. 일 변동성 (Daily Volatility / std)

변동성은 바뀌지 않습니다.

  • 변동성은 내 자산 가치가 얼마나 왔다 갔다 하는지(위험도)를 측정하는 값입니다.
  • 시중 금리($R_f$)가 몇 %든 상관없이, 내 포트폴리오의 가격이 흔들리는 정도(daily_std)는 동일하기 때문입니다.

변동성은 유지되기만, 수익률이 달라짐 RF에 따라서

 

단리, 복리 설명

 

 Sharpe Ratio 와MMD 접목시키기

daily_total_value #일 평균가치을 구함 백테스팅으로

누적 최대값 cummax

dd = tv/tv.cummax()는 **MDD(최대 낙폭)를 계산하기 위해 현재 시점의 자산 가치가 역대 최고점 대비 어느 정도 수준인지(Drawdown)**를 구하는 공식입니다.

 

[0]이 있는 이유: 데이터의 "위치" 지정

코드 상단에서 tv = pd.DataFrame(daily_total_value)를 통해 데이터를 Pandas DataFrame 형식으로 만드셨습니다.

100이 있는 이유: "비율"을 "퍼센트(%)"로 변환