[Strategy] VAA (Vigilant Asset Allocation)


개요

동적 자산배분 전략인 VAA (Vigilant Asset Allocation) 전략을 구현합니다.

안정적인 전략은 이미 all seasons, permanent portfolio 등 정적인 자산배분 전략들에서 많이 했으니 조금 더 거칠지만 수익성이 더 좋은 동적 자산배분 전략도 알아볼 것입니다. 6월 3일에 올린 섹터 로테이션 전략도 이런 방향을 같이 합니다. VAA (Vigilant Asset Allocation) 전략은 꽤 공격적인 동적 자산배분 (모멘텀) 전략입니다. 공격 자산 또는 방어 자산으로 구성된 유니버스에서 매월 포트폴리오의 100%를 단일 자산에 할당합니다. VAA의 모멘텀 측정은 최근일수록 더 가중치를 크게 줍니다. 그러므로, VAA는 시장 변화에 더 빠르게 대응할 수 있지만, 거래 횟수가 많아질 수 있습니다.

VAA 전략 논문의 저자는 글로벌 기준으로 12개 공격 자산(S&P 500, Russell 2000, NASDAQ 100, Europe Stock, Japan Stock, US REITs, Commodities, Gold, Long Term Treasury, Investment Grade Corporate Bond, High Yield Bond)과 3개 방어 자산(1-3 Year Treasury, 7-10 Year Treasury, Investment Grade Corporate Bond)을 사용하는 전략도 제시합니다. 그러나, 전략의 골격은 같고 아래에서 테스트하는 공격 자산 4개, 방어 자산 3개 조합이 더 간단하니 그렇게 합니다.

import pandas as pd
import pandas_datareader as pdr
from datetime import datetime, timedelta
import backtrader as bt
import numpy as np
%matplotlib inline
import matplotlib.pyplot as plt
import pyfolio as pf
import quantstats
import math
import seaborn
plt.rcParams["figure.figsize"] = (10, 6) # (w, h)

사용되는 공격 자산은 SPY (S&P 500), EFA (MSCI EAFE), EEM (Emerging), AGG (Aggregate US Bond)이고, 방어 자산은 LQD (Investment Grade Corporate Bond), IEF (US 7-10 Year Treausry), SHY (US 1-3 Year Treasury)입니다. 실제 거래를 할 때는 ETF를 사용할 것이니 유동성도 풍부하고 사용하기도 쉬운 ETF들로 테스트합니다.

AGG ETF는 채권인데 (심지어 만기도 짧은 편입니다) 공격 자산입니다. 그것에 대해서는 Allocate Smartly라는 사이트에서 테스트한 것이 있습니다. AGG의 모멘텀이 양수일 때 미국, 선진국, 신흥국 주식이 다음 달에 수익을 낼 확률은 60% 이상이고, 그 경우 연간으로 환산한 수익률은 13%-17% 수준으로 나타났습니다. AGG의 모멘텀이 음수일 때에도 다음달에 수익이 날 확률이 50%에 가까우나, 연간 환산 수익률은 상당한 손실을 기록하고 있어 AGG 모멘텀이 음수인 경우 큰 손실이 나는 경우가 많았다는 것을 의미합니다.

화면 캡처 2021-06-13 190259

# 공격 자산: SPY (S&P 500), EFA (MSCI EAFE), EEM (Emerging), AGG (Aggregate US Bond) 
# 방어 자산: LQD (Investment Grade Corporate Bond), IEF (US 7-10 Year Treausry), SHY (US 1-3 Year Treasury)

# AGG starts: 2003-09-29
start = datetime(2003,10,1)
end = datetime(2021,5,31)

tickers = ['SPY','EFA','EEM','AGG','LQD','SHY','IEF']

def get_price_data(tickers):
    df_asset = pd.DataFrame(columns=tickers)
    
    for ticker in tickers:
        df_asset[ticker] = pdr.get_data_yahoo(ticker, start, end)['Adj Close']  
         
    return df_asset
df_asset = get_price_data(tickers)
df_asset
SPYEFAEEMAGGLQDSHYIEF
Date
2003-09-3070.70180523.79189510.89756158.13217553.13911161.20437652.677532
2003-10-0172.20850424.57276311.18377058.10382853.29197761.21392152.911316
2003-10-0272.47021524.53412111.34411058.01326053.08715861.21392152.721107
2003-10-0373.13515524.89201911.45634957.59445252.34888861.05848751.990978
2003-10-0673.46760625.15230411.50044357.67933352.40126061.10285252.144318
........................
2021-05-24419.17001379.43377753.346413114.283142131.32325786.288010114.241051
2021-05-25418.23999079.45351453.874104114.562744131.81230286.288010114.660759
2021-05-26419.07000779.40418254.272358114.522797131.74243286.307999114.530853
2021-05-27419.29000979.55216254.282314114.383003131.34321686.307999114.330994
2021-05-28420.04000979.73960154.630787114.383003131.45300386.307999114.320999

4447 rows × 7 columns

모멘텀을 특이한 방식으로 측정합니다. 섹터 로테이션에서는 12개월 전과 비교했는데, VAA 전략은 1, 3, 6, 12개월 전과 비교하면서 최근일수록 가중치를 크게 줍니다. 365일, 180일, 90일, 30일 전이 거래일이 아닐 수 있으니 그 근처 5일을 가져오고 그 중 가장 최근 값을 취합니다.

def get_momentum(x):
    temp = [0 for _ in range(len(x.index))]
    momentum = pd.Series(temp, index=x.index)
    
    try:
        before_1m = df_asset[x.name-timedelta(days=35):x.name-timedelta(days=30)].iloc[-1]
        before_3m = df_asset[x.name-timedelta(days=95):x.name-timedelta(days=90)].iloc[-1]
        before_6m = df_asset[x.name-timedelta(days=185):x.name-timedelta(days=180)].iloc[-1]
        before_12m = df_asset[x.name-timedelta(days=370):x.name-timedelta(days=365)].iloc[-1]
        momentum = (x/before_1m - 1) * 12 + (x/before_3m - 1) * 4 + (x/before_6m - 1) * 2 + (x/before_12m - 1) * 1
        
    except:
        pass
    
    return momentum
momentum_col = [col + '_m' for col in df_asset.columns]
df_asset[momentum_col] = df_asset.apply(lambda x: get_momentum(x), axis=1)
df_asset
SPYEFAEEMAGGLQDSHYIEFSPY_mEFA_mEEM_mAGG_mLQD_mSHY_mIEF_m
Date
2003-09-3070.70180523.79189510.89756158.13217553.13911161.20437652.6775320.0000000.0000000.0000000.0000000.0000000.0000000.000000
2003-10-0172.20850424.57276311.18377058.10382853.29197761.21392152.9113160.0000000.0000000.0000000.0000000.0000000.0000000.000000
2003-10-0272.47021524.53412111.34411058.01326053.08715861.21392152.7211070.0000000.0000000.0000000.0000000.0000000.0000000.000000
2003-10-0373.13515524.89201911.45634957.59445252.34888861.05848751.9909780.0000000.0000000.0000000.0000000.0000000.0000000.000000
2003-10-0673.46760625.15230411.50044357.67933352.40126061.10285252.1443180.0000000.0000000.0000000.0000000.0000000.0000000.000000
.............................................
2021-05-24419.17001379.43377753.346413114.283142131.32325786.288010114.2410511.1817111.1816930.269094-0.081027-0.0178660.009609-0.220735
2021-05-25418.23999079.45351453.874104114.562744131.81230286.288010114.6607591.0897241.1734090.484626-0.0333570.0467740.010535-0.146308
2021-05-26419.07000779.40418254.272358114.522797131.74243286.307999114.5308531.1864381.1687600.6510340.0057850.1011210.023759-0.104925
2021-05-27419.29000979.55216254.282314114.383003131.34321686.307999114.3309941.2015431.2913190.710826-0.0219040.0400610.020275-0.126803
2021-05-28420.04000979.73960154.630787114.383003131.45300386.307999114.3209991.2437481.2949600.758176-0.0275360.0532670.017028-0.143446

4447 rows × 14 columns

12개월 모멘텀 때문에 앞쪽 1년은 사용 못 합니다. 그러니 제거하고 2004년 10월 1일부터로 보겠습니다.

df_asset = df_asset.loc[df_asset.index >= '2004-10-01']
df_asset
SPYEFAEEMAGGLQDSHYIEFSPY_mEFA_mEEM_mAGG_mLQD_mSHY_mIEF_m
Date
2004-10-0181.69033129.65620014.16742659.89566856.06347761.97622354.8566360.4716890.6785171.5982040.1394400.2300540.0162830.183581
2004-10-0481.82690429.70162214.40919559.91322356.02806561.98379954.8759500.4420710.7991151.9461680.2211930.3888680.0562960.312958
2004-10-0581.87002629.71815514.30281459.90738356.26072761.99136454.8759500.4397790.7538441.7271940.2190280.4628430.0573890.311708
2004-10-0682.43065629.82967014.46802159.79602156.09888861.93840054.6958200.5973240.8560042.0328490.1824240.4081780.0406170.253906
2004-10-0781.54656229.62522314.34471759.79602155.97747861.91565754.5993230.2993420.5190071.7204990.1932120.2926580.0336260.204132
.............................................
2021-05-24419.17001379.43377753.346413114.283142131.32325786.288010114.2410511.1817111.1816930.269094-0.081027-0.0178660.009609-0.220735
2021-05-25418.23999079.45351453.874104114.562744131.81230286.288010114.6607591.0897241.1734090.484626-0.0333570.0467740.010535-0.146308
2021-05-26419.07000779.40418254.272358114.522797131.74243286.307999114.5308531.1864381.1687600.6510340.0057850.1011210.023759-0.104925
2021-05-27419.29000979.55216254.282314114.383003131.34321686.307999114.3309941.2015431.2913190.710826-0.0219040.0400610.020275-0.126803
2021-05-28420.04000979.73960154.630787114.383003131.45300386.307999114.3209991.2437481.2949600.758176-0.0275360.0532670.017028-0.143446

4194 rows × 14 columns

월말 리밸런싱으로 할 것이니 월말 데이터만 남깁니다.

df_asset = df_asset.resample(rule='M').last()
df_asset
SPYEFAEEMAGGLQDSHYIEFSPY_mEFA_mEEM_mAGG_mLQD_mSHY_mIEF_m
Date
2004-10-3181.36685930.22411714.29717460.52293856.97900862.20337355.6220780.3957831.0642951.3960090.3790250.4939580.1219960.460091
2004-11-3084.98909832.06214515.71955560.07558456.28387861.84536754.5219651.0433741.6729852.7623060.0466130.056559-0.052612-0.116270
2004-12-3187.54904933.60329416.46561160.55459657.06678861.98909855.1584130.7834161.3756791.9298410.2879570.4061640.0388340.276794
2005-01-3185.58614332.96372616.37832860.85030757.76775761.96619455.6389270.1565590.5835541.3021600.1956750.384630-0.0047430.233932
2005-02-2887.37522934.21140317.96411560.62432157.22690661.80676354.8775290.7012131.3423322.9256990.0235530.029131-0.046518-0.125266
.............................................
2021-01-31368.86190871.41346053.077591116.402031134.26939486.304970118.2582630.9265971.0140001.941232-0.028883-0.0849300.031092-0.165833
2021-02-28379.11828673.01160453.495758114.634903131.23045386.254997115.4634480.8823210.7526530.941667-0.292006-0.4278420.016124-0.560360
2021-03-31396.32998774.84651253.107460113.320808129.29086386.207031112.7088551.4565841.0857290.769576-0.313018-0.377560-0.008304-0.702032
2021-04-30417.29998877.05629753.744667114.150345130.63958786.256012113.8373342.1950121.6537381.131010-0.0201210.0442790.005093-0.178007
2021-05-31420.04000979.73960154.630787114.383003131.45300386.307999114.3209991.2437481.2949600.758176-0.0275360.0532670.017028-0.143446

200 rows × 14 columns

공격 자산의 모멘텀이 모두 0 이상이면, 공격 자산 중 모멘텀이 가장 좋은 자산에 투자합니다. 만약 공격 자산 중 1개라도 모멘텀이 음수이면 방어 자산 중 모멘텀이 가장 좋은 자산에 투자합니다. 1개라도 음수면 방어 자산으로 전환하기 때문에 이런 면에서는 방어적이라고 볼 수 있습니다.

def select_asset(x):
    selected_asset = pd.Series([0,0], index=['ASSET','PRICE'])
    
    # 모든 공격 자산 > 0
    if x['SPY_m'] > 0 and x['EFA_m'] > 0 and x['EEM_m'] > 0 and x['AGG_m'] > 0:
        selected_momentum = max(x['SPY_m'], x['EFA_m'], x['EEM_m'], x['AGG_m'])
    
    # 공격 자산 중 1개라도 < 0
    else:
        selected_momentum = max(x['LQD_m'], x['SHY_m'], x['IEF_m'])
    
    selected_asset['ASSET'] = x[x==selected_momentum].index[0][:3]
    selected_asset['PRICE'] = x[selected_asset['ASSET']]
    
    return selected_asset
df_asset[['ASSET','PRICE']] = df_asset.apply(lambda x: select_asset(x), axis=1) 
df_asset
SPYEFAEEMAGGLQDSHYIEFSPY_mEFA_mEEM_mAGG_mLQD_mSHY_mIEF_mASSETPRICE
Date
2004-10-3181.36685930.22411714.29717460.52293856.97900862.20337355.6220780.3957831.0642951.3960090.3790250.4939580.1219960.460091EEM14.297174
2004-11-3084.98909832.06214515.71955560.07558456.28387861.84536754.5219651.0433741.6729852.7623060.0466130.056559-0.052612-0.116270EEM15.719555
2004-12-3187.54904933.60329416.46561160.55459657.06678861.98909855.1584130.7834161.3756791.9298410.2879570.4061640.0388340.276794EEM16.465611
2005-01-3185.58614332.96372616.37832860.85030757.76775761.96619455.6389270.1565590.5835541.3021600.1956750.384630-0.0047430.233932EEM16.378328
2005-02-2887.37522934.21140317.96411560.62432157.22690661.80676354.8775290.7012131.3423322.9256990.0235530.029131-0.046518-0.125266EEM17.964115
...................................................
2021-01-31368.86190871.41346053.077591116.402031134.26939486.304970118.2582630.9265971.0140001.941232-0.028883-0.0849300.031092-0.165833SHY86.304970
2021-02-28379.11828673.01160453.495758114.634903131.23045386.254997115.4634480.8823210.7526530.941667-0.292006-0.4278420.016124-0.560360SHY86.254997
2021-03-31396.32998774.84651253.107460113.320808129.29086386.207031112.7088551.4565841.0857290.769576-0.313018-0.377560-0.008304-0.702032SHY86.207031
2021-04-30417.29998877.05629753.744667114.150345130.63958786.256012113.8373342.1950121.6537381.131010-0.0201210.0442790.005093-0.178007LQD130.639587
2021-05-31420.04000979.73960154.630787114.383003131.45300386.307999114.3209991.2437481.2949600.758176-0.0275360.0532670.017028-0.143446LQD131.453003

200 rows × 16 columns

각각의 수익률을 계산합니다.

return_col = [ticker + '_r' for ticker in tickers]
df_asset[return_col] = df_asset[tickers].pct_change()
df_asset
SPYEFAEEMAGGLQDSHYIEFSPY_mEFA_mEEM_m...IEF_mASSETPRICESPY_rEFA_rEEM_rAGG_rLQD_rSHY_rIEF_r
Date
2004-10-3181.36685930.22411714.29717460.52293856.97900862.20337355.6220780.3957831.0642951.396009...0.460091EEM14.297174NaNNaNNaNNaNNaNNaNNaN
2004-11-3084.98909832.06214515.71955560.07558456.28387861.84536754.5219651.0433741.6729852.762306...-0.116270EEM15.7195550.0445170.0608130.099487-0.007391-0.012200-0.005755-0.019778
2004-12-3187.54904933.60329416.46561160.55459657.06678861.98909855.1584130.7834161.3756791.929841...0.276794EEM16.4656110.0301210.0480680.0474600.0079730.0139100.0023240.011673
2005-01-3185.58614332.96372616.37832860.85030757.76775761.96619455.6389270.1565590.5835541.302160...0.233932EEM16.378328-0.022421-0.019033-0.0053010.0048830.012283-0.0003690.008712
2005-02-2887.37522934.21140317.96411560.62432157.22690661.80676354.8775290.7012131.3423322.925699...-0.125266EEM17.9641150.0209040.0378500.096822-0.003714-0.009363-0.002573-0.013685
..................................................................
2021-01-31368.86190871.41346053.077591116.402031134.26939486.304970118.2582630.9265971.0140001.941232...-0.165833SHY86.304970-0.010190-0.0078120.031740-0.007446-0.0183160.000232-0.010921
2021-02-28379.11828673.01160453.495758114.634903131.23045386.254997115.4634480.8823210.7526530.941667...-0.560360SHY86.2549970.0278050.0223790.007878-0.015181-0.022633-0.000579-0.023633
2021-03-31396.32998774.84651253.107460113.320808129.29086386.207031112.7088551.4565841.0857290.769576...-0.702032SHY86.2070310.0453990.025132-0.007258-0.011463-0.014780-0.000556-0.023857
2021-04-30417.29998877.05629753.744667114.150345130.63958786.256012113.8373342.1950121.6537381.131010...-0.178007LQD130.6395870.0529100.0295240.0119980.0073200.0104320.0005680.010012
2021-05-31420.04000979.73960154.630787114.383003131.45300386.307999114.3209991.2437481.2949600.758176...-0.143446LQD131.4530030.0065660.0348230.0164880.0020380.0062260.0006030.004249

200 rows × 23 columns

전략의 월별 수익률을 구합니다.

df_asset['RETURN'] = 0
df_asset['RETURN_ACC'] = 0
df_asset['LOG_RETURN'] = 0
df_asset['LOG_RETURN_ACC'] = 0

for i in range(len(df_asset)):
    strat_return = 0
    log_strat_return = 0
    
    # 직전 달 모멘텀이 좋은 것으로 리밸런싱해서 앞으로 한 달 가져가는 것
    if i > 0:
        strat_return = df_asset[df_asset.iloc[i-1]['ASSET']+'_r'].iloc[i]
        log_strat_return = math.log(strat_return + 1)
        
    df_asset.loc[df_asset.index[i], 'RETURN'] = strat_return
    # 누적 = 직전 누적 * 현재
    df_asset.loc[df_asset.index[i], 'RETURN_ACC'] = (1+df_asset.loc[df_asset.index[i-1], 'RETURN_ACC'])*(1+strat_return)-1
    df_asset.loc[df_asset.index[i], 'LOG_RETURN'] = log_strat_return
    # 로그누적 = 직전 로그누적 + 현재 로그
    df_asset.loc[df_asset.index[i], 'LOG_RETURN_ACC'] = df_asset.loc[df_asset.index[i-1], 'LOG_RETURN_ACC'] + log_strat_return
    
# 수익률 * 100
df_asset[['RETURN','RETURN_ACC','LOG_RETURN','LOG_RETURN_ACC']] = df_asset[['RETURN','RETURN_ACC','LOG_RETURN','LOG_RETURN_ACC']]*100
df_asset[return_col] = df_asset[return_col] * 100
df_asset
SPYEFAEEMAGGLQDSHYIEFSPY_mEFA_mEEM_m...EFA_rEEM_rAGG_rLQD_rSHY_rIEF_rRETURNRETURN_ACCLOG_RETURNLOG_RETURN_ACC
Date
2004-10-3181.36685930.22411714.29717460.52293856.97900862.20337355.6220780.3957831.0642951.396009...NaNNaNNaNNaNNaNNaN0.0000000.0000000.0000000.000000
2004-11-3084.98909832.06214515.71955560.07558456.28387861.84536754.5219651.0433741.6729852.762306...6.0813299.948690-0.739147-1.219975-0.575540-1.9778359.9486909.9486909.4843619.484361
2004-12-3187.54904933.60329416.46561160.55459657.06678861.98909855.1584130.7834161.3756791.929841...4.8067564.7460350.7973481.3910010.2324021.1673244.74603515.1668934.63685214.121213
2005-01-3185.58614332.96372616.37832860.85030757.76775761.96619455.6389270.1565590.5835541.302160...-1.903291-0.5300880.4883391.228332-0.0369480.871154-0.53008814.556407-0.53149813.589716
2005-02-2887.37522934.21140317.96411560.62432157.22690661.80676354.8775290.7012131.3423322.925699...3.7849999.682226-0.371381-0.936252-0.257288-1.3684639.68222625.6480189.24171522.831430
..................................................................
2021-01-31368.86190871.41346053.077591116.402031134.26939486.304970118.2582630.9265971.0140001.941232...-0.7812483.173997-0.744573-1.8316180.023166-1.0921233.173997876.8099273.124667227.912190
2021-02-28379.11828673.01160453.495758114.634903131.23045386.254997115.4634480.8823210.7526530.941667...2.2378760.787841-1.518125-2.263316-0.057902-2.363315-0.057902876.244332-0.057919227.854271
2021-03-31396.32998774.84651253.107460113.320808129.29086386.207031112.7088551.4565841.0857290.769576...2.513172-0.725848-1.146330-1.478003-0.055610-2.385684-0.055610875.701447-0.055625227.798646
2021-04-30417.29998877.05629753.744667114.150345130.63958786.256012113.8373342.1950121.6537381.131010...2.9524231.1998450.7320251.0431710.0568181.0012340.056818876.2558160.056801227.855447
2021-05-31420.04000979.73960154.630787114.383003131.45300386.307999114.3209991.2437481.2949600.758176...3.4822641.6487590.2038180.6226410.0602700.4248740.622641882.3343840.620711228.476158

200 rows × 27 columns

# MDD

df_asset['BALANCE'] = (1+df_asset['RETURN']/100).cumprod()
df_asset['DRAWDOWN'] = -(df_asset['BALANCE'].cummax() - df_asset['BALANCE']) / df_asset['BALANCE'].cummax()

df_asset[['BALANCE','DRAWDOWN']] = df_asset[['BALANCE','DRAWDOWN']] * 100
df_asset
SPYEFAEEMAGGLQDSHYIEFSPY_mEFA_mEEM_m...AGG_rLQD_rSHY_rIEF_rRETURNRETURN_ACCLOG_RETURNLOG_RETURN_ACCBALANCEDRAWDOWN
Date
2004-10-3181.36685930.22411714.29717460.52293856.97900862.20337355.6220780.3957831.0642951.396009...NaNNaNNaNNaN0.0000000.0000000.0000000.000000100.000000-0.000000
2004-11-3084.98909832.06214515.71955560.07558456.28387861.84536754.5219651.0433741.6729852.762306...-0.739147-1.219975-0.575540-1.9778359.9486909.9486909.4843619.484361109.948690-0.000000
2004-12-3187.54904933.60329416.46561160.55459657.06678861.98909855.1584130.7834161.3756791.929841...0.7973481.3910010.2324021.1673244.74603515.1668934.63685214.121213115.166893-0.000000
2005-01-3185.58614332.96372616.37832860.85030757.76775761.96619455.6389270.1565590.5835541.302160...0.4883391.228332-0.0369480.871154-0.53008814.556407-0.53149813.589716114.556407-0.530088
2005-02-2887.37522934.21140317.96411560.62432157.22690661.80676354.8775290.7012131.3423322.925699...-0.371381-0.936252-0.257288-1.3684639.68222625.6480189.24171522.831430125.648018-0.000000
..................................................................
2021-01-31368.86190871.41346053.077591116.402031134.26939486.304970118.2582630.9265971.0140001.941232...-0.744573-1.8316180.023166-1.0921233.173997876.8099273.124667227.912190976.809927-0.000000
2021-02-28379.11828673.01160453.495758114.634903131.23045386.254997115.4634480.8823210.7526530.941667...-1.518125-2.263316-0.057902-2.363315-0.057902876.244332-0.057919227.854271976.244332-0.057902
2021-03-31396.32998774.84651253.107460113.320808129.29086386.207031112.7088551.4565841.0857290.769576...-1.146330-1.478003-0.055610-2.385684-0.055610875.701447-0.055625227.798646975.701447-0.113480
2021-04-30417.29998877.05629753.744667114.150345130.63958786.256012113.8373342.1950121.6537381.131010...0.7320251.0431710.0568181.0012340.056818876.2558160.056801227.855447976.255816-0.056727
2021-05-31420.04000979.73960154.630787114.383003131.45300386.307999114.3209991.2437481.2949600.758176...0.2038180.6226410.0602700.4248740.622641882.3343840.620711228.476158982.334384-0.000000

200 rows × 29 columns

2004년 10월부터 2021년 5월까지 200개월 동안 낸 성과입니다. 수익, 변동성, MDD 어느 하나 빠질 것 없이 압도적인 성능을 냅니다. 다만, 매달 1개 자산에 100% 비중을 주기 때문에 거래 비용 및 세금이 꽤 나올 것으로 보입니다. 그래서 직접 운영하는 전략보다는 전략 자체의 성능이 괜찮으니 이 전략이 가리키는 다음 달 매수 자산을 참고하여 시장에 대한 관점으로 삼는 것이 더 좋을 수 있습니다.

total_month = len(df_asset)
profit_month = len(df_asset[df_asset['RETURN'] >= 0])
loss_month = len(df_asset[df_asset['RETURN'] < 0])
win_rate = profit_month / total_month * 100
CAGR = ((1+df_asset['RETURN_ACC'][-1]/100)**(1/(total_month/12)))-1
STDEV = np.std(df_asset['RETURN'][1:])*math.sqrt(12)
RRR = CAGR * 100 / STDEV

print(total_month, "개월 중 수익 월 :", profit_month, "개월")
print(total_month, "개월 중 손실 월 :", loss_month, "개월")
print("승률 :", round(win_rate, 2))

print('CAGR : ', round(CAGR*100, 2))
print('MDD : ', round(df_asset['DRAWDOWN'].min(), 2))
print('STDEV :', round(STDEV, 2))
print('Return-Risk Ratio: ', round(RRR, 2))
200 개월 중 수익 월 : 140 개월
200 개월 중 손실 월 : 60 개월
승률 : 70.0
CAGR :  14.69
MDD :  -11.97
STDEV : 11.85
Return-Risk Ratio:  1.24
plt.figure(figsize=(15,5))
seaborn.lineplot(data=df_asset, x=df_asset.index, y=df_asset['LOG_RETURN_ACC'])
<matplotlib.axes._subplots.AxesSubplot at 0x25b0f461a48>

output_23_1

plt.figure(figsize=(15,5))
seaborn.lineplot(data=df_asset, x=df_asset.index, y=df_asset['DRAWDOWN'])
<matplotlib.axes._subplots.AxesSubplot at 0x25b0f403488>

output_24_1

아래 그림의 샤프 비율은 월간 데이터로 구해진 것이라 왜곡되어 있습니다. 환산시킨 1.22나 Return-Risk Ratio 1.24가 더 믿을 만 합니다.

quantstats.stats.sharpe(df_asset['RETURN'])/math.sqrt(252/12)
1.2201114376272868
quantstats.reports.plots(df_asset['RETURN']/100, mode='basic')

output_27_0 output_27_1

참고

Keller, Wouter J. and Keuning, Jan Willem, Breadth Momentum and Vigilant Asset Allocation (VAA): Winning More by Losing Less (July 14, 2017). Available at SSRN: https://ssrn.com/abstract=3002624 or http://dx.doi.org/10.2139/ssrn.3002624

https://allocatesmartly.com/vigilant-asset-allocation-dr-wouter-keller-jw-keuning/




© 2021.03. by JacobJinwonLee

Powered by theorydb