spreadsheet
typeScript/javascript spreadsheet parser, with formulas.
git clone https://git.vogt.world/spreadsheet.git
Log | Files | README.md
← Commit log
commit
message
[MIRR] formula added and tested
author
Ben Vogt <[email protected]>
date
2017-06-30 12:26:51
stats
7 file(s) changed, 113 insertions(+), 12 deletions(-)
files
DOCS.md
TODO.md
dist/Formulas/AllFormulas.js
dist/Formulas/Financial.js
src/Formulas/AllFormulas.ts
src/Formulas/Financial.ts
tests/Formulas/FinancialTest.ts
  1diff --git a/DOCS.md b/DOCS.md
  2index c58e161..774de27 100644
  3--- a/DOCS.md
  4+++ b/DOCS.md
  5@@ -492,6 +492,17 @@
  6 @returns {number} 
  7 @constructor
  8 ```
  9+
 10+### MIRR 
 11+
 12+```
 13+  Calculates the modified internal rate of return of a series of investments. 
 14+@param values - Range or values of payments. 
 15+@param financeRate - The rate of interest of the investments. 
 16+@param reinvestRate - The rate of interest of the reinvestment. 
 17+@returns {number} 
 18+@constructor TODO: This relies on NPV and will therefore be prone to floating-point errors.
 19+```
 20 ## Info
 21 
 22 
 23diff --git a/TODO.md b/TODO.md
 24index 73f0ca7..88df852 100644
 25--- a/TODO.md
 26+++ b/TODO.md
 27@@ -142,7 +142,6 @@ For example 64 tbs to a qt.
 28 * INTRATE
 29 * IPMT
 30 * IRR
 31-* MIRR
 32 * PPMT - Similar to PMT, which is already written.
 33 * PRICE
 34 * PRICEDISC
 35diff --git a/dist/Formulas/AllFormulas.js b/dist/Formulas/AllFormulas.js
 36index 7932146..f4f8937 100644
 37--- a/dist/Formulas/AllFormulas.js
 38+++ b/dist/Formulas/AllFormulas.js
 39@@ -115,6 +115,7 @@ exports.SLN = Financial_1.SLN;
 40 exports.NPV = Financial_1.NPV;
 41 exports.NPER = Financial_1.NPER;
 42 exports.NOMINAL = Financial_1.NOMINAL;
 43+exports.MIRR = Financial_1.MIRR;
 44 var Statistical_1 = require("./Statistical");
 45 exports.AVERAGE = Statistical_1.AVERAGE;
 46 exports.AVERAGEA = Statistical_1.AVERAGEA;
 47diff --git a/dist/Formulas/Financial.js b/dist/Formulas/Financial.js
 48index 8d3fffd..ae57292 100644
 49--- a/dist/Formulas/Financial.js
 50+++ b/dist/Formulas/Financial.js
 51@@ -477,19 +477,19 @@ var NPV = function (rate) {
 52     for (var _i = 1; _i < arguments.length; _i++) {
 53         values[_i - 1] = arguments[_i];
 54     }
 55-    ArgsChecker_1.ArgsChecker.checkAtLeastLength(arguments, 2, "SYD");
 56+    ArgsChecker_1.ArgsChecker.checkAtLeastLength(arguments, 2, "NPV");
 57     var range = Filter_1.Filter.flattenAndThrow(values).map(function (value) {
 58         try {
 59             return TypeConverter_1.TypeConverter.valueToNumber(value);
 60         }
 61         catch (e) {
 62-            throw new Errors_1.ValueError("Function NPV parameter 8 expects number values. But '" + value + "' is " + (typeof value)
 63+            throw new Errors_1.ValueError("Function NPV expects number values. But '" + value + "' is " + (typeof value)
 64                 + " and cannot be coerced to a number.");
 65         }
 66     });
 67     var value = 0;
 68-    for (var j = 0; j < range.length; j++) {
 69-        value += range[j] / Math.pow(1 + rate, j);
 70+    for (var i = 0; i < range.length; i++) {
 71+        value += range[i] / Math.pow(1 + rate, i);
 72     }
 73     return value;
 74 };
 75@@ -544,3 +544,36 @@ var NOMINAL = function (rate, periods) {
 76     return (Math.pow(rate + 1, 1 / periods) - 1) * periods;
 77 };
 78 exports.NOMINAL = NOMINAL;
 79+/**
 80+ * Calculates the modified internal rate of return of a series of investments.
 81+ * @param values - Range or values of payments.
 82+ * @param financeRate - The rate of interest of the investments.
 83+ * @param reinvestRate - The rate of interest of the reinvestment.
 84+ * @returns {number}
 85+ * @constructor
 86+ * TODO: This relies on NPV and will therefore be prone to floating-point errors.
 87+ */
 88+var MIRR = function (values, financeRate, reinvestRate) {
 89+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 3, "MIRR");
 90+    values = Filter_1.Filter.flattenAndThrow(values).map(function (value) {
 91+        return TypeConverter_1.TypeConverter.valueToNumber(value);
 92+    });
 93+    var n = values.length;
 94+    var payments = [];
 95+    var incomes = [];
 96+    for (var i = 0; i < n; i++) {
 97+        if (values[i] < 0) {
 98+            payments.push(values[i]);
 99+        }
100+        else {
101+            incomes.push(values[i]);
102+        }
103+    }
104+    if (incomes.length === 0 || payments.length === 0) {
105+        throw new Errors_1.DivZeroError("For MIRR, the values must include positive and negative numbers.");
106+    }
107+    var num = -NPV(reinvestRate, incomes) * Math.pow(1 + reinvestRate, n - 1);
108+    var den = NPV(financeRate, payments) * (1 + financeRate);
109+    return Math.pow(num / den, 1 / (n - 1)) - 1;
110+};
111+exports.MIRR = MIRR;
112diff --git a/src/Formulas/AllFormulas.ts b/src/Formulas/AllFormulas.ts
113index c443a59..80e70fd 100644
114--- a/src/Formulas/AllFormulas.ts
115+++ b/src/Formulas/AllFormulas.ts
116@@ -118,7 +118,8 @@ import {
117   SLN,
118   NPV,
119   NPER,
120-  NOMINAL
121+  NOMINAL,
122+  MIRR
123 } from "./Financial";
124 import {
125   AVERAGE,
126@@ -375,5 +376,6 @@ export {
127   SLN,
128   NPV,
129   NPER,
130-  NOMINAL
131+  NOMINAL,
132+  MIRR
133 }
134\ No newline at end of file
135diff --git a/src/Formulas/Financial.ts b/src/Formulas/Financial.ts
136index 5d343d7..3d282bc 100644
137--- a/src/Formulas/Financial.ts
138+++ b/src/Formulas/Financial.ts
139@@ -487,18 +487,18 @@ var SLN = function (cost, salvage, life) {
140  * TODO: This function can return results that are prone to floating point precision errors.
141  */
142 var NPV = function (rate, ...values) {
143-  ArgsChecker.checkAtLeastLength(arguments, 2, "SYD");
144+  ArgsChecker.checkAtLeastLength(arguments, 2, "NPV");
145   var range = Filter.flattenAndThrow(values).map(function (value) {
146     try {
147       return TypeConverter.valueToNumber(value);
148     } catch (e) {
149-      throw new ValueError("Function NPV parameter 8 expects number values. But '" + value + "' is " + (typeof value)
150+      throw new ValueError("Function NPV expects number values. But '" + value + "' is " + (typeof value)
151           + " and cannot be coerced to a number.")
152     }
153   });
154   var value = 0;
155-  for (var j = 0; j < range.length; j++) {
156-    value += range[j] / Math.pow(1 + rate, j);
157+  for (var i = 0; i < range.length; i++) {
158+    value += range[i] / Math.pow(1 + rate, i);
159   }
160   return value;
161 };
162@@ -553,6 +553,41 @@ var NOMINAL =  function (rate, periods) {
163   return (Math.pow(rate + 1, 1 / periods) - 1) * periods;
164 };
165 
166+
167+/**
168+ * Calculates the modified internal rate of return of a series of investments.
169+ * @param values - Range or values of payments.
170+ * @param financeRate - The rate of interest of the investments.
171+ * @param reinvestRate - The rate of interest of the reinvestment.
172+ * @returns {number}
173+ * @constructor
174+ * TODO: This relies on NPV and will therefore be prone to floating-point errors.
175+ */
176+var MIRR = function (values, financeRate, reinvestRate) {
177+  ArgsChecker.checkLength(arguments, 3, "MIRR");
178+  values =  Filter.flattenAndThrow(values).map(function (value) {
179+    return TypeConverter.valueToNumber(value);
180+  });
181+  var n = values.length;
182+
183+  var payments = [];
184+  var incomes = [];
185+  for (var i = 0; i < n; i++) {
186+    if (values[i] < 0) {
187+      payments.push(values[i]);
188+    } else {
189+      incomes.push(values[i]);
190+    }
191+  }
192+  if (incomes.length === 0 || payments.length === 0) {
193+    throw new DivZeroError("For MIRR, the values must include positive and negative numbers.");
194+  }
195+
196+  var num = -NPV(reinvestRate, incomes) * Math.pow(1 + reinvestRate, n - 1);
197+  var den = NPV(financeRate, payments) * (1 + financeRate);
198+  return Math.pow(num / den, 1 / (n - 1)) - 1;
199+};
200+
201 export {
202   ACCRINT,
203   CUMPRINC,
204@@ -568,5 +603,6 @@ export {
205   SLN,
206   NPV,
207   NPER,
208-  NOMINAL
209+  NOMINAL,
210+  MIRR
211 }
212\ No newline at end of file
213diff --git a/tests/Formulas/FinancialTest.ts b/tests/Formulas/FinancialTest.ts
214index 862bfa6..b015061 100644
215--- a/tests/Formulas/FinancialTest.ts
216+++ b/tests/Formulas/FinancialTest.ts
217@@ -13,7 +13,8 @@ import {
218   SLN,
219   NPV,
220   NPER,
221-  NOMINAL
222+  NOMINAL,
223+  MIRR
224 } from "../../src/Formulas/Financial";
225 import {
226   DATE
227@@ -358,4 +359,19 @@ test("NOMINAL", function() {
228   catchAndAssertEquals(function() {
229     NOMINAL.apply(this, [0.04]);
230   }, ERRORS.NA_ERROR);
231+});
232+
233+
234+test("MIRR", function() {
235+  assertEquals(MIRR([10, 20, -30, 40], 0.05, 0.06), 0.3458084697540138);
236+  assertEquals(MIRR([10, 20, -30, 40, 10, 22, -100], 0.01, 0.02), -0.02762369541445353);
237+  catchAndAssertEquals(function() {
238+    MIRR([10, 20, 30, 40], 0.05, 0.06);
239+  }, ERRORS.DIV_ZERO_ERROR);
240+  catchAndAssertEquals(function() {
241+    MIRR.apply(this, [[10, 20, 30, -10], 0.05]);
242+  }, ERRORS.NA_ERROR);
243+  catchAndAssertEquals(function() {
244+    MIRR.apply(this, [[10, 20, 30, -10], 0.05, 0.01, 0.09]);
245+  }, ERRORS.NA_ERROR);
246 });
247\ No newline at end of file