Created
November 29, 2012 04:55
-
-
Save pies/4166888 to your computer and use it in GitHub Desktop.
Few Excel formulas - PMT, PPMT, XIRR - expressed in Javascript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Based on | |
* - EGM Mathematical Finance class by Enrique Garcia M. <[email protected]> | |
* - A Guide to the PMT, FV, IPMT and PPMT Functions by Kevin (aka MWVisa1) | |
*/ | |
var ExcelFormulas = { | |
PVIF: function(rate, nper) { | |
return Math.pow(1 + rate, nper); | |
}, | |
FVIFA: function(rate, nper) { | |
return rate == 0? nper: (this.PVIF(rate, nper) - 1) / rate; | |
}, | |
PMT: function(rate, nper, pv, fv, type) { | |
if (!fv) fv = 0; | |
if (!type) type = 0; | |
if (rate == 0) return -(pv + fv)/nper; | |
var pvif = Math.pow(1 + rate, nper); | |
var pmt = rate / (pvif - 1) * -(pv * pvif + fv); | |
if (type == 1) { | |
pmt /= (1 + rate); | |
}; | |
return pmt; | |
}, | |
IPMT: function(pv, pmt, rate, per) { | |
var tmp = Math.pow(1 + rate, per); | |
return 0 - (pv * tmp * rate + pmt * (tmp - 1)); | |
}, | |
PPMT: function(rate, per, nper, pv, fv, type) { | |
if (per < 1 || (per >= nper + 1)) return null; | |
var pmt = this.PMT(rate, nper, pv, fv, type); | |
var ipmt = this.IPMT(pv, pmt, rate, per - 1); | |
return pmt - ipmt; | |
}, | |
DaysBetween: function(date1, date2) { | |
var oneDay = 24*60*60*1000; | |
return Math.round(Math.abs((date1.getTime() - date2.getTime())/oneDay)); | |
}, | |
// Change Date and Flow to date and value fields you use | |
XNPV: function(rate, values) { | |
var xnpv = 0.0; | |
var firstDate = new Date(values[0].Date); | |
for (var key in values) { | |
var tmp = values[key]; | |
var value = tmp.Flow; | |
var date = new Date(tmp.Date); | |
xnpv += value / Math.pow(1 + rate, this.DaysBetween(firstDate, date)/365); | |
}; | |
return xnpv; | |
}, | |
XIRR: function(values, guess) { | |
if (!guess) guess = 0.1; | |
var x1 = 0.0; | |
var x2 = guess; | |
var f1 = this.XNPV(x1, values); | |
var f2 = this.XNPV(x2, values); | |
for (var i = 0; i < 100; i++) { | |
if ((f1 * f2) < 0.0) break; | |
if (Math.abs(f1) < Math.abs(f2)) { | |
f1 = this.XNPV(x1 += 1.6 * (x1 - x2), values); | |
} | |
else { | |
f2 = this.XNPV(x2 += 1.6 * (x2 - x1), values); | |
} | |
}; | |
if ((f1 * f2) > 0.0) return null; | |
var f = this.XNPV(x1, values); | |
if (f < 0.0) { | |
var rtb = x1; | |
var dx = x2 - x1; | |
} | |
else { | |
var rtb = x2; | |
var dx = x1 - x2; | |
}; | |
for (var i = 0; i < 100; i++) { | |
dx *= 0.5; | |
var x_mid = rtb + dx; | |
var f_mid = this.XNPV(x_mid, values); | |
if (f_mid <= 0.0) rtb = x_mid; | |
if ((Math.abs(f_mid) < 1.0e-6) || (Math.abs(dx) < 1.0e-6)) return x_mid; | |
}; | |
return null; | |
} | |
}; |
for XNPV (and XIRR): need to be sorted by date, so may be adding:
values.sort((a, b) => a.Date.getTime() - b.Date.getTime());
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
thanks