var error = require('./utils/error'); var dateTime = require('./date-time'); var utils = require('./utils/common'); function validDate(d) { return d && d.getTime && !isNaN(d.getTime()); } function ensureDate(d) { return (d instanceof Date)?d:new Date(d); } exports.ACCRINT = function(issue, first, settlement, rate, par, frequency, basis) { // Return error if either date is invalid issue = ensureDate(issue); first = ensureDate(first); settlement = ensureDate(settlement); if (!validDate(issue) || !validDate(first) || !validDate(settlement)) { return error.value; } // Return error if either rate or par are lower than or equal to zero if (rate <= 0 || par <= 0) { return error.num; } // Return error if frequency is neither 1, 2, or 4 if ([1, 2, 4].indexOf(frequency) === -1) { return error.num; } // Return error if basis is neither 0, 1, 2, 3, or 4 if ([0, 1, 2, 3, 4].indexOf(basis) === -1) { return error.num; } // Return error if settlement is before or equal to issue if (settlement <= issue) { return error.num; } // Set default values par = par || 0; basis = basis || 0; // Compute accrued interest return par * rate * dateTime.YEARFRAC(issue, settlement, basis); }; // TODO exports.ACCRINTM = function() { throw new Error('ACCRINTM is not implemented'); }; // TODO exports.AMORDEGRC = function() { throw new Error('AMORDEGRC is not implemented'); }; // TODO exports.AMORLINC = function() { throw new Error('AMORLINC is not implemented'); }; // TODO exports.COUPDAYBS = function() { throw new Error('COUPDAYBS is not implemented'); }; // TODO exports.COUPDAYS = function() { throw new Error('COUPDAYS is not implemented'); }; // TODO exports.COUPDAYSNC = function() { throw new Error('COUPDAYSNC is not implemented'); }; // TODO exports.COUPNCD = function() { throw new Error('COUPNCD is not implemented'); }; // TODO exports.COUPNUM = function() { throw new Error('COUPNUM is not implemented'); }; // TODO exports.COUPPCD = function() { throw new Error('COUPPCD is not implemented'); }; exports.CUMIPMT = function(rate, periods, value, start, end, type) { // Credits: algorithm inspired by Apache OpenOffice // Credits: Hannes Stiebitzhofer for the translations of function and variable names // Requires exports.FV() and exports.PMT() from exports.js [http://stoic.com/exports/] rate = utils.parseNumber(rate); periods = utils.parseNumber(periods); value = utils.parseNumber(value); if (utils.anyIsError(rate, periods, value)) { return error.value; } // Return error if either rate, periods, or value are lower than or equal to zero if (rate <= 0 || periods <= 0 || value <= 0) { return error.num; } // Return error if start < 1, end < 1, or start > end if (start < 1 || end < 1 || start > end) { return error.num; } // Return error if type is neither 0 nor 1 if (type !== 0 && type !== 1) { return error.num; } // Compute cumulative interest var payment = exports.PMT(rate, periods, value, 0, type); var interest = 0; if (start === 1) { if (type === 0) { interest = -value; start++; } } for (var i = start; i <= end; i++) { if (type === 1) { interest += exports.FV(rate, i - 2, payment, value, 1) - payment; } else { interest += exports.FV(rate, i - 1, payment, value, 0); } } interest *= rate; // Return cumulative interest return interest; }; exports.CUMPRINC = function(rate, periods, value, start, end, type) { // Credits: algorithm inspired by Apache OpenOffice // Credits: Hannes Stiebitzhofer for the translations of function and variable names rate = utils.parseNumber(rate); periods = utils.parseNumber(periods); value = utils.parseNumber(value); if (utils.anyIsError(rate, periods, value)) { return error.value; } // Return error if either rate, periods, or value are lower than or equal to zero if (rate <= 0 || periods <= 0 || value <= 0) { return error.num; } // Return error if start < 1, end < 1, or start > end if (start < 1 || end < 1 || start > end) { return error.num; } // Return error if type is neither 0 nor 1 if (type !== 0 && type !== 1) { return error.num; } // Compute cumulative principal var payment = exports.PMT(rate, periods, value, 0, type); var principal = 0; if (start === 1) { if (type === 0) { principal = payment + value * rate; } else { principal = payment; } start++; } for (var i = start; i <= end; i++) { if (type > 0) { principal += payment - (exports.FV(rate, i - 2, payment, value, 1) - payment) * rate; } else { principal += payment - exports.FV(rate, i - 1, payment, value, 0) * rate; } } // Return cumulative principal return principal; }; exports.DB = function(cost, salvage, life, period, month) { // Initialize month month = (month === undefined) ? 12 : month; cost = utils.parseNumber(cost); salvage = utils.parseNumber(salvage); life = utils.parseNumber(life); period = utils.parseNumber(period); month = utils.parseNumber(month); if (utils.anyIsError(cost, salvage, life, period, month)) { return error.value; } // Return error if any of the parameters is negative if (cost < 0 || salvage < 0 || life < 0 || period < 0) { return error.num; } // Return error if month is not an integer between 1 and 12 if ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].indexOf(month) === -1) { return error.num; } // Return error if period is greater than life if (period > life) { return error.num; } // Return 0 (zero) if salvage is greater than or equal to cost if (salvage >= cost) { return 0; } // Rate is rounded to three decimals places var rate = (1 - Math.pow(salvage / cost, 1 / life)).toFixed(3); // Compute initial depreciation var initial = cost * rate * month / 12; // Compute total depreciation var total = initial; var current = 0; var ceiling = (period === life) ? life - 1 : period; for (var i = 2; i <= ceiling; i++) { current = (cost - total) * rate; total += current; } // Depreciation for the first and last periods are special cases if (period === 1) { // First period return initial; } else if (period === life) { // Last period return (cost - total) * rate; } else { return current; } }; exports.DDB = function(cost, salvage, life, period, factor) { // Initialize factor factor = (factor === undefined) ? 2 : factor; cost = utils.parseNumber(cost); salvage = utils.parseNumber(salvage); life = utils.parseNumber(life); period = utils.parseNumber(period); factor = utils.parseNumber(factor); if (utils.anyIsError(cost, salvage, life, period, factor)) { return error.value; } // Return error if any of the parameters is negative or if factor is null if (cost < 0 || salvage < 0 || life < 0 || period < 0 || factor <= 0) { return error.num; } // Return error if period is greater than life if (period > life) { return error.num; } // Return 0 (zero) if salvage is greater than or equal to cost if (salvage >= cost) { return 0; } // Compute depreciation var total = 0; var current = 0; for (var i = 1; i <= period; i++) { current = Math.min((cost - total) * (factor / life), (cost - salvage - total)); total += current; } // Return depreciation return current; }; // TODO exports.DISC = function() { throw new Error('DISC is not implemented'); }; exports.DOLLARDE = function(dollar, fraction) { // Credits: algorithm inspired by Apache OpenOffice dollar = utils.parseNumber(dollar); fraction = utils.parseNumber(fraction); if (utils.anyIsError(dollar, fraction)) { return error.value; } // Return error if fraction is negative if (fraction < 0) { return error.num; } // Return error if fraction is greater than or equal to 0 and less than 1 if (fraction >= 0 && fraction < 1) { return error.div0; } // Truncate fraction if it is not an integer fraction = parseInt(fraction, 10); // Compute integer part var result = parseInt(dollar, 10); // Add decimal part result += (dollar % 1) * Math.pow(10, Math.ceil(Math.log(fraction) / Math.LN10)) / fraction; // Round result var power = Math.pow(10, Math.ceil(Math.log(fraction) / Math.LN2) + 1); result = Math.round(result * power) / power; // Return converted dollar price return result; }; exports.DOLLARFR = function(dollar, fraction) { // Credits: algorithm inspired by Apache OpenOffice dollar = utils.parseNumber(dollar); fraction = utils.parseNumber(fraction); if (utils.anyIsError(dollar, fraction)) { return error.value; } // Return error if fraction is negative if (fraction < 0) { return error.num; } // Return error if fraction is greater than or equal to 0 and less than 1 if (fraction >= 0 && fraction < 1) { return error.div0; } // Truncate fraction if it is not an integer fraction = parseInt(fraction, 10); // Compute integer part var result = parseInt(dollar, 10); // Add decimal part result += (dollar % 1) * Math.pow(10, -Math.ceil(Math.log(fraction) / Math.LN10)) * fraction; // Return converted dollar price return result; }; // TODO exports.DURATION = function() { throw new Error('DURATION is not implemented'); }; exports.EFFECT = function(rate, periods) { rate = utils.parseNumber(rate); periods = utils.parseNumber(periods); if (utils.anyIsError(rate, periods)) { return error.value; } // Return error if rate <=0 or periods < 1 if (rate <= 0 || periods < 1) { return error.num; } // Truncate periods if it is not an integer periods = parseInt(periods, 10); // Return effective annual interest rate return Math.pow(1 + rate / periods, periods) - 1; }; exports.FV = function(rate, periods, payment, value, type) { // Credits: algorithm inspired by Apache OpenOffice value = value || 0; type = type || 0; rate = utils.parseNumber(rate); periods = utils.parseNumber(periods); payment = utils.parseNumber(payment); value = utils.parseNumber(value); type = utils.parseNumber(type); if (utils.anyIsError(rate, periods, payment, value, type)) { return error.value; } // Return future value var result; if (rate === 0) { result = value + payment * periods; } else { var term = Math.pow(1 + rate, periods); if (type === 1) { result = value * term + payment * (1 + rate) * (term - 1) / rate; } else { result = value * term + payment * (term - 1) / rate; } } return -result; }; exports.FVSCHEDULE = function(principal, schedule) { principal = utils.parseNumber(principal); schedule = utils.parseNumberArray(utils.flatten(schedule)); if (utils.anyIsError(principal, schedule)) { return error.value; } var n = schedule.length; var future = principal; // Apply all interests in schedule for (var i = 0; i < n; i++) { // Apply scheduled interest future *= 1 + schedule[i]; } // Return future value return future; }; // TODO exports.INTRATE = function() { throw new Error('INTRATE is not implemented'); }; exports.IPMT = function(rate, period, periods, present, future, type) { // Credits: algorithm inspired by Apache OpenOffice future = future || 0; type = type || 0; rate = utils.parseNumber(rate); period = utils.parseNumber(period); periods = utils.parseNumber(periods); present = utils.parseNumber(present); future = utils.parseNumber(future); type = utils.parseNumber(type); if (utils.anyIsError(rate, period, periods, present, future, type)) { return error.value; } // Compute payment var payment = exports.PMT(rate, periods, present, future, type); // Compute interest var interest; if (period === 1) { if (type === 1) { interest = 0; } else { interest = -present; } } else { if (type === 1) { interest = exports.FV(rate, period - 2, payment, present, 1) - payment; } else { interest = exports.FV(rate, period - 1, payment, present, 0); } } // Return interest return interest * rate; }; exports.IRR = function(values, guess) { // Credits: algorithm inspired by Apache OpenOffice guess = guess || 0; values = utils.parseNumberArray(utils.flatten(values)); guess = utils.parseNumber(guess); if (utils.anyIsError(values, guess)) { return error.value; } // 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 error.num; } // Initialize guess and resultRate guess = (guess === undefined) ? 0.1 : guess; var resultRate = guess; // Set maximum epsilon for end of iteration var epsMax = 1e-10; // Implement Newton's method var newRate, epsRate, resultValue; 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); // Return internal rate of return return resultRate; }; exports.ISPMT = function(rate, period, periods, value) { rate = utils.parseNumber(rate); period = utils.parseNumber(period); periods = utils.parseNumber(periods); value = utils.parseNumber(value); if (utils.anyIsError(rate, period, periods, value)) { return error.value; } // Return interest return value * rate * (period / periods - 1); }; // TODO exports.MDURATION = function() { throw new Error('MDURATION is not implemented'); }; exports.MIRR = function(values, finance_rate, reinvest_rate) { values = utils.parseNumberArray(utils.flatten(values)); finance_rate = utils.parseNumber(finance_rate); reinvest_rate = utils.parseNumber(reinvest_rate); if (utils.anyIsError(values, finance_rate, reinvest_rate)) { return error.value; } // Initialize number of values var n = values.length; // Lookup payments (negative values) and incomes (positive values) var payments = []; var incomes = []; for (var i = 0; i < n; i++) { if (values[i] < 0) { payments.push(values[i]); } else { incomes.push(values[i]); } } // Return modified internal rate of return var num = -exports.NPV(reinvest_rate, incomes) * Math.pow(1 + reinvest_rate, n - 1); var den = exports.NPV(finance_rate, payments) * (1 + finance_rate); return Math.pow(num / den, 1 / (n - 1)) - 1; }; exports.NOMINAL = function(rate, periods) { rate = utils.parseNumber(rate); periods = utils.parseNumber(periods); if (utils.anyIsError(rate, periods)) { return error.value; } // Return error if rate <=0 or periods < 1 if (rate <= 0 || periods < 1) { return error.num; } // Truncate periods if it is not an integer periods = parseInt(periods, 10); // Return nominal annual interest rate return (Math.pow(rate + 1, 1 / periods) - 1) * periods; }; exports.NPER = function(rate, payment, present, future, type) { type = (type === undefined) ? 0 : type; future = (future === undefined) ? 0 : future; rate = utils.parseNumber(rate); payment = utils.parseNumber(payment); present = utils.parseNumber(present); future = utils.parseNumber(future); type = utils.parseNumber(type); if (utils.anyIsError(rate, payment, present, future, type)) { return error.value; } // Return number of periods var num = payment * (1 + rate * type) - future * rate; var den = (present * rate + payment * (1 + rate * type)); return Math.log(num / den) / Math.log(1 + rate); }; exports.NPV = function() { var args = utils.parseNumberArray(utils.flatten(arguments)); if (args instanceof Error) { return args; } // Lookup rate var rate = args[0]; // Initialize net present value var value = 0; // Loop on all values for (var j = 1; j < args.length; j++) { value += args[j] / Math.pow(1 + rate, j); } // Return net present value return value; }; // TODO exports.ODDFPRICE = function() { throw new Error('ODDFPRICE is not implemented'); }; // TODO exports.ODDFYIELD = function() { throw new Error('ODDFYIELD is not implemented'); }; // TODO exports.ODDLPRICE = function() { throw new Error('ODDLPRICE is not implemented'); }; // TODO exports.ODDLYIELD = function() { throw new Error('ODDLYIELD is not implemented'); }; exports.PDURATION = function(rate, present, future) { rate = utils.parseNumber(rate); present = utils.parseNumber(present); future = utils.parseNumber(future); if (utils.anyIsError(rate, present, future)) { return error.value; } // Return error if rate <=0 if (rate <= 0) { return error.num; } // Return number of periods return (Math.log(future) - Math.log(present)) / Math.log(1 + rate); }; exports.PMT = function(rate, periods, present, future, type) { // Credits: algorithm inspired by Apache OpenOffice future = future || 0; type = type || 0; rate = utils.parseNumber(rate); periods = utils.parseNumber(periods); present = utils.parseNumber(present); future = utils.parseNumber(future); type = utils.parseNumber(type); if (utils.anyIsError(rate, periods, present, future, type)) { return error.value; } // Return payment var result; if (rate === 0) { result = (present + future) / periods; } else { var term = Math.pow(1 + rate, periods); if (type === 1) { result = (future * rate / (term - 1) + present * rate / (1 - 1 / term)) / (1 + rate); } else { result = future * rate / (term - 1) + present * rate / (1 - 1 / term); } } return -result; }; exports.PPMT = function(rate, period, periods, present, future, type) { future = future || 0; type = type || 0; rate = utils.parseNumber(rate); periods = utils.parseNumber(periods); present = utils.parseNumber(present); future = utils.parseNumber(future); type = utils.parseNumber(type); if (utils.anyIsError(rate, periods, present, future, type)) { return error.value; } return exports.PMT(rate, periods, present, future, type) - exports.IPMT(rate, period, periods, present, future, type); }; // TODO exports.PRICE = function() { throw new Error('PRICE is not implemented'); }; // TODO exports.PRICEDISC = function() { throw new Error('PRICEDISC is not implemented'); }; // TODO exports.PRICEMAT = function() { throw new Error('PRICEMAT is not implemented'); }; exports.PV = function(rate, periods, payment, future, type) { future = future || 0; type = type || 0; rate = utils.parseNumber(rate); periods = utils.parseNumber(periods); payment = utils.parseNumber(payment); future = utils.parseNumber(future); type = utils.parseNumber(type); if (utils.anyIsError(rate, periods, payment, future, type)) { return error.value; } // Return present value if (rate === 0) { return -payment * periods - future; } else { return (((1 - Math.pow(1 + rate, periods)) / rate) * payment * (1 + rate * type) - future) / Math.pow(1 + rate, periods); } }; exports.RATE = function(periods, payment, present, future, type, guess) { // Credits: rabugento guess = (guess === undefined) ? 0.01 : guess; future = (future === undefined) ? 0 : future; type = (type === undefined) ? 0 : type; periods = utils.parseNumber(periods); payment = utils.parseNumber(payment); present = utils.parseNumber(present); future = utils.parseNumber(future); type = utils.parseNumber(type); guess = utils.parseNumber(guess); if (utils.anyIsError(periods, payment, present, future, type, guess)) { return error.value; } // Set maximum epsilon for end of iteration var epsMax = 1e-10; // Set maximum number of iterations var iterMax = 50; // Implement Newton's method var y, y0, y1, x0, x1 = 0, f = 0, i = 0; var rate = guess; if (Math.abs(rate) < epsMax) { y = present * (1 + periods * rate) + payment * (1 + rate * type) * periods + future; } else { f = Math.exp(periods * Math.log(1 + rate)); y = present * f + payment * (1 / rate + type) * (f - 1) + future; } y0 = present + payment * periods + future; y1 = present * f + payment * (1 / rate + type) * (f - 1) + future; i = x0 = 0; x1 = rate; while ((Math.abs(y0 - y1) > epsMax) && (i < iterMax)) { rate = (y1 * x0 - y0 * x1) / (y1 - y0); x0 = x1; x1 = rate; if (Math.abs(rate) < epsMax) { y = present * (1 + periods * rate) + payment * (1 + rate * type) * periods + future; } else { f = Math.exp(periods * Math.log(1 + rate)); y = present * f + payment * (1 / rate + type) * (f - 1) + future; } y0 = y1; y1 = y; ++i; } return rate; }; // TODO exports.RECEIVED = function() { throw new Error('RECEIVED is not implemented'); }; exports.RRI = function(periods, present, future) { periods = utils.parseNumber(periods); present = utils.parseNumber(present); future = utils.parseNumber(future); if (utils.anyIsError(periods, present, future)) { return error.value; } // Return error if periods or present is equal to 0 (zero) if (periods === 0 || present === 0) { return error.num; } // Return equivalent interest rate return Math.pow(future / present, 1 / periods) - 1; }; exports.SLN = function(cost, salvage, life) { cost = utils.parseNumber(cost); salvage = utils.parseNumber(salvage); life = utils.parseNumber(life); if (utils.anyIsError(cost, salvage, life)) { return error.value; } // Return error if life equal to 0 (zero) if (life === 0) { return error.num; } // Return straight-line depreciation return (cost - salvage) / life; }; exports.SYD = function(cost, salvage, life, period) { // Return error if any of the parameters is not a number cost = utils.parseNumber(cost); salvage = utils.parseNumber(salvage); life = utils.parseNumber(life); period = utils.parseNumber(period); if (utils.anyIsError(cost, salvage, life, period)) { return error.value; } // Return error if life equal to 0 (zero) if (life === 0) { return error.num; } // Return error if period is lower than 1 or greater than life if (period < 1 || period > life) { return error.num; } // Truncate period if it is not an integer period = parseInt(period, 10); // Return straight-line depreciation return ((cost - salvage) * (life - period + 1) * 2) / (life * (life + 1)); }; exports.TBILLEQ = function(settlement, maturity, discount) { settlement = utils.parseDate(settlement); maturity = utils.parseDate(maturity); discount = utils.parseNumber(discount); if (utils.anyIsError(settlement, maturity, discount)) { return error.value; } // Return error if discount is lower than or equal to zero if (discount <= 0) { return error.num; } // Return error if settlement is greater than maturity if (settlement > maturity) { return error.num; } // Return error if maturity is more than one year after settlement if (maturity - settlement > 365 * 24 * 60 * 60 * 1000) { return error.num; } // Return bond-equivalent yield return (365 * discount) / (360 - discount * dateTime.DAYS360(settlement, maturity, false)); }; exports.TBILLPRICE = function(settlement, maturity, discount) { settlement = utils.parseDate(settlement); maturity = utils.parseDate(maturity); discount = utils.parseNumber(discount); if (utils.anyIsError(settlement, maturity, discount)) { return error.value; } // Return error if discount is lower than or equal to zero if (discount <= 0) { return error.num; } // Return error if settlement is greater than maturity if (settlement > maturity) { return error.num; } // Return error if maturity is more than one year after settlement if (maturity - settlement > 365 * 24 * 60 * 60 * 1000) { return error.num; } // Return bond-equivalent yield return 100 * (1 - discount * dateTime.DAYS360(settlement, maturity, false) / 360); }; exports.TBILLYIELD = function(settlement, maturity, price) { settlement = utils.parseDate(settlement); maturity = utils.parseDate(maturity); price = utils.parseNumber(price); if (utils.anyIsError(settlement, maturity, price)) { return error.value; } // Return error if price is lower than or equal to zero if (price <= 0) { return error.num; } // Return error if settlement is greater than maturity if (settlement > maturity) { return error.num; } // Return error if maturity is more than one year after settlement if (maturity - settlement > 365 * 24 * 60 * 60 * 1000) { return error.num; } // Return bond-equivalent yield return (100 - price) * 360 / (price * dateTime.DAYS360(settlement, maturity, false)); }; // TODO exports.VDB = function() { throw new Error('VDB is not implemented'); }; // TODO needs better support for date // exports.XIRR = function(values, dates, guess) { // // Credits: algorithm inspired by Apache OpenOffice // // values = utils.parseNumberArray(utils.flatten(values)); // dates = utils.parseDateArray(utils.flatten(dates)); // guess = utils.parseNumber(guess); // // if (utils.anyIsError(values, dates, guess)) { // return error.value; // } // // // 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, dateTime.DAYS(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 = dateTime.DAYS(dates[i], dates[0]) / 365; // result -= frac * values[i] / Math.pow(r, frac + 1); // } // return result; // }; // // // Check that values contains at least one positive value and one negative value // var positive = false; // var negative = false; // for (var i = 0; i < values.length; i++) { // 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 error.num; // } // // // Initialize guess and resultRate // guess = guess || 0.1; // var resultRate = guess; // // // Set maximum epsilon for end of iteration // var epsMax = 1e-10; // // // Implement Newton's method // var newRate, epsRate, resultValue; // 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); // // // Return internal rate of return // return resultRate; // }; exports.XNPV = function(rate, values, dates) { rate = utils.parseNumber(rate); values = utils.parseNumberArray(utils.flatten(values)); dates = utils.parseDateArray(utils.flatten(dates)); if (utils.anyIsError(rate, values, dates)) { return error.value; } var result = 0; for (var i = 0; i < values.length; i++) { result += values[i] / Math.pow(1 + rate, dateTime.DAYS(dates[i], dates[0]) / 365); } return result; }; // TODO exports.YIELD = function() { throw new Error('YIELD is not implemented'); }; // TODO exports.YIELDDISC = function() { throw new Error('YIELDDISC is not implemented'); }; // TODO exports.YIELDMAT = function() { throw new Error('YIELDMAT is not implemented'); };