-
Geographical Analysis 6
-
Lecture1.1
-
Lecture1.2
-
Lecture1.3
-
Lecture1.4
-
Lecture1.5
-
Lecture1.6
-
-
Cap Table 3
-
Lecture2.1
-
Lecture2.2
-
Lecture2.3
-
-
Simulation 6
-
Lecture3.1
-
Lecture3.2
-
Lecture3.3
-
Lecture3.4
-
Lecture3.5
-
Lecture3.6
-
-
Search Index 8
-
Lecture4.1
-
Lecture4.2
-
Lecture4.3
-
Lecture4.4
-
Lecture4.5
-
Lecture4.6
-
Lecture4.7
-
Lecture4.8
-
-
Fund Distributions 5
-
Lecture5.1
-
Lecture5.2
-
Lecture5.3
-
Lecture5.4
-
Lecture5.5
-
Limited Partners Profit Part 2
Limited Partner Profit Function¶
Let’s work through an example of keeping track of fees and its impact on distributions. It is important to understand that companies can do all sorts of different things in this respect, this is just one example! First recall what we originally had.
print(distributions)
print()
print()
print(mgmt_fees)
1 2 3 4 5 6 7 8
Committed Capital 500 500 500 500 500 500 500 500
Capital Call 200 150 150 0 0 0 0 0
Total Called Capital 200 350 500 500 500 500 500 500
Distributions 0 0 0 350 200 200 400 200
Total Distributions 0 0 0 350 550 750 1150 1350
1 2 3 4 5 6 7 8
Committed Capital 500.0 500.0 500.0 500.0 500.0 500.0 500.0 500.0
Management Fee 5.0 5.0 5.0 5.0 5.0 5.0 5.0 5.0
Now, take the running total of distributions before any fees are applied and link add in a running total of the management fees.
lp_profits = pd.concat([distributions.loc['Total Distributions'], mgmt_fees.loc['Management Fee'].cumsum()], axis=1).transpose()
print(lp_profits)
1 2 3 4 5 6 7 8
Total Distributions 0.0 0.0 0.0 350.0 550.0 750.0 1150.0 1350.0
Management Fee 5.0 10.0 15.0 20.0 25.0 30.0 35.0 40.0
Now, get the net position based on the difference.
lp_profits.loc['Net Mgmt Fees'] = lp_profits.loc['Total Distributions'] - lp_profits.loc['Management Fee']
print(lp_profits)
1 2 3 4 5 6 7 8
Total Distributions 0.0 0.0 0.0 350.0 550.0 750.0 1150.0 1350.0
Management Fee 5.0 10.0 15.0 20.0 25.0 30.0 35.0 40.0
Net Mgmt Fees -5.0 -10.0 -15.0 330.0 525.0 720.0 1115.0 1310.0
For the performance fee, we will work the hypothetical assumption that agreement states performance fees of 20% may be charged on any performance net of management fees over the committed capital. Using this assumption, we add in the performance fee accrued.
lp_profits.loc['Performance Fee'] = lp_profits.loc['Net Mgmt Fees'].apply(lambda x: max(x-500,0) * .2)
print(lp_profits)
1 2 3 4 5 6 7 8
Total Distributions 0.0 0.0 0.0 350.0 550.0 750.0 1150.0 1350.0
Management Fee 5.0 10.0 15.0 20.0 25.0 30.0 35.0 40.0
Net Mgmt Fees -5.0 -10.0 -15.0 330.0 525.0 720.0 1115.0 1310.0
Performance Fee 0.0 0.0 0.0 0.0 5.0 44.0 123.0 162.0
Get the net performance fee total distribution.
lp_profits.loc['Net Perf Fee'] = lp_profits.loc['Net Mgmt Fees'] - lp_profits.loc['Performance Fee']
print(lp_profits)
1 2 3 4 5 6 7 8
Total Distributions 0.0 0.0 0.0 350.0 550.0 750.0 1150.0 1350.0
Management Fee 5.0 10.0 15.0 20.0 25.0 30.0 35.0 40.0
Net Mgmt Fees -5.0 -10.0 -15.0 330.0 525.0 720.0 1115.0 1310.0
Performance Fee 0.0 0.0 0.0 0.0 5.0 44.0 123.0 162.0
Net Perf Fee -5.0 -10.0 -15.0 330.0 520.0 676.0 992.0 1148.0
The net performance fee when it is negative is just a paper loss because we already have the money from the investors drawn down to pay out these. Because of this we may want a new row which describes the total distributions net fees and has values of 0 when there are negative values.
lp_profits.loc['Total Distributions Net Fees'] = lp_profits.loc['Net Perf Fee'].apply(lambda x: max(x, 0))
print(lp_profits)
1 2 3 4 5 6 7 \
Total Distributions 0.0 0.0 0.0 350.0 550.0 750.0 1150.0
Management Fee 5.0 10.0 15.0 20.0 25.0 30.0 35.0
Net Mgmt Fees -5.0 -10.0 -15.0 330.0 525.0 720.0 1115.0
Performance Fee 0.0 0.0 0.0 0.0 5.0 44.0 123.0
Net Perf Fee -5.0 -10.0 -15.0 330.0 520.0 676.0 992.0
Total Distributions Net Fees 0.0 0.0 0.0 330.0 520.0 676.0 992.0
8
Total Distributions 1350.0
Management Fee 40.0
Net Mgmt Fees 1310.0
Performance Fee 162.0
Net Perf Fee 1148.0
Total Distributions Net Fees 1148.0
Finally, to find the distributions necessary each period, we can subtract the prior period value (by using shift). For the case of year 1, we need to make sure we fill null values with 0 in the shifted series (because distributions are 0 before beginning).
ending = lp_profits.loc['Total Distributions Net Fees']
starting = lp_profits.loc['Total Distributions Net Fees'].shift(1).fillna(0)
lp_profits.loc['Distribution'] = ending - starting
print(lp_profits)
1 2 3 4 5 6 7 \
Total Distributions 0.0 0.0 0.0 350.0 550.0 750.0 1150.0
Management Fee 5.0 10.0 15.0 20.0 25.0 30.0 35.0
Net Mgmt Fees -5.0 -10.0 -15.0 330.0 525.0 720.0 1115.0
Performance Fee 0.0 0.0 0.0 0.0 5.0 44.0 123.0
Net Perf Fee -5.0 -10.0 -15.0 330.0 520.0 676.0 992.0
Total Distributions Net Fees 0.0 0.0 0.0 330.0 520.0 676.0 992.0
Distribution 0.0 0.0 0.0 330.0 190.0 156.0 316.0
8
Total Distributions 1350.0
Management Fee 40.0
Net Mgmt Fees 1310.0
Performance Fee 162.0
Net Perf Fee 1148.0
Total Distributions Net Fees 1148.0
Distribution 156.0
The limited partner cash flows are found by subtracting these distributions from the capital that was called over the years prior.
lp_cash_flows = lp_profits.loc["Distribution"] - distributions.loc["Capital Call"]
print(lp_cash_flows)
1 -200.0
2 -150.0
3 -150.0
4 330.0
5 190.0
6 156.0
7 316.0
8 156.0
dtype: float64
Find the IRR from these cashflows.
IRR = minimize(sq_distance, .05, args=(lp_cash_flows.index, lp_cash_flows), method="Nelder-Mead")['x'][0]
print(IRR)
0.24787109375000071
We can compare this to the IRR without fees.
pre_fees_cash_flows = distributions.loc["Distributions"] - distributions.loc["Capital Call"]
IRR = minimize(sq_distance, .05, args=(pre_fees_cash_flows.index, pre_fees_cash_flows), method="Nelder-Mead")['x'][0]
print(IRR)
0.29453125000000085
The J Curve¶
Often with this type of investing, there is a "J Curve" to profits because in the beginning money is only withdrawn leading to very negative overall cash flow which then eventually turns positive in later years. It is tough to evaluate performance in the beginning because these funds start very much so in the red, but it is well known that this is the trajectory. Let's plot the J Curve for these cashflows by getting total distributions then plotting that.
total_distributions = lp_cash_flows.cumsum()
total_distributions.plot(kind='line')
plt.show()
Expanding Period IRR¶
Let's look at what the IRR is at each point in time where we have at least one positive value.
rolling_irr = distributions.loc['Distributions'] - distributions.loc['Capital Call']
rolling_irr = rolling_irr.expanding().apply(lambda x: minimize(sq_distance, .05, args=(x.index, x), method="Nelder-Mead")['x'][0] if max(x) > 0 else np.NaN)
print(rolling_irr)
1 NaN
2 NaN
3 NaN
4 -0.160464
5 0.039297
6 0.150059
7 0.263262
8 0.294531
dtype: float64