평균모멘텀스코어 백테스트(ETF & 현금 vs ETF)
본문 바로가기
파이썬(Python)/파이썬으로 투자실험

평균모멘텀스코어 백테스트(ETF & 현금 vs ETF)

by Squat Lee 2024. 3. 8.

지난번 포스트에 이어서 평균모멘텀스코어 백테스트를 계속 진행해 보겠습니다.

2024.03.04 - [파이썬(Python)/파이썬으로 투자실험] - 미국 ETF 평균모멘텀스코어로 비중 조절 백테스트(ETF 포트폴리오 투자)

 

지난 포스트에서는 ETF 단독으로 투자했을 때 보다 평균모멘텀스코어의 CAGR이 많이 저조했습니다.

 

이번 포스트에는 1. 투자기간을 좀 더 길게 하고, 2. MDD도 구해보고, 3. 현금대신 채권을 넣어서 백테스트 결과를 보도록 하겠습니다.

 

MDD를 구하는 코드는 아래 포스트에 설명을 했습니다.

 

2024.03.06 - [파이썬(Python)/파이썬으로 투자실험] - 파이썬으로 MDD 구하기(pandas, numpy)

 

 

지난번에는 S&P500을 추종하는 ETF 중 'VOO'를 이용해서 백테스트를 했습니다. 아무래도 기간이 짧아서 그런지 CAGR 차이가 크더라구요.

 

그래서 이번에는 'SPY'를 가지고 백테스트 해 보겠습니다. 전체 코드는 아래와 같습니다.

 

import pandas as pd
from pandas_datareader import data as pdr
import yfinance as yf
yf.pdr_override()
import numpy as np
import matplotlib.pyplot as plt

pd.options.display.float_format = '{:,.2f}'.format

ticker = 'SPY'
df_o = pdr.get_data_yahoo(ticker)

# 월말 날짜만 가져오기
def get_monthly_end(df_d):
    df_d['Odate'] = df_d.index
    df = df_d.groupby(by=[df_d.index.year, df_d.index.month]).last()
    df.set_index('Odate', inplace=True)
    return df

#평균모멘텀스코어 구하기
def score(df_d):
    df = get_monthly_end(df_d)
    li_score = []
    for i in range(len(df)):
        if i < 11:
            li_score.append(0)
        else:
            val = 0
            for m in range(1, 13):
                if df['Close'].iloc[i] - df['Close'].iloc[i - m] > 0:
                    val += 1
            li_score.append(val / 12)

    df['평균모멘텀스코어'] = li_score
    return df

#MDD 구하기
def get_mdd(col):
    arr_v = np.array(col)
    peak_lower = np.argmax(np.maximum.accumulate(arr_v) - arr_v)
    peak_upper = np.argmax(arr_v[:peak_lower])

    return (arr_v[peak_lower] - arr_v[peak_upper]) / arr_v[peak_upper]

#백테스트
def back_test(df_d):
    df = score(df_d)
    주식 = []
    현금 = []
    합계 = []
    for i in range(len(df)):
        if i == 0:
            주식.append(0)
            현금.append(1000)
            합계.append(1000)
        else:
            합계.append(주식[i-1]*df['Close'].iloc[i]/df['Close'].iloc[i-1] + 현금[i-1]*(1 + 0.03/12))
            주식.append(합계[i]*df['평균모멘텀스코어'].iloc[i])
            현금.append(합계[i]-주식[i])

    df['주식'] = 주식
    df['현금'] = 현금
    df['합계'] = 합계

    # 2가지 자산의 누적수익률을 비교하기 위해 백분율로 표현
    df['주가백분율'] = df['Close'] / df['Close'].iloc[0]
    df['전략백분율'] = df['합계'] / df['합계'].iloc[0]

    #CAGR
    cagr_etf = (df['주가백분율'].iloc[-1] / df['주가백분율'].iloc[0]) ** (1 / (len(df) / 12)) - 1
    cagr_전략 = (df['전략백분율'].iloc[-1] / df['전략백분율'].iloc[0]) ** (1 / (len(df) / 12)) - 1
    cagr = f'CAGR(ETF) : {round(cagr_etf, 2)} / CAGR(전략) : {round(cagr_전략, 2)}'
    print(cagr)

    #MDD구하기
    mdd_etf = get_mdd(df['Close'])
    mdd_전략 = get_mdd(df['합계'])
    mdd = f'MDD(ETF) : {round(mdd_etf,2)} / MDD(전략) : {round(mdd_전략,2)}'
    print(mdd)

    #그래프로 표현하기
    plt.rcParams['figure.figsize'] = (16, 9)

    plt.plot(df.index, df['주가백분율'], color='blue', label=ticker)
    plt.plot(df.index, df['전략백분율'], color='red', label='Ave_momentum')
    plt.grid(True)
    plt.legend(loc='best')

    plt.title(f'{ticker} vs Average_Momentum')

    plt.show()

    return df



back_test(df_o)

 

결과는 아래와 같습니다.

 

  • CAGR(ETF) : 0.08 / CAGR(전략) : 0.08
  • MDD(ETF) : -0.25 / MDD(전략) : -0.13

 

ETP만 단독으로 투자했을 때와 현금과 비중을 조절하면서 투자를 한 경우와 CAGR은 거의 동일하게 나옵니다. 하지만, MDD는 ETF만 투자했을 때 보다 반으로 줄었습니다.

 

ETF 단독투자는 리밸런싱 개념이 없지만, 평균모멘텀스코어는 매달 말일 기준으로 최근 1년치의 매달 말일 모멘텀에 따라서 비중을 조절했습니다. 

 

그런데 여기에서 SPY의 MDD가 잘못 되었다는 것을 혹시 눈치 채셨나요? 포트폴리오 비주얼라이저로 백테스트를 하면 약 -50%가량 나오는데 여기는 -25%밖에 나오지 않잖아요.

포트폴리오비주얼라이저에서 SPY 백테스트 결과

 

저도 코드가 잘못 나온건지 고민을 했는데, 저는 일 데이터가 아니라 월 데이터로 했기 때문에 달과 달 사이에 폭락한 수치는 반영이 안 되어서 그런것 같습니다. 한 달에 한 번만 주식을 보면 이게 일리가 있는데 현실적으로는 그렇지 않겠죠?

 

이 부분은 다음에 코드에서 업데이트 해 보고 이제 현금 대신 채권을 투자하면 수익률이 어떻게 달라지는지 보겠습니다.

 

현금은 쉽게 연 3% 이율로 계산했지만, 채권은 주식처럼 채권의 변동성에 따라서 수익률이 달라지겠죠? 코드를 좀 고쳐서 다음 포스트에 이어서 설명하도록 하겠습니다.

728x90
반응형

댓글