9

I'm using a IRR function in javascript to create calculation a that is done in excel using its own IRR function. The problem is mine is little off and I have no idea why. Here's the code below.

var IRRval = [];

IRRval.push(-financed);
for (i = 0; i < period; i++) {
    IRRval.push(rental);
}

var IRR = IRRCalc(IRRval, 0.001) * 0.01;

function IRRCalc(CArray, guest) {
    inc = 0.000001;
    do {
        guest += inc;
        NPV = 0;
        for (var j=0; j < CArray.length; j++) {
            NPV += CArray[j] / Math.pow((1 + guest), j);
        }
    } while (NPV > 0);
    return guest * 100;
}

Now if you use these figures:


Period 24

Financed 22000

Rental 1017.5000

My Result is: 0.008523000000000175

Excel Result is: 0.008522918


OR


Period 42

Financed 218000

Rental 5917.1429

My Result is: 0.006247000000000489

Excel Result is: 0.00624616


The Excel function is called: =IRR(T12:T73,0.01) T12-T73 is the same figures I'm using.

Any help would be much appreciated, Thanks Jason

UPDATE

I've solved it by changing the values below. But now the performance is too slow. Any ideas on how to improve this?

IRRCalc(IRRval, 0.001)

//to

IRRCalc(IRRval, 0.0001)

inc = 0.000001;

//to

inc = 0.00000001;
Jaak Kütt
  • 2,566
  • 4
  • 31
  • 39
dciso
  • 1,264
  • 3
  • 18
  • 31
  • Not a good way to program an IRR function, you are going through the domain of the function and finding it's value over the range. For a good number of cash flows, your code will never come to a halt as NPV will never be below or at zero. –  Mar 02 '13 at 01:33
  • @AbrahamA Thanks for your comment. If you have the time I'd love to see how it should be done correctly. – dciso Mar 04 '13 at 15:07
  • @dciso Have a look at tadJS JavaScript financial functions library at https://github.com/FinancialEngineer/tadJS –  Feb 10 '14 at 06:11

8 Answers8

12

We modified the code to achieve performance and accuracy. Try this:

function IRRCalc(CArray) {

  min = 0.0;
  max = 1.0;
  do {
    guest = (min + max) / 2;
    NPV = 0;
    for (var j=0; j<CArray.length; j++) {
          NPV += CArray[j]/Math.pow((1+guest),j);
    }
    if (NPV > 0) {
      min = guest;
    }
    else {
      max = guest;
    }
  } while(Math.abs(NPV) > 0.000001);
  return guest * 100;
}
Zohaib
  • 496
  • 7
  • 21
  • Zohaib, Thanks for your code, its working like a charm when we giving more terms like 20 or 25 years, but its hanged when giving terms like 3 05 5. can you help on this? – Giridaran Regunathan Feb 13 '14 at 08:22
  • @GiridaranRegunathan - you need to add a counter and exit after a certain number of iterations without resolution (the method fails and you have an undefined result). The actual value depends on your accuracy requirements, but we use 100000 for some non-critical path code. – Edward Jul 23 '14 at 21:48
  • 2
    if you want to reproduce Excels IRR function that also returns negative values, setting min = -1.0; works for me. – schudel Aug 27 '15 at 08:36
  • @Zohaib just trying to come up with a good way of exiting after a certain number of iterations and returning `undefined`. my attempt is below which I though you might be able to help me with. – HattrickNZ Dec 20 '15 at 22:47
  • How does this work for anyone? By itself there are errors thrown for min, max guest and NPV being undefined. Where does guest (guess) get incremented? – Altimus Prime Jun 29 '17 at 14:36
10

After a quick skim read through your code, the error seems to be related to floating point precision error. More information can be found here: http://ajaxian.com/archives/crock-on-floating-points-in-javascript

In older javascript engines, if you did 0.3 + 0.3 you get something like 0.600000000001

Though most javascript engines today return 0.6, under the hood, the problem remains. Adding floats together causes unexpected results. So in your case the

inc = 0.000001;
guest += inc;

seems to me, to be the problem.

A way to solve this would be to use whole numbers. So instead of 0.000001 you would use 1 and instead of 0.001 you would use 1000. Then divide your return result by 100000

whitneyit
  • 1,226
  • 8
  • 17
2

Try this.

function NPV(discountRate, cashFlow){
    var npv = 0;
    for(var t = 0; t < cashFlow.length; t++) {
        npv += cashFlow[t] / Math.pow((1+ discountRate),t);
    }
    return npv;
}


function IRR(cashFlow,guess){
    guess = guess ? guess : 0.1;
    var npv;
    do
    {
        npv = NPV(guess,cashFlow);
        guess+= 0.001;

    }
    while(npv > 0)

    return guess;
}
Mohamed Sayed
  • 491
  • 7
  • 10
Zulfugar Ismayilzadeh
  • 2,643
  • 3
  • 16
  • 26
1

It's a floating point issue to be sure. You're going to want to use a library like BigDecimal.js to handle your values. It's really the only way to avoid this issue.

Ben Lesh
  • 107,825
  • 47
  • 247
  • 232
1

I would like to build on from @Zohaib Answer, but what I would like to do is show undefined where appropriate. The best I can do is get it to equal to zero. I am using this simple dataset irr_arr=[-100, 100, 100, 100, 100,100]. I would appreciate some advice.

//IRRCALC funtion that handles irr going to infinity 

function IRRCalc_test(CArray) {

  min = 0.0;
  max = 1.0;
  c=0;
  do {
    guest = (min + max) / 2;
    NPV = 0;
    for (var j=0; j<CArray.length; j++) {
          NPV += CArray[j]/Math.pow((1+guest),j);
    }
    if (NPV > 0) {
      min = guest;
      c++; 
    }
    else {
      max = guest;
      c++;
    }

    if(c>=15){ return guest*100; }
  } while(Math.abs(NPV) > 0.000001);
  return guest*100;
}


// some testing 

irr_arr=[-100, 100, 100, 100, 100,100]
irr_res_arr_expected=[0,0,61.8,83.93,92.76,96.6]
for(i=1;i<=irr_arr.length;i++){
console.log("irr_arr - ",irr_arr.slice(0,i));
console.log("IRRCalc - ",IRRCalc(irr_arr.slice(0,i)))
//console.log("irr_expected - ", irr_res_arr_expected[i-1])
//if(IRRCalc(irr_arr.slice(0,i))===parseFloat(irr_res_arr_expected[i-1]).toFixed(2)){console.log(i,"- TRUE")} else {console.log(i,"- FALSE")}
}

this is the output

irr_arr -  [-100]
IRRCalc -  0.00      <<<<<<<<<<<<<<<<<<<------- this should be 'undefined' and not 'zero'
irr_arr -  [-100, 100]
IRRCalc -  0.00
irr_arr -  [-100, 100, 100]
IRRCalc -  61.80
irr_arr -  [-100, 100, 100, 100]
IRRCalc -  83.93
irr_arr -  [-100, 100, 100, 100, 100]
IRRCalc -  92.76
irr_arr -  [-100, 100, 100, 100, 100, 100]
IRRCalc -  96.60

here is the excel of what I am trying to produce

enter image description here

HattrickNZ
  • 4,373
  • 15
  • 54
  • 98
0

Don't set your min IRR to 0. That is what bounds your answer. Try allowing it to go negative. Also don't try to calc an IRR on one number.

Set F
  • 1
0

This might be a better way of writing the calculation. This allows the code to calculate instances of negative IRR or IRR above 100%.

        var IRRval = [];

        IRRval.push(-financed);
            for (i = 0; i < period; i++) {
            IRRval.push(rental);
        }
        var IRR = IRRCalc(IRRval, 0.001) * 0.01;

        function IRRCalc(CArray) {
            var r = 0
            min = -1.0;
            max = 10000.0;
            do {
                guest = (min + max) / 2;
                NPV = 0;
                for (var j = 0; j < CArray.length; j++) {
                    NPV += CArray[j] / Math.pow((1 + guest), j);
                }
                if (NPV > 0) {
                    min = guest;
                }
                else {
                    max = guest;
                }
                r++
            } while (r < 100);
            return guest * 100
        }
Lim Han Yang
  • 350
  • 6
  • 23
0

Here is an optimized cote to calculate IRR

This starts incrementing in 1 and if the algo reaches negative it change signal and divide increment by 10, now it decrements in 0.1 and now if it goes to positive change signal again divides by 10,and now it increments again in 0.01, it goes on till react temp between -100 and 100, wuat guarants a precise IRR.

function tirCalc(amounts, investment) {
    // This algorithm goes up and down till reach zero.
    let tir = 0.0;
    let result = false;
    let dividend = 100;
    let actualSignal = 1; //positive, means tir is increasing

    if (investment > 0) investment *= -1;
    do {
        let temp = 0;
        //Calculate tir based on the formula
        for (let i = 0; i < amounts.length; i++) {
            temp += amounts[i] / Math.pow(1 + tir, i + 1);
        }
        temp += investment;

        //Checks if tir is zero, zero is the expected result
        if (temp < 100 && temp > -100) {
            result = true;
            break;
        }

        //During calculation this code change the sum direction and rate changing the signal and dividend size to reduce the increment
        if ((temp < -100 && actualSignal > 0) || (temp > 100 && actualSignal < 0)) {
            actualSignal *= -1;
            dividend /= 10;
        }
        if (actualSignal > 0) {
            tir += dividend / 100;
        }
        if (actualSignal < 0) {
            tir -= dividend / 100;
        }
    } while (result === false);
    return (tir * 100).toFixed(2);
}