미국 ETF 적립식 투자시 초기 비용에 따른 수익률 비교
본문 바로가기
파이썬(Python)/파이썬으로 투자실험

미국 ETF 적립식 투자시 초기 비용에 따른 수익률 비교

by Squat Lee 2024. 4. 5.

이번 포스트에서는 당연한 얘기를 검증하는 과정을 진행하도록 하겠습니다.

 

초반에 투자금액이 클 수록 수익률은 올라갈까요?

 

우리 아내가 혹시나 이 포스트를 본다면 당연한 얘기를 하고 있냐고 얘기 하겠네요.

 

여태껏 제 포스트에서는 뇌피셜만 가지고 평소에 당연하다고 생각한 것을 검증하는 과정을 거쳤습니다.

 

이번에도 당연한 소리지만 진짜 초기 투자금에 비례해서 수익률이 올라 가는지, 그리고 그 차이는 얼마나 되는지를 검증ㅎ해 보도록 하겠습니다.

 

우선 백테스트 조건부터 말씀드리겠습니다.

 

자산은 QQQ와 GLD를 평균모멘텀스코어라는 방법을 통해 동적자산분배를 매월 리밸런싱을 통해 진행할 예정입니다. 백테스트 기간은 2005년 1월 2일부터 2024년 2월 29일까지 입니다.  

 

세금, 배당금, 수수료는 고려하지 않았습니다. 그리고 아래 링크와 같이 지난 포스트에서 수행한 동적추가금액 투입 방법으로 모멘텀스코어가 1일때만 추가금액을 투입하였습니다.

 

총 투자 자금은 $230,000이며, 초기투자금을 제외한 나머지 자금은 230개월에 나눠서 투자하도록 하겠습니다.

 

우선 초기자금이 10%인 경우의 수익률입니다.

 

초기자금 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천만원이 되었습니다.

 

초기자산 50%일때 월별적립식 투자 백테스트 결과

 

아직 MDD를 더 줄어야 좀 더 안정적으로 장기간 투자가 가능할 것 같습니다. 하지만, MDD를 줄이면 CAGR이 또 줄어들겠죠? 

QQQ+GLD+현금 조합의 투자 백테스트

 

현금을 포함해서 투자를 하면 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자금, 추가자금, 이율, 현금분배여부)
728x90
반응형

댓글