직장인 취미 퀀트 투자 개념(4. 포트폴리오 비중조절(리벨런싱))

게으른직장인·2022년 2월 5일
0
post-thumbnail

포트폴리오 비중조절(리벨런싱)

포트폴리오 전략의 수익률 산정 - CAGR(연평균 성장률)

앞서 우리는 ETF 2종목을 6:4로 구성하는 아주 간단한 포트폴리오를 구성해 보았다.

수익률을 CAGR로 산출하였고, 전략 평가방법 역시 -CAGR/MDD 로 산출하여 점수와 수익률 모두 표준화 하였다.

위 표를 토대로 우리는 우리 포트폴리오는 연평균 수익률이 11.1% 정도 나오며 해당 포트폴리오 전략의 점수는 대략 0.215점 정도 된다고 평가 할 수 있다.

하지만 전략점수가 높게나온다고 해도 결국 SPY 몰빵 전략이 수익률이 조금 더 높다. 하지만 6:4 전략을 유지하면서도 수익률도 몰빵전략보다 더욱 높이는 방법이 있다. 바로 리벨런싱이다.

벡테스팅 - 리벨런싱

복습겸 SPY, KODEX200 지표와 우리의 6:4 투자 후 존버 전략을 정리해보자.

import pandas as pd

# sql = """
# SELECT
# 	A.TICKER
# 	,A.DATE
# 	,A.ADJ_CLOSE PRICE
# FROM 
# 	DAILY_CHART A
# WHERE 
# 	A.TICKER IN ('SPY', 'KRX.069500')
# 	AND A.DATE BETWEEN '20020101' AND '20220131'
# """
# cur.execute(sql)
# data = cur.fetchall()

# pd.DataFrame(data).set_index(['TICKER', 'DATE']).to_csv('04_pf_01.csv')

df = pd.read_csv('04_pf_01.csv').set_index(['TICKER', 'DATE'])

data = {
    'SPY': df.loc['SPY', :]['PRICE'], 
    'KODEX200': df.loc['KRX.069500', :]['PRICE']
}
chart = pd.DataFrame(data)

# 빈값은 앞의 값으로 채운다.(특정 나라에만 공휴일로 휴장일 경우), 앞의값이 없는경우 해당날짜 제외
chart = chart.fillna(method='ffill').loc[
    chart['SPY'].notnull() & 
    chart['KODEX200'].notnull()
]

chart = chart / chart.iloc[0] # 기준가 1로 동일화
chart['Benchmark'] = chart['SPY'] * 0.6 + chart['KODEX200'] * 0.4
chart.plot(rot=45)

cagr = (chart.iloc[-1]/chart.iloc[0]).pow(1/(2021 - 2002)) - 1
mdd = ((chart - chart.cummax()) / chart.cummax()).min()
sp = -cagr/mdd

print('단위(%)')
table = pd.DataFrame.from_dict(cagr.to_dict(), orient='index', columns=['CAGR']) * 100
table['MDD'] = mdd * 100
table['SP'] = sp * 100

table

04_pf_01.csv

단위(%)

벤치마크란?

벤치마크는 전략의 기반이 되는 지수를 말한다. 예를들어 우리의 6:4 전략에서의 기초가 되는 지수(비교군)는 SPY와 KODEX200의 6:4 투자 수익률이 되며 이를 벤치마크라 정한다. 해당 지표를 기반으로 추가적인 전략을 사용하여 투자했을때 기존(벤치마크)대비 얼마만큼의 초과수익(알파)을 창출했는지 쉽게 파악할 수 있다.

백테스팅을 위한 코드

  • 아래 백테스팅의 리벨런싱은 환율변동에 따른 환차익을 적용하지 않음. 이는 이후 상관계수 포스팅에서 함께 다룰 예정.
idxs = chart.index
acc = {'CASH': 1, 'SPY': 0, 'KODEX200': 0}
hist = []
info = {'LAST_RB_YYMM': '0000-00'}

for i, (date, row) in zip(range(len(chart)), chart.iterrows()):  
    
    # 최초셋팅
    if i == 0:   
        acc['SPY'] = acc['CASH'] * 0.6 / row['SPY']
        acc['KODEX200'] = acc['CASH'] * 0.4 / row['KODEX200']
        acc['CASH'] -= acc['SPY'] + acc['KODEX200']
        print(acc)
    
    month = date.split('-')[1]
    yymm = "{}-{}".format(date[0], date[1])    
    
    
    spy_eval_amt = acc['SPY'] * row['SPY']
    kodex200_eval_amt = acc['KODEX200'] * row['KODEX200']   
    eval_amt = spy_eval_amt + kodex200_eval_amt + acc['CASH']
    
    # 리벨런싱
    if month in ['05','11']:
        if info['LAST_RB_YYMM'] != yymm:
            
            target_spy_amt = eval_amt * 0.6
            target_kodex200_amt = eval_amt * 0.4
            
            diff_spy_amt = target_spy_amt - spy_eval_amt
            diff_kodex200_amt = target_kodex200_amt - kodex200_eval_amt
            
            diff_spy_qty = diff_spy_amt / row['SPY']
            diff_kodex200_qty = diff_kodex200_amt / row['KODEX200']
            
            acc['SPY'] += diff_spy_qty
            acc['KODEX200'] += diff_kodex200_qty        
    
    # 평가금 기록
    hist.append({'DATE': date, 'PF1': eval_amt})

pf1 = pd.DataFrame(hist).set_index('DATE')
print(acc)
# print(hist)
chart['PF1'] = pf1

chart.plot(rot=45)

{'CASH': 0.0, 'SPY': 0.6, 'KODEX200': 0.4}
{'CASH': 0.0, 'SPY': 0.6055940024862003, 'KODEX200': 0.46178392003736474}

  • SPY: SPY 몰빵 후 존버
  • KODEX200: KODEX200 몰빵 후 존버
  • Benchmark: SPY와 KDOEX200 최초 6:4 진입 후 존버
  • PF1: SPY와 KODEX200 6:4 비중으로 연 2회 리벨런싱

수익률

  • PF1 > SPY > 벤치마크 > KODEX200

-MDD (높을수록 좋게 만들기 위해)

  • PF1 > 벤치마크 > KODEX200 > SPY

전략점수

  • PF1 > 벤치마크 > SPY > KODEX200

단순 리벨런싱만 했을뿐인데 모든부분에서 PF1이 좋아보인다. 왜 이런 현상이 일어나는 걸까? 바로 리벨런싱을 할때 6:4 비중을 맞추기위해 상대적으로 많이 오른자산을 팔아 많이 내린 자산을 사기 때문이다. 이는 지속적인 고가매도와 저가매수를 의미한다.

투자 자산이 모두 우상향을 하고 서로의 오르고 내리는 타이밍과 폭이 다를 수록 리벨런싱을 지속적으로 하게되면 수익이 극대화 된다.

그러면 어떤 자산 혹은 종목을 골라야 수익을 극대화 할 수 있을까?

profile
취미로 퀀트 투자를 도전하는 직장인

0개의 댓글