ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [파이썬] 주식 포트폴리오 수익률 계산하기 (Calculate portfolio returns)
    코딩/파이썬 2021. 10. 11. 17:14
    반응형

    지난 포스트에서는 개별 주식 수익률을 파이썬으로 계산하는 방법에 대해서 살펴보았습니다. 이번에는 개별 주식이 아니라 여러 주식의 묶음인 포트폴리오(portfolio) 수익률을 계산하는 법에 대해서 알아보도록 하겠습니다. 개별 주식 수익률 계산에서 이어지는 내용이기 때문에 아래 링크에서 개별 주식 수익률 포스트를 먼저 확인하시는 것을 추천드립니다.

     

     

    2021.10.09 - [코딩/파이썬] - [파이썬] 개별 주식 수익률 계산 (Calculate individual stock returns)

     

    [파이썬] 개별 주식 수익률 계산 (Calculate individual stock returns)

    주식 투자 혹은 주식 시장을 연구할 때 가장 기초가 되는 데이터가 바로 주식 수익률 입니다. 이번 포스트 에서는 파이썬으로 개별 주식 수익률을 계산하는 법에 대하여 살펴보도록 하겠습니다.

    invest-in-yourself.tistory.com

     

    파이썬으로 포트폴리오 수익률 계산하기

     

    1. 각 주식의 포트폴리오 비중 계산(Calculate portfolio weights for each stock)

    구체적인 상황은 먼저 가정해 보겠습니다. 우리가 가진 데이터의 맨 처음 날짜 초에 Apple 주식 100달러 어치, Google 주식, 200 달러 어치 상당이 계좌에 있다고 생각해 보겠습니다. 이를 다음 코드를 통하여 pandas dataframe으로 만듭니다.

     

    1
    2
    #Initial investment balance (초기 잔액)
    invst_money = pd.DataFrame([['Apple',100],['Google',200]],columns = ['Name','Invest'])
    cs

     

    다음과 같은 결과를 볼 수 있습니다. 

     

    다음으로 원래 수익률 데이터의 column 이름을 바로 위에 있는 표의 Name 열과 일치시켜주기 위해서 .rename() 메서드를 이용하여 바꾸어 줍니다. 그리고 .stack() 메서드를 이용하여 data 형태를 다음과 같이 바꾸어 줍니다. .sort_values() 메서드를 이용하여 첫번째로 Name, 두번째로 Date 열을 기준으로 오름차순으로 정렬합니다. (이 예시에서 사용된 수익률은 (오늘 종가 - 어제 종가) / (어제 종가) 로 계산된 첫번째 수익률 데이터 입니다.)

    1
    ptc_ret = ptc_ret.rename(columns={'Ret_Apple':'Apple','Ret_Google':'Google'}).stack().reset_index().rename(columns={'level_1':'Name',0:'Ret'}).sort_values(by=['Name','Date'])
    cs

     

    결과는 다음과 같습니다.

     

    다음 코드를 이용하여 투자 금액과 수익률 데이터를 합쳐줍니다.

     

    1
    2
    #Merge with return data (수익률 데이터와 합병)
    ptc_ret_1 = pd.merge(ptc_ret,invst_money,how='left')
    cs

     

    결과는 다음과 같습니다. 주의해야 하는 점은 Invest 열에 있는 값은 데이터 맨 처음 날짜인 2019-01-03 초의 값이라는 것입니다. 다시 말하면 2019-01-02 말 값이라고 할 수도 있습니다.

     

     

    매일 변화하는 포트폴리오 비중을 계산하기 위한 전략은 누적수익률 X 초기 투자비용 입니다. 누적 수익률 계산하는 법은 지난 포스트에 있으니 참고해 주시기 바랍니다. 

     

    여기에서 바로 누적수익률 X 초기 투자비용을 계산하면 각 날짜의 말 값이 나오기 때문에 .shift() 메서드를 이용하여 lag 누적 수익률을 만든 후 초기 투자비용을 곱하여 각 날짜의 초 값으로 만들어 줍니다. 해당 변수의 이름을 wt 라고 하겠습니다. .shift() 메서드를 사용할 때 각 주식별로 따로 적용이 되어야 하기 때문에 .groupby() 메서드를 이용하여 계산해 주도록 하겠습니다.

     

    1
    2
    3
    4
    5
    6
    7
    #Portfolio return
    ptc_ret_1['1+ret'= ptc_ret_1['Ret'+ 1
    ptc_ret_1['cumret'= ptc_ret_1.groupby(['Name'])['1+ret'].cumprod()
    ptc_ret_1['lcumret'= ptc_ret_1.groupby(['Name'])['cumret'].shift(1)
     
    ptc_ret_1['wt'= np.where(ptc_ret_1['lcumret'].isna(), ptc_ret_1['Invest'], ptc_ret_1['Invest']*ptc_ret_1['lcumret']) 
     
    cs

     

    np.where(조건, true 값, false 값) 함수는 조건이 True 이면 true 값False 라면 false 값을 할당해 주는 함수 입니다. 결과는 다음과 같습니다. 표에서 알 수 있듯이 wt 열에 매일 변화하는 (각 날짜의 초) 포트폴리오 비중이 계산되어 있습니다.

     

     

     

    2. 가중 평균 함수 작성(Write a value weighted average function)

    다음으로 가중 평균을 구하는 함수를 작성해 보겠습니다. 각 날짜별로 포트폴리오 내에 있는 모든 주식을 가중평균 해야 하므로 .groupby() 메서드를 이용할 것을 감안하여 group 매개변수가 들어가며, 평균을 계산할 변수를 avg_name 매개변수로, 가중치를 weight_name 매개변수로 설정합니다.

     

    1
    2
    3
    4
    5
    6
    7
    8
    # Weighted average (가중 평균 수익률 구하는 함수)
    def weighted_average(group, avg_name, weight_name):
        d = group[avg_name]
        w = group[weight_name]
        try:
            return (d * w).sum() / w.sum()
        except ZeroDivisionError:
            return np.nan
    cs

     

     

    2. 일별 포트폴리오 수익률 계산(Calculate daily porfolio returns)

    데이터 기간 내에 추가 주식 매입 및 매도가 없다고 가정buy and hold portfolio의 일별 수익률을 .groupby().apply() 메서드를 이용하여 다음과 같이 계산합니다. .apply(함수 이름, 매개변수 1, 매개변수 2) 로 적어주며, to_frame()은 dataframe으로 만들어 주는 메서드 입니다. 포트 폴리오 수익률은 vwret라는 변수 이름을 적용해 주겠습니다.

     

    1
    2
    3
    #Apple: 100, Google: 200 at the begining of 2019-01-03  
    #Buy and hold strategy
    port_ret = ptc_ret_1.groupby(['Date']).apply(weighted_average, 'Ret','wt').to_frame().reset_index().rename(columns={0'vwret'})
    cs

     

    결과는 다음과 같습니다.

     

     

    3. 동일 가중 평균 수익률과의 비교(Comparison with equal weighted returns)

    포트 폴리오 수익률을 계산할 때 매일 투자 금액이 변동하기 때문에 수익률을 평균 낼 때 주의해야 합니다. 흔히 하기 쉬운 실수는 다음과 같이 각 날짜별로 (동일 가중) 평균을 내서 포트폴리오 수익률을 계산하는 것 입니다. 얼마나 차이가 나는지 한번 비교해 보도록 하겠습니다.

     

    다음 코드를 이용하여 동일 가중 평균 수익률을 구하고 위에서 계산한 포트폴리오 수익률 dataframe과 합병해 보겠습니다. .mean() 메서드가 동일 가중 평균 수익률을 구하는 코드 입니다. 해당 변수명은 ewret라고 하겠습니다.

     

    1
    2
    3
    4
    #Equal-weighted return for comparison
    ew_ret = ptc_ret_1.groupby(['Date'])['Ret'].mean().reset_index().rename(columns={'Ret':'ewret'})
     
    merged_ret = pd.merge(port_ret,ew_ret).set_index(['Date'])
    cs

     

    결과는 다음과 같습니다. 

     

    정확하게 얼마나 차이가 나는지 보기 위해서 ewret - vwret 의 절대값을 계산한 후 그래프로 그려보도록 하겠습니다.

    abs() 함수가 절대값을 계산하는 함수이고 소수점이 아니라 %로 보기 위해서 100을 곱해 주었습니다. 

    그래프는 .plot.line() 메서드를 이용해서 그렸으며, .axes.title.set_size() 메서드는 그래프 제목의 글자 크기를 설정해 주는 역할을 합니다. 

     

    1
    2
    3
    merged_ret['diff'= abs(merged_ret['ewret'- merged_ret['vwret']) * 100
    fig = merged_ret['diff'].plot.line(title='Absolute value of Difference between vwret and ewret (%p)',figsize=(158))
    fig.axes.title.set_size(15)
    cs

     

    결과는 다음과 같습니다.

    가장 크게 차이가 나는 값은 1.2% 포인트이며 차이에 상당한 변동성이 보입니다. 동일 가중 평균을 이용하여 포트폴리오 수익률을 계산하면 오차가 매우 클 수 있다는 것을 알 수 있습니다.

     

     

    4. 가중치의 변화를 적용하지 않은 수익률과의 비교(Comparison with value weighted returns where weights are unchanged over time)

    또다른 실수는 가중치가 매일 변화한다는 것을 고려하지 않고 가중 평균을 내서 포트폴리오 수익률을 계산하는 것 입니다. 얼마나 차이가 나는지 한번 비교해 보도록 하겠습니다.

     

    다음 코드를 통하여 가중치가 변하지 않는 가중평균 수익률을 계산합니다. 이때 변하는 부분은 .apply()에 들어가는 매개변수 'wt'가 'Invest'로 변한다는 것 입니다. 데이터를 보시면 아시겠지만 Invest 열은 날짜가 변해도 값이 변하지 않습니다. 변수 이름은 vwret_no_change로 하겠습니다.

     

    1
    2
    3
    4
    #Portfolio returns with no weight change
    no_change_ret = ptc_ret_1.groupby(['Date']).apply(weighted_average, 'Ret','Invest').to_frame().reset_index().rename(columns={0'vwret_no_change'})
     
    merged_ret = pd.merge(port_ret,no_change_ret).set_index(['Date'])
    cs

     

    다음으로 차이를 계산하여 그래프를 통해 얼마나 다른지 확인해 보겠습니다.

     

    1
    2
    3
    merged_ret['diff'= abs(merged_ret['vwret_no_change'- merged_ret['vwret']) * 100
    fig = merged_ret['diff'].plot.line(title='Absolute value of Difference between vwret and vwret_no_change (%p)',figsize=(158))
    fig.axes.title.set_size(15)
    cs

     

    결과는 다음과 같습니다.

    초기에는 원래 비중과 크게 달라지지 않기 때문에 오차가 별로 없는 듯 보이다가 시간이 지날 수록 오차가 커짐을 알 수 있습니다. 초기 주가와 다를 수록 적용되는 가중치의 변화가 커지기 때문에 이렇게 계산해도 오차가 매우 클 수 있습니다.

     

     

     

    지금까지의 모든 파이썬 코드는 아래의 Google Colab 링크에서 연습해 보실 수 있습니다.

    Go to Google Colab Link

     

    Portfolio_Return_Jaseup_TStoryBlog.ipynb

    Colaboratory notebook

    colab.research.google.com

     

    주간/월별 수익률을 이용하여 포트폴리오 수익률을 계산하고자 하시는 분들은 다음 링크에서 주간/월별 수익률을 계산하는 법을 참고하시기 바랍니다.

    2021.10.15 - [코딩/파이썬] - [파이썬] 개별 주식 주간/월별 주익률 계산하기 (How to Calculate Weekly and Monthly returns of individual stocks)

     

    [파이썬] 개별 주식 주간/월별 주익률 계산하기 (How to Calculate Weekly and Monthly returns of individual stocks

    주식 관련 분석을 할 때 일별 데이터도 자주 사용하지만 주간 혹은 월간 데이터를 사용하는 경우도 많이 있습니다. 이번 포스트에서는 파이썬으로 주간 및 월별 수익률을 계산하는 방법을 알아

    invest-in-yourself.tistory.com

     

    반응형

    댓글

Designed by Tistory.