An illustration of how to calculate expected net returns to a private equity limited partner given an assumed probability distribution of gross returns. Files for this post are found at https://github.com/karlpolen/waterfallqd
In an earlier post, http://rpubs.com/kpolen/16871 , we talked about methods to calculate general partner compensation in a private equity partnership. As a refresher, the following code uses a function waterfall discussed in the earlier post to calculate gross and net returns for a partnership with a 1.5% management and incentive of 20% over an 8% preferred return with an 80% catchup. We plot the comparative returns and the fee paid to the sponsor as a function of gross return.
source('waterfallqd.r')
dmat.df=data.frame(am=1.5,pref=8,catchup=.8,carry=.2)
ret=100+seq(-20,30,.1)
wat=waterfall(dmat.df,ret)
plot(wat$grossreturn,wat$grossreturn,col='red',type='l',main='Gross and Net Returns',
ylab='Return',xlab='Gross Return')
lines(wat$grossreturn,wat$netreturn,col='blue')
legend('topleft',legend=c('Gross Return','Net Return'),col=c('red','blue'),lwd=1)
We can also use the waterfall function to calculate net returns assuming a probability distribution of gross returns. Suppose we think gross returns are normally distributed, with a mean of 15% and standard deviation of 15%. How are the net returns distributed?
In order to do this, we are going to create a set of gross returns that are distributed according to the above assumption. The R language has native capability for this and we simply use the dnorm function to build the distribution. We consider cases plus or minus 3 standard deviations from the mean. Note the use of rep to replicate more likely values to give them appropriate weight in the distribution. Then you calculate the returns using the waterfall function.
mu=15
sd=15
ret.seq=seq(mu-3*sd,mu+3*sd,.1)
ret.den=dnorm(ret.seq,mu,sd)
ret.count=round(1000*ret.den)
ret2=100+rep(ret.seq,ret.count)
wat2=waterfall(dmat.df,ret2)
Now we are ready to plot a density function of the gross and net returns.
with(wat2,plot(density(netreturn),col='blue',main='Return Density',xlab='Gross Return'))
with(wat2,lines(density(grossreturn),col='red'))
legend('topleft',legend=c('Gross Return','Net Return'),col=c('red','blue'),lwd=1)
Here we plot a cumulative density function. Note the use of ecdf to calculate an empirical density on the net returns.
with(wat2,plot(ecdf(netreturn),col='blue',pch=20,
main='Cumulative Density',xlab='Gross Return'))
lines(ret.seq,pnorm(ret.seq,mu,sd),col='red',lwd=3)
legend('topleft',legend=c('Gross Return','Net Return'),col=c('red','blue'),lwd=3)
Next we calculate the distribution of fees, i.e. the difference between gross and net returns. Note the use of from in the density function to cut off values below the minimum fee, which is the asset management fee. Contractually, the fees are never lower than that. I’ve added a vertical dotted line at the mean fee.
fee=wat2$grossreturn-wat2$netreturn
mean.fee=mean(fee)
plot(density(fee,from=min(fee)),col='purple',lwd=2,
main='Fee Drag Distribution\nGross Return minus Net Return',
xlim=c(1,14))
abline(v=mean.fee,lty='dotted',col='purple')
Suppose a sponsor gives you an option where it proposes to cut the asset management fee in half provided you agree to pay 50% of profit above a 15% preferred return. This type of tiered compensation is not typical in private equity, but common in real estate deals.
The following code calculates the watefall for this structure. We’ll call this “Deal 2” and the earlier structure “Deal 1”.
Here is the code to set up the waterfall for deal 2.
dmat.3=data.frame(am=c(.75,0),pref=c(8,15),catchup=c(.8,0),carry=c(.2,.5))
wat3=waterfall(dmat.3,ret)
Now let’s plot a comparison of the two deals.
plot (wat3$grossreturn,wat3$grossreturn,type='l',col='red',
main='Comparison of Alternate PE Deals',
xlab='Gross Return',ylab='Return')
lines(wat$grossreturn,wat$netreturn,type='l',col='blue')
lines(wat3$grossreturn,wat3$netreturn,type='l',col='green')
legend('topleft',col=c('red','blue','green'),legend=c('Gross Return','Deal 1','Deal 2'),lwd=1)
Let’s compare the two fees.
fee1=wat$grossreturn-wat$netreturn
fee2=wat3$grossreturn-wat3$netreturn
plot(wat3$grossreturn,fee2,type='l',col='green',
main='Comparison of Fees',
xlab='Gross Return',ylab='Fee')
lines(wat$grossreturn,fee1,type='l',col='blue')
legend('topleft',legend=c('Deal 1','Deal 2'),col=c('blue','green'),lwd=1)
As you can see, you are giving up a fair amount of upside in exchange for a guaranteed reduction in asset management fee. Is this a good deal?
We will analyze this with the same gross return distribution assumptions as above, i.e. gross returns are normally distributed with a mean of 15% and standard deviation of 15%. I’ve added dotted vertical lines to show the mean net returns of the two strategies.
wat4=waterfall(dmat.3,ret2)
with(wat4,plot(density(netreturn),col='green',main='Return Density',xlab='Return'))
with(wat4,abline(v=mean(netreturn),lty='dotted',col='green'))
with(wat2,lines(density(netreturn),col='blue'))
with(wat2,abline(v=mean(netreturn),lty='dotted',col='blue'))
with(wat4,lines(density(grossreturn),col='red'))
legend('topleft',
legend=c('Gross Return','Net Return Deal 1', 'Net Return Deal 2'),
col=c('red','blue','green'),lwd=1)
You may be tempted to plot the mean returns and standard deviations.
plot(mu,sd,col='red',pch=19,
main='Means and Standard Deviations',
xlab='Return',
ylab='Standard Deviation')
points(mean(wat2$netreturn),sd(wat2$netreturn),col='blue',pch=19)
points(mean(wat4$netreturn),sd(wat4$netreturn),col='green',pch=19)
legend('topleft',
legend=c('Gross Return','Net Return Deal 1', 'Net Return Deal 2'),
col=c('red','blue','green'),pch=19)
But this is misleading, because the reduction in standard deviation in Deal 2 comes from the truncating the right side of the distribution.
The following summary statistics show this.
require(fBasics)
statmat=basicStats(cbind(wat2$grossreturn,wat2$netreturn,wat4$netreturn))
colnames(statmat)=c('Gross','Deal 1','Deal 2')
round(statmat,2)
## Gross Deal 1 Deal 2
## nobs 9969.00 9969.00 9969.00
## NAs 0.00 0.00 0.00
## Minimum -27.20 -28.70 -27.95
## Maximum 57.20 44.56 33.85
## 1. Quartile 4.90 3.40 4.15
## 3. Quartile 25.10 18.88 17.80
## Mean 15.00 10.71 10.15
## Median 15.00 10.80 11.40
## Sum 149535.00 106767.26 101172.87
## SE Mean 0.15 0.12 0.11
## LCL Mean 14.71 10.47 9.94
## UCL Mean 15.29 10.95 10.36
## Variance 217.89 150.52 113.94
## Stdev 14.76 12.27 10.67
## Skewness 0.00 -0.23 -0.69
## Kurtosis -0.20 0.12 0.36
Another way to look at this is to consider the distribution of wealth generation, presented as TVPI. Let’s look at the distribution of TVPIs assuming an investment hold period of 5 years.
wealthgross=(1+.01*wat2$grossreturn)^5
wealthnet1=(1+.01*wat2$netreturn)^5
wealthnet2=(1+.01*wat4$netreturn)^5
plot(density(wealthnet1),col='blue',
ylim=c(0,.7),xlim=c(-1,8),
main='Comparison of TVPI distribution\nassuming 5 year hold',
xlab='TVPI')
lines(density(wealthgross),col='red')
lines(density(wealthnet2),col='green')
statmat5=basicStats(cbind(wealthgross,wealthnet1,wealthnet2))
colnames(statmat5)=c('Gross','Deal 1','Deal 2')
round(statmat5,2)
## Gross Deal 1 Deal 2
## nobs 9969.00 9969.00 9969.00
## NAs 0.00 0.00 0.00
## Minimum 0.20 0.18 0.19
## Maximum 9.60 6.31 4.30
## 1. Quartile 1.27 1.18 1.23
## 3. Quartile 3.06 2.37 2.27
## Mean 2.35 1.87 1.77
## Median 2.01 1.67 1.72
## Sum 23430.64 18603.02 17603.33
## SE Mean 0.01 0.01 0.01
## LCL Mean 2.32 1.85 1.75
## UCL Mean 2.38 1.89 1.78
## Variance 2.21 0.98 0.56
## Stdev 1.49 0.99 0.75
## Skewness 1.36 1.07 0.30
## Kurtosis 2.36 1.56 -0.18
You could also look at this is from an option pricing perspective. Deal 2 can be thought of as selling a call on the assets at a strike price reflecting the second preferred return hurdle for a premium equal to the asset management fee reduction. I will attempt this in a future post.