이번 포스트에서는 당연한 얘기를 검증하는 과정을 진행하도록 하겠습니다.
초반에 투자금액이 클 수록 수익률은 올라갈까요?
우리 아내가 혹시나 이 포스트를 본다면 당연한 얘기를 하고 있냐고 얘기 하겠네요.
여태껏 제 포스트에서는 뇌피셜만 가지고 평소에 당연하다고 생각한 것을 검증하는 과정을 거쳤습니다.
이번에도 당연한 소리지만 진짜 초기 투자금에 비례해서 수익률이 올라 가는지, 그리고 그 차이는 얼마나 되는지를 검증ㅎ해 보도록 하겠습니다.
우선 백테스트 조건부터 말씀드리겠습니다.
자산은 QQQ와 GLD를 평균모멘텀스코어라는 방법을 통해 동적자산분배를 매월 리밸런싱을 통해 진행할 예정입니다. 백테스트 기간은 2005년 1월 2일부터 2024년 2월 29일까지 입니다.
세금, 배당금, 수수료는 고려하지 않았습니다. 그리고 아래 링크와 같이 지난 포스트에서 수행한 동적추가금액 투입 방법으로 모멘텀스코어가 1일때만 추가금액을 투입하였습니다.
총 투자 자금은 $230,000이며, 초기투자금을 제외한 나머지 자금은 230개월에 나눠서 투자하도록 하겠습니다.
우선 초기자금이 10%인 경우의 수익률입니다.
구 분 | CAGR | MDD | 총수익률 (수익률/총투입금액) |
QQQ 지수 | 14.7% | -51.2% | 1249% |
QQQ 월별적립식 투자 | 10.3% | -43% | 540% |
QQQ+ GLD 적립식투자(일괄분할매수) (동적자산분배+동적투자금액) |
10.9% | -37.3% | 610% |
QQQ+ GLD 적립식투자(초기10%) (동적자산분배+동적투자금액) |
11.6% | -37.3% | 700% |
※ 추가금을 수익으로 고려하지 않고 구한 CAGR 입니다.
생각했던대로 초기자금이 늘어나니 수익률이 눈에 뛰게 좋아지네요.
평균모멘텀스코어가 1일때만 투자하기 때문에 총 투자금액은 $229,100이며, 최종 총자산은 $1,834,223으로 자산이 약 8배로 늘었습니다.
한화로 계산하면 환율 달러당 1,318원 기준으로 총 3억가량 투자했고, 최종 총 자산은 24억정도가 되었습니다.
이제 초기자금 비율에 따른 투자금의 차이를 좀 더 살펴보도록 하겠습니다.
구 분 | CAGR | MDD | 총수익률 (수익률/총투입금액) |
QQQ 지수 | 14.7% | -51.2% | 1,249% |
QQQ 월별적립식 투자 | 10.3% | -43% | 540% |
QQQ+ GLD 적립식투자(일괄분할매수) (동적자산분배+동적투자금액) |
10.9% | -37.3% | 610% |
QQQ+ GLD 적립식투자(초기10%) (동적자산분배+동적투자금액) |
11.6% | -37.3% | 700% |
QQQ+ GLD 적립식투자(초기30%) (동적자산분배+동적투자금액) |
12.8 | -37.3% | 889% |
QQQ+ GLD 적립식투자(초기50%) (동적자산분배+동적투자금액) |
13.9% | -37.5% | 1,077% |
※ 추가금을 수익으로 고려하지 않고 구한 CAGR 입니다.
당연한 얘기지만 초기자금이 클 수록 CAGR은 올라갑니다. MDD는 이 전략이 자산을 월별 리밸런싱을 통해 분배하는 방법이라서 거의 변동이 없습니다.
총 투입자산 $229,500 대비 $2,702,372로 늘었습니다. 한화로 약 3억대비 35억 6천만원이 되었습니다.
아직 MDD를 더 줄어야 좀 더 안정적으로 장기간 투자가 가능할 것 같습니다. 하지만, MDD를 줄이면 CAGR이 또 줄어들겠죠?
현금을 포함해서 투자를 하면 MDD는 -20.9%까지 양호해지지만 CAGR은 8.8%로 떨어지게 됩니다.
쉬운게 없네요. CAGR은 높고 MDD는 낮출 수 있는 방법 없을까요? 좀 더 공부를 해 봐야겠습니다.
결론
이미 모든분이 아시듯이 초기 자금이 클 수록 수익률은 올라갑니다. 당연한 결과였습니다.
전체 코드는 아래와 같습니다.
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'
#초기 투입자금
총투입자금 = 1000*230
p1자금 = 총투입자금*0.5/2
p2자금 = 총투입자금*0.5/2
현금분배여부 = 0 #1이면 현금분배 0이면 현금분배하지 않음
이율 = 0.03
#추가자금
추가자금 = (총투입자금-(p1자금+p2자금))/230
#평균모멘텀스코어 기간
moment_months = 3
start = datetime(2005, 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 = []
li_tscore = [] #현금을 제외한 자산의 합에대한 비율
for i in range(len(df)):
if i < 11:
li_score.append(0)
li_tscore.append(0)
else:
tval = 0
val = 0
for m in range(1, mm+1):
if (df['p1'].iloc[i]+df['p2'].iloc[i]) - (df['p1'].iloc[i-1]+df['p2'].iloc[i-1]) > 0:
tval += 1
if df['p1'].iloc[i] - df['p1'].iloc[i - m] > 0:
val += 1
li_score.append(val / mm)
li_tscore.append(tval / mm)
df['투자모멘텀스코어'] = li_tscore
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_add = []
원금누계 = []
li_sum = []
추가투입 = 0
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(0)
li_add.append(0)
원금누계.append(p1자금+p2자금)
li_sum.append(li_p1[i]+li_p2[i])
else:
if df['평균모멘텀스코어'].iloc[i] != 1 or df['투자모멘텀스코어'].iloc[i] != 1:
li_sum.append(li_p1c[i - 1] * df['p1'].iloc[i] + li_p2c[i - 1] * df['p2'].iloc[i] + li_cash[i - 1] * (
1 + 이율 / 12))
원금누계.append(원금누계[i - 1])
추가투입 += 추가자금
li_add.append(0)
print(f'{df.index[i]} 추가투입 : {추가투입}')
else:
print(f'{df.index[i]} 추가투입 : {추가투입+추가자금}')
이율 = (1+0.03/12)*(추가투입/추가자금)
li_sum.append(li_p1c[i-1]*df['p1'].iloc[i] + li_p2c[i-1]*df['p2'].iloc[i] + li_cash[i-1]*(1+이율/12)+
추가투입+추가자금*이율)
원금누계.append(원금누계[i - 1] + 추가투입 + 추가자금)
li_add.append(추가투입+추가자금)
추가투입 = 0
li_cash.append(li_sum[i]*(1-df['투자모멘텀스코어'].iloc[i])*현금분배여부)
li_p1.append((li_sum[i]-li_cash[i])*df['평균모멘텀스코어'].iloc[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['원금누계'] = 원금누계
df['합계'] = li_sum
df['자산1 개수'] = li_p1c
df['자산2 개수'] = li_p2c
df['추가투입자금'] = li_add
# 월말 데이터를 일별 데이터로 만들기
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['원금누계'].iloc[s-1]
df['합계'].iloc[s] = df['자산1'].iloc[s] + df['자산2'].iloc[s] + df['현금'].iloc[s]
df['투자모멘텀스코어'].iloc[s] = df['투자모멘텀스코어'].iloc[s-1]
df['평균모멘텀스코어'].iloc[s] = df['평균모멘텀스코어'].iloc[s-1]
df['추가투입자금'].iloc[s] = 0
# 합계열에서 0이 아닌 숫자가 나오면 행삭제
df.drop(df[df['합계']==0].index, inplace=True)
# 2가지 자산의 누적수익률을 비교하기 위해 백분율로 표현
df['주가백분율'] = df['p1'] / df['p1'].iloc[0]
df['전략백분율'] = df['합계'] / df['원금누계']
#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)
ETF수익률 = (df['p1'].iloc[-1] / df['p1'].iloc[0] - 1)*100
전략수익률 = (df['합계'].iloc[-1] / df['원금누계'].iloc[-1] - 1)*100
print(f'ETF 수익률 : {round(ETF수익률, 2)}')
print(f'전략 수익률 : {round(전략수익률, 2)}')
print(f'총투자금액:{df["원금누계"].iloc[-1]:,} 최종총자산: {round(df["합계"].iloc[-1],0):,}')
#엑셀파일로 만들기
# df.to_excel(f'평균모멘텀스코어 백테스트({p1}, {p2}, 추가자금 {round(추가자금/(p1자금+p2자금)*100,2)}% CAGR '
# f'{round(cagr_전략*100,1)} 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, p1자금, p2자금, 추가자금, 이율, 현금분배여부)
'파이썬(Python) > 파이썬으로 투자실험' 카테고리의 다른 글
미국 배당성장ETF SCHD로 적립식 투자를 하면 10년 후에 수익률이 어떻게 될까?(SCHD 적립식 투자 백테스트 배당금 포함) (2) | 2024.04.10 |
---|---|
파이썬으로 미국주식 배당금 데이터 가져오기 (TypeError: Cannot join tz-naive with tz-aware DatetimeIndex) (0) | 2024.04.08 |
매월적립식 미국주식(ETF) 투자, 수익률을 더 올릴 방법은 없을까? (0) | 2024.04.03 |
미국주식(ETF) 적립식 투자가 정답일까? (0) | 2024.04.01 |
미국ETF와 한국ETF를 자산분배해서 투자하면 어떻게 될까요? (0) | 2024.03.29 |
댓글