평균모멘텀스코어는 일종의 추세추종 투자방법입니다.
지난번 포스트까지는 매월 주가를 비교해서 주가가 오르면 1을 부여하고, 반대로 주가가 떨어지면 0을 부여했습니다. 그리고 12개월치의 평균을 내서 그 비율만큼 주식에 투자를 하는 방법으로 백테스트를 하였습니다.
그런데 궁금증이 들더라구요. 12개월 평균이 최선일까 하는 의구심이 들었습니다.
그래서 개월수(1개월 ~ 12개월)에 따른 CAGR과 MDD 를 분석하였습니다.
리밸런싱 주기는 1개월이고, 세금, 수수료, 배당금은 고려하지 않았습니다.
SPY vs TLT 와 QQQ vs GLD를 분석해 보았습니다.
(위와같이 종목을 선정한 특별한 이유는 없습니다. 무난한 종목을 선택하였습니다.)
SPY vs TLT
개월수 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
전략 CAGR |
9.2 | 9.3 | 9.9 | 10.1 | 10.4 | 10.1 | 10.3 | 10.2 | 9.9 | 10.0 | 9.9 | 9.9 |
전략 MDD |
-44.2 | -40.4 | -39.1 | -38.9 | -37.7 | -37.8 | -37.0 | -36.4 | -36.0 | -35.6 | -35.3 | -34.9 |
※ SPY단독의 CAGR은 10.2%, MDD는 -51.5%
전체적으로 대동소이하게 결과값이 나오지만 1개월보다 5개월이 여러모로 유리하다는 점을 확인할 수 있습니다.
SPY 단독으로 MDD가 -51.5%인데 반해서 SPY와 TLT를 배분해서 투자하니 MDD가 -30%대로 떨어진 것을 확인할 수 있습니다. 서로다른 자산에 분산해서 리밸런싱을 하면 효과가 있다는 사실을 확인할 수 있습니다.
다른 개월수에 비해 5개월 모멘텀은 전략곡선이 SPY 단독으로 투자할 때 보다 항상 위에 위치해 있습니다. 즉, 수익률이 SPY 단독으로 할 때보다 더 높다는 사실을 확인할 수 있습니다.
이번에는 QQQ와 GLD를 가지고 백테스트 해 보겠습니다.
QQQ vs GLD
개월수 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
전략 CAGR |
11.8 | 12.7 | 14.7 | 14.0 | 13.9 | 13.5 | 13.3 | 13.3 | 13.1 | 13.1 | 13.1 | 13.1 |
전략 MDD |
-37.9 | 41.5 | -38.9 | -36.7 | -36.1 | -36.0 | -36.9 | -35.9 | -36.7 | -38.1 | -39.3 | -39.4 |
※ QQQ단독의 CAGR은 13.8%, MDD는 -51.2%
QQQ는 SPY와 비교해서 MDD는 비슷하지만 CAGR은 더 높으다는 사실을 확인할 수 있습니다.
MDD 는 QQQ 단독투자와 비교해서 확실히 낮아졌고, 3~5개월은 CAGR이 QQQ 단독투자보다 더 높으다는 사실을 확인할 수 있습니다.
결론
하나의 자산만 투자하는 것 보다 반대되는 자산을 함께 투자하는 것이 MDD가 낮아진다는 사실을 확인할 수 있었습니다. 또한 추세추종 개월수를 자산별로 조절하면 단독투자 보다 더 높은 수익률(CAGR)을 기대할 수 있습니다.
이번 포스트는 여기까지고 전체 코드는 아래와 같습니다.
import pandas as pd
from pandas_datareader import data as pdr
import yfinance as yf
yf.pdr_override()
import matplotlib.pyplot as plt
pd.options.display.float_format = '{:,.2f}'.format
p1 = 'QQQ'
p2 = 'GLD'
현금비율 = 0 #현금비율
#초기 투입자금
주식자금 = 500
채권자금 = 500
현금자금 = 0
#평균모멘텀스코어 기간
moment_months = 12
df1 = pdr.get_data_yahoo(p1)
print(df1)
df2 = pdr.get_data_yahoo(p2)
print(df2)
df_o = pd.DataFrame({'p1': df1['Adj Close'], 'p2' : df2['Adj Close']})
df_o.set_index(df1.index)
df_o.dropna(inplace=True)
# 월말 날짜만 가져오기
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, moment_months):
mm = int(moment_months)
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, mm+1):
if df['p1'].iloc[i] - df['p1'].iloc[i - m] > 0:
val += 1
li_score.append(val / mm)
df['평균모멘텀스코어'] = li_score
return df
#MDD 구하기
def get_mdd(col):
window = 252 #1년간 영업일을 252일로 가정
peak = col.rolling(window, min_periods=1).max() #기간 동안에 최고 주가(결과값은 시리즈로 나옴)
drawdown = col/peak - 1 #최고치 대비 현재 주가가 얼마나 하락했는지 구함
연도별mdd = drawdown.rolling(window, min_periods=1).min()#"최고치-현재주가" 중 가장 작은값을 구함
mdd = 연도별mdd.min()
return mdd
#백테스트
def back_test(df_d, moment_months, 현금비율, 주식자금, 채권자금, 현금자금):
df = score(df_d, moment_months)
주식 = []
주식수 = []
채권 = []
채권수 = []
현금 = []
합계 = []
for i in range(len(df)):
if i == 0:
주식.append(주식자금)
주식수.append(주식자금/df['p1'].iloc[i])
채권.append(채권자금)
채권수.append(채권자금/df['p2'].iloc[i])
현금.append(현금자금)
합계.append(주식[i]+채권[i]+현금[i])
else:
합계.append(주식수[i-1]*df['p1'].iloc[i] + 채권수[i-1]*df['p2'].iloc[i] + 현금[i-1])
주식.append(합계[i]*(1-현금비율)*df['평균모멘텀스코어'].iloc[i])
현금.append(합계[i]*현금비율)
채권.append(합계[i]-주식[i]-현금[i])
주식수.append(주식[i]/df['p1'].iloc[i])
채권수.append(채권[i]/df['p2'].iloc[i])
df['주식'] = 주식
df['채권'] = 채권
df['현금'] = 현금
df['합계'] = 합계
df['주식수'] = 주식수
df['채권수'] = 채권수
# 월말 데이터를 일별 데이터로 만들기
df = pd.merge(df_o[['p1', 'p2']], df[['평균모멘텀스코어', '주식', '채권', '현금', '합계', '주식수', '채권수']],
left_index=True, right_index=True, how='outer')
df.fillna(0, inplace=True)
for s in range(1, len(df)):
if df['합계'].iloc[s] == 0:
df['주식수'].iloc[s] = df['주식수'].iloc[s-1]
df['채권수'].iloc[s] = df['채권수'].iloc[s-1]
df['주식'].iloc[s] = df['p1'].iloc[s] * df['주식수'].iloc[s]
df['채권'].iloc[s] = df['p2'].iloc[s] * df['채권수'].iloc[s]
df['현금'].iloc[s] = df['현금'].iloc[s-1]
df['합계'].iloc[s] = df['주식'].iloc[s] + df['채권'].iloc[s] + df['현금'].iloc[s]
df['평균모멘텀스코어'].iloc[s] = df['평균모멘텀스코어'].iloc[s-1]
# 합계열에서 0이 아닌 숫자가 나오면 행삭제
df.drop(df[df['합계']==0].index, inplace=True)
# 2가지 자산의 누적수익률을 비교하기 위해 백분율로 표현
df['주가백분율'] = df['p1'] / df['p1'].iloc[0]
df['전략백분율'] = df['합계'] / df['합계'].iloc[0]
#CAGR
diff = df.index[-1].year - df.index[0].year
cagr_etf = (df['주가백분율'].iloc[-1] / df['주가백분율'].iloc[0]) ** (1 / diff) - 1
cagr_전략 = (df['전략백분율'].iloc[-1] / df['전략백분율'].iloc[0]) ** (1 / diff) - 1
cagr = f'CAGR(ETF) : {round(cagr_etf, 3)} / CAGR(전략) : {round(cagr_전략, 3)}'
print(cagr)
#MDD구하기
mdd_etf = get_mdd(df['p1'])
mdd_전략 = get_mdd(df['합계'])
mdd_t = f'MDD(ETF) : {round(mdd_etf,3)} / MDD(전략) : {round(mdd_전략,3)}'
print(mdd_t)
#엑셀파일로 만들기
# df.to_excel(f'평균모멘텀스코어 백테스트({p1}, {p2}, CAGR {round(cagr_전략*100,1)} '
# f'MDD {round(mdd_전략*100,1)}).xlsx')
#그래프로 표현하기
plt.rcParams['figure.figsize'] = (16, 9)
plt.plot(df.index, df['주가백분율'], color='blue', label=p1)
plt.plot(df.index, df['전략백분율'], color='red', label='Ave_momentum')
plt.grid(True)
plt.legend(loc='best')
plt.title(f'{p1} vs Average_Momentum({p1}, {p2})_{moment_months} Months')
plt.show()
return df
back_test(df_o, moment_months, 현금비율, 주식자금, 채권자금, 현금자금)
'파이썬(Python) > 파이썬으로 투자실험' 카테고리의 다른 글
월말 vs 월평균 비교(평균모멘텀스코어) (2) | 2024.03.18 |
---|---|
포트폴리오에 현금을 포함 시킬 때 CAGR과 MDD 결과 (0) | 2024.03.16 |
SPY & TLT 5:5 투자와 평균모멘텀스코어로 투자 시 CAGR 및 MDD 비교(정적 배분 투자 vs 동적 배분 투자) (1) | 2024.03.11 |
평균모멘텀스코어 백테스트(ETF & 현금 vs ETF) (1) | 2024.03.08 |
파이썬으로 MDD 구하기(pandas, numpy) (2) | 2024.03.06 |
댓글