IRR

IRR (Internal Rate of Return)

假設:

(1) 現在資產價值(PV)=100
(2) 十年後的未來資產價值(FV)=121.8994
(3) 利率(rate) R= 2%
(4) 期數(nper) n=10 (年)
(5) 十年後的末來值:FV= PV x [(1+R) ^n] = 100 x [(1+2%) ^10] =121.8994元
(6) 現值 :PV = FV / [(1+R)^n] = 121.8994 / [(1+2%) ^10]= 100

所以:

已知本金為100元及十年後的定存本利和為121.8994元,但不知道定存利率為多少,就以用 IRR 來做計算出定存利率的值。

用程式計算:

IRR = Math.round((Math.pow(121.899/100, 1/10) - 1) * 100)

及為Math.round((Math.pow(投資結束後資金總和/原始投入資金, 1/投資總年數) - 1) * 100)

Math.pow(x, y)為 x的y次方,所以轉換為10分之1次方,然後因為原本100 * (1.02 ** 10) ,所以會有121.899/100

最後面 - 1) * 100) 的原因是要把 1.02 轉換為 2%,方便看。

Math.round 為化整,四捨五入

參考:https://www.moneysmart.tw/articles/%E8%B2%B7%E5%84%B2%E8%93%84%E9%9A%AA%E5%85%88%E6%87%82-irr/

較複雜的公式

function IRR(values, guess) {
  // Calculates the resulting amount
  const irrResult = function(values, dates, rate) {
    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 irrResultDeriv = function(values, dates, rate) {
    const r = rate + 1;
    let 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
  let dates = [];
  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 '#NUM!';

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

  // Set maximum epsilon for end of iteration
  let epsMax = 1e-10;

  // Set maximum number of iterations
  let 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 / 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;
}

e.g. 第一年投入25,之後每年的利息分別為,2,3,3,3最後一年退出時賣出30

IRR([-25,2,3,3,3,30]) = 0.12258040492894852 = 12.26%

e.g. 投入三年,第一年投入10元,之後三年的利息均得到2元,利率及為20%。

IRR([-10,2,2,12]) = 0.2

但上面的公式還是會有不準確的時候

參考 https://www.ijeat.org/wp-content/uploads/papers/v8i3S/C10810283S19.pdf 新增了加強版的算法

let guess = Math.pow(arrSum(values) / values[0], 1 / ((values.length - 1) / 2 + 1)) - 1;

加強版

function IRR(values) {
  // Calculates the resulting amount
  const irrResult = function(values, dates, rate) {
    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 irrResultDeriv = function(values, dates, rate) {
    const r = rate + 1;
    let 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
  let dates = [];
  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 '#NUM!';

  // Initialize guess and resultRate

  // This value of guess is from Enhanced Newton-Raphson Algorithm

  const arrSum = values => values.reduce((a, b) => a + b, 0);
  // 參考至 https://www.ijeat.org/wp-content/uploads/papers/v8i3S/C10810283S19.pdf
  let guess = Math.pow(arrSum(values) / values[0], 1 / ((values.length - 1) / 2 + 1)) - 1;

  let resultRate = guess ? guess : 1e-8;
  // Set maximum epsilon for end of iteration
  let epsMax = 1e-10;

  // Set maximum number of iterations
  let 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 / 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;
}

Last updated