자산배분의 기본 개념은 장기적으로 우상향하는 서로 반대로 움직이는 자산에 투자하는 것입니다.
보통 주식+채권의 형태로 하는데, 상관성이 0인 현금으로 백테스트를 해 보았습니다.
SPY와 TLT 그리고 QQQ와 GLD를 기준으로 현금과 자산배분을 했을시 결과값을 비교했습니다.
배당수익, 수수료, 세금은 고려하지 않았습니다. 현금은 수익률을 4%로 설정하였습니다.(현재 외화 RP가 4~4.5%정도 됩니다.)
주식과 현금은 최초 동일한 비율로 투입이 되고 각 모멘텀 기간별로 아래와 같이 결과값이 나옵니다.
구 분 | 3개월 | 6개월 | 9개월 | 12개월 | ||||
CAGR | MDD | CAGR | MDD | CAGR | MDD | CAGR | MDD | |
SPY | 10.2 | -51.5 | 10.2 | -51.5 | 10.2 | -51.5 | 10.2 | -51.5 |
SPY+TLT | 9.9 | -39.1 | 10.1 | -37.8 | 9.9 | -36.0 | 9.9 | -34.9 |
SPY+현금 | 7.9 | -22.3 | 8.9 | -17.7 | 8.9 | -23.1 | 9 | -25.8 |
Momentum 기간별로 CAGR과 MDD 비교(2005.1.2~2024.2.29)
구 분 | 3개월 | 6개월 | 9개월 | 12개월 | ||||
CAGR | MDD | CAGR | MDD | CAGR | MDD | CAGR | MDD | |
QQQ | 14.7 | -51.2 | 14.7 | -51.2 | 14.7 | -51.2 | 14.7 | -51.2 |
QQQ+GLD | 15.9 | -38.9 | 14.7 | -36.0 | 14.3 | -36.7 | 14.3 | -39.4 |
QQQ+현금 | 11.8 | -23.5 | 12.2 | -23.4 | 12.3 | -25.1 | 12.5 | -26.0 |
Momentum 기간별로 CAGR과 MDD 비교(2005.1.2~2024.2.29)
QQQ로 했을시 CAGR이 12%넘고 MDD도 -23%까지 떨어집니다.
당연히 CAGR은 QQQ+GLD가 가장 높지만, 안정적으로 투자를 하시고 싶은 분은 현금과 자산을 분배해서 투자하는 방법도 괜찮은 것 같습니다.
백테스트를 한 전체 코드는 아래와 같습니다.
import pandas as pd
from pandas_datareader import data as pdr
import yfinance as yf
yf.pdr_override()
from datetime import datetime
import matplotlib.pyplot as plt
pd.options.display.float_format = '{:,.2f}'.format
p1 = 'QQQ'
p2 = 'GLD'
현금비율 = 0 #현금비율
#초기 투입자금
주식자금 = 500
채권자금 = 500
현금자금 = 0
#평균모멘텀스코어 기간
moment_months = 3
start = datetime(2000, 1, 1)
end = datetime(2024, 2, 29)
def get_etf_data(start, end, p):
if p[-2:] == 'KS': #한국 ETF를 달러로 계산
df_ex = pdr.get_data_yahoo('USDKRW=X', start, end) # 환율데이터 가져오기
df_ko = pdr.get_data_yahoo(p, start, end)
df = pd.merge(df_ko['Adj Close'], df_ex['Adj Close'], left_index=True, right_index=True)
df.columns = ['국내ETF', '환율']
df['Adj Close'] = df['국내ETF'] / df['환율']
else:
df = pdr.get_data_yahoo(p, start, end)
return df
df1 = get_etf_data(start, end, p1)
print(df1)
df2 = get_etf_data(start, end, p2)
print(df2)
df_o = pd.merge(df1['Adj Close'], df2['Adj Close'], left_index=True, right_index=True)
df_o.columns = ['p1', 'p2']
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, 현금비율, p1자금, p2자금, 현금자금):
df = score(df_d, moment_months)
li_p1 = []
li_p1c = [] #자산p1의 개수
li_p2 = []
li_p2c = [] #자산p2의 개수
li_cash = []
li_sum = []
for i in range(len(df)):
if i == 0:
li_p1.append(p1자금)
li_p1c.append(p1자금/df['p1'].iloc[i])
li_p2.append(p2자금)
li_p2c.append(p2자금/df['p2'].iloc[i])
li_cash.append(현금자금)
li_sum.append(li_p1[i]+li_p2[i]+li_cash[i])
else:
li_sum.append(li_p1c[i-1]*df['p1'].iloc[i] + li_p2c[i-1]*df['p2'].iloc[i] + li_cash[i-1]*(1+0.03/12))
li_p1.append(li_sum[i]*(1-현금비율)*df['평균모멘텀스코어'].iloc[i])
li_cash.append(li_sum[i]*현금비율)
li_p2.append(li_sum[i]-li_p1[i]-li_cash[i])
li_p1c.append(li_p1[i]/df['p1'].iloc[i])
li_p2c.append(li_p2[i]/df['p2'].iloc[i])
df['자산1'] = li_p1
df['자산2'] = li_p2
df['현금'] = li_cash
df['합계'] = li_sum
df['자산1 개수'] = li_p1c
df['자산2 개수'] = li_p2c
# 월말 데이터를 일별 데이터로 만들기
df = pd.merge(df_o[['p1', 'p2']], df[['평균모멘텀스코어', '자산1', '자산2', '현금', '합계', '자산1 개수', '자산2 개수']],
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['자산1 개수'].iloc[s] = df['자산1 개수'].iloc[s-1]
df['자산2 개수'].iloc[s] = df['자산2 개수'].iloc[s-1]
df['자산1'].iloc[s] = df['p1'].iloc[s] * df['자산1 개수'].iloc[s]
df['자산2'].iloc[s] = df['p2'].iloc[s] * df['자산2 개수'].iloc[s]
df['현금'].iloc[s] = df['현금'].iloc[s-1]
df['합계'].iloc[s] = df['자산1'].iloc[s] + df['자산2'].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}, CASH {int(현금비율*100)}%)_{moment_months} Months')
plt.show()
return df
back_test(df_o, moment_months, 현금비율, 주식자금, 채권자금, 현금자금)
728x90
반응형
'파이썬(Python) > 파이썬으로 투자실험' 카테고리의 다른 글
미국ETF와 한국ETF를 자산분배해서 투자하면 어떻게 될까요? (0) | 2024.03.29 |
---|---|
한국, 미국 ETF 자산 간 상관 관계 구하기 (우상향하는 반대되는 자산) (2) | 2024.03.27 |
파이썬으로 CAGR과 MDD 구현하기(Pandas) (0) | 2024.03.20 |
월말 vs 월평균 비교(평균모멘텀스코어) (2) | 2024.03.18 |
포트폴리오에 현금을 포함 시킬 때 CAGR과 MDD 결과 (0) | 2024.03.16 |
댓글