0

I have a dataframe with quarterly returns of financial entities and I want to calculate 1, 3, 5 10-year annualized returns. The formula for calculating annualized returns is:

R = product(1+r)^(4/N) -1

r are the quarterly return of an entity, N is the number of quarters

for example 3-year annualized return is:

R_3yr = product(1+r)^(4/12) -1 = ((1+r1)*(1+r2)*(1+r3)*...*(1+r12))^(1/3) -1

r1, r2, r3 ... r12 are the quarterly returns of the previous 11 quarters plus current quarter.

I created a code which provides the right results but it is very slow because it is looping through each row of the dataframe. The code below is an extract of my code for 1-year and 3-year annualized retruns (I applied the same concept for 5, 7, 10, 15 and 20-year returns). r_qrt is the field with the quarterly returns

import pandas as pd
import numpy as np

#create dataframe where I append the results
df_final = pd.DataFrame()
columns=['Date','Entity','r_qrt','R_1yr','R_3yr']

#loop thorugh the dataframe
for row in df.itertuples():
    
    R_1yr=np.nan #1-year annualized return
    R_3yr=np.nan #3-year annualized return
    
    #Calculate 1 YR Annualized Return
    date_previous_period=row.Date+ pd.DateOffset(years=-1)
    temp_table=df.loc[(df['Date']>date_previous_period) & 
                              (df['Date']<=row.Date) & 
                              (df['Entity']==row.Entity)]
    if temp_table['r_qrt'].count()>=4:
        b=(1+(temp_table.r_qrt))[-4:].product()
        R_1yr=(b-1)
    
    #Calculate 3 YR Annualized Return
    date_previous_period=row.Date+ pd.DateOffset(years=-3)
    temp_table=df.loc[(df['Date']>date_previous_period) & 
                              (df['Date']<=row.Date) &
                              (df['Entity']==row.Entity)]
        
    if temp_table['r_qrt'].count()>=12:
        b=(1+(temp_table.r_qrt))[-12:].product()
        R_3yr=((b**(1/3))-1)
        
    d=[row.Date,row.Entity,row.r_qrt,R_1yr,R_3yr]
    df_final = df_final.append(pd.Series(d, index=columns), ignore_index=True)

df_final looks as below (only reporting 1-year return results for space limitations)

Date Entity r_qrt R_1yr
2015-03-31 A 0.035719 NaN
2015-06-30 A 0.031417 NaN
2015-09-30 A 0.030872 NaN
2015-12-31 A 0.029147 0.133335
2016-03-31 A 0.022100 0.118432
2016-06-30 A 0.020329 0.106408
2016-09-30 A 0.017676 0.092245
2016-12-31 A 0.017304 0.079676
2015-03-31 B 0.034705 NaN
2015-06-30 B 0.037772 NaN
2015-09-30 B 0.036726 NaN
2015-12-31 B 0.031889 0.148724
2016-03-31 B 0.029567 0.143020
2016-06-30 B 0.028958 0.133312
2016-09-30 B 0.028890 0.124746
2016-12-31 B 0.030389 0.123110

I am sure there is a more efficient way to run the same calculations but I have not been able to find it. My code is not efficient and takes more than 2 hours for large dataframes with long time series and many entities.

Thanks

SimonC
  • 53
  • 6

2 Answers2

0

see (https://www.investopedia.com/terms/a/annualized-total-return.asp) for the definition of annualized return

data=[ 3, 7, 5, 12, 1]
def annualize_rate(data):
    retVal=0
    accum=1
    for item in data:
        print(1+(item/100))
        accum*=1+(item/100)
        retVal=pow(accum,1/len(data))-1
    return retVal
    
print(annualize_rate(data))

output

0.05533402290765199

2015 (a and b)

data=[0.133335,0.148724]

print(annualize_rate(data))

output:

0.001410292043902306

2016 (a&b)

data=[0.079676,0.123110]
print(annualize_rate(data))

output

0.0010139064424810051

you can store each year annualized value then use pct_chg to get a 3 year result

data=[0.05,0.06,0.07]
df=pd.DataFrame({'Annualized':data})
df['Percent_Change']=df['Annualized'].pct_change().fillna(0)

amount=1

returns_plus_one=df['Percent_Change']+1
cumulative_return = returns_plus_one.cumprod()

df['Cumulative']=cumulative_return.mul(amount)

df['2item']=df['Cumulative'].rolling(window=2).mean().plot()

print(df)
Golden Lion
  • 3,840
  • 2
  • 26
  • 35
  • Thanks but that page reports the same definition that I am using for annualized return which is not my problem. I need to improve my code implementation. – SimonC Nov 19 '21 at 20:52
  • I posted a table with quarterly returns (column r_qrt) and the results for the annualized 1-yr returns (R_1yr). Also keep in mind that I have returns for different entities in the same table (Entity A and B in the sample table) – SimonC Nov 19 '21 at 20:57
  • r_qrt is quarterly return. I also reported the results for the annualized 1-yr returns from my code (R_1yr) – SimonC Nov 19 '21 at 21:02
  • I just manually checked the results I posted for 1-yr annualized returns. They are correct. For example, Entity A in Dec 2015: ((1+0.035719)*(1+0.031417)*(1+0.030872)*(1+0.029147))^(1/1) -1 = 0.133335 – SimonC Nov 19 '21 at 21:16
  • your annualizing the quarters into an annual rate? Can you provide a source for this approach – Golden Lion Nov 19 '21 at 21:18
  • see (https://bizfluent.com/how-8684386-annualize-quarterly-numbers.html) on how to annualize using quarterly rates. (add them together and divide by 4) – Golden Lion Nov 19 '21 at 21:21
  • The link you posted from Investopedia reports the same methodology that I applied. See formula in section "Annualized Return Formula and Calculation" – SimonC Nov 19 '21 at 21:22
  • (0.035719+0.031417+0.030872+0.029147)/4 -> 0.03178875 (2015 A) – Golden Lion Nov 19 '21 at 21:23
  • ((1+0.035719)*(1+0.031417)*(1+0.030872)*(1+0.029147))^(1/4) you put n as 1 – Golden Lion Nov 19 '21 at 21:25
  • Let's not keep discussing the methodology. I know what I am doing is right in terms of formula. I need to improve the code. The code you posted, I am not sure how makes it more efficient. What is your proposed solution? I see two different approaches in your reply – SimonC Nov 19 '21 at 21:25
  • use df.shift to improve your efficiency and then use either map or apply to calculate annualize – Golden Lion Nov 19 '21 at 21:41
0

For future reference of other users, this is the new version of the code that I implemented following Golden Lion suggestion:

def compoundfunct(arr):
    return np.product(1+arr)**(4/len(arr)) - 1

# 1-yr annulized return
df["R_1Yr"]=df.groupby('Entity')['r_qrt'].rolling(4).apply(compoundfunct).groupby('Entity').shift(0).reset_index().set_index('level_1').drop('Entity',axis=1)

# 3-yr annulized return
df["R_3Yr"]=df.groupby('Entity')['r_qrt'].rolling(12).apply(compoundfunct).groupby('Entity').shift(0).reset_index().set_index('level_1').drop('Entity',axis=1)

The performance of the previous code was 36.4 sec for a dataframe of 5,640 rows. The new code is more than 10x faster, it took 2.8 sec

One of the issues with this new code is that one has to make sure that rows are sorted by group (Entity in my case) and date before running the calculations, otherwise results could be wrong.

Thanks, S.

SimonC
  • 53
  • 6