name:
dist/Formulas/Financial.js
-rw-r--r--
39655
1"use strict";
2exports.__esModule = true;
3var ArgsChecker_1 = require("../Utilities/ArgsChecker");
4var TypeConverter_1 = require("../Utilities/TypeConverter");
5var Errors_1 = require("../Errors");
6var Date_1 = require("./Date");
7var Filter_1 = require("../Utilities/Filter");
8var MoreUtils_1 = require("../Utilities/MoreUtils");
9/**
10 * Calculates the depreciation of an asset for a specified period using the double-declining balance method.
11 * @param cost - The initial cost of the asset.
12 * @param salvage - The value of the asset at the end of depreciation.
13 * @param life - The number of periods over which the asset is depreciated.
14 * @param period - The single period within life for which to calculate depreciation.
15 * @param factor - [ OPTIONAL - 2 by default ] - The factor by which depreciation decreases.
16 * @returns {number} depreciation of an asset for a specified period
17 * @constructor
18 */
19var DDB = function (cost, salvage, life, period, factor) {
20 ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 4, 5, "DDB");
21 cost = TypeConverter_1.TypeConverter.firstValueAsNumber(cost);
22 salvage = TypeConverter_1.TypeConverter.firstValueAsNumber(salvage);
23 life = TypeConverter_1.TypeConverter.firstValueAsNumber(life);
24 period = TypeConverter_1.TypeConverter.firstValueAsNumber(period);
25 factor = factor === undefined ? 2 : TypeConverter_1.TypeConverter.firstValueAsNumber(factor);
26 if (cost < 0) {
27 throw new Errors_1.NumError("Function DDB parameter 1 value is "
28 + cost + ". It should be greater than or equal to 0.");
29 }
30 if (salvage < 0) {
31 throw new Errors_1.NumError("Function DDB parameter 2 value is "
32 + salvage + ". It should be greater than or equal to 0.");
33 }
34 if (life < 0) {
35 throw new Errors_1.NumError("Function DDB parameter 3 value is "
36 + life + ". It should be greater than or equal to 0.");
37 }
38 if (period < 0) {
39 throw new Errors_1.NumError("Function DDB parameter 4 value is "
40 + period + ". It should be greater than or equal to 0.");
41 }
42 if (period > life) {
43 throw new Errors_1.NumError("Function DDB parameter 4 value is "
44 + life + ". It should be less than or equal to value of Function DB parameter 3 with " + period + ".");
45 }
46 if (salvage >= cost) {
47 return 0;
48 }
49 var total = 0;
50 var current = 0;
51 for (var i = 1; i <= period; i++) {
52 current = Math.min((cost - total) * (factor / TypeConverter_1.checkForDevideByZero(life)), (cost - salvage - total));
53 total += current;
54 }
55 return current;
56};
57exports.DDB = DDB;
58/**
59 * Calculates the depreciation of an asset for a specified period using the arithmetic declining balance method.
60 * @param cost - The initial cost of the asset.
61 * @param salvage - The value of the asset at the end of depreciation.
62 * @param life - The number of periods over which the asset is depreciated.
63 * @param period - The single period within life for which to calculate depreciation.
64 * @param month - [ OPTIONAL - 12 by default ] - The number of months in the first year of depreciation.
65 * @returns {number} depreciated value
66 * @constructor
67 */
68var DB = function (cost, salvage, life, period, month) {
69 ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 4, 5, "DB");
70 cost = TypeConverter_1.TypeConverter.firstValueAsNumber(cost);
71 salvage = TypeConverter_1.TypeConverter.firstValueAsNumber(salvage);
72 life = TypeConverter_1.TypeConverter.firstValueAsNumber(life);
73 period = TypeConverter_1.TypeConverter.firstValueAsNumber(period);
74 month = month !== undefined ? Math.floor(TypeConverter_1.TypeConverter.firstValueAsNumber(month)) : 12;
75 if (cost < 0) {
76 throw new Errors_1.NumError("Function DB parameter 1 value is "
77 + cost + ". It should be greater than or equal to 0.");
78 }
79 if (salvage < 0) {
80 throw new Errors_1.NumError("Function DB parameter 2 value is "
81 + salvage + ". It should be greater than or equal to 0.");
82 }
83 if (life < 0) {
84 throw new Errors_1.NumError("Function DB parameter 3 value is "
85 + life + ". It should be greater than or equal to 0.");
86 }
87 if (period < 0) {
88 throw new Errors_1.NumError("Function DB parameter 4 value is "
89 + period + ". It should be greater than or equal to 0.");
90 }
91 if (month > 12 || month < 1) {
92 throw new Errors_1.NumError("Function DB parameter 5 value is "
93 + month + ". Valid values are between 1 and 12 inclusive.");
94 }
95 if (period > life) {
96 throw new Errors_1.NumError("Function DB parameter 4 value is "
97 + life + ". It should be less than or equal to value of Function DB parameter 3 with " + period + ".");
98 }
99 if (salvage >= cost) {
100 return 0;
101 }
102 if (cost === 0 && salvage !== 0) {
103 throw new Errors_1.DivZeroError("Evaluation of function DB cause a divide by zero error.");
104 }
105 var rate = (1 - Math.pow(salvage / cost, 1 / life));
106 var initial = cost * rate * month / 12;
107 var total = initial;
108 var current = 0;
109 var ceiling = (period === life) ? life - 1 : period;
110 for (var i = 2; i <= ceiling; i++) {
111 current = (cost - total) * rate;
112 total += current;
113 }
114 if (period === 1) {
115 return initial;
116 }
117 else if (period === life) {
118 return (cost - total) * rate;
119 }
120 else {
121 return current;
122 }
123};
124exports.DB = DB;
125/**
126 * Formats a number into the locale-specific currency format. WARNING: Currently the equivalent of TRUNC, since this
127 * returns numbers
128 * @param number - The value to be formatted.
129 * @param places - [ OPTIONAL - 2 by default ] - The number of decimal places to display.
130 * @returns {number} dollars
131 * @constructor
132 */
133var DOLLAR = function (number, places) {
134 ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 1, 2, "DOLLAR");
135 var v = TypeConverter_1.TypeConverter.firstValueAsNumber(number);
136 places = places !== undefined ? TypeConverter_1.TypeConverter.firstValueAsNumber(places) : 2;
137 var sign = (v > 0) ? 1 : -1;
138 var divisor = sign * (Math.floor(Math.abs(v) * Math.pow(10, places)));
139 var pow = Math.pow(10, places);
140 if (pow === 0 && divisor !== 0) {
141 throw new Errors_1.DivZeroError("Evaluation of function DOLLAR cause a divide by zero error.");
142 }
143 return divisor / pow;
144};
145exports.DOLLAR = DOLLAR;
146/**
147 * Converts a price quotation given as a decimal fraction into a decimal value.
148 * @param fractionalPrice - The price quotation given using fractional decimal conventions.
149 * @param unit - The units of the fraction, e.g. 8 for 1/8ths or 32 for 1/32nds.
150 * @returns {number} decimal value.
151 * @constructor
152 */
153var DOLLARDE = function (fractionalPrice, unit) {
154 ArgsChecker_1.ArgsChecker.checkLength(arguments, 2, "DOLLARDE");
155 var dollar = TypeConverter_1.TypeConverter.firstValueAsNumber(fractionalPrice);
156 var fraction = Math.floor(TypeConverter_1.TypeConverter.firstValueAsNumber(unit));
157 if (fraction === 0) {
158 throw new Errors_1.DivZeroError("Function DOLLARDE parameter 2 cannot be zero.");
159 }
160 var result = parseInt(dollar.toString(), 10);
161 result += (dollar % 1) * Math.pow(10, Math.ceil(Math.log(fraction) / Math.LN10)) / fraction;
162 var power = Math.pow(10, Math.ceil(Math.log(fraction) / Math.LN2) + 1);
163 if (power === 0) {
164 throw new Errors_1.DivZeroError("Evaluation of function DOLLARDE cause a divide by zero error.");
165 }
166 result = Math.round(result * power) / power;
167 return result;
168};
169exports.DOLLARDE = DOLLARDE;
170/**
171 * Converts a price quotation given as a decimal value into a decimal fraction.
172 * @param decimalPrice - The price quotation given as a decimal value.
173 * @param unit - The units of the desired fraction, e.g. 8 for 1/8ths or 32 for 1/32nds
174 * @returns {number} price quotation as decimal fraction.
175 * @constructor
176 */
177var DOLLARFR = function (decimalPrice, unit) {
178 ArgsChecker_1.ArgsChecker.checkLength(arguments, 2, "DOLLARFR");
179 decimalPrice = TypeConverter_1.TypeConverter.firstValueAsNumber(decimalPrice);
180 unit = Math.floor(TypeConverter_1.TypeConverter.firstValueAsNumber(unit));
181 if (unit === 0) {
182 throw new Errors_1.DivZeroError("Function DOLLARFR parameter 2 cannot be zero.");
183 }
184 var result = parseInt(decimalPrice.toString(), 10);
185 result += (decimalPrice % 1) * Math.pow(10, -Math.ceil(Math.log(unit) / Math.LN10)) * unit;
186 return result;
187};
188exports.DOLLARFR = DOLLARFR;
189/**
190 * Calculates the annual effective interest rate given the nominal rate and number of compounding periods per year.
191 * @param nominalRate - The nominal interest rate per year.
192 * @param periodsPerYear - The number of compounding periods per year.
193 * @returns {number} annual effective interest rate
194 * @constructor
195 */
196var EFFECT = function (nominalRate, periodsPerYear) {
197 ArgsChecker_1.ArgsChecker.checkLength(arguments, 2, "EFFECT");
198 var rate = TypeConverter_1.TypeConverter.firstValueAsNumber(nominalRate);
199 var periods = TypeConverter_1.TypeConverter.firstValueAsNumber(periodsPerYear);
200 if (rate <= 0) {
201 throw new Errors_1.NumError("Function EFFECT parameter 1 value is " + rate + ". It should be greater than to 0");
202 }
203 if (periods < 1) {
204 throw new Errors_1.NumError("Function EFFECT parameter 2 value is " + periods + ". It should be greater than or equal to 1");
205 }
206 periods = Math.floor(periods);
207 return Math.pow(1 + rate / periods, periods) - 1;
208};
209exports.EFFECT = EFFECT;
210/**
211 * Calculates the periodic payment for an annuity investment based on constant-amount periodic payments and a constant
212 * interest rate.
213 * @param rate - The interest rate.
214 * @param periods - The number of payments to be made.
215 * @param presentValue - The current value of the annuity.
216 * @param futureValue [ OPTIONAL ] - The future value remaining after the final payment has been made.
217 * @param endOrBeginning [ OPTIONAL - 0 by default ] - Whether payments are due at the end (0) or beginning (1) of each
218 * period.
219 * @returns {number}
220 * @constructor
221 */
222var PMT = function (rate, periods, presentValue, futureValue, endOrBeginning) {
223 ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 3, 5, "PMT");
224 rate = TypeConverter_1.TypeConverter.firstValueAsNumber(rate);
225 periods = TypeConverter_1.TypeConverter.firstValueAsNumber(periods);
226 presentValue = TypeConverter_1.TypeConverter.firstValueAsNumber(presentValue);
227 futureValue = futureValue ? TypeConverter_1.TypeConverter.firstValueAsNumber(futureValue) : 0;
228 endOrBeginning = endOrBeginning ? TypeConverter_1.TypeConverter.firstValueAsNumber(endOrBeginning) : 0;
229 var result;
230 if (rate === 0) {
231 result = (presentValue + futureValue) / periods;
232 }
233 else {
234 var term = Math.pow(1 + rate, periods);
235 if (endOrBeginning) {
236 result = (futureValue * rate / (term - 1) + presentValue * rate / (1 - 1 / term)) / (1 + rate);
237 }
238 else {
239 result = futureValue * rate / (term - 1) + presentValue * rate / (1 - 1 / term);
240 }
241 }
242 return -result;
243};
244exports.PMT = PMT;
245/**
246 * Returns the future value of an investment based on periodic, constant payments and a constant interest rate.
247 * @param rate - The rate of periodic interest.
248 * @param periods - The total number of periods.
249 * @param payment - The annuity paid regularly per period
250 * @param value - [OPTIONAL] - The present cash value of an investment.
251 * @param type - [OPTIONAL] - Defines whether the payment is due at the beginning (1) or the end (0) of a period.
252 * @returns {number}
253 * @constructor
254 */
255var FV = function (rate, periods, payment, value, type) {
256 ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 3, 5, "FV");
257 rate = TypeConverter_1.TypeConverter.firstValueAsNumber(rate);
258 periods = TypeConverter_1.TypeConverter.firstValueAsNumber(periods);
259 payment = TypeConverter_1.TypeConverter.firstValueAsNumber(payment);
260 value = (typeof value === 'undefined') ? 0 : TypeConverter_1.TypeConverter.firstValueAsNumber(value);
261 type = (typeof type === 'undefined') ? 0 : TypeConverter_1.TypeConverter.firstValueAsNumber(type);
262 var result;
263 if (rate === 0) {
264 result = value + payment * periods;
265 }
266 else {
267 var term = Math.pow(1 + rate, periods);
268 if (type === 0) {
269 result = value * term + payment * (term - 1) / rate;
270 }
271 else {
272 result = value * term + payment * (1 + rate) * (term - 1.0) / rate;
273 }
274 }
275 return -result;
276};
277exports.FV = FV;
278/**
279 * Calculates the cumulative principal paid over a range of payment periods for an investment based on constant-amount
280 * periodic payments and a constant interest rate.
281 * @param rate - The interest rate.
282 * @param numberOfPeriods - The number of payments to be made.
283 * @param presentValue - The current value of the annuity.
284 * @param firstPeriod - The number of the payment period to begin the cumulative calculation. must be greater
285 * than or equal to 1.
286 * @param lastPeriod - The number of the payment period to end the cumulative calculation, must be greater
287 * than first_period.
288 * @param endOrBeginning - Whether payments are due at the end (0) or beginning (1) of each period
289 * @returns {number} cumulative principal
290 * @constructor
291 */
292var CUMPRINC = function (rate, numberOfPeriods, presentValue, firstPeriod, lastPeriod, endOrBeginning) {
293 ArgsChecker_1.ArgsChecker.checkLength(arguments, 6, "CUMPRINC");
294 rate = TypeConverter_1.TypeConverter.firstValueAsNumber(rate);
295 var periods = TypeConverter_1.TypeConverter.firstValueAsNumber(numberOfPeriods);
296 var value = TypeConverter_1.TypeConverter.firstValueAsNumber(presentValue);
297 var start = TypeConverter_1.TypeConverter.firstValueAsNumber(firstPeriod);
298 if (start < 1) {
299 throw new Errors_1.NumError("Function CUMPRINC parameter 4 value is " + start + ". It should be greater than or equal to 1.");
300 }
301 var end = TypeConverter_1.TypeConverter.firstValueAsNumber(lastPeriod);
302 if (end < 1) {
303 throw new Errors_1.NumError("Function CUMPRINC parameter 5 value is " + end + ". It should be greater than or equal to 1.");
304 }
305 if (end < start) {
306 throw new Errors_1.NumError("Function CUMPRINC parameter 5 value is " + end + ". It should be greater than or equal to " + start + ".");
307 }
308 var type = TypeConverter_1.TypeConverter.firstValueAsBoolean(endOrBeginning);
309 var payment = PMT(rate, periods, value, 0, type);
310 var principal = 0;
311 if (start === 1) {
312 if (type) {
313 principal = payment;
314 }
315 else {
316 principal = payment + value * rate;
317 }
318 start++;
319 }
320 for (var i = start; i <= end; i++) {
321 if (type) {
322 principal += payment - (FV(rate, i - 2, payment, value, 1) - payment) * rate;
323 }
324 else {
325 principal += payment - FV(rate, i - 1, payment, value, 0) * rate;
326 }
327 }
328 return principal;
329};
330exports.CUMPRINC = CUMPRINC;
331/**
332 * Calculates the cumulative interest over a range of payment periods for an investment based on constant-amount
333 * periodic payments and a constant interest rate.
334 * @param rate - The interest rate.
335 * @param numberOfPeriods - The number of payments to be made.
336 * @param presentValue - The current value of the annuity.
337 * @param firstPeriod - The number of the payment period to begin the cumulative calculation, must be greater
338 * than or equal to 1.
339 * @param lastPeriod - The number of the payment period to end the cumulative calculation, must be greater
340 * than first_period.
341 * @param endOrBeginning - Whether payments are due at the end (0) or beginning (1) of each period.
342 * @returns {number} cumulative interest
343 * @constructor
344 */
345var CUMIPMT = function (rate, numberOfPeriods, presentValue, firstPeriod, lastPeriod, endOrBeginning) {
346 ArgsChecker_1.ArgsChecker.checkLength(arguments, 6, "CUMIPMT");
347 rate = TypeConverter_1.TypeConverter.firstValueAsNumber(rate);
348 var periods = TypeConverter_1.TypeConverter.firstValueAsNumber(numberOfPeriods);
349 var value = TypeConverter_1.TypeConverter.firstValueAsNumber(presentValue);
350 var start = TypeConverter_1.TypeConverter.firstValueAsNumber(firstPeriod);
351 if (start < 1) {
352 throw new Errors_1.NumError("Function CUMPRINC parameter 4 value is " + start + ". It should be greater than or equal to 1.");
353 }
354 var end = TypeConverter_1.TypeConverter.firstValueAsNumber(lastPeriod);
355 if (end < 1) {
356 throw new Errors_1.NumError("Function CUMPRINC parameter 5 value is " + end + ". It should be greater than or equal to 1.");
357 }
358 if (end < start) {
359 throw new Errors_1.NumError("Function CUMPRINC parameter 5 value is " + end + ". It should be greater than or equal to " + start + ".");
360 }
361 var type = TypeConverter_1.TypeConverter.firstValueAsBoolean(endOrBeginning);
362 var payment = PMT(rate, periods, value, 0, type);
363 var interest = 0;
364 if (start === 1) {
365 if (!type) {
366 interest = -value;
367 start++;
368 }
369 else {
370 start++;
371 }
372 }
373 for (var i = start; i <= end; i++) {
374 if (type) {
375 interest += FV(rate, i - 2, payment, value, 1) - payment;
376 }
377 else {
378 interest += FV(rate, i - 1, payment, value, 0);
379 }
380 }
381 interest *= rate;
382 return interest;
383};
384exports.CUMIPMT = CUMIPMT;
385/**
386 * Calculates the accrued interest of a security that has periodic payments.
387 * WARNING: This function has been implemented to specifications as outlined in Google Spreadsheets, LibreOffice, and
388 * OpenOffice. It functions much the same as MSExcel's ACCRINT, but there are several key differences. Below are links
389 * to illustrate the differences. Please see the source code for more information on differences. Links: https://quant.stackexchange.com/questions/7040/whats-the-algorithm-behind-excels-accrint, https://support.office.com/en-us/article/ACCRINT-function-fe45d089-6722-4fb3-9379-e1f911d8dc74, https://quant.stackexchange.com/questions/7040/whats-the-algorithm-behind-excels-accrint, https://support.google.com/docs/answer/3093200 .
390 * @param issue - The date the security was initially issued.
391 * @param firstPayment - The first date interest will be paid.
392 * @param settlement - The settlement date of the security, the date after issuance when the security is
393 * delivered to the buyer. Is the maturity date of the security if it is held until maturity rather than sold.
394 * @param rate - The annualized rate of interest.
395 * @param redemption - The redemption amount per 100 face value, or par.
396 * @param frequency - The number of coupon payments per year. For annual payments, frequency = 1; for
397 * semiannual, frequency = 2; for quarterly, frequency = 4.
398 * @param dayCountConvention - [ OPTIONAL - 0 by default ] - An indicator of what day count method to use.
399 * 0 or omitted = US (NASD) 30/360, 1 = Actual/actual, 2 = Actual/360, 3 = Actual/365, 4 = European 30/360.
400 * @returns {number}
401 * @constructor
402 * TODO: This function is based off of the open-source versions I was able to dig up online. We should implement a
403 * TODO: second version that is closer to what MSExcel does and is named something like `ACCRINT.MS`.
404 */
405var ACCRINT = function (issue, firstPayment, settlement, rate, redemption, frequency, dayCountConvention) {
406 ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 6, 7, "ACCRINT");
407 issue = TypeConverter_1.TypeConverter.firstValueAsDateNumber(issue);
408 // "firstPayment" param is only here to check for errors for GS implementation.
409 // In MSE, there is a 7th (zero-indexed-6th) param that indicates the calculation-method to use, which indicates
410 // weather the total accrued interest starting at the first_intrest date, instead of the issue date.
411 firstPayment = TypeConverter_1.TypeConverter.firstValueAsDateNumber(firstPayment);
412 if (firstPayment < 0) {
413 throw new Errors_1.NumError("Function ACCRINT parameter 2 value is " + firstPayment
414 + ". It should be greater than 0.");
415 }
416 settlement = TypeConverter_1.TypeConverter.firstValueAsDateNumber(settlement);
417 if (issue > settlement) {
418 throw new Errors_1.NumError("Function ACCRINT parameter 1 (" + issue.toString()
419 + ") should be on or before Function ACCRINT parameter 3 (" + settlement.toString() + ").");
420 }
421 rate = TypeConverter_1.TypeConverter.firstValueAsNumber(rate);
422 // redemption, aka "par"
423 redemption = TypeConverter_1.TypeConverter.firstValueAsNumber(redemption);
424 // The frequency parameter also does not affect the resulting value of the formula in the GS implementation.
425 // In MSE, frequency is used to calculate a more accurate value, by breaking apart the year, and computing interest
426 // on an on-going basis. In this implementation, we use YEARFRAC to get a numerical value that encompasses the
427 // functionality of "frequency".
428 frequency = TypeConverter_1.TypeConverter.firstValueAsNumber(frequency);
429 // dayCountConvention, aka "basis"
430 dayCountConvention = dayCountConvention !== undefined ? TypeConverter_1.TypeConverter.firstValueAsNumber(dayCountConvention) : 1;
431 var factor = Date_1.YEARFRAC(issue, settlement, dayCountConvention);
432 return redemption * rate * factor;
433};
434exports.ACCRINT = ACCRINT;
435/**
436 * Returns the arithmetic-declining depreciation rate. Use this function to calculate the depreciation amount for one
437 * period of the total depreciation span of an object. Arithmetic declining depreciation reduces the depreciation amount
438 * from period to period by a fixed sum.
439 * @param cost - The initial cost of an asset.
440 * @param salvage - the value of an asset after depreciation.
441 * @param life - The period fixing the time span over which an asset is depreciated.
442 * @param period - The period for which the depreciation is to be calculated.
443 * @returns {number}
444 * @constructor
445 */
446var SYD = function (cost, salvage, life, period) {
447 ArgsChecker_1.ArgsChecker.checkLength(arguments, 4, "SYD");
448 cost = TypeConverter_1.TypeConverter.firstValueAsNumber(cost);
449 salvage = TypeConverter_1.TypeConverter.firstValueAsNumber(salvage);
450 life = TypeConverter_1.TypeConverter.firstValueAsNumber(life);
451 period = TypeConverter_1.TypeConverter.firstValueAsNumber(period);
452 // Return error if period is lower than 1 or greater than life
453 if (period > life) {
454 throw new Errors_1.NumError("Function SYD parameter 4 value is " + period +
455 ". It should be less than or equal to value of Function SYD parameter 3 with " + life + ".");
456 }
457 if (period < 1) {
458 throw new Errors_1.NumError("Function SYD parameter 4 value is " + period + ". It should be greater than 0.");
459 }
460 period = Math.floor(period);
461 return (cost - salvage) * (life - period + 1) * 2 / (life * (life + 1));
462};
463exports.SYD = SYD;
464/**
465 * Returns the straight-line depreciation of an asset for one period. The amount of the depreciation is constant during
466 * the depreciation period.
467 * @param cost - The initial cost of the asset.
468 * @param salvage - The value of an asset at the end of the depreciation.
469 * @param life - The depreciation period determining the number of periods in the deprecation of the asset.
470 * @returns {number}
471 * @constructor
472 */
473var SLN = function (cost, salvage, life) {
474 ArgsChecker_1.ArgsChecker.checkLength(arguments, 3, "SYD");
475 cost = TypeConverter_1.TypeConverter.firstValueAsNumber(cost);
476 salvage = TypeConverter_1.TypeConverter.firstValueAsNumber(salvage);
477 life = TypeConverter_1.TypeConverter.firstValueAsNumber(life);
478 if (life === 0) {
479 throw new Errors_1.DivZeroError("Function SLN parameter 3 cannot be zero.");
480 }
481 return (cost - salvage) / life;
482};
483exports.SLN = SLN;
484/**
485 * Returns the net present value of an investment based on a series of periodic cash flows and a discount rate.
486 * @param rate - The discount rate for a period.
487 * @param values - The values representing deposits or withdrawals.
488 * @returns {number}
489 * @constructor
490 * TODO: This function can return results that are prone to floating point precision errors.
491 */
492var NPV = function (rate) {
493 var values = [];
494 for (var _i = 1; _i < arguments.length; _i++) {
495 values[_i - 1] = arguments[_i];
496 }
497 ArgsChecker_1.ArgsChecker.checkAtLeastLength(arguments, 2, "NPV");
498 var range = Filter_1.Filter.flattenAndThrow(values).map(function (value) {
499 try {
500 return TypeConverter_1.TypeConverter.valueToNumber(value);
501 }
502 catch (e) {
503 throw new Errors_1.ValueError("Function NPV expects number values. But '" + value + "' is " + (typeof value)
504 + " and cannot be coerced to a number.");
505 }
506 });
507 var value = 0;
508 for (var i = 0; i < range.length; i++) {
509 value += range[i] / Math.pow(1 + rate, i);
510 }
511 return value;
512};
513exports.NPV = NPV;
514/**
515 * Returns the number of payment for an investment. Number is based on constant-amount payments made periodically and a
516 * constant interest rate.
517 * @param rate - The interest rate.
518 * @param payment - The amount of each payment.
519 * @param present - THe current value.
520 * @param future - [OPTIONAL] - The future value remaining after the final payment has been made.
521 * @param type [OPTIONAL 0 by default] - 1 indicates payments are due at the beginning of each period. 0 indicates
522 * payments are due at the end of each period.
523 * @returns {number}
524 * @constructor
525 */
526var NPER = function (rate, payment, present, future, type) {
527 ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 3, 5, "NPER");
528 rate = TypeConverter_1.TypeConverter.firstValueAsNumber(rate);
529 payment = TypeConverter_1.TypeConverter.firstValueAsNumber(payment);
530 present = TypeConverter_1.TypeConverter.firstValueAsNumber(present);
531 type = (typeof type === 'undefined') ? 0 : TypeConverter_1.TypeConverter.firstValueAsNumber(type);
532 future = (typeof future === 'undefined') ? 0 : TypeConverter_1.TypeConverter.firstValueAsNumber(future);
533 var num = payment * (1 + rate * type) - future * rate;
534 var den = (present * rate + payment * (1 + rate * type));
535 if (den === 0) {
536 throw new Errors_1.DivZeroError("Evaluation of function NPER cause a divide by zero error.");
537 }
538 var div = Math.log(1 + rate);
539 var logNumDen = Math.log(num / den);
540 if (isNaN(logNumDen)) {
541 throw new Errors_1.NumError("Parameters given function NPER are not possible.");
542 }
543 return logNumDen / div;
544};
545exports.NPER = NPER;
546/**
547 * Calculates the yearly nominal interest rate, given the effective rate and the number of compounding periods per year.
548 * @param rate - The effective interest rate.
549 * @param periods - The number of periodic interest payments per year.
550 * @returns {number}
551 * @constructor
552 */
553var NOMINAL = function (rate, periods) {
554 ArgsChecker_1.ArgsChecker.checkLength(arguments, 2, "NOMINAL");
555 rate = TypeConverter_1.TypeConverter.firstValueAsNumber(rate);
556 periods = Math.round(TypeConverter_1.TypeConverter.firstValueAsNumber(periods));
557 if (periods < 1) {
558 throw new Errors_1.NumError("Function NOMINAL parameter 2 value is " + periods
559 + ". It should be greater than or equal to 1.");
560 }
561 return (Math.pow(rate + 1, 1 / periods) - 1) * periods;
562};
563exports.NOMINAL = NOMINAL;
564/**
565 * Calculates the modified internal rate of return of a series of investments.
566 * @param values - Range or values of payments. Ignores text values.
567 * @param financeRate - The rate of interest of the investments.
568 * @param reinvestRate - The rate of interest of the reinvestment.
569 * @returns {number}
570 * @constructor
571 * TODO: This relies on NPV and will therefore be prone to floating-point errors.
572 */
573var MIRR = function (values, financeRate, reinvestRate) {
574 ArgsChecker_1.ArgsChecker.checkLength(arguments, 3, "MIRR");
575 values = Filter_1.Filter.flattenAndThrow(values).filter(function (value) {
576 return (typeof value !== "string");
577 }).map(function (value) {
578 return TypeConverter_1.TypeConverter.valueToNumber(value);
579 });
580 var n = values.length;
581 var payments = [];
582 var incomes = [];
583 for (var i = 0; i < n; i++) {
584 if (values[i] < 0) {
585 payments.push(values[i]);
586 }
587 else {
588 incomes.push(values[i]);
589 }
590 }
591 if (incomes.length === 0 || payments.length === 0) {
592 throw new Errors_1.DivZeroError("For MIRR, the values must include positive and negative numbers.");
593 }
594 var num = -NPV(reinvestRate, incomes) * Math.pow(1 + reinvestRate, n - 1);
595 var den = NPV(financeRate, payments) * (1 + financeRate);
596 return Math.pow(num / den, 1 / (n - 1)) - 1;
597};
598exports.MIRR = MIRR;
599/**
600 * Calculates the internal rate of return for an investment. The values represent cash flow values at regular intervals;
601 * at least one value must be negative (payments), and at least one value must be positive (income).
602 *
603 * Relevant StackOverflow discussion: https://stackoverflow.com/questions/15089151/javascript-irr-internal-rate-of-return-formula-accuracy
604 *
605 * @param values - Range containing values. Ignores text values.
606 * @param guess - [OPTIONAL] - The estimated value. Defaults to 0.01.
607 * @returns {number}
608 * @constructor
609 */
610var IRR = function (values, guess) {
611 ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 1, 2, "IRR");
612 values = Filter_1.Filter.flattenAndThrow(values).filter(function (value) {
613 return (typeof value !== "string");
614 }).map(function (value) {
615 return TypeConverter_1.TypeConverter.valueToNumber(value);
616 });
617 guess = (guess === undefined) ? 0.1 : TypeConverter_1.TypeConverter.firstValueAsNumber(guess);
618 var min = -1.0;
619 var max = 10.0;
620 var val;
621 var counter = 1;
622 var MAX_ITERATIONS = 500000;
623 do {
624 guess = (min + max) / 2;
625 val = 0;
626 for (var j = 0; j < values.length; j++) {
627 val += values[j] / Math.pow((1 + guess), j);
628 }
629 if (val > 0) {
630 min = guess;
631 }
632 else {
633 max = guess;
634 }
635 } while (Math.abs(val) > 0.000001 && ++counter < MAX_ITERATIONS);
636 return guess;
637};
638exports.IRR = IRR;
639/**
640 * Calculates the periodic amortization for an investment with regular payments and a constant interest rate.
641 * @param rate - The periodic interest rate.
642 * @param period - The period for which the compound interest is calculated.
643 * @param periods - The total number of periods during which the annuity is paid.
644 * @param present - The present cash value in sequence of payments.
645 * @param future - [OPTIONAL] - The desired value (future value) at the end of the periods.
646 * @param type - [OPTIONAL] - Defines whether the payment is due at the beginning (1) or the end (0) of a period.
647 * @returns {number}
648 * @constructor
649 */
650var IPMT = function (rate, period, periods, present, future, type) {
651 ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 4, 6, "IPMT");
652 rate = TypeConverter_1.TypeConverter.firstValueAsNumber(rate);
653 period = TypeConverter_1.TypeConverter.firstValueAsNumber(period);
654 periods = TypeConverter_1.TypeConverter.firstValueAsNumber(periods);
655 present = TypeConverter_1.TypeConverter.firstValueAsNumber(present);
656 future = (typeof future === 'undefined') ? 0 : TypeConverter_1.TypeConverter.firstValueAsNumber(future);
657 type = (typeof type === 'undefined') ? 0 : TypeConverter_1.TypeConverter.firstValueAsNumber(type);
658 var payment = PMT(rate, periods, present, future, type);
659 var interest;
660 if (period === 1) {
661 if (type === 1) {
662 interest = 0;
663 }
664 else {
665 interest = -present;
666 }
667 }
668 else {
669 if (type === 1) {
670 interest = FV(rate, period - 2, payment, present, 1) - payment;
671 }
672 else {
673 interest = FV(rate, period - 1, payment, present, 0);
674 }
675 }
676 return interest * rate;
677};
678exports.IPMT = IPMT;
679/**
680 * Returns for a given period the payment on the principal for an investment that is based on periodic and constant
681 * payments and a constant interest rate.
682 * @param rate - The periodic interest rate.
683 * @param period - The amortization period.
684 * @param periods - The total number of periods during which the annuity is paid.
685 * @param present - The present value in the sequence of payments.
686 * @param future - [OPTIONAL] - The desired future value. Defaults to 0.
687 * @param type - [OPTIONAL] - Indicates how the year is to be calculated. 0 indicates payments are due at end of
688 * period, 1 indicates payments are due at beginning of period. Defaults to 0.
689 * @returns {number}
690 * @constructor
691 */
692var PPMT = function (rate, period, periods, present, future, type) {
693 ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 4, 6, "PPMT");
694 rate = TypeConverter_1.TypeConverter.firstValueAsNumber(rate);
695 period = TypeConverter_1.TypeConverter.firstValueAsNumber(period);
696 if (period < 1) {
697 throw new Errors_1.NumError("Function PPMT parameter 2 value is " + period + ", but should be greater than or equal to 1.");
698 }
699 periods = TypeConverter_1.TypeConverter.firstValueAsNumber(periods);
700 if (periods <= 0) {
701 throw new Errors_1.NumError("Function PPMT parameter 3 value is " + periods + ", but should be greater than 0.");
702 }
703 present = TypeConverter_1.TypeConverter.firstValueAsNumber(present);
704 future = (typeof future === 'undefined') ? 0 : TypeConverter_1.TypeConverter.firstValueAsNumber(future);
705 type = (typeof type === 'undefined') ? 0 : TypeConverter_1.TypeConverter.firstValueAsNumber(type);
706 return PMT(rate, periods, present, future, type) - IPMT(rate, period, periods, present, future, type);
707};
708exports.PPMT = PPMT;
709/**
710 * Calculates the accumulated value of the starting capital for a series of periodically varying interest rates.
711 * @param principal - The starting capital.
712 * @param rateSchedule - Range or Array that is a series of interest rates.
713 * @returns {number}
714 * @constructor
715 */
716var FVSCHEDULE = function (principal, rateSchedule) {
717 ArgsChecker_1.ArgsChecker.checkLength(arguments, 2, "FVSCHEDULE");
718 var future = TypeConverter_1.TypeConverter.firstValueAsNumber(principal);
719 rateSchedule = Filter_1.Filter.flattenAndThrow(rateSchedule);
720 for (var i = 0; i < rateSchedule.length; i++) {
721 // Apply scheduled interest
722 future *= 1 + rateSchedule[i];
723 }
724 return future;
725};
726exports.FVSCHEDULE = FVSCHEDULE;
727/**
728 * Returns the present value of an investment resulting from a series of regular payments.
729 * @param rate - The interest rate per period.
730 * @param periods - The total number of payment periods
731 * @param paymentPerPeriod - The regular payment made per period.
732 * @param future - [OPTIONAL defaults to 0] The future value remaining after the final installment has been made
733 * @param type - [OPTIONAL defaults to 0] Defines whether the payment is due at the beginning (1) or the end (0) of a
734 * period.
735 * @constructor
736 */
737var PV = function (rate, periods, paymentPerPeriod, future, type) {
738 ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 3, 5, "PV");
739 rate = TypeConverter_1.TypeConverter.firstValueAsNumber(rate);
740 if (rate < 0) {
741 throw new Errors_1.NumError("Function PV parameter 21value is " + rate + ", but should be greater than or equal to 0.");
742 }
743 periods = TypeConverter_1.TypeConverter.firstValueAsNumber(periods);
744 paymentPerPeriod = TypeConverter_1.TypeConverter.firstValueAsNumber(paymentPerPeriod);
745 future = MoreUtils_1.isUndefined(future) ? 0 : TypeConverter_1.TypeConverter.firstValueAsNumber(future);
746 type = MoreUtils_1.isUndefined(type) ? 0 : TypeConverter_1.TypeConverter.firstValueAsNumber(type);
747 if (rate === 0) {
748 return -paymentPerPeriod * periods - future;
749 }
750 else {
751 return (((1 - Math.pow(1 + rate, periods)) / rate) * paymentPerPeriod * (1 + rate * type) - future) / Math.pow(1 + rate, periods);
752 }
753};
754exports.PV = PV;
755/**
756 * Returns the constant interest rate per period of an annuity.
757 * @param periods - The total number of periods, during which payments are made (payment period).
758 * @param paymentPerPeriod - The constant payment (annuity) paid during each period.
759 * @param presentValue - The cash value in the sequence of payments
760 * @param futureValue - [OPTIONAL defaults to 0] The future value, which is reached at the end of the periodic payments.
761 * @param beginningOrEnd - [OPTIONAL defaults to 0] Defines whether the payment is due at the beginning (1) or the end
762 * (0) of a period.
763 * @param guessRate - [OPTIONAL] - Determines the estimated value of the interest with iterative
764 * calculation.
765 * @constructor
766 */
767var RATE = function (periods, paymentPerPeriod, presentValue, futureValue, beginningOrEnd, guessRate) {
768 ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 3, 6, "RATE");
769 periods = TypeConverter_1.TypeConverter.firstValueAsNumber(periods);
770 if (periods < 1) {
771 throw new Errors_1.NumError("Function RATE parameter 1 value is" + periods + ", but it should be greater than 0.");
772 }
773 paymentPerPeriod = TypeConverter_1.TypeConverter.firstValueAsNumber(paymentPerPeriod);
774 presentValue = TypeConverter_1.TypeConverter.firstValueAsNumber(presentValue);
775 futureValue = MoreUtils_1.isDefined(futureValue) ? TypeConverter_1.TypeConverter.firstValueAsNumber(futureValue) : 0;
776 beginningOrEnd = MoreUtils_1.isDefined(beginningOrEnd) ? TypeConverter_1.TypeConverter.firstValueAsNumber(beginningOrEnd) : 0;
777 guessRate = MoreUtils_1.isDefined(guessRate) ? TypeConverter_1.TypeConverter.firstValueAsNumber(guessRate) : 0.1;
778 // Sets the limits for possible guesses to any
779 // number between 0% and 100%
780 var lowLimit = 0;
781 var highLimit = 1;
782 var guess = guessRate;
783 // Defines a tolerance of up to +/- 0.00005% of pmt, to accept
784 // the solution as valid.
785 var tolerance = Math.abs(0.00000005 * paymentPerPeriod);
786 // Tries at most 40 times to find a solution within the tolerance.
787 for (var i = 0; i < 40; i++) {
788 // Resets the balance to the original pv.
789 var balance = presentValue;
790 // Calculates the balance at the end of the loan, based
791 // on loan conditions.
792 for (var j = 0; j < periods; j++) {
793 if (beginningOrEnd == 0) {
794 // Interests applied before payment
795 balance = balance * (1 + guess) + paymentPerPeriod;
796 }
797 else {
798 // Payments applied before insterests
799 balance = (balance + paymentPerPeriod) * (1 + guess);
800 }
801 }
802 // Returns the guess if balance is within tolerance. If not, adjusts
803 // the limits and starts with a new guess.
804 if (Math.abs(balance + futureValue) < tolerance) {
805 return guess;
806 }
807 else if (balance + futureValue > 0) {
808 // Sets a new highLimit knowing that
809 // the current guess was too big.
810 highLimit = guess;
811 }
812 else {
813 // Sets a new lowLimit knowing that
814 // the current guess was too small.
815 lowLimit = guess;
816 }
817 // Calculates the new guess.
818 guess = (highLimit + lowLimit) / 2;
819 }
820 throw new Errors_1.NumError("RATE attempted to complete but it was not able to.");
821};
822exports.RATE = RATE;