Skip to content

Commit

Permalink
add financial package, moved from amd-utils. see millermedeiros/amd-u…
Browse files Browse the repository at this point in the history
  • Loading branch information
millermedeiros committed Dec 20, 2012
1 parent 46118ce commit 45531fe
Show file tree
Hide file tree
Showing 12 changed files with 421 additions and 2 deletions.
80 changes: 80 additions & 0 deletions src/financial/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# amd-utils / financial #

Financial math helpers.



## compoundInterest(interestRate, nPeriods, presentValue):Number

Calculates [compount
interest](http://en.wikipedia.org/wiki/Future_value#Compound_interest) over
a period.

```js
compoundInterest(0.1, 3, 100); // 133.1
```



## futureValue(rate, nPeriods, payment[, presentValue, isDue]):Number

Calculate the future value of an investment based on periodic, constant
payments at a constant interest rate. (aka.
[annuity](http://en.wikipedia.org/wiki/Annuity_%28finance_theory%29)) - Works
similarly to Microsoft Excel `FV()` function.

Payments are made at the end of each period by default, set `isDue` to `true`
if payments should be made at the beginning of each period.

See: [`presentValue()`](#presentValue)

```js
futureValue(0.12, 12, 1000); // 24133.13
futureValue(0.12, 12, 1000, 0, true); // 27029.11
futureValue(0.12, 12, 1000, 500); // 26081.12
futureValue(0.12, 12, 1000, 500, true); // 28977.10
```



## npv(discountRate, values):Number

Calculates the [net present value](http://en.wikipedia.org/wiki/Net_present_value).

```js
npv(0.1, [-100, 30, 35, 28, 46]); // 7.867073163159489
npv(0.1, [30, 35, 28, 46]) - 100; // 8.653780479475415
```



## payment(rate, nPeriods, presentValue, futureValue, isDue):Number

Calculates the payment for a loan based on constant payments and a constant
interest rate. Similar to Microsoft Excel `PMT()` function. (aka. mortgage or
[annuity](http://en.wikipedia.org/wiki/Annuity_%28finance_theory%29))

```js
payment(0.05, 10, 1000); // 129.50457496545667
payment(0.05, 10, 0, 1000); // 79.50457496545668
```



## presentValue(rate, nPeriods, payment[, futurevalue, isDue]):Number

Returns the present value of an investment. The present value is the total
amount that a series of future payments is worth now (aka.
[annuity](http://en.wikipedia.org/wiki/Annuity_%28finance_theory%29)). - Works
similarly to Microsoft Excel `PV()` function.

Payments are made at the end of each period by default, set `isDue` to `true`
if payments should be made at the beginning of each period.

See: [`futureValue()`](#futureValue)

```js
presentValue(0.12, 12, 1000); // 6194.37
presentValue(0.12, 12, 1000, 0, true); // 6937.70
```

13 changes: 13 additions & 0 deletions src/financial/compoundInterest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
define(function () {

/**
* Basic compound interest
* @version 0.2.0 (2012/03/20)
*/
function compoundInterest(interestRate, nPeriods, presentValue) {
return presentValue * Math.pow(1 + interestRate, nPeriods);
}

return compoundInterest;

});
26 changes: 26 additions & 0 deletions src/financial/futureValue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
define(['./compoundInterest'], function (compoundInterest) {

/**
* Calculate future value of an investment.
* http://en.wikipedia.org/wiki/Annuity_%28finance_theory%29
* @version 0.2.0 (2012/03/20)
*/
function futureValue(rate, nPeriods, payment, presentValue, isDue){
if (rate === 0) {
//isDue makes no difference since rate is zero..
return payment * nPeriods;
} else {
var s = payment * ((Math.pow(1 + rate, nPeriods) - 1) / rate);
if (isDue) {
s *= (1 + rate);
}
if (presentValue) {
s += presentValue * Math.pow(1 + rate, nPeriods);
}
return s;
}
}

return futureValue;

});
22 changes: 22 additions & 0 deletions src/financial/npv.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
define(function () {


/**
* Net present value
* http://en.wikipedia.org/wiki/Net_present_value
* @version 0.1.0 (2011/12/30)
*/
function npv(discountRate, values) {
var val = 0,
n = values.length;

while (n--) {
val += values[n] / Math.pow(1 + discountRate, n + 1);
}

return val;
}

return npv;

});
28 changes: 28 additions & 0 deletions src/financial/payment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
define(['./compoundInterest'], function (compoundInterest) {

/**
* Calculates the payment for a loan based on constant payments and
* a constant interest rate.
* http://en.wikipedia.org/wiki/Annuity_%28finance_theory%29
* @version 0.1.1 (2012/03/20)
*/
function payment(rate, nPeriods, presentValue, futureValue, isDue){
futureValue = futureValue || 0;
if (!presentValue && !futureValue) {
return 0;
} else if (rate === 0) {
return (presentValue + futureValue) / nPeriods;
} else {
var r;
r = (rate / (1 - Math.pow(1 + rate, -nPeriods))) * presentValue;
if (futureValue) {
//the "opposite" of financial/futureValue
r += futureValue / ((Math.pow(1 + rate, nPeriods) -1) / rate);
}
return isDue? r / (1 + rate) : r;
}
}

return payment;

});
25 changes: 25 additions & 0 deletions src/financial/presentValue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
define(function () {

/**
* Returns the present value of an investment.
* http://en.wikipedia.org/wiki/Annuity_%28finance_theory%29
* @version 0.2.0 (2012/03/20)
*/
function presentValue(rate, nPeriods, payment, futureValue, isDue){
if (rate === 0) {
return payment * nPeriods;
} else {
var p = ((1 - Math.pow(1 + rate, - nPeriods)) / rate) * payment;
if (isDue) {
p *= (1 + rate);
}
if (futureValue) {
p += futureValue / Math.pow(1 + rate, nPeriods);
}
return p;
}
}

return presentValue;

});
10 changes: 8 additions & 2 deletions tests/spec-runner.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
require({
baseUrl : 'spec',
paths : {
'src' : '../../src'
'src' : '../../src',
'amd-utils' : '../../lib/amd-utils/src'
},
waitSeconds : (location.protocol === 'file:' || location.href.indexOf('://localhost') !== -1)? 5 : 45, //fail early locally
urlArgs : 'bust='+ (+new Date)
Expand All @@ -25,7 +26,12 @@
//init
require(
[
'spec-linkedList'
'spec-linkedList',
'financial/spec-compoundInterest',
'financial/spec-futureValue',
'financial/spec-npv',
'financial/spec-payment',
'financial/spec-presentValue'
],
function(){
jasmine.getEnv().addReporter(new jasmine.TrivialReporter());
Expand Down
23 changes: 23 additions & 0 deletions tests/spec/financial/spec-compoundInterest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
define(['src/financial/compoundInterest', 'amd-utils/number/enforcePrecision'], function (compoundInterest, enforcePrecision) {

describe('financial/compoundInterest()', function () {

beforeEach(function(){
this.addMatchers({
toFinanciallyEqual : function(val){
return (enforcePrecision(this.actual, 2) === val);
}
});
});


it('should calculate the compound interest', function () {

expect( compoundInterest(0.1, 20, 100) ).toFinanciallyEqual( 672.75 );
expect( compoundInterest(0.1, 3, 100) ).toFinanciallyEqual( 133.1 );

});

});

});
50 changes: 50 additions & 0 deletions tests/spec/financial/spec-futureValue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
define(['src/financial/futureValue', 'amd-utils/number/enforcePrecision'], function (futureValue, enforcePrecision) {

describe('financial/futureValue()', function () {

beforeEach(function(){
this.addMatchers({
toFinanciallyEqual : function(val){
return (enforcePrecision(this.actual, 2) === val);
}
});
});


it('should calculate the future value of an investment based on periodic, constant payments at a constant interest rate.', function () {

expect( futureValue(0.12, 12, 1000) ).toFinanciallyEqual( 24133.13 );
expect( futureValue(0.12, 12, 1000, null, true) ).toFinanciallyEqual( 27029.11 );

expect( futureValue(0.06, 40, 2000) ).toFinanciallyEqual( 309523.93 );
expect( futureValue(0.06, 40, 2000, null, true) ).toFinanciallyEqual( 328095.37 );

expect( futureValue(0.06, 25, 2400) ).toFinanciallyEqual( 131674.83 );
expect( futureValue(0.06, 25, 2400, null, true) ).toFinanciallyEqual( 139575.32 );

});

it('presentValue should compound over the periods', function () {

expect( futureValue(0.12, 12, 1000, 500) ).toFinanciallyEqual( 26081.12 );
expect( futureValue(0.12, 12, 1000, 500, true) ).toFinanciallyEqual( 28977.10 );

expect( futureValue(0.06, 25, 2400, 10000) ).toFinanciallyEqual( 174593.54 );
expect( futureValue(0.06, 25, 2400, 10000, true) ).toFinanciallyEqual( 182494.03 );

});

it('should return sum of payments if rate is zero', function () {
expect( futureValue(0.0, 25, 2400) ).toFinanciallyEqual( 60000 );
expect( futureValue(0.0, 25, 2400, null, true) ).toFinanciallyEqual( 60000 );
});

it('should decrement the value if rate is negative (deflation)', function () {
expect( futureValue(-0.06, 25, 2400) ).toFinanciallyEqual( 31483.59 );
expect( futureValue(-0.06, 25, 2400, null, true) ).toFinanciallyEqual( 29594.58 );
});

});


});
40 changes: 40 additions & 0 deletions tests/spec/financial/spec-npv.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
define(['src/financial/npv', 'amd-utils/number/enforcePrecision'], function (npv, enforcePrecision) {

describe('financial/npv()', function () {

beforeEach(function(){
this.addMatchers({
toFinanciallyEqual : function(val){
return (enforcePrecision(this.actual, 2) === val);
}
});
});


it('should return the net present value', function () {

var vals = [-100, 5, 10, 12, 20, 30];

expect( npv(0.06, vals) ).toFinanciallyEqual( -35.89 );
expect( npv(0.10, vals) ).toFinanciallyEqual( -41.71 );

vals.push(40, 55);

expect( npv(0.06, vals) ).toFinanciallyEqual( 25.22 );
expect( npv(0.10, vals) ).toFinanciallyEqual( 4.47 );


// based on wikipedia example
// http://en.wikipedia.org/wiki/Net_present_value#Example
vals = [25000, 25000, 25000, 25000, 25000, 25000];

expect( npv(0.1, vals) - 100000 ).toFinanciallyEqual( 8881.52 );
});

it('should work if rate is zero', function () {
expect( npv(0, [100, 100, 100]) ).toFinanciallyEqual( 300 );
});

});

});
58 changes: 58 additions & 0 deletions tests/spec/financial/spec-payment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
define(['src/financial/payment', 'amd-utils/number/enforcePrecision'], function (payment, enforcePrecision) {

describe('financial/payment()', function () {

beforeEach(function(){
this.addMatchers({
toFinanciallyEqual : function(val){
return (enforcePrecision(this.actual, 2) === val);
}
});
});


it('payment should be based on a constant interest rate', function () {
expect( payment(0.05, 10, 1000) ).toFinanciallyEqual( 129.50 );
expect( payment(6.5/100/12, 30*12, 200000) ).toFinanciallyEqual( 1264.14 );
});

it('isDue should change behavior', function () {
expect( payment(0.05, 10, 1000, null, true) ).toFinanciallyEqual( 123.34 );
expect( payment(6.5/100/12, 30*12, 200000, null, true) ).toFinanciallyEqual( 1257.33 );
});

it('futureValue should be computed into the result', function () {
expect( payment(0.05, 10, 1000, 500) ).toFinanciallyEqual( 169.26 );
expect( payment(0.05, 10, 1000, 500, true) ).toFinanciallyEqual( 161.20 );
});

it('should be zero if presentValue is zero', function () {
expect( payment(0.05, 10, 0) ).toFinanciallyEqual( 0 );
});

it('shouldn\'t be zero if futureValue is specified', function () {
expect( payment(0.05, 10, 0, 500) ).toFinanciallyEqual( 39.75 );
});

it('should work with negative rate', function () {
expect( payment(-0.05, 10, 1000) ).toFinanciallyEqual( 74.61 );
expect( payment(-0.05, 10, 1000, 500) ).toFinanciallyEqual( 136.91 );
expect( payment(-0.05, 10, 1000, 500, true) ).toFinanciallyEqual( 144.12 );
expect( payment(-0.05, 10, 0, 500) ).toFinanciallyEqual( 62.30 );
expect( payment(-0.05, 10, 0, 500, true) ).toFinanciallyEqual( 65.58 );
});

it('should work with zero rate', function () {
expect( payment(0, 10, 1000) ).toFinanciallyEqual( 100 );
expect( payment(0, 10, 1000, 500) ).toFinanciallyEqual( 150 );
});

it('should work with negative values', function () {
expect( payment(0.05, 10, 1000, -500) ).toFinanciallyEqual( 89.75 );
expect( payment(0.05, 10, -1000, 500) ).toFinanciallyEqual( -89.75 );
expect( payment(0.05, 10, -1000, -500) ).toFinanciallyEqual( -169.26 );
});

});

});
Loading

0 comments on commit 45531fe

Please sign in to comment.