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