Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IRR improvement: use [binary search] idea to improve both performance and [result value range]. #49

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Mocha Tests",
"program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
"args": [
"-u",
"bdd",// set to bdd, not tdd
"--timeout",
"999999",
"--colors",
"${workspaceFolder}/test/**/*.js"
],
"internalConsoleOptions": "openOnSessionStart"

// "skipFiles": [
// "<node_internals>/**"
// ],
// "program": "${workspaceFolder}\\test\\index.js"
}
]
}
49 changes: 43 additions & 6 deletions finance.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,53 @@ Finance.prototype.NPV = function (rate) {
return Math.round(npv * 100) / 100;
};

const MIN_NEGATIVE_RATE = -99.999;
const MAX_POSITIVE_RATE = 10000;
const RATE_DIFFERENCE_IGNORE = 0.001;
// seekZero seeks the zero point of the function fn(x), accurate to within x \pm 0.01. fn(x) must be decreasing with x.
// [Gang Li]: Use [binary search] idea to quickly narrow down the rate range, returns when difference is small enough
function seekZero(fn) {
var x = 1;
while (fn(x) > 0) {
x += 1;
let lessRate = MIN_NEGATIVE_RATE;
let moreRate = MAX_POSITIVE_RATE;
let medianRate = (lessRate + moreRate) / 2;
let lastMedianRate;

let lessRateResult = fn(lessRate);
if (lessRateResult <= 0) {
// There is no need to calculate further when IRR result will be < MIN_NEGATIVE_RATE
return lessRate;
}
while (fn(x) < 0) {
x -= 0.01

let moreRateResult = fn(moreRate);
if (moreRateResult >= 0) {
// There is no need to calculate further when IRR result will be > MAX_POSITIVE_RATE
return moreRate;
}

let medianRateResult = fn(medianRate);
while (lessRateResult > 0 && moreRateResult < 0) {
if (medianRateResult === 0) {
return medianRate;
}
else if (medianRateResult > 0) {
lessRate = medianRate;
lessRateResult = fn(lessRate);
}
else {
moreRate = medianRate;
moreRateResult = fn(moreRate);
}

lastMedianRate = medianRate;
medianRate = (lessRate + moreRate) / 2;
if (Math.abs(Math.abs(medianRate) - Math.abs(lastMedianRate)) < RATE_DIFFERENCE_IGNORE) {
break;
}

medianRateResult = fn(medianRate);
}
return x + 0.01;

return medianRate;
}

// Internal Rate of Return (IRR)
Expand Down
1 change: 1 addition & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ describe('FinanceJS', function() {
};
var irr = cal.IRR(data);
// should be ~4951.29
console.log(irr);
(irr).should.be.within(4951, 4952);
});

Expand Down