가치투자퀀트

[Python] Step#5 백테스트 해보기 2/2 본문

주식 코딩

[Python] Step#5 백테스트 해보기 2/2

곰발이 2022. 9. 4. 16:12

지난 시간에는 한개의 유니버스 (1개의 종목 list) 의 수익률을 구해 보았습니다.

우리가 운용하는 포트폴리오의 종목이 변하지 않고 일정하다면 상관 없겠으나, 실제로는 종목 교체가 자주 이루어 집니다.

그리고 거기서 발생하는 슬리피지 (비용) 에 대해 생각하지 않을 수 없습니다.

 

퀀트킹의 경우 월/분기/연 리벨런싱을 지원하고 전략에 맞추어 정해지 날짜에 매수/매도를 수행합니다.

따라서 이번글은 "분기 리벨런싱"을 기준으로 전략의 전체 수익률을 구해보도록 하겠습니다.

 

동일 비중 리벨런싱 비용

지난번 코드에 아래와 같이 매월 말일자 동일비중 리벨런싱코드 와 리벨런싱 비용을 반영하는 코드를 작성하겠습니다.

# Check Rebalancing Date
        if (date.day == 1):
            if (Rebalancing == True):
                df_ALL.loc[date,'REBAL_CHK'] = True
            else:
                df_ALL.loc[date,'REBAL_CHK'] = False
        else:
            df_ALL.loc[date,'REBAL_CHK'] = False

 

매월 말일일 경우 28일 30일 31일 중 어떤 날짜인지 정확히 알 수 없으므로

본 코드에서는 매월 1일 리벨런싱 하는 기준으로 코드를 작성하겠습니다.

 

리벨런싱 여부를 나타내는 열인 'REBAL_CHK' 라는 열을 생성하고 1일에 True 라는 값을 할당하였습니다.

 

# Monthly Rebalancing..
        for j in range(Universe_Cnt):
            if ( df_ALL['REBAL_CHK'].iloc[i] == True):
                df_ALL.loc[date,'ASSET'+'{}'.format(j)+'_BAL'] = (total_Bal*((100-Fee)/100)) / Universe_Cnt
            else:
                pass

 

해당 리벨런싱 일자에 전체 자산을 동일비중으로 리벨런싱하는 코드를 작성 하였습니다.

 

이제 지난번 코드와 병합하면 포트폴리오 자산을 계산중 매월 1일 동일비중으로 리벨런싱하는 포트폴리오를 만들었습니다.

여기서 'Fee' 는 리벨런싱 비용으로 사고파는 매매과정에서 발생하는 슬리피지를 의미합니다.

Fee = 1 일 경우 전체 자산의 1%를 비용으로 지불한다는 의미 입니다.

 

포트폴리오 교체 리벨런싱 비용

여기까지 작성한 함수를 호출하는 코드입니다.

이미 언급한 내용은 '---' 로 적었습니다. ㅎ

def Asset_Rebalancing(Universe,Initial_Balance,start_day,end_day,Rebalancing,Fee):

	---
    balance = df_ALL['ASSET_BAL'].iloc[-1])
    
    return df_ALL, balance

Universe : 포트폴리오 자산군

Initial_Balance : 포트폴리오 매수시 자산

start_day / end_day : 백테스트 시작/종료 날짜

Rebalancing : 동일비중 리벨런싱 수행 여부 (0 : 미수행 , 1 : 수행)

Fee : 동일비중 리벨런싱 비용

 

전체 전략의 백테스트 코드는 이제 매우 간단합니다.

포트폴리오 자산군이 변하는 때 (Universe 가 변할 때) 위의 함수를 각기 호출 시켜주면 됩니다.

 

예를들어 2021.03.31 부터 2022.03.31까지 1년간의 전략은 아래와 같습니다.

 

    Universe2021_1Q = ['189860','290270','067920','096870','197140']
    Universe2021_2Q = ['123750','078350','031310','059120','067920']
    Universe2021_3Q = ['069330','225590','091340','123750','048470']
    Universe2021_4Q = ['069330','079950','225590','054040','115500']
    
    df_ALL_2021_1Q, Balance = \
    	Asset_Rebalancing(Universe2021_1Q,round(((Balance*(100-Fee_Q))/100),1),datetime(2021,3,31),  datetime(2021,5,18), Rebalancing,Fee_M)
    df_ALL_2021_2Q, Balance = \
    	Asset_Rebalancing(Universe2021_2Q,round(((Balance*(100-Fee_Q))/100),1),datetime(2021,5,18), datetime(2021,8,18), Rebalancing,Fee_M)
    df_ALL_2021_3Q, Balance = \
    	Asset_Rebalancing(Universe2021_3Q,round(((Balance*(100-Fee_Q))/100),1),datetime(2021,8,18), datetime(2021,11,18),Rebalancing,Fee_M)
    df_ALL_2021_4Q, Balance = \
    	Asset_Rebalancing(Universe2021_4Q,round(((Balance*(100-Fee_Q))/100),1),datetime(2021,11,18),datetime(2022,3,31),  Rebalancing,Fee_M)

 

Fee_M : 월초 동일비중 리벨런싱 비용

Fee_Q : 포트폴리오 교체 비용

 

이전 분기의 마지막 자산을 기초자산으로 포트폴리오를 교체해 가면서 계산하는 코드입니다.

이렇게 되면 정해진 기간내 매수한 자산군 (Universe) 히스토리만 알고 있다면 월봉 백테스트가 가능하게 되는 것입니다.

 

마지막으로 각각의 자산군을 합쳐서 하나의 Data Frame 으로 만들어 보겠습니다.

 

    df_ALL = pd.concat([df_ALL_2021_1Q,df_ALL_2021_2Q])
    df_ALL = pd.concat([df_ALL,df_ALL_2021_3Q])
    df_ALL = pd.concat([df_ALL,df_ALL_2021_4Q])

    return df_ALL

concat 이라는 명령어를 사용하면 데이터 프레임을 연결할 수 있습니다.

concat 은 Index가 다를때 병렬로 붙이는 명령어로

리벨런싱후의 Data Frame 은 각각의 시계열이 다르므로 Concat 을 사용하면 보다 쉽게 병합할 수 있습니다.

 

제가 사용중인 분기 리벨런싱 전략을 10년간 백테스트한 결과입니다. (비용처리 x)

 

퀀트킹의 툴을 이용한 백테스트 결과와 동일한 결과값임을 알 수 있습니다.

 

Comments