자산배분

TQQQ, TMF 가속듀얼모멘텀 백테스트

unius 2023. 1. 27. 12:05
728x90
반응형

이번 글에서는 가속듀얼모멘텀을 백테스트 해보겠다. 우선 가속듀얼모멘텀의 기본 구조는 다음과 같다.

  • n개의 투자자산과 1개의 안전자산을 선정한다.
  • n개 투자자산의 1,3,6개월 평균모멘텀을 산출한다.
  • n개 투자자산의 평균모멘텀 상위 m개의 자산에 동일 비중으로 투자한다.
  • 단, 선정된 m개의 투자자산 중에서 모멘텀이 음수인 자산은 안전자산으로 교체한다.


가속듀얼모멘텀의 원문은 아래 링크를 참조하도록 한다.

 

Accelerating Dual Momentum Investing

Warren Buffett has said that trying to time the market is the number one mistake to avoid. Market timing is hard, if not impossible to do, as it often results in the investor buying or selling too …

engineeredportfolio.com

이글에서는 투자자산으로 TQQQ, TMF를 안전자산은 BIL을 선택하여 테스트를 수행하였으며 전략 및 백테스트 생성 코드는 아래와 같다.

# 가속듀얼모멘텀 백테스트
def WeighADM_BT(assets, rank, start_day, run_on_end_of_period=False, lag=1, name='Accelerating DM'):
    s = bt.Strategy(name, [bt.algos.RunAfterDate(start_day),
                           bt.algos.RunMonthly(run_on_first_date=False, run_on_end_of_period=run_on_end_of_period, run_on_last_date=False),
                           bt.algos.SelectAll(),
                           WeighADM(rank=rank, lag=pd.DateOffset(days=lag)),
                           bt.algos.Rebalance()])
    return bt.Backtest(s, assets, initial_capital=100000000.0)
    
# 가속 듀얼모멘텀 1~2종목
assets_adm = assets[tickers].copy()
assets_adm[dualtickers[1]] = assets[dualtickers[1]]
          
adms = {}
for i in range(0,2):
    adms[i] = WeighADM_BT(assets_adm, i+1, start_day+pd.DateOffset(months=6), False, lag, name='ADM_{}R'.format(str(i+1)))
adm_report = bt.run(*adms.values())

WeighADM_BT()메소드의 두번째 파라미터인 rank는 선정되는 투자자산의 순위이며 rank가 1 인 경우 투자자산군에서 모멘텀이 양수인 1개 종목만 선정하고 2 인 경우에는 모멘텀이 양수인 2개 종목을 선정하는 것을 의미한다. WeighADM()은 가속듀얼모멘텀을 산출하여 비중을 할당하는 사용자 정의 클래스이다. 따라서 rank가 1 이고 투자자산군에서 모멘텀이 가장 좋은 자산의 수익률이 음수이면 안전자산인 BIL에 100% 투자되고 rank가 2 이고 투자자산군에서 TQQQ만 모멘텀이 양수인 경우에는 TQQQ와 BIL에 50:50으로 투자된다.

 

백테스트 결과는 아래와 같다.

모멘텀 상위 1종목, 2종목으로 백테스트를 수행한 결과는 연간 수익률은 모두 괜찮은 편이지만 상위 1종목만 선택하는 경우 최대 손실율은 -79%로 매우 높으며 상위2종목 선택시에는 -49%로 개선되지만 여전히 높다.  따라서 TQQQ, TMF, BIL로 구성시 가속듀얼모멘텀은 과거 백테스트 결과가 효과적이지 않다는 것을 확인할 수 있다.

 

WeighADM() 사용자 정의 클래스는 아래와 같다.

class WeighADM(bt.Algo):
    """
    n개의 공격자산과 1개의 안전자산으로 구성된 투자자산(target.temp['selected'])에 가속듀얼모멘텀 방식으로 비중을 할당한다.
    n개의 공격자산중에서 (1,3,6개월) 평균 수익률 순위(self.rank)내에 있는 공격자산에 동일 비중으로 투자한다.
    단, 해당 공격자산의 평균 수익률이 0보다 작을 경우 안전자산으로 변경함.
        Args:
            rank : 투자할 공격자산의 개수
            lag : 리밸런스 지연일
        Returns:
            target.temp['weights']에 가속듀얼모멘텀 방식에 따른 비중을 반환
    """
    def __init__(self, rank=1, lag=1):
        super(WeighADM, self).__init__()
        self.rank = rank
        self.lag = lag
        
    def __call__(self, target):
        selected = target.temp['selected'].copy()
        t0 = target.now - self.lag
        momentum1 = target.universe[selected].loc[t0 - pd.DateOffset(months=1):t0]
        momentum3 = target.universe[selected].loc[t0 - pd.DateOffset(months=1):t0]
        momentum6 = target.universe[selected].loc[t0 - pd.DateOffset(months=1):t0]
        
        ret1 = momentum1.calc_total_return()
        ret3 = momentum3.calc_total_return()
        ret6 = momentum6.calc_total_return()
        
        assetcount = len(selected) - 1
        
        avg = (ret1 + ret3 + ret6) / 3
        #print('avg \n', avg)
        rank = avg[selected[0:assetcount]].rank(ascending=False)
        
        weights = pd.Series([0]*len(selected), index=selected)
        for i in range(0, assetcount):
            if rank[selected[i]] <= self.rank:
                if avg[selected[i]] > 0:
                    weights[selected[i]] = 1
                else:
                    #print('selected[{}]={}'.format(assetcount, selected[assetcount]))
                    weights[selected[assetcount]] = 1
        
        #print('weights \n', weights)
        weights = weights / weights.sum()
        #print('final weights \n', weights)
        target.temp['weights'] = weights
        return True

1편 : TQQQ, TMF 고정 비중 백테스트
2편 : TQQQ, TMF 상대 모멘텀 백테스트
3편 : TQQQ, TMF 절대 모멘텀 백테스트(1)
4편 : TQQQ, TMF 절대 모멘텀 백테스트(2)
5편: TQQQ,TMF 듀얼 모멘텀 백테스트
6편: TQQQ, TMF 평균 모멘텀 스코어 백테스트
7편: TQQQ, TMF 수익곡선 모멘텀 백테스트

8편: TQQQ, TMF 평균모멘텀 전략의 비중 계산 Algo 클래스

9편: TQQQ, TMF 가속듀얼모멘텀 백스트

 

☞ 이 글은 순수하게 개인적인 의도로 테스트한 결과이며 내용상 오류가 있을 수도 있습니다.

728x90
반응형