일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |
- 퀀트킹
- 11월 투자
- Python
- 모멘텀
- 마켓타이밍
- 하락장
- Quantking
- 퀀트
- 리벨런싱
- 영구포트폴리오
- MDD
- 60 : 40
- 6040
- 코스피 pbr
- FinanceDataReader
- 곰발
- 현금화
- Classic portfolio
- 슬리피지
- 레버리지
- 백테스트
- PBR
- 10월 수익인증
- pandas_datareader
- 저점매수
- backtest
- Permanent Portfolio
- 현금비중
- 결산
- 파이썬
- Today
- Total
가치투자퀀트
[자산배분#4 - 코딩] 영구포트폴리오 (PP : Permanent Portfoilio) 본문
영구 포트폴리오 코딩 방법에 대해 글을 써 보겠습니다.
영구 포트폴리오 기본
# Makeup Universe
Universe = ['SPY','SHY','GLD','TLT']
df_PP = get_yahoo_price_data(Universe, start_day, end_day)
df_PP.index = df_PP.index.values.astype('<M8[m]')
# Duration of backtest
df_PP = df_PP[start_day:end_day]
우선 자산군을 선택하고 백테스트 기간을 입력합니다.
# calculate profit
for i in range(len(Universe)):
df_PP.rename(columns = {Universe[i]:'ASSET'+'{}'.format(i)}, inplace = True)
col_list_P = [col+'_P' for col in df_PP.columns]
df_PP[col_list_P] = df_PP.pct_change()
df_PP[col_list_P] = df_PP[col_list_P]*100
# calculate balance
df_PP = Cal_PP_Profit(df_PP,Universe)
return df_PP
우선 각 자산군의 이름을 ASSETx 로 변경합니다 (자산군을 변경하면서 테스트 할수 있도록)
각 자산군의 수익률을 계산합니다.
이전에 설명한 것과 같이 pct_change() 메서드를 사용하면 간단하게 계산이 가능합니다.
그리고 영구포트폴리오의 메인 함수를 호출하여 줍니다.
def Cal_PP_Profit(df_PP,Universe):
Universe_Cnt = len(Universe)
Initial_Balance = 100
for j in range(Universe_Cnt):
df_PP['ASSET'+'{}'.format(j)+'_BAL'] = 0
df_PP['ASSET'+'{}'.format(j)+'_MDD'] = 1
for i in range(len(df_PP)):
date = df_PP.index[i]
total_Bal = 0
# Check Rebalancing Date
if date.day == 30 and date.month == 12:
df_PP.loc[date,'REBAL_CHK'] = True
else:
df_PP.loc[date,'REBAL_CHK'] = False
1. 초기 자산은 100 으로 설정
2. 각각의 자산군의 Balance MDD 초기값 세팅
3. 리벨런싱은 12월 30일에 수행합니다.
# Calculate Balance
for j in range(Universe_Cnt):
if i==0:
temp = Initial_Balance/Universe_Cnt
df_PP.loc[date,'ASSET'+'{}'.format(j)+'_BAL'] = temp
else:
temp = (df_PP['ASSET'+'{}'.format(j)+'_BAL'].iloc[i-1] * (1 + df_PP['ASSET'+'{}'.format(j)+'_P'].iloc[i]/100))
df_PP.loc[date,'ASSET'+'{}'.format(j)+'_BAL'] = temp
total_Bal = total_Bal + (df_PP['ASSET'+'{}'.format(j)+'_BAL'].iloc[i])
전체 자산을 구하는 코드입니다.
초기값 i = 0 인 경우 기초 자산 (Initial_Balance) 를 균등하게 분배합니다. (4개의 자산군이므로 25% 씩 할당 되겠습니다)
각 자산군의 이전 자산에 수익률을 곱하여 계산해 줍니다.
그리고 모든 자산군의 합을 Total_bal 이라는 전체 자산으로 계산합니다.
# Rebalancing..
for j in range(Universe_Cnt):
if ( df_PP['REBAL_CHK'].iloc[i] == True):
df_PP.loc[date,'ASSET'+'{}'.format(j)+'_BAL'] = total_Bal / Universe_Cnt
else:pass
df_PP.loc[date,'PP_BAL'] = total_Bal
return df_PP
리벨런싱 코드 입니다.
위에서 12월 30일을 리벨런싱 시점으로 지정하였고, 해당 일자에 전체 total_balance 를 균등 분배 해 줍니다.
그리고 전체 df_PP 를 반환 합니다.
*MDD 는 Quantstats 에서 자동 계산해 주어 따로 코드를 작성할 필요는 없으나,
matplot 으로 시각화 하고 싶은 경우 아래와 같이 작성 가능합니다.
df_PP = df_PP.assign(PP_DD=lambda x: -(df_PP['PP_BAL'].cummax() - df_PP['PP_BAL']) / df_PP['PP_BAL'].cummax())
df_PP['PP_DD'] = df_PP['PP_DD'] * 100
MDD 계산식은 아래와 같으며 cummax() 라는 메서드로 손쉽게 계산 가능합니다.
% MDD = - (고점 - 현재값) / 고점
qs.reports.html(df_AA['PP_BAL'],'SPY', mode='full', title='PP(ORI)', download_filename='PP.html')
자 이제 Quantstats 함수를 이용해 레포트를 추출하도록 하겠습니다.
정상적으로 리포트가 생성 되었습니다.
벤치마크 (SPY) 와 비교하였을때 CAGR 은 -7% 정도 하락하였지만, MDD 는 -4% 정도밖에 감소하지 않았습니다.
2005년 부터 Portfolio visualize 로 계산한 값과 비교하였을때 영구포트폴리오의 약점이 확연히 드러 납니다.
최근 10년과 같이 금융위기와 같은 System Risk 가 없는 (큰폭의 하락이 없는) 구간에서는 MDD 방어가 잘 되지 않을 뿐더러 수익률을 많이 깎아 먹고 있습니다.
영구 포트폴리오 (레버리지)
레버리지를 사용하면 어떻게 될까요?
SPY 대신 SSO (S&P500 2배 레버리지) 를 사용해 보겠습니다.
2배 레버리지를 사용하여 리스크 테이큰 하였음에도 수익률은 S&P500 을 따라가지 못하였습니다.
더군다나 마켓타이밍 장치가 없기 때문에 MDD 는 32% 언더워터 기간은 두배로 증가하였습니다.
따라서 영구포트폴리오 내에서 레버리지 사용은 별로 좋지 않다는 결론입니다.
'자산배분' 카테고리의 다른 글
[자산배분#3 - 이론] 영구포트폴리오 (PP : Permanent Portfoilio) (0) | 2022.09.12 |
---|---|
[자산배분#1 - 이론] 60 : 40 포트폴리오 (CP : Classic Portfoilio) (0) | 2022.08.28 |