calcufly-finance 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +57 -0
- package/index.js +528 -0
- package/package.json +17 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 CalcuFly (https://calcufly.com)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# calcufly-finance
|
|
2
|
+
|
|
3
|
+
Professional finance calculator functions for Node.js. Mortgage, loan, investment, retirement, tax, ROI and more.
|
|
4
|
+
|
|
5
|
+
Part of the [CalcuFly](https://calcufly.com) calculator suite — 600+ free online calculators.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install calcufly-finance
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
const finance = require('calcufly-finance');
|
|
17
|
+
|
|
18
|
+
// Mortgage calculation
|
|
19
|
+
const mortgage = finance.mortgage(300000, 6.5, 30);
|
|
20
|
+
console.log(mortgage.monthlyPayment); // 1896.20
|
|
21
|
+
|
|
22
|
+
// Compound interest
|
|
23
|
+
const growth = finance.compoundInterest(10000, 7, 10);
|
|
24
|
+
console.log(growth.futureValue); // 20096.61
|
|
25
|
+
|
|
26
|
+
// ROI
|
|
27
|
+
const roi = finance.roi(5000, 7500);
|
|
28
|
+
console.log(roi.roiPercent); // 50%
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## API Reference
|
|
32
|
+
|
|
33
|
+
| Function | Description |
|
|
34
|
+
|----------|-------------|
|
|
35
|
+
| `mortgage(principal, rate, years)` | Monthly mortgage payment |
|
|
36
|
+
| `compoundInterest(principal, rate, years, n)` | Compound interest growth |
|
|
37
|
+
| `simpleInterest(principal, rate, years)` | Simple interest |
|
|
38
|
+
| `loanPayment(principal, rate, years)` | Loan monthly payment |
|
|
39
|
+
| `amortizationSchedule(principal, rate, years)` | Full amortization table |
|
|
40
|
+
| `roi(initial, final)` | Return on investment |
|
|
41
|
+
| `profitMargin(revenue, cost)` | Gross profit margin |
|
|
42
|
+
| `breakEven(fixed, price, cost)` | Break-even analysis |
|
|
43
|
+
| `futureValue(pv, rate, years)` | Future value of money |
|
|
44
|
+
| `presentValue(fv, rate, years)` | Present value of money |
|
|
45
|
+
| `ruleOf72(rate)` | Years to double investment |
|
|
46
|
+
| `retirementSavings(...)` | Retirement planning |
|
|
47
|
+
| `fireNumber(expenses, rate)` | FIRE movement target |
|
|
48
|
+
| `tipCalculator(bill, tipPct, people)` | Tip splitting |
|
|
49
|
+
| `discountPrice(price, pct)` | Discount calculation |
|
|
50
|
+
| `inflationAdjust(amount, rate, years)` | Inflation adjustment |
|
|
51
|
+
| `depreciationStraightLine(cost, salvage, life)` | Depreciation schedule |
|
|
52
|
+
|
|
53
|
+
Try these calculations online at **[CalcuFly.com](https://calcufly.com)** — free, no signup required.
|
|
54
|
+
|
|
55
|
+
## License
|
|
56
|
+
|
|
57
|
+
MIT — [CalcuFly](https://calcufly.com)
|
package/index.js
ADDED
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* calcufly-finance
|
|
3
|
+
* Professional finance calculator functions
|
|
4
|
+
* https://calcufly.com
|
|
5
|
+
*
|
|
6
|
+
* Try these calculations online at https://calcufly.com
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Calculate monthly mortgage payment and totals.
|
|
13
|
+
* @param {number} principal - Loan amount
|
|
14
|
+
* @param {number} annualRate - Annual interest rate (e.g. 6.5 for 6.5%)
|
|
15
|
+
* @param {number} years - Loan term in years
|
|
16
|
+
* @returns {{ monthlyPayment: number, totalPayment: number, totalInterest: number }}
|
|
17
|
+
*/
|
|
18
|
+
function mortgage(principal, annualRate, years) {
|
|
19
|
+
const r = annualRate / 100 / 12;
|
|
20
|
+
const n = years * 12;
|
|
21
|
+
if (r === 0) {
|
|
22
|
+
const monthlyPayment = principal / n;
|
|
23
|
+
return { monthlyPayment, totalPayment: principal, totalInterest: 0 };
|
|
24
|
+
}
|
|
25
|
+
const monthlyPayment = principal * (r * Math.pow(1 + r, n)) / (Math.pow(1 + r, n) - 1);
|
|
26
|
+
const totalPayment = monthlyPayment * n;
|
|
27
|
+
const totalInterest = totalPayment - principal;
|
|
28
|
+
return {
|
|
29
|
+
monthlyPayment: parseFloat(monthlyPayment.toFixed(2)),
|
|
30
|
+
totalPayment: parseFloat(totalPayment.toFixed(2)),
|
|
31
|
+
totalInterest: parseFloat(totalInterest.toFixed(2)),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Calculate compound interest.
|
|
37
|
+
* @param {number} principal - Initial amount
|
|
38
|
+
* @param {number} rate - Annual interest rate (e.g. 5 for 5%)
|
|
39
|
+
* @param {number} years - Time in years
|
|
40
|
+
* @param {number} [n=12] - Compounding periods per year
|
|
41
|
+
* @returns {{ futureValue: number, totalInterest: number }}
|
|
42
|
+
*/
|
|
43
|
+
function compoundInterest(principal, rate, years, n = 12) {
|
|
44
|
+
const r = rate / 100;
|
|
45
|
+
const futureValue = principal * Math.pow(1 + r / n, n * years);
|
|
46
|
+
const totalInterest = futureValue - principal;
|
|
47
|
+
return {
|
|
48
|
+
futureValue: parseFloat(futureValue.toFixed(2)),
|
|
49
|
+
totalInterest: parseFloat(totalInterest.toFixed(2)),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Calculate simple interest.
|
|
55
|
+
* @param {number} principal - Initial amount
|
|
56
|
+
* @param {number} rate - Annual interest rate (e.g. 5 for 5%)
|
|
57
|
+
* @param {number} years - Time in years
|
|
58
|
+
* @returns {{ interest: number, total: number }}
|
|
59
|
+
*/
|
|
60
|
+
function simpleInterest(principal, rate, years) {
|
|
61
|
+
const interest = principal * (rate / 100) * years;
|
|
62
|
+
const total = principal + interest;
|
|
63
|
+
return {
|
|
64
|
+
interest: parseFloat(interest.toFixed(2)),
|
|
65
|
+
total: parseFloat(total.toFixed(2)),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Calculate loan payment (same formula as mortgage but named generically).
|
|
71
|
+
* @param {number} principal - Loan amount
|
|
72
|
+
* @param {number} annualRate - Annual interest rate (e.g. 6 for 6%)
|
|
73
|
+
* @param {number} years - Loan term in years
|
|
74
|
+
* @returns {{ monthlyPayment: number, totalPayment: number, totalInterest: number }}
|
|
75
|
+
*/
|
|
76
|
+
function loanPayment(principal, annualRate, years) {
|
|
77
|
+
return mortgage(principal, annualRate, years);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Generate a full amortization schedule.
|
|
82
|
+
* @param {number} principal - Loan amount
|
|
83
|
+
* @param {number} annualRate - Annual interest rate (e.g. 6 for 6%)
|
|
84
|
+
* @param {number} years - Loan term in years
|
|
85
|
+
* @returns {Array<{ month: number, payment: number, principal: number, interest: number, balance: number }>}
|
|
86
|
+
*/
|
|
87
|
+
function amortizationSchedule(principal, annualRate, years) {
|
|
88
|
+
const r = annualRate / 100 / 12;
|
|
89
|
+
const n = years * 12;
|
|
90
|
+
const schedule = [];
|
|
91
|
+
let balance = principal;
|
|
92
|
+
|
|
93
|
+
const monthlyPayment = r === 0
|
|
94
|
+
? principal / n
|
|
95
|
+
: principal * (r * Math.pow(1 + r, n)) / (Math.pow(1 + r, n) - 1);
|
|
96
|
+
|
|
97
|
+
for (let month = 1; month <= n; month++) {
|
|
98
|
+
const interestPayment = balance * r;
|
|
99
|
+
const principalPayment = monthlyPayment - interestPayment;
|
|
100
|
+
balance -= principalPayment;
|
|
101
|
+
schedule.push({
|
|
102
|
+
month,
|
|
103
|
+
payment: parseFloat(monthlyPayment.toFixed(2)),
|
|
104
|
+
principal: parseFloat(principalPayment.toFixed(2)),
|
|
105
|
+
interest: parseFloat(interestPayment.toFixed(2)),
|
|
106
|
+
balance: parseFloat(Math.max(0, balance).toFixed(2)),
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
return schedule;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Calculate the effective APR including fees.
|
|
114
|
+
* @param {number} principal - Loan amount
|
|
115
|
+
* @param {number} totalFees - Total fees/closing costs
|
|
116
|
+
* @param {number} rate - Nominal annual rate (e.g. 6 for 6%)
|
|
117
|
+
* @param {number} years - Loan term in years
|
|
118
|
+
* @returns {number} effectiveAPR as a percentage
|
|
119
|
+
*/
|
|
120
|
+
function apr(principal, totalFees, rate, years) {
|
|
121
|
+
const adjustedPrincipal = principal - totalFees;
|
|
122
|
+
const r = rate / 100 / 12;
|
|
123
|
+
const n = years * 12;
|
|
124
|
+
const monthlyPayment = principal * (r * Math.pow(1 + r, n)) / (Math.pow(1 + r, n) - 1);
|
|
125
|
+
|
|
126
|
+
// Newton-Raphson to find APR
|
|
127
|
+
let guess = rate / 100 / 12;
|
|
128
|
+
for (let i = 0; i < 1000; i++) {
|
|
129
|
+
const f = monthlyPayment * (1 - Math.pow(1 + guess, -n)) / guess - adjustedPrincipal;
|
|
130
|
+
const fPrime = monthlyPayment * (
|
|
131
|
+
(Math.pow(1 + guess, -n) - 1) / (guess * guess) +
|
|
132
|
+
n * Math.pow(1 + guess, -n - 1) / guess
|
|
133
|
+
);
|
|
134
|
+
const next = guess - f / fPrime;
|
|
135
|
+
if (Math.abs(next - guess) < 1e-10) break;
|
|
136
|
+
guess = next;
|
|
137
|
+
}
|
|
138
|
+
return parseFloat((guess * 12 * 100).toFixed(4));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Calculate effective Annual Percentage Yield.
|
|
143
|
+
* @param {number} nominalRate - Nominal annual rate (e.g. 5 for 5%)
|
|
144
|
+
* @param {number} compoundingPeriods - Number of compounding periods per year
|
|
145
|
+
* @returns {number} effectiveAPY as a percentage
|
|
146
|
+
*/
|
|
147
|
+
function apy(nominalRate, compoundingPeriods) {
|
|
148
|
+
const r = nominalRate / 100;
|
|
149
|
+
const effectiveAPY = (Math.pow(1 + r / compoundingPeriods, compoundingPeriods) - 1) * 100;
|
|
150
|
+
return parseFloat(effectiveAPY.toFixed(4));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Calculate Return on Investment.
|
|
155
|
+
* @param {number} initialInvestment - Amount invested
|
|
156
|
+
* @param {number} finalValue - Final value of investment
|
|
157
|
+
* @returns {{ roi: number, roiPercent: number }}
|
|
158
|
+
*/
|
|
159
|
+
function roi(initialInvestment, finalValue) {
|
|
160
|
+
const roiValue = finalValue - initialInvestment;
|
|
161
|
+
const roiPercent = (roiValue / initialInvestment) * 100;
|
|
162
|
+
return {
|
|
163
|
+
roi: parseFloat(roiValue.toFixed(2)),
|
|
164
|
+
roiPercent: parseFloat(roiPercent.toFixed(2)),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Calculate gross profit margin.
|
|
170
|
+
* @param {number} revenue - Total revenue
|
|
171
|
+
* @param {number} cost - Cost of goods sold
|
|
172
|
+
* @returns {{ grossMargin: number, grossMarginPercent: number }}
|
|
173
|
+
*/
|
|
174
|
+
function profitMargin(revenue, cost) {
|
|
175
|
+
const grossMargin = revenue - cost;
|
|
176
|
+
const grossMarginPercent = (grossMargin / revenue) * 100;
|
|
177
|
+
return {
|
|
178
|
+
grossMargin: parseFloat(grossMargin.toFixed(2)),
|
|
179
|
+
grossMarginPercent: parseFloat(grossMarginPercent.toFixed(2)),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Calculate markup percentage.
|
|
185
|
+
* @param {number} cost - Cost of item
|
|
186
|
+
* @param {number} sellingPrice - Selling price
|
|
187
|
+
* @returns {{ markup: number, markupPercent: number }}
|
|
188
|
+
*/
|
|
189
|
+
function markup(cost, sellingPrice) {
|
|
190
|
+
const markupValue = sellingPrice - cost;
|
|
191
|
+
const markupPercent = (markupValue / cost) * 100;
|
|
192
|
+
return {
|
|
193
|
+
markup: parseFloat(markupValue.toFixed(2)),
|
|
194
|
+
markupPercent: parseFloat(markupPercent.toFixed(2)),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Calculate break-even point.
|
|
200
|
+
* @param {number} fixedCosts - Total fixed costs
|
|
201
|
+
* @param {number} pricePerUnit - Selling price per unit
|
|
202
|
+
* @param {number} costPerUnit - Variable cost per unit
|
|
203
|
+
* @returns {{ breakEvenUnits: number, breakEvenRevenue: number }}
|
|
204
|
+
*/
|
|
205
|
+
function breakEven(fixedCosts, pricePerUnit, costPerUnit) {
|
|
206
|
+
const contributionMargin = pricePerUnit - costPerUnit;
|
|
207
|
+
const breakEvenUnits = fixedCosts / contributionMargin;
|
|
208
|
+
const breakEvenRevenue = breakEvenUnits * pricePerUnit;
|
|
209
|
+
return {
|
|
210
|
+
breakEvenUnits: parseFloat(breakEvenUnits.toFixed(2)),
|
|
211
|
+
breakEvenRevenue: parseFloat(breakEvenRevenue.toFixed(2)),
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Calculate future value of a present sum.
|
|
217
|
+
* @param {number} presentValue - Current amount
|
|
218
|
+
* @param {number} rate - Annual interest rate (e.g. 5 for 5%)
|
|
219
|
+
* @param {number} years - Time in years
|
|
220
|
+
* @returns {number} futureValue
|
|
221
|
+
*/
|
|
222
|
+
function futureValue(presentValue, rate, years) {
|
|
223
|
+
const fv = presentValue * Math.pow(1 + rate / 100, years);
|
|
224
|
+
return parseFloat(fv.toFixed(2));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Calculate present value of a future sum.
|
|
229
|
+
* @param {number} fv - Future value
|
|
230
|
+
* @param {number} rate - Annual discount rate (e.g. 5 for 5%)
|
|
231
|
+
* @param {number} years - Time in years
|
|
232
|
+
* @returns {number} presentValue
|
|
233
|
+
*/
|
|
234
|
+
function presentValue(fv, rate, years) {
|
|
235
|
+
const pv = fv / Math.pow(1 + rate / 100, years);
|
|
236
|
+
return parseFloat(pv.toFixed(2));
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Estimate years to double an investment using the Rule of 72.
|
|
241
|
+
* @param {number} rate - Annual interest rate (e.g. 6 for 6%)
|
|
242
|
+
* @returns {number} yearsToDouble
|
|
243
|
+
*/
|
|
244
|
+
function ruleOf72(rate) {
|
|
245
|
+
return parseFloat((72 / rate).toFixed(2));
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Sort debts using the Debt Snowball method (lowest balance first).
|
|
250
|
+
* @param {Array<{ name: string, balance: number, minPayment: number }>} debts
|
|
251
|
+
* @returns {Array} Sorted payoff order with debt info
|
|
252
|
+
*/
|
|
253
|
+
function debtSnowball(debts) {
|
|
254
|
+
return [...debts]
|
|
255
|
+
.sort((a, b) => a.balance - b.balance)
|
|
256
|
+
.map((d, i) => ({ ...d, payoffOrder: i + 1 }));
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Sort debts using the Debt Avalanche method (highest interest rate first).
|
|
261
|
+
* @param {Array<{ name: string, balance: number, minPayment: number, rate: number }>} debts
|
|
262
|
+
* @returns {Array} Sorted payoff order with debt info
|
|
263
|
+
*/
|
|
264
|
+
function debtAvalanche(debts) {
|
|
265
|
+
return [...debts]
|
|
266
|
+
.sort((a, b) => b.rate - a.rate)
|
|
267
|
+
.map((d, i) => ({ ...d, payoffOrder: i + 1 }));
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Estimate retirement savings at retirement age.
|
|
272
|
+
* @param {number} currentAge - Current age
|
|
273
|
+
* @param {number} retirementAge - Target retirement age
|
|
274
|
+
* @param {number} currentSavings - Current savings balance
|
|
275
|
+
* @param {number} monthlyContribution - Monthly contribution amount
|
|
276
|
+
* @param {number} annualReturn - Expected annual return rate (e.g. 7 for 7%)
|
|
277
|
+
* @returns {{ totalAtRetirement: number }}
|
|
278
|
+
*/
|
|
279
|
+
function retirementSavings(currentAge, retirementAge, currentSavings, monthlyContribution, annualReturn) {
|
|
280
|
+
const years = retirementAge - currentAge;
|
|
281
|
+
const months = years * 12;
|
|
282
|
+
const r = annualReturn / 100 / 12;
|
|
283
|
+
|
|
284
|
+
const futureCurrentSavings = currentSavings * Math.pow(1 + r, months);
|
|
285
|
+
const futureContributions = r === 0
|
|
286
|
+
? monthlyContribution * months
|
|
287
|
+
: monthlyContribution * (Math.pow(1 + r, months) - 1) / r;
|
|
288
|
+
|
|
289
|
+
const totalAtRetirement = futureCurrentSavings + futureContributions;
|
|
290
|
+
return { totalAtRetirement: parseFloat(totalAtRetirement.toFixed(2)) };
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Calculate FIRE (Financial Independence, Retire Early) number.
|
|
295
|
+
* @param {number} annualExpenses - Annual living expenses
|
|
296
|
+
* @param {number} [withdrawalRate=0.04] - Safe withdrawal rate (e.g. 0.04 for 4%)
|
|
297
|
+
* @returns {number} requiredSavings
|
|
298
|
+
*/
|
|
299
|
+
function fireNumber(annualExpenses, withdrawalRate = 0.04) {
|
|
300
|
+
const required = annualExpenses / withdrawalRate;
|
|
301
|
+
return parseFloat(required.toFixed(2));
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Convert annual salary to hourly rate.
|
|
306
|
+
* @param {number} annualSalary - Annual salary amount
|
|
307
|
+
* @param {number} [hoursPerWeek=40] - Hours worked per week
|
|
308
|
+
* @param {number} [weeksPerYear=52] - Weeks worked per year
|
|
309
|
+
* @returns {number} hourlyRate
|
|
310
|
+
*/
|
|
311
|
+
function salaryToHourly(annualSalary, hoursPerWeek = 40, weeksPerYear = 52) {
|
|
312
|
+
const hourlyRate = annualSalary / (hoursPerWeek * weeksPerYear);
|
|
313
|
+
return parseFloat(hourlyRate.toFixed(2));
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Convert hourly rate to annual salary.
|
|
318
|
+
* @param {number} hourlyRate - Hourly pay rate
|
|
319
|
+
* @param {number} [hoursPerWeek=40] - Hours worked per week
|
|
320
|
+
* @param {number} [weeksPerYear=52] - Weeks worked per year
|
|
321
|
+
* @returns {number} annualSalary
|
|
322
|
+
*/
|
|
323
|
+
function hourlyToSalary(hourlyRate, hoursPerWeek = 40, weeksPerYear = 52) {
|
|
324
|
+
const annualSalary = hourlyRate * hoursPerWeek * weeksPerYear;
|
|
325
|
+
return parseFloat(annualSalary.toFixed(2));
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Calculate tip and split a restaurant bill.
|
|
330
|
+
* @param {number} billAmount - Total bill amount
|
|
331
|
+
* @param {number} tipPercent - Tip percentage (e.g. 18 for 18%)
|
|
332
|
+
* @param {number} [numPeople=1] - Number of people splitting the bill
|
|
333
|
+
* @returns {{ tipAmount: number, totalAmount: number, perPerson: number }}
|
|
334
|
+
*/
|
|
335
|
+
function tipCalculator(billAmount, tipPercent, numPeople = 1) {
|
|
336
|
+
const tipAmount = billAmount * (tipPercent / 100);
|
|
337
|
+
const totalAmount = billAmount + tipAmount;
|
|
338
|
+
const perPerson = totalAmount / numPeople;
|
|
339
|
+
return {
|
|
340
|
+
tipAmount: parseFloat(tipAmount.toFixed(2)),
|
|
341
|
+
totalAmount: parseFloat(totalAmount.toFixed(2)),
|
|
342
|
+
perPerson: parseFloat(perPerson.toFixed(2)),
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Calculate discounted price.
|
|
348
|
+
* @param {number} originalPrice - Original price
|
|
349
|
+
* @param {number} discountPercent - Discount percentage (e.g. 20 for 20%)
|
|
350
|
+
* @returns {{ discount: number, finalPrice: number }}
|
|
351
|
+
*/
|
|
352
|
+
function discountPrice(originalPrice, discountPercent) {
|
|
353
|
+
const discount = originalPrice * (discountPercent / 100);
|
|
354
|
+
const finalPrice = originalPrice - discount;
|
|
355
|
+
return {
|
|
356
|
+
discount: parseFloat(discount.toFixed(2)),
|
|
357
|
+
finalPrice: parseFloat(finalPrice.toFixed(2)),
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Adjust an amount for inflation over time.
|
|
363
|
+
* @param {number} amount - Original amount
|
|
364
|
+
* @param {number} inflationRate - Annual inflation rate (e.g. 3 for 3%)
|
|
365
|
+
* @param {number} years - Number of years
|
|
366
|
+
* @returns {number} adjustedAmount (future purchasing cost equivalent)
|
|
367
|
+
*/
|
|
368
|
+
function inflationAdjust(amount, inflationRate, years) {
|
|
369
|
+
const adjusted = amount * Math.pow(1 + inflationRate / 100, years);
|
|
370
|
+
return parseFloat(adjusted.toFixed(2));
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Calculate net cash flow from inflows and outflows.
|
|
375
|
+
* @param {number[]} inflows - Array of inflow amounts
|
|
376
|
+
* @param {number[]} outflows - Array of outflow amounts
|
|
377
|
+
* @returns {{ netCashFlow: number, totalInflows: number, totalOutflows: number }}
|
|
378
|
+
*/
|
|
379
|
+
function cashFlow(inflows, outflows) {
|
|
380
|
+
const totalInflows = inflows.reduce((sum, v) => sum + v, 0);
|
|
381
|
+
const totalOutflows = outflows.reduce((sum, v) => sum + v, 0);
|
|
382
|
+
const netCashFlow = totalInflows - totalOutflows;
|
|
383
|
+
return {
|
|
384
|
+
netCashFlow: parseFloat(netCashFlow.toFixed(2)),
|
|
385
|
+
totalInflows: parseFloat(totalInflows.toFixed(2)),
|
|
386
|
+
totalOutflows: parseFloat(totalOutflows.toFixed(2)),
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Calculate straight-line depreciation schedule.
|
|
392
|
+
* @param {number} cost - Original asset cost
|
|
393
|
+
* @param {number} salvageValue - Estimated salvage/residual value
|
|
394
|
+
* @param {number} usefulLife - Useful life in years
|
|
395
|
+
* @returns {{ annualDepreciation: number, schedule: Array<{ year: number, depreciation: number, bookValue: number }> }}
|
|
396
|
+
*/
|
|
397
|
+
function depreciationStraightLine(cost, salvageValue, usefulLife) {
|
|
398
|
+
const annualDepreciation = (cost - salvageValue) / usefulLife;
|
|
399
|
+
const schedule = [];
|
|
400
|
+
let bookValue = cost;
|
|
401
|
+
for (let year = 1; year <= usefulLife; year++) {
|
|
402
|
+
bookValue -= annualDepreciation;
|
|
403
|
+
schedule.push({
|
|
404
|
+
year,
|
|
405
|
+
depreciation: parseFloat(annualDepreciation.toFixed(2)),
|
|
406
|
+
bookValue: parseFloat(Math.max(salvageValue, bookValue).toFixed(2)),
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
return {
|
|
410
|
+
annualDepreciation: parseFloat(annualDepreciation.toFixed(2)),
|
|
411
|
+
schedule,
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Calculate net present value of a series of cash flows.
|
|
417
|
+
* @param {number} discountRate - Annual discount rate (e.g. 10 for 10%)
|
|
418
|
+
* @param {number[]} cashFlows - Array of cash flows (index 0 = year 0, typically negative)
|
|
419
|
+
* @returns {number} npv
|
|
420
|
+
*/
|
|
421
|
+
function npv(discountRate, cashFlows) {
|
|
422
|
+
const r = discountRate / 100;
|
|
423
|
+
const result = cashFlows.reduce((acc, cf, t) => acc + cf / Math.pow(1 + r, t), 0);
|
|
424
|
+
return parseFloat(result.toFixed(2));
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Calculate Internal Rate of Return using Newton-Raphson.
|
|
429
|
+
* @param {number[]} cashFlows - Array of cash flows (index 0 is typically negative initial investment)
|
|
430
|
+
* @returns {number} irr as a percentage
|
|
431
|
+
*/
|
|
432
|
+
function irr(cashFlows) {
|
|
433
|
+
let rate = 0.1;
|
|
434
|
+
for (let i = 0; i < 1000; i++) {
|
|
435
|
+
let npvVal = 0;
|
|
436
|
+
let npvDerivative = 0;
|
|
437
|
+
for (let t = 0; t < cashFlows.length; t++) {
|
|
438
|
+
npvVal += cashFlows[t] / Math.pow(1 + rate, t);
|
|
439
|
+
npvDerivative -= t * cashFlows[t] / Math.pow(1 + rate, t + 1);
|
|
440
|
+
}
|
|
441
|
+
const next = rate - npvVal / npvDerivative;
|
|
442
|
+
if (Math.abs(next - rate) < 1e-10) break;
|
|
443
|
+
rate = next;
|
|
444
|
+
}
|
|
445
|
+
return parseFloat((rate * 100).toFixed(4));
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Calculate debt-to-income ratio.
|
|
450
|
+
* @param {number} monthlyDebtPayments - Total monthly debt payments
|
|
451
|
+
* @param {number} grossMonthlyIncome - Gross monthly income
|
|
452
|
+
* @returns {{ dtiRatio: number, dtiPercent: number }}
|
|
453
|
+
*/
|
|
454
|
+
function debtToIncome(monthlyDebtPayments, grossMonthlyIncome) {
|
|
455
|
+
const dtiRatio = monthlyDebtPayments / grossMonthlyIncome;
|
|
456
|
+
const dtiPercent = dtiRatio * 100;
|
|
457
|
+
return {
|
|
458
|
+
dtiRatio: parseFloat(dtiRatio.toFixed(4)),
|
|
459
|
+
dtiPercent: parseFloat(dtiPercent.toFixed(2)),
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Calculate savings goal progress and time to reach goal.
|
|
465
|
+
* @param {number} goalAmount - Target savings amount
|
|
466
|
+
* @param {number} currentSavings - Current savings balance
|
|
467
|
+
* @param {number} monthlyContribution - Monthly savings contribution
|
|
468
|
+
* @param {number} annualReturn - Expected annual return rate (e.g. 5 for 5%)
|
|
469
|
+
* @returns {{ monthsToGoal: number, progressPercent: number }}
|
|
470
|
+
*/
|
|
471
|
+
function savingsGoal(goalAmount, currentSavings, monthlyContribution, annualReturn) {
|
|
472
|
+
const r = annualReturn / 100 / 12;
|
|
473
|
+
const progressPercent = (currentSavings / goalAmount) * 100;
|
|
474
|
+
let balance = currentSavings;
|
|
475
|
+
let months = 0;
|
|
476
|
+
const maxMonths = 600;
|
|
477
|
+
while (balance < goalAmount && months < maxMonths) {
|
|
478
|
+
balance = balance * (1 + r) + monthlyContribution;
|
|
479
|
+
months++;
|
|
480
|
+
}
|
|
481
|
+
return {
|
|
482
|
+
monthsToGoal: months >= maxMonths ? null : months,
|
|
483
|
+
progressPercent: parseFloat(progressPercent.toFixed(2)),
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Calculate currency exchange.
|
|
489
|
+
* @param {number} amount - Amount to convert
|
|
490
|
+
* @param {number} exchangeRate - Exchange rate (units of target currency per 1 unit of source)
|
|
491
|
+
* @returns {number} convertedAmount
|
|
492
|
+
*/
|
|
493
|
+
function currencyConvert(amount, exchangeRate) {
|
|
494
|
+
return parseFloat((amount * exchangeRate).toFixed(2));
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
module.exports = {
|
|
498
|
+
mortgage,
|
|
499
|
+
compoundInterest,
|
|
500
|
+
simpleInterest,
|
|
501
|
+
loanPayment,
|
|
502
|
+
amortizationSchedule,
|
|
503
|
+
apr,
|
|
504
|
+
apy,
|
|
505
|
+
roi,
|
|
506
|
+
profitMargin,
|
|
507
|
+
markup,
|
|
508
|
+
breakEven,
|
|
509
|
+
futureValue,
|
|
510
|
+
presentValue,
|
|
511
|
+
ruleOf72,
|
|
512
|
+
debtSnowball,
|
|
513
|
+
debtAvalanche,
|
|
514
|
+
retirementSavings,
|
|
515
|
+
fireNumber,
|
|
516
|
+
salaryToHourly,
|
|
517
|
+
hourlyToSalary,
|
|
518
|
+
tipCalculator,
|
|
519
|
+
discountPrice,
|
|
520
|
+
inflationAdjust,
|
|
521
|
+
cashFlow,
|
|
522
|
+
depreciationStraightLine,
|
|
523
|
+
npv,
|
|
524
|
+
irr,
|
|
525
|
+
debtToIncome,
|
|
526
|
+
savingsGoal,
|
|
527
|
+
currencyConvert,
|
|
528
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "calcufly-finance",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Professional finance calculator functions - mortgage, loan, investment, retirement, tax calculations. By CalcuFly.com",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"keywords": ["finance", "calculator", "mortgage", "loan", "investment", "compound-interest", "amortization", "retirement", "tax", "roi", "apr", "apy", "debt", "savings", "budget"],
|
|
7
|
+
"author": "CalcuFly <contact@calcufly.com>",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"homepage": "https://calcufly.com",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/mansouewalks-bit2/calcufly-finance"
|
|
13
|
+
},
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/mansouewalks-bit2/calcufly-finance/issues"
|
|
16
|
+
}
|
|
17
|
+
}
|