Internal Rate of Return versus Total Return
Apr
23
Written by:
Charles Flock
4/23/2015 3:18 PM
There are a lot of financial calculations that compute 'returns'. There are internal rates of return, return on investment, timeweighted rate of return, multiples of invested capital, compound annual growth rate, and many more. The good news is that you can perform all these calculations in SQL Server using XLeratorDB.
In this article we explore the difference between the two return calculations that we get the most questions about: internal rate of return and total return. You won't need an MBA in Finance to understand these examples; just follow along with SQL Server Management Studio and you will very quickly see how these calculations differ.
Internal rate of return is a moneyweighted return meaning that the amount and the timing of cash movements affect the result. An internal rate of return is calculated using the XLeratorDB XIRR function.
XIRR calculates a discount rate such that the XNPV of the cash flows is approximately zero. Generally, there is no closedform solution for an internal rate of return and the result is found using iterative techniques.
Given a set of cash flows in ascending date order, the formula for XNPV is:
Where
N

=

number of cash flows

c_{i}

=

cash flow amount

d_{i}

=

date of the cash flow

r

=

1 + discount arte

Total return, on the other hand, is a timeweighted return; the timing of cash contributions is inconsequential and the calculation captures portfolio or market performance over some time horizon. There are numerous XLeratorDB functions to calculate timeweighted return. For this article, we will use the GTWRR function which uses the following formula.
Where
N

=

the number of periods

MV_{t}

=

the ending market value for period t

MV_{t1}

=

the ending market value for the previous period

CF_{t}

=

the sum of the cash flows greater than the date of MV_{t1 }and less than or equal to the date of MV_{t}

Let's look at an example.
Given a portfolio of one security, let's assume that the price of the security is flat for years one and two and that the price of the security doubles in year three. Given this information, we will construct two scenarios. In the first scenario there are investments of 10,000 on 20121231 and 10,000 on 20131231. In the second there are investments of 10,000 on 20121231 and 10,000 on 20141231. The value of the position on 20151231 is 40,000. The following SQL calculates the internal rate of return for each scenario.
SELECT
scenario
,wct.XIRR(cf,dt,NULL) as [Internal Rate of Return]
FROM (VALUES
(1,'20121231',10000)
,(1,'20131231',10000)
,(1,'20151231',40000)
,(2,'20121231',10000)
,(2,'20141231',10000)
,(2,'20151231',40000)
)n(scenario,dt,cf)
GROUP BY
scenario
This produces the following result.
These results tell us that in scenario number 1 the investments earned 31.46% per year compounded annually while in investment number 2 the investments earned 37.88% per year compounded annually. The following SQL demonstrates that.
SELECT
n.scenario
,SUM(n.cf*POWER(1+x.IRR,wct.YEARFRAC(n.dt,'20151231',3))) as [Ending Value]
FROM (VALUES
(1,'20121231',10000)
,(1,'20131231',10000)
,(1,'20151231',40000)
,(2,'20121231',10000)
,(2,'20141231',10000)
,(2,'20151231',40000)
)n(scenario,dt,cf)
CROSS APPLY
(VALUES (1,0.314596212276752),(2,0.378796700129481))x(scenario,IRR)
WHERE
n.dt < '20151231'
AND n.scenario = x.scenario
GROUP BY
n.scenario
This produces the following result.
In both scenarios the internal rate of return compounded annually results in the ending value. The differences in the 2 internal rates of return arise solely based on whether or not the second cash flow occurs in 2013 or in 2014. This is why internal rate of return is a moneyweighted rate of return,
When calculating a total, or timeweighted, rate of return, we get a much different result.
SELECT
scenario
,wct.GTWRR(amt,dt,mv,1) as [Timeweighted Rate of Return]
FROM (VALUES
(1,'20121231',10000,0)
,(1,'20121231',10000,1)
,(1,'20131231',10000,0)
,(1,'20131231',20000,1)
,(1,'20141231',20000,1)
,(1,'20151231',40000,1)
,(2,'20121231',10000,0)
,(2,'20121231',10000,1)
,(2,'20131231',10000,1)
,(2,'20141231',10000,0)
,(2,'20141231',20000,1)
,(2,'20151231',40000,1)
)n(scenario,dt,amt,mv)
GROUP BY
scenario
This produces the following result.
The timeweighted rate of return is 100% in both cases. In other words, the return was unaffected by the timing of the cash flows.
Notice that the calculation of the timeweighted rate of return included more cash flows than the calculation of the internal rate of return. This is because in addition to the cash flows we also used the endofyear marketvalues in the calculation. The following SQL produces a resultant table that splits the cash flows and the market values into separate columns.
SELECT
scenario
,dt
,ISNULL([0],0) as CF
,[1] as [Ending Market Value]
FROM (
SELECT
*
FROM (VALUES
(1,'20121231',10000,0)
,(1,'20121231',10000,1)
,(1,'20131231',10000,0)
,(1,'20131231',20000,1)
,(1,'20141231',20000,1)
,(1,'20151231',40000,1)
,(2,'20121231',10000,0)
,(2,'20121231',10000,1)
,(2,'20131231',10000,1)
,(2,'20141231',10000,0)
,(2,'20141231',20000,1)
,(2,'20151231',40000,1)
)n(scenario,dt,amt,mv)
) d
PIVOT(SUM(amt) FOR mv in([0],[1])) pvt
ORDER BY
1,2
This produces the following result.
While it should be obvious, the following SQL shows how the timeweighted rate of return developed over 1, 2, and 3 years in each scenario.
SELECT
scenario
,x.dt
,x.trm as [term]
,wct.GTWRR(n.amt,n.dt,n.mv,1) as [Timeweighted Rate of Return]
FROM (VALUES
(1,'20121231',10000,0)
,(1,'20121231',10000,1)
,(1,'20131231',10000,0)
,(1,'20131231',20000,1)
,(1,'20141231',20000,1)
,(1,'20151231',40000,1)
,(2,'20121231',10000,0)
,(2,'20121231',10000,1)
,(2,'20131231',10000,1)
,(2,'20141231',10000,0)
,(2,'20141231',20000,1)
,(2,'20151231',40000,1)
)n(scenario,dt,amt,mv)
CROSS APPLY(VALUES
('1Y','20131231'),('2Y','20141231'),('3Y','20151231')
)x(trm,dt)
WHERE
n.dt <= x.dt
GROUP BY
n.scenario
,x.dt
,x.trm
ORDER BY
1,2
This produces the following result.
It's tempting to turn the total return into an annual return and that is relatively easy to do in this case. A 100% return over 3 years = = 0.25991. However, it is impossible to take that value and apply it to the cash flows and come up with the ending values. In fact, what happened is that the investment was flat for 2 years and then doubled in the third year.
Which calculation is right for you? Obviously that depends on what it is you are trying to measure. If you want a rate that you can apply to a series of cash flows that will get you to an ending balance, then internal rate of return is the calculation that you want. If you want to measure how an investment or an investment manager performed over some time horizon and you want remove the impact of cash movements, then timeweighted rate of return is the calculation that you want.
You can try out these calculations for yourself by downloading the free 15day trial of XLeratorDB right now and installing it on you SQL Server. Then just copy the examples into SMS and run them. XLeratorDB has over 900 functions for indatabase analytics that can turn your SQL Server into an analytics engine.