Internal rate of return.

Finding an internal rate of return is thus to find a root of of the equation

As any textbook in basic finance, such as Brealey and Myers (1996), Ross et al. (1996) or Sharpe and Alexander (1990) will tell, there is a number of problems with the IRR, most of them stemming from the possibility for more than one interest rate being defined.

If we know that there is one IRR, the following method is probably simplest, bisection. It is an adaption of the bracketing approach discussed in Press et al. (1992), chapter 9. Note that this approach will only find one interest rate, if there is more than one irr, the simplest is always to graph the PV as a function of interest rates, and use that to understand when an investment is a good one.

// file cflow_irr.cc // author: Bernt A Oedegaard #include <cmath> #include <algorithm> #include <vector> #include "fin_algoritms.h" const double ERROR=-1e30; double cash_flow_irr(vector<double>& cflow_times, vector<double>& cflow_amounts) { // simple minded irr function. Will find one root (if it exists.) // adapted from routine in Numerical Recipes in C. if (cflow_times.size()!=cflow_amounts.size()) return ERROR; const double ACCURACY = 1.0e-5; const int MAX_ITERATIONS = 50; double x1=0.0; double x2 = 0.2; // create an initial bracket, with a root somewhere between bot,top double f1 = cash_flow_pv(cflow_times, cflow_amounts, x1); double f2 = cash_flow_pv(cflow_times, cflow_amounts, x2); int i; for (i=0;i<MAX_ITERATIONS;i++) { if ( (f1*f2) < 0.0) { break; }; // if (fabs(f1)<fabs(f2)) { f1 = cash_flow_pv(cflow_times,cflow_amounts, x1+=1.6*(x1-x2)); } else {f2 = cash_flow_pv(cflow_times,cflow_amounts, x2+=1.6*(x2-x1)); }; }; if (f2*f1>0.0) { return ERROR; }; double f = cash_flow_pv(cflow_times,cflow_amounts, x1); double rtb; double dx=0; if (f<0.0) { rtb = x1; dx=x2-x1; } else { rtb = x2; dx = x1-x2; }; for (i=0;i<MAX_ITERATIONS;i++){ dx *= 0.5; double x_mid = rtb+dx; double f_mid = cash_flow_pv(cflow_times,cflow_amounts, x_mid); if (f_mid<=0.0) { rtb = x_mid; } if ( (fabs(f_mid)<ACCURACY) || (fabs(dx)<ACCURACY) ) return x_mid; }; return ERROR; // error. };

Check for unique irr.

If you worry about finding more than one IRR, the following implements a simple check for that. It is only a neccesary condition for a unique IRR, not sufficient, so you may still have a well-defined IRR even if this returns false.

The first test is just to count the number of sign changes in the cash flow. From Descartes rule we know that the number of real roots is one if there is only one sign change. If there is more than one change in the sign of cash flows, we can go further and check the aggregated cash flows for sign changes (See Norstrom (1972), or Berck and Sydsæter (1995)).

// file cflow_irr_test_unique.cc // author Bernt A Oedegaard #include <cmath> #include <vector> inline int sgn(double& r){ if (r>=0) {return 1;} else {return -1;}; }; bool cash_flow_unique_irr(vector<double>& cflow_times, vector<double>& cflow_amounts) { // check whether the cash flow has a unique irr. int sign_changes=0; // first check Descartes rule for (unsigned t=1;t<cflow_times.size();++t){ if (sgn(cflow_amounts[t-1]) !=sgn(cflow_amounts[t])) sign_changes++; }; if (sign_changes==0) return false; // can not find any irr if (sign_changes==1) return true; double A = cflow_amounts[0]; // check the aggregate cash flows, due to Norstrom sign_changes=0; for (unsigned t=1;t<cflow_times.size();++t){ if (sgn(A) != sgn(A+=cflow_amounts[t])) sign_changes++; }; if (sign_changes<=1) return true; return false; }