From e0ff23f498bb097df222a59f22465a4b200ec9c6 Mon Sep 17 00:00:00 2001 From: gangli Date: Mon, 29 Jun 2020 16:40:29 -0600 Subject: [PATCH 1/2] IRR improvement: use [binary search] idea to improve both performance and [result value range]. --- finance.js | 49 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/finance.js b/finance.js index f31c8fd..b4b5530 100755 --- a/finance.js +++ b/finance.js @@ -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) From e7254e92111a79114447f8fcc9557484f96f87a4 Mon Sep 17 00:00:00 2001 From: gangli Date: Fri, 5 Feb 2021 09:58:04 -0700 Subject: [PATCH 2/2] Commit changes made. --- .vscode/launch.json | 28 ++++++++++++++++++++++++++++ test/index.js | 1 + 2 files changed, 29 insertions(+) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..12888f0 --- /dev/null +++ b/.vscode/launch.json @@ -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": [ + // "/**" + // ], + // "program": "${workspaceFolder}\\test\\index.js" + } + ] +} \ No newline at end of file diff --git a/test/index.js b/test/index.js index 67cd382..1b9eda9 100755 --- a/test/index.js +++ b/test/index.js @@ -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); });