Skip to content

Instantly share code, notes, and snippets.

@ghalimi
Created January 22, 2013 01:45
Show Gist options
  • Save ghalimi/4591338 to your computer and use it in GitHub Desktop.
Save ghalimi/4591338 to your computer and use it in GitHub Desktop.
IRR Function
// Copyright (c) 2012 Sutoiku, Inc. (MIT License)
// Some algorithms have been ported from Apache OpenOffice:
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
function IRR(values, guess) {
// Credits: algorithm inspired by Apache OpenOffice
// Calculates the resulting amount
var irrResult = function(values, dates, rate) {
var r = rate + 1;
var result = values[0];
for (var i = 1; i < values.length; i++) {
result += values[i] / Math.pow(r, (dates[i] - dates[0]) / 365);
}
return result;
}
// Calculates the first derivation
var irrResultDeriv = function(values, dates, rate) {
var r = rate + 1;
var result = 0;
for (var i = 1; i < values.length; i++) {
var frac = (dates[i] - dates[0]) / 365;
result -= frac * values[i] / Math.pow(r, frac + 1);
}
return result;
}
// Initialize dates and check that values contains at least one positive value and one negative value
var dates = [];
var positive = false;
var negative = false;
for (var i = 0; i < values.length; i++) {
dates[i] = (i === 0) ? 0 : dates[i - 1] + 365;
if (values[i] > 0) positive = true;
if (values[i] < 0) negative = true;
}
// Return error if values does not contain at least one positive value and one negative value
if (!positive || !negative) return '#NUM!';
// Initialize guess and resultRate
var guess = (typeof guess === 'undefined') ? 0.1 : guess;
var resultRate = guess;
// Set maximum epsilon for end of iteration
var epsMax = 1e-10;
// Set maximum number of iterations
var iterMax = 50;
// Implement Newton's method
var newRate, epsRate, resultValue;
var iteration = 0;
var contLoop = true;
do {
resultValue = irrResult(values, dates, resultRate);
newRate = resultRate - resultValue / irrResultDeriv(values, dates, resultRate);
epsRate = Math.abs(newRate - resultRate);
resultRate = newRate;
contLoop = (epsRate > epsMax) && (Math.abs(resultValue) > epsMax);
} while(contLoop && (++iteration < iterMax));
if(contLoop) return '#NUM!';
// Return internal rate of return
return resultRate;
}
@kodejuice
Copy link

IRR([-200,5,5,5,5,5]) yields Infinity

@MaxVilson
Copy link

Thank you very much!

@sabohat
Copy link

sabohat commented Jan 26, 2022

Thanks you a lot!
This was the most accurate formula I have found

@kirkol
Copy link

kirkol commented Jan 26, 2023

Bro, you're great! 🥇

@kevinuzan
Copy link

Thanks, that was perfect

@ntltd
Copy link

ntltd commented Nov 28, 2024

Here's the TypeScript modern version:

// Calculates the resulting amount
const _irrResult = function (values: number[], dates: number[], rate: number) {
  const r = rate + 1
  let result = values[0]
  for (let i = 1; i < values.length; i++) {
    result += values[i] / Math.pow(r, (dates[i] - dates[0]) / 365)
  }
  return result
}

// Calculates the first derivation
const _irrResultDerivation = function (values: number[], dates: number[], rate: number) {
  const r = rate + 1
  let result = 0
  for (let i = 1; i < values.length; i++) {
    const frac = (dates[i] - dates[0]) / 365
    result -= (frac * values[i]) / Math.pow(r, frac + 1)
  }
  return result
}

const IRR = (values: number[], guess: number): number => {
  // Initialize dates and check values contains at least one positive value and one negative value
  const dates = [] as number[]
  let positive = false
  let negative = false
  for (let i = 0; i < values.length; i++) {
    dates[i] = i === 0 ? 0 : dates[i - 1] + 365
    if (values[i] > 0) positive = true
    if (values[i] < 0) negative = true
  }

  // Return error if values does not contain at least one positive value and one negative value
  if (!positive || !negative) {
    return NaN
  }

  // Initialize the guess and the resultRate
  let resultRate = typeof guess === 'undefined' ? 0.1 : guess

  // Set maximum epsilon for the end of the iteration
  const epsMax = 1e-10

  // Set maximum count of iterations
  const iterMax = 50

  // Implement Newton's method
  let newRate, epsRate, resultValue
  let iteration = 0
  let contLoop = true
  do {
    resultValue = _irrResult(values, dates, resultRate)
    newRate = resultRate - resultValue / _irrResultDerivation(values, dates, resultRate)
    epsRate = Math.abs(newRate - resultRate)
    resultRate = newRate
    contLoop = epsRate > epsMax && Math.abs(resultValue) > epsMax
  } while (contLoop && ++iteration < iterMax)

  if (contLoop) {
    return NaN
  }

  // Return internal rate of return
  return resultRate
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment