@pyverret/ratejs 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 +16 -0
- package/README.md +220 -0
- package/dist/index.cjs +430 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +235 -0
- package/dist/index.d.ts +235 -0
- package/dist/index.js +410 -0
- package/dist/index.js.map +1 -0
- package/package.json +65 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
// src/utils/assertions.ts
|
|
2
|
+
function assertFiniteNumber(value, name) {
|
|
3
|
+
if (!Number.isFinite(value)) {
|
|
4
|
+
throw new RangeError(`${name} must be a finite number`);
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
function assertNonNegative(value, name) {
|
|
8
|
+
assertFiniteNumber(value, name);
|
|
9
|
+
if (value < 0) {
|
|
10
|
+
throw new RangeError(`${name} must be >= 0`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function assertPositive(value, name) {
|
|
14
|
+
assertFiniteNumber(value, name);
|
|
15
|
+
if (value <= 0) {
|
|
16
|
+
throw new RangeError(`${name} must be > 0`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// src/interest/cagr.ts
|
|
21
|
+
function cagr(params) {
|
|
22
|
+
const { startValue, endValue, years } = params;
|
|
23
|
+
assertPositive(startValue, "startValue");
|
|
24
|
+
assertPositive(years, "years");
|
|
25
|
+
if (endValue <= 0) return -1;
|
|
26
|
+
return (endValue / startValue) ** (1 / years) - 1;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// src/interest/compound.ts
|
|
30
|
+
function compound(params) {
|
|
31
|
+
const { principal, rate, timesPerYear, years } = params;
|
|
32
|
+
assertNonNegative(principal, "principal");
|
|
33
|
+
assertPositive(timesPerYear, "timesPerYear");
|
|
34
|
+
assertNonNegative(years, "years");
|
|
35
|
+
if (rate === 0 || years === 0) return principal;
|
|
36
|
+
const n = timesPerYear;
|
|
37
|
+
const t = years;
|
|
38
|
+
return principal * (1 + rate / n) ** (n * t);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// src/interest/effectiveAnnualRate.ts
|
|
42
|
+
function effectiveAnnualRate(params) {
|
|
43
|
+
const { nominalRate, timesPerYear } = params;
|
|
44
|
+
assertFiniteNumber(nominalRate, "nominalRate");
|
|
45
|
+
assertPositive(timesPerYear, "timesPerYear");
|
|
46
|
+
const r = nominalRate / timesPerYear;
|
|
47
|
+
return (1 + r) ** timesPerYear - 1;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// src/interest/futureValue.ts
|
|
51
|
+
function futureValue(params) {
|
|
52
|
+
const { presentValue: presentValue2, rate, timesPerYear, years } = params;
|
|
53
|
+
assertNonNegative(presentValue2, "presentValue");
|
|
54
|
+
assertPositive(timesPerYear, "timesPerYear");
|
|
55
|
+
assertNonNegative(years, "years");
|
|
56
|
+
if (rate === 0 || years === 0) return presentValue2;
|
|
57
|
+
const n = timesPerYear;
|
|
58
|
+
const t = years;
|
|
59
|
+
return presentValue2 * (1 + rate / n) ** (n * t);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/interest/inflationAdjustedAmount.ts
|
|
63
|
+
function inflationAdjustedAmount(params) {
|
|
64
|
+
const { amount, annualInflationRate, years, direction } = params;
|
|
65
|
+
assertFiniteNumber(amount, "amount");
|
|
66
|
+
assertFiniteNumber(annualInflationRate, "annualInflationRate");
|
|
67
|
+
assertNonNegative(years, "years");
|
|
68
|
+
const factor = (1 + annualInflationRate) ** years;
|
|
69
|
+
return direction === "toPast" ? amount / factor : amount * factor;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// src/interest/investmentGrowth.ts
|
|
73
|
+
function investmentGrowth(params) {
|
|
74
|
+
const {
|
|
75
|
+
initial,
|
|
76
|
+
contributionPerPeriod = 0,
|
|
77
|
+
rate,
|
|
78
|
+
timesPerYear,
|
|
79
|
+
years,
|
|
80
|
+
contributionTiming = "end"
|
|
81
|
+
} = params;
|
|
82
|
+
assertNonNegative(initial, "initial");
|
|
83
|
+
assertNonNegative(contributionPerPeriod, "contributionPerPeriod");
|
|
84
|
+
assertPositive(timesPerYear, "timesPerYear");
|
|
85
|
+
assertNonNegative(years, "years");
|
|
86
|
+
const periods = Math.round(timesPerYear * years);
|
|
87
|
+
if (periods === 0) {
|
|
88
|
+
return {
|
|
89
|
+
futureValue: initial,
|
|
90
|
+
totalContributions: 0,
|
|
91
|
+
totalInterest: 0
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
const r = rate / timesPerYear;
|
|
95
|
+
const fvInitial = rate === 0 ? initial : initial * (1 + r) ** periods;
|
|
96
|
+
let fvContrib = 0;
|
|
97
|
+
if (contributionPerPeriod !== 0) {
|
|
98
|
+
if (rate === 0) {
|
|
99
|
+
fvContrib = contributionPerPeriod * periods;
|
|
100
|
+
} else {
|
|
101
|
+
const ordinary = contributionPerPeriod * (((1 + r) ** periods - 1) / r);
|
|
102
|
+
fvContrib = contributionTiming === "begin" ? ordinary * (1 + r) : ordinary;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const futureValue2 = fvInitial + fvContrib;
|
|
106
|
+
const totalContributions = contributionPerPeriod * periods;
|
|
107
|
+
const totalInterest = futureValue2 - initial - totalContributions;
|
|
108
|
+
return { futureValue: futureValue2, totalContributions, totalInterest };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// src/interest/irr.ts
|
|
112
|
+
function npv(rate, cashFlows) {
|
|
113
|
+
let sum = 0;
|
|
114
|
+
for (let t = 0; t < cashFlows.length; t++) {
|
|
115
|
+
const cf = cashFlows[t];
|
|
116
|
+
if (cf !== void 0) sum += cf / (1 + rate) ** t;
|
|
117
|
+
}
|
|
118
|
+
return sum;
|
|
119
|
+
}
|
|
120
|
+
function npvDerivative(rate, cashFlows) {
|
|
121
|
+
let sum = 0;
|
|
122
|
+
for (let t = 1; t < cashFlows.length; t++) {
|
|
123
|
+
const cf = cashFlows[t];
|
|
124
|
+
if (cf !== void 0) sum -= t * cf / (1 + rate) ** (t + 1);
|
|
125
|
+
}
|
|
126
|
+
return sum;
|
|
127
|
+
}
|
|
128
|
+
function irr(params) {
|
|
129
|
+
const { cashFlows, guess = 0.1, maxIterations = 100 } = params;
|
|
130
|
+
if (cashFlows.length === 0) return NaN;
|
|
131
|
+
for (const cf of cashFlows) assertFiniteNumber(cf, "cashFlows[]");
|
|
132
|
+
let r = guess;
|
|
133
|
+
for (let i = 0; i < maxIterations; i++) {
|
|
134
|
+
const val = npv(r, cashFlows);
|
|
135
|
+
if (Math.abs(val) < 1e-10) return r;
|
|
136
|
+
const der = npvDerivative(r, cashFlows);
|
|
137
|
+
if (!Number.isFinite(der) || Math.abs(der) < 1e-15) break;
|
|
138
|
+
r = r - val / der;
|
|
139
|
+
if (r < -0.99) r = -0.99;
|
|
140
|
+
if (r > 10) r = 10;
|
|
141
|
+
}
|
|
142
|
+
return r;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// src/interest/paymentFromPresentValue.ts
|
|
146
|
+
function paymentFromPresentValue(params) {
|
|
147
|
+
const { presentValue: presentValue2, ratePerPeriod, periods, timing = "end" } = params;
|
|
148
|
+
assertNonNegative(presentValue2, "presentValue");
|
|
149
|
+
assertNonNegative(periods, "periods");
|
|
150
|
+
if (presentValue2 === 0 || periods === 0) return 0;
|
|
151
|
+
if (ratePerPeriod === 0) {
|
|
152
|
+
const pmt2 = presentValue2 / periods;
|
|
153
|
+
return timing === "begin" ? pmt2 / (1 + ratePerPeriod) : pmt2;
|
|
154
|
+
}
|
|
155
|
+
let pmt = ratePerPeriod * presentValue2 / (1 - (1 + ratePerPeriod) ** -periods);
|
|
156
|
+
if (timing === "begin") pmt /= 1 + ratePerPeriod;
|
|
157
|
+
return pmt;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// src/interest/periodsToReachGoal.ts
|
|
161
|
+
function periodsToReachGoal(params) {
|
|
162
|
+
const {
|
|
163
|
+
principal,
|
|
164
|
+
targetFutureValue,
|
|
165
|
+
rate,
|
|
166
|
+
timesPerYear,
|
|
167
|
+
contributionPerPeriod = 0,
|
|
168
|
+
contributionTiming = "end"
|
|
169
|
+
} = params;
|
|
170
|
+
assertNonNegative(principal, "principal");
|
|
171
|
+
assertNonNegative(targetFutureValue, "targetFutureValue");
|
|
172
|
+
assertFiniteNumber(rate, "rate");
|
|
173
|
+
assertPositive(timesPerYear, "timesPerYear");
|
|
174
|
+
assertNonNegative(contributionPerPeriod, "contributionPerPeriod");
|
|
175
|
+
if (targetFutureValue <= principal) return 0;
|
|
176
|
+
const r = rate / timesPerYear;
|
|
177
|
+
if (r <= -1) {
|
|
178
|
+
throw new RangeError("rate / timesPerYear must be > -1");
|
|
179
|
+
}
|
|
180
|
+
if (contributionPerPeriod === 0) {
|
|
181
|
+
if (principal === 0) return Number.POSITIVE_INFINITY;
|
|
182
|
+
if (rate === 0) return targetFutureValue <= principal ? 0 : Number.POSITIVE_INFINITY;
|
|
183
|
+
if (r < 0) return Number.POSITIVE_INFINITY;
|
|
184
|
+
const n = Math.log(targetFutureValue / principal) / Math.log(1 + r);
|
|
185
|
+
return Number.isFinite(n) && n >= 0 ? Math.ceil(n) : Number.POSITIVE_INFINITY;
|
|
186
|
+
}
|
|
187
|
+
if (rate === 0) {
|
|
188
|
+
const needed = targetFutureValue - principal;
|
|
189
|
+
if (needed <= 0) return 0;
|
|
190
|
+
return Math.ceil(needed / contributionPerPeriod);
|
|
191
|
+
}
|
|
192
|
+
let fv = principal;
|
|
193
|
+
let periods = 0;
|
|
194
|
+
const maxPeriods = 1e4;
|
|
195
|
+
while (fv < targetFutureValue && periods < maxPeriods) {
|
|
196
|
+
if (contributionTiming === "begin") fv += contributionPerPeriod;
|
|
197
|
+
fv = fv * (1 + r) + (contributionTiming === "end" ? contributionPerPeriod : 0);
|
|
198
|
+
periods++;
|
|
199
|
+
}
|
|
200
|
+
return fv >= targetFutureValue ? periods : Number.POSITIVE_INFINITY;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// src/interest/presentValue.ts
|
|
204
|
+
function presentValue(params) {
|
|
205
|
+
const { futureValue: fv, rate, timesPerYear, years } = params;
|
|
206
|
+
assertNonNegative(fv, "futureValue");
|
|
207
|
+
assertPositive(timesPerYear, "timesPerYear");
|
|
208
|
+
assertNonNegative(years, "years");
|
|
209
|
+
if (rate === 0 || years === 0) return fv;
|
|
210
|
+
const n = timesPerYear;
|
|
211
|
+
const t = years;
|
|
212
|
+
return fv / (1 + rate / n) ** (n * t);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// src/interest/presentValueOfAnnuity.ts
|
|
216
|
+
function presentValueOfAnnuity(params) {
|
|
217
|
+
const { paymentPerPeriod, ratePerPeriod, periods, timing = "end" } = params;
|
|
218
|
+
assertNonNegative(paymentPerPeriod, "paymentPerPeriod");
|
|
219
|
+
assertNonNegative(periods, "periods");
|
|
220
|
+
if (periods === 0) return 0;
|
|
221
|
+
if (ratePerPeriod === 0) {
|
|
222
|
+
const pv2 = paymentPerPeriod * periods;
|
|
223
|
+
return timing === "begin" ? pv2 * (1 + ratePerPeriod) : pv2;
|
|
224
|
+
}
|
|
225
|
+
const pv = paymentPerPeriod * (1 - (1 + ratePerPeriod) ** -periods) / ratePerPeriod;
|
|
226
|
+
return timing === "begin" ? pv * (1 + ratePerPeriod) : pv;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// src/interest/rateToReachGoal.ts
|
|
230
|
+
function rateToReachGoal(params) {
|
|
231
|
+
const {
|
|
232
|
+
principal,
|
|
233
|
+
targetFutureValue,
|
|
234
|
+
periods,
|
|
235
|
+
contributionPerPeriod = 0,
|
|
236
|
+
contributionTiming = "end"
|
|
237
|
+
} = params;
|
|
238
|
+
assertNonNegative(principal, "principal");
|
|
239
|
+
assertNonNegative(targetFutureValue, "targetFutureValue");
|
|
240
|
+
assertPositive(periods, "periods");
|
|
241
|
+
assertNonNegative(contributionPerPeriod, "contributionPerPeriod");
|
|
242
|
+
if (periods === 0) return 0;
|
|
243
|
+
if (targetFutureValue <= principal && contributionPerPeriod === 0) return 0;
|
|
244
|
+
if (contributionPerPeriod === 0) {
|
|
245
|
+
if (targetFutureValue <= principal) return 0;
|
|
246
|
+
return (targetFutureValue / principal) ** (1 / periods) - 1;
|
|
247
|
+
}
|
|
248
|
+
const dueFactor = contributionTiming === "begin" ? 1 : 0;
|
|
249
|
+
let r = (targetFutureValue / (principal + contributionPerPeriod * periods)) ** (1 / periods) - 1;
|
|
250
|
+
if (r <= -1) r = 0.01;
|
|
251
|
+
for (let i = 0; i < 100; i++) {
|
|
252
|
+
const onePlusR = 1 + r;
|
|
253
|
+
const onePlusRN = onePlusR ** periods;
|
|
254
|
+
const fvLump = principal * onePlusRN;
|
|
255
|
+
const annuity = contributionPerPeriod * ((onePlusRN - 1) / (r || 1e-14)) * (1 + dueFactor * r);
|
|
256
|
+
const fv = fvLump + annuity;
|
|
257
|
+
const err = fv - targetFutureValue;
|
|
258
|
+
if (Math.abs(err) < 1e-10) return r;
|
|
259
|
+
const dr = r * 1e-6 || 1e-10;
|
|
260
|
+
const r2 = r + dr;
|
|
261
|
+
const onePlusR2 = 1 + r2;
|
|
262
|
+
const onePlusR2N = onePlusR2 ** periods;
|
|
263
|
+
const fv2 = principal * onePlusR2N + contributionPerPeriod * ((onePlusR2N - 1) / r2) * (1 + dueFactor * r2);
|
|
264
|
+
const dFvDr = (fv2 - fv) / dr;
|
|
265
|
+
r = r - err / dFvDr;
|
|
266
|
+
if (r <= -1) r = 0.01;
|
|
267
|
+
if (r > 10) r = 10;
|
|
268
|
+
}
|
|
269
|
+
return r;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// src/interest/realReturn.ts
|
|
273
|
+
function realReturn(params) {
|
|
274
|
+
const { nominalReturn, inflationRate } = params;
|
|
275
|
+
assertFiniteNumber(nominalReturn, "nominalReturn");
|
|
276
|
+
assertFiniteNumber(inflationRate, "inflationRate");
|
|
277
|
+
if (inflationRate <= -1) return NaN;
|
|
278
|
+
return (1 + nominalReturn) / (1 + inflationRate) - 1;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// src/interest/ruleOf72.ts
|
|
282
|
+
function ruleOf72(params) {
|
|
283
|
+
const { rate, constant = 72 } = params;
|
|
284
|
+
assertFiniteNumber(rate, "rate");
|
|
285
|
+
assertPositive(rate, "rate");
|
|
286
|
+
return constant / 100 / rate;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// src/loans/loanPayment.ts
|
|
290
|
+
function loanPayment(params) {
|
|
291
|
+
const { principal, annualRate, paymentsPerYear, years } = params;
|
|
292
|
+
assertNonNegative(principal, "principal");
|
|
293
|
+
assertPositive(paymentsPerYear, "paymentsPerYear");
|
|
294
|
+
assertNonNegative(years, "years");
|
|
295
|
+
const n = Math.round(paymentsPerYear * years);
|
|
296
|
+
if (n === 0) return 0;
|
|
297
|
+
const r = annualRate / paymentsPerYear;
|
|
298
|
+
if (r === 0) return principal / n;
|
|
299
|
+
return r * principal / (1 - (1 + r) ** -n);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// src/loans/amortizationSchedule.ts
|
|
303
|
+
function amortizationSchedule(params) {
|
|
304
|
+
const { principal, annualRate, paymentsPerYear, years, extraPaymentPerPeriod = 0 } = params;
|
|
305
|
+
assertNonNegative(principal, "principal");
|
|
306
|
+
assertPositive(paymentsPerYear, "paymentsPerYear");
|
|
307
|
+
assertNonNegative(years, "years");
|
|
308
|
+
assertNonNegative(extraPaymentPerPeriod, "extraPaymentPerPeriod");
|
|
309
|
+
const scheduledN = Math.round(paymentsPerYear * years);
|
|
310
|
+
if (scheduledN === 0 || principal === 0) {
|
|
311
|
+
return { paymentPerPeriod: 0, schedule: [], totalPaid: 0, totalInterest: 0 };
|
|
312
|
+
}
|
|
313
|
+
const basePayment = loanPayment({ principal, annualRate, paymentsPerYear, years });
|
|
314
|
+
const r = annualRate / paymentsPerYear;
|
|
315
|
+
const schedule = [];
|
|
316
|
+
let balance = principal;
|
|
317
|
+
let totalPaid = 0;
|
|
318
|
+
let totalInterest = 0;
|
|
319
|
+
for (let period = 1; period <= scheduledN && balance > 0; period++) {
|
|
320
|
+
const interestPayment = r === 0 ? 0 : balance * r;
|
|
321
|
+
let payment = basePayment + extraPaymentPerPeriod;
|
|
322
|
+
let principalPayment = payment - interestPayment;
|
|
323
|
+
if (principalPayment > balance) {
|
|
324
|
+
principalPayment = balance;
|
|
325
|
+
payment = principalPayment + interestPayment;
|
|
326
|
+
}
|
|
327
|
+
balance = balance - principalPayment;
|
|
328
|
+
if (Math.abs(balance) < 1e-12) balance = 0;
|
|
329
|
+
totalPaid += payment;
|
|
330
|
+
totalInterest += interestPayment;
|
|
331
|
+
schedule.push({
|
|
332
|
+
period,
|
|
333
|
+
payment,
|
|
334
|
+
principalPayment,
|
|
335
|
+
interestPayment,
|
|
336
|
+
balance
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
return {
|
|
340
|
+
paymentPerPeriod: basePayment,
|
|
341
|
+
schedule,
|
|
342
|
+
totalPaid,
|
|
343
|
+
totalInterest
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// src/loans/payoffPeriodWithExtra.ts
|
|
348
|
+
function payoffPeriodWithExtra(params) {
|
|
349
|
+
const { principal, annualRate, paymentsPerYear, basePaymentPerPeriod, extraPaymentPerPeriod } = params;
|
|
350
|
+
assertNonNegative(principal, "principal");
|
|
351
|
+
assertFiniteNumber(annualRate, "annualRate");
|
|
352
|
+
assertPositive(paymentsPerYear, "paymentsPerYear");
|
|
353
|
+
assertNonNegative(basePaymentPerPeriod, "basePaymentPerPeriod");
|
|
354
|
+
assertNonNegative(extraPaymentPerPeriod, "extraPaymentPerPeriod");
|
|
355
|
+
if (principal <= 0) return 0;
|
|
356
|
+
const payment = basePaymentPerPeriod + extraPaymentPerPeriod;
|
|
357
|
+
if (payment <= 0) return Number.POSITIVE_INFINITY;
|
|
358
|
+
const r = annualRate / paymentsPerYear;
|
|
359
|
+
let balance = principal;
|
|
360
|
+
let period = 0;
|
|
361
|
+
while (balance > 1e-12 && period < 1e5) {
|
|
362
|
+
const interest = r > 0 ? balance * r : 0;
|
|
363
|
+
if (payment <= interest) return Number.POSITIVE_INFINITY;
|
|
364
|
+
const principalPaid = Math.min(payment - interest, balance);
|
|
365
|
+
balance -= principalPaid;
|
|
366
|
+
period++;
|
|
367
|
+
}
|
|
368
|
+
return balance <= 1e-12 ? period : Number.POSITIVE_INFINITY;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// src/loans/remainingBalance.ts
|
|
372
|
+
function remainingBalance(params) {
|
|
373
|
+
const { principal, annualRate, paymentsPerYear, years, afterPeriodNumber } = params;
|
|
374
|
+
assertNonNegative(principal, "principal");
|
|
375
|
+
assertPositive(paymentsPerYear, "paymentsPerYear");
|
|
376
|
+
assertNonNegative(years, "years");
|
|
377
|
+
assertNonNegative(afterPeriodNumber, "afterPeriodNumber");
|
|
378
|
+
const n = Math.round(paymentsPerYear * years);
|
|
379
|
+
if (n === 0 || afterPeriodNumber >= n) return 0;
|
|
380
|
+
if (afterPeriodNumber <= 0) return principal;
|
|
381
|
+
const payment = loanPayment({ principal, annualRate, paymentsPerYear, years });
|
|
382
|
+
const r = annualRate / paymentsPerYear;
|
|
383
|
+
const k = afterPeriodNumber;
|
|
384
|
+
if (r === 0) return Math.max(0, principal - payment * k);
|
|
385
|
+
const balance = principal * (1 + r) ** k - payment * (((1 + r) ** k - 1) / r);
|
|
386
|
+
return Math.max(0, balance);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// src/utils/roundToCurrency.ts
|
|
390
|
+
function roundToCurrency(params) {
|
|
391
|
+
const { value, decimals = 2, mode = "half-up" } = params;
|
|
392
|
+
assertFiniteNumber(value, "value");
|
|
393
|
+
const d = Math.max(0, Math.floor(decimals));
|
|
394
|
+
const factor = 10 ** d;
|
|
395
|
+
if (mode === "half-even") {
|
|
396
|
+
const scaled = value * factor;
|
|
397
|
+
const rounded = Math.round(scaled);
|
|
398
|
+
const remainder = Math.abs(scaled - rounded);
|
|
399
|
+
if (remainder === 0.5) {
|
|
400
|
+
const down = Math.floor(scaled);
|
|
401
|
+
return (down % 2 === 0 ? down : down + 1) / factor;
|
|
402
|
+
}
|
|
403
|
+
return rounded / factor;
|
|
404
|
+
}
|
|
405
|
+
return Math.round(value * factor) / factor;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
export { amortizationSchedule, cagr, compound, effectiveAnnualRate, futureValue, inflationAdjustedAmount, investmentGrowth, irr, loanPayment, paymentFromPresentValue, payoffPeriodWithExtra, periodsToReachGoal, presentValue, presentValueOfAnnuity, rateToReachGoal, realReturn, remainingBalance, roundToCurrency, ruleOf72 };
|
|
409
|
+
//# sourceMappingURL=index.js.map
|
|
410
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/assertions.ts","../src/interest/cagr.ts","../src/interest/compound.ts","../src/interest/effectiveAnnualRate.ts","../src/interest/futureValue.ts","../src/interest/inflationAdjustedAmount.ts","../src/interest/investmentGrowth.ts","../src/interest/irr.ts","../src/interest/paymentFromPresentValue.ts","../src/interest/periodsToReachGoal.ts","../src/interest/presentValue.ts","../src/interest/presentValueOfAnnuity.ts","../src/interest/rateToReachGoal.ts","../src/interest/realReturn.ts","../src/interest/ruleOf72.ts","../src/loans/loanPayment.ts","../src/loans/amortizationSchedule.ts","../src/loans/payoffPeriodWithExtra.ts","../src/loans/remainingBalance.ts","../src/utils/roundToCurrency.ts"],"names":["presentValue","futureValue","pmt","pv"],"mappings":"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","file":"index.js","sourcesContent":["export function assertFiniteNumber(value: number, name: string): void {\n if (!Number.isFinite(value)) {\n throw new RangeError(`${name} must be a finite number`);\n }\n}\n\nexport function assertNonNegative(value: number, name: string): void {\n assertFiniteNumber(value, name);\n if (value < 0) {\n throw new RangeError(`${name} must be >= 0`);\n }\n}\n\nexport function assertPositive(value: number, name: string): void {\n assertFiniteNumber(value, name);\n if (value <= 0) {\n throw new RangeError(`${name} must be > 0`);\n }\n}\n","import { assertPositive } from \"../utils/assertions.js\";\n\nexport type CagrParams = {\n startValue: number;\n endValue: number;\n years: number;\n};\n\n/**\n * Compound annual growth rate (CAGR) between start and end value over the given years.\n * CAGR = (endValue/startValue)^(1/years) - 1\n */\nexport function cagr(params: CagrParams): number {\n const { startValue, endValue, years } = params;\n assertPositive(startValue, \"startValue\");\n assertPositive(years, \"years\");\n if (endValue <= 0) return -1;\n return (endValue / startValue) ** (1 / years) - 1;\n}\n","import { assertNonNegative, assertPositive } from \"../utils/assertions.js\";\n\nexport type CompoundParams = {\n principal: number;\n rate: number;\n timesPerYear: number;\n years: number;\n};\n\n/**\n * Computes compound growth using nominal annual `rate` compounded `timesPerYear`.\n * Returns the final amount (principal + interest).\n */\nexport function compound(params: CompoundParams): number {\n const { principal, rate, timesPerYear, years } = params;\n assertNonNegative(principal, \"principal\");\n assertPositive(timesPerYear, \"timesPerYear\");\n assertNonNegative(years, \"years\");\n\n if (rate === 0 || years === 0) return principal;\n\n const n = timesPerYear;\n const t = years;\n return principal * (1 + rate / n) ** (n * t);\n}\n","import { assertFiniteNumber, assertPositive } from \"../utils/assertions.js\";\n\nexport type EffectiveAnnualRateParams = {\n nominalRate: number;\n timesPerYear: number;\n};\n\n/**\n * Converts a nominal annual rate to effective annual rate (EAR).\n * EAR = (1 + nominalRate/timesPerYear)^timesPerYear - 1\n */\nexport function effectiveAnnualRate(params: EffectiveAnnualRateParams): number {\n const { nominalRate, timesPerYear } = params;\n assertFiniteNumber(nominalRate, \"nominalRate\");\n assertPositive(timesPerYear, \"timesPerYear\");\n\n const r = nominalRate / timesPerYear;\n return (1 + r) ** timesPerYear - 1;\n}\n","import { assertNonNegative, assertPositive } from \"../utils/assertions.js\";\n\nexport type FutureValueParams = {\n presentValue: number;\n rate: number;\n timesPerYear: number;\n years: number;\n};\n\n/**\n * Future value of a lump sum using nominal annual `rate` compounded `timesPerYear`.\n */\nexport function futureValue(params: FutureValueParams): number {\n const { presentValue, rate, timesPerYear, years } = params;\n assertNonNegative(presentValue, \"presentValue\");\n assertPositive(timesPerYear, \"timesPerYear\");\n assertNonNegative(years, \"years\");\n\n if (rate === 0 || years === 0) return presentValue;\n\n const n = timesPerYear;\n const t = years;\n return presentValue * (1 + rate / n) ** (n * t);\n}\n","import { assertFiniteNumber, assertNonNegative } from \"../utils/assertions.js\";\n\nexport type InflationAdjustedAmountParams = {\n amount: number;\n annualInflationRate: number;\n years: number;\n direction: \"toPast\" | \"toFuture\";\n};\n\n/**\n * Purchasing power across time. toPast: amount/(1+rate)^years. toFuture: amount*(1+rate)^years.\n */\nexport function inflationAdjustedAmount(params: InflationAdjustedAmountParams): number {\n const { amount, annualInflationRate, years, direction } = params;\n assertFiniteNumber(amount, \"amount\");\n assertFiniteNumber(annualInflationRate, \"annualInflationRate\");\n assertNonNegative(years, \"years\");\n const factor = (1 + annualInflationRate) ** years;\n return direction === \"toPast\" ? amount / factor : amount * factor;\n}\n","import { assertNonNegative, assertPositive } from \"../utils/assertions.js\";\n\nexport type InvestmentGrowthParams = {\n initial: number;\n contributionPerPeriod?: number;\n rate: number;\n timesPerYear: number;\n years: number;\n contributionTiming?: \"end\" | \"begin\";\n};\n\nexport type InvestmentGrowthResult = {\n futureValue: number;\n totalContributions: number;\n totalInterest: number;\n};\n\n/**\n * Future value of an investment with optional periodic contributions.\n *\n * - `rate` is nominal annual rate (e.g. 0.05 for 5%)\n * - contributions occur each compounding period (`timesPerYear`)\n */\nexport function investmentGrowth(params: InvestmentGrowthParams): InvestmentGrowthResult {\n const {\n initial,\n contributionPerPeriod = 0,\n rate,\n timesPerYear,\n years,\n contributionTiming = \"end\",\n } = params;\n\n assertNonNegative(initial, \"initial\");\n assertNonNegative(contributionPerPeriod, \"contributionPerPeriod\");\n assertPositive(timesPerYear, \"timesPerYear\");\n assertNonNegative(years, \"years\");\n\n const periods = Math.round(timesPerYear * years);\n if (periods === 0) {\n return {\n futureValue: initial,\n totalContributions: 0,\n totalInterest: 0,\n };\n }\n\n const r = rate / timesPerYear;\n\n // Lump sum growth\n const fvInitial = rate === 0 ? initial : initial * (1 + r) ** periods;\n\n // Annuity future value (ordinary vs due)\n let fvContrib = 0;\n if (contributionPerPeriod !== 0) {\n if (rate === 0) {\n fvContrib = contributionPerPeriod * periods;\n } else {\n const ordinary = contributionPerPeriod * (((1 + r) ** periods - 1) / r);\n fvContrib = contributionTiming === \"begin\" ? ordinary * (1 + r) : ordinary;\n }\n }\n\n const futureValue = fvInitial + fvContrib;\n const totalContributions = contributionPerPeriod * periods;\n const totalInterest = futureValue - initial - totalContributions;\n\n return { futureValue, totalContributions, totalInterest };\n}\n","import { assertFiniteNumber } from \"../utils/assertions.js\";\n\nexport type IrrParams = {\n cashFlows: number[];\n guess?: number;\n maxIterations?: number;\n};\n\nfunction npv(rate: number, cashFlows: number[]): number {\n let sum = 0;\n for (let t = 0; t < cashFlows.length; t++) {\n const cf = cashFlows[t];\n if (cf !== undefined) sum += cf / (1 + rate) ** t;\n }\n return sum;\n}\n\nfunction npvDerivative(rate: number, cashFlows: number[]): number {\n let sum = 0;\n for (let t = 1; t < cashFlows.length; t++) {\n const cf = cashFlows[t];\n if (cf !== undefined) sum -= (t * cf) / (1 + rate) ** (t + 1);\n }\n return sum;\n}\n\nexport function irr(params: IrrParams): number {\n const { cashFlows, guess = 0.1, maxIterations = 100 } = params;\n if (cashFlows.length === 0) return NaN;\n for (const cf of cashFlows) assertFiniteNumber(cf, \"cashFlows[]\");\n let r = guess;\n for (let i = 0; i < maxIterations; i++) {\n const val = npv(r, cashFlows);\n if (Math.abs(val) < 1e-10) return r;\n const der = npvDerivative(r, cashFlows);\n if (!Number.isFinite(der) || Math.abs(der) < 1e-15) break;\n r = r - val / der;\n if (r < -0.99) r = -0.99;\n if (r > 10) r = 10;\n }\n return r;\n}\n","import { assertNonNegative } from \"../utils/assertions.js\";\n\nexport type PaymentFromPresentValueParams = {\n presentValue: number;\n ratePerPeriod: number;\n periods: number;\n /** Payment at start of each period (annuity due). Default end (ordinary). */\n timing?: \"end\" | \"begin\";\n};\n\n/**\n * Periodic payment required to pay off a present value (e.g. loan) over periods.\n * PMT = PV * r / (1 - (1+r)^-n). For due: divide by (1+r).\n */\nexport function paymentFromPresentValue(params: PaymentFromPresentValueParams): number {\n const { presentValue, ratePerPeriod, periods, timing = \"end\" } = params;\n assertNonNegative(presentValue, \"presentValue\");\n assertNonNegative(periods, \"periods\");\n\n if (presentValue === 0 || periods === 0) return 0;\n if (ratePerPeriod === 0) {\n const pmt = presentValue / periods;\n return timing === \"begin\" ? pmt / (1 + ratePerPeriod) : pmt;\n }\n let pmt = (ratePerPeriod * presentValue) / (1 - (1 + ratePerPeriod) ** -periods);\n if (timing === \"begin\") pmt /= 1 + ratePerPeriod;\n return pmt;\n}\n","import { assertFiniteNumber, assertNonNegative, assertPositive } from \"../utils/assertions.js\";\n\nexport type PeriodsToReachGoalParams = {\n principal: number;\n targetFutureValue: number;\n rate: number;\n timesPerYear: number;\n contributionPerPeriod?: number;\n contributionTiming?: \"end\" | \"begin\";\n};\n\n/**\n * Number of compounding periods until future value reaches the goal.\n * With no contributions: closed form. With contributions: iterative.\n */\nexport function periodsToReachGoal(params: PeriodsToReachGoalParams): number {\n const {\n principal,\n targetFutureValue,\n rate,\n timesPerYear,\n contributionPerPeriod = 0,\n contributionTiming = \"end\",\n } = params;\n\n assertNonNegative(principal, \"principal\");\n assertNonNegative(targetFutureValue, \"targetFutureValue\");\n assertFiniteNumber(rate, \"rate\");\n assertPositive(timesPerYear, \"timesPerYear\");\n assertNonNegative(contributionPerPeriod, \"contributionPerPeriod\");\n\n if (targetFutureValue <= principal) return 0;\n\n const r = rate / timesPerYear;\n if (r <= -1) {\n throw new RangeError(\"rate / timesPerYear must be > -1\");\n }\n\n if (contributionPerPeriod === 0) {\n if (principal === 0) return Number.POSITIVE_INFINITY;\n if (rate === 0) return targetFutureValue <= principal ? 0 : Number.POSITIVE_INFINITY;\n if (r < 0) return Number.POSITIVE_INFINITY;\n const n = Math.log(targetFutureValue / principal) / Math.log(1 + r);\n return Number.isFinite(n) && n >= 0 ? Math.ceil(n) : Number.POSITIVE_INFINITY;\n }\n\n if (rate === 0) {\n const needed = targetFutureValue - principal;\n if (needed <= 0) return 0;\n return Math.ceil(needed / contributionPerPeriod);\n }\n\n // With contributions: iterate until FV >= target\n let fv = principal;\n let periods = 0;\n const maxPeriods = 10000;\n while (fv < targetFutureValue && periods < maxPeriods) {\n if (contributionTiming === \"begin\") fv += contributionPerPeriod;\n fv = fv * (1 + r) + (contributionTiming === \"end\" ? contributionPerPeriod : 0);\n periods++;\n }\n return fv >= targetFutureValue ? periods : Number.POSITIVE_INFINITY;\n}\n","import { assertNonNegative, assertPositive } from \"../utils/assertions.js\";\n\nexport type PresentValueParams = {\n futureValue: number;\n rate: number;\n timesPerYear: number;\n years: number;\n};\n\n/**\n * Present value of a lump sum using nominal annual `rate` compounded `timesPerYear`.\n */\nexport function presentValue(params: PresentValueParams): number {\n const { futureValue: fv, rate, timesPerYear, years } = params;\n assertNonNegative(fv, \"futureValue\");\n assertPositive(timesPerYear, \"timesPerYear\");\n assertNonNegative(years, \"years\");\n\n if (rate === 0 || years === 0) return fv;\n\n const n = timesPerYear;\n const t = years;\n return fv / (1 + rate / n) ** (n * t);\n}\n","import { assertNonNegative } from \"../utils/assertions.js\";\n\nexport type PresentValueOfAnnuityParams = {\n paymentPerPeriod: number;\n ratePerPeriod: number;\n periods: number;\n timing?: \"end\" | \"begin\";\n};\n\n/**\n * Present value of an annuity. PV = PMT * (1 - (1+r)^-n) / r. Due: multiply by (1+r).\n */\nexport function presentValueOfAnnuity(params: PresentValueOfAnnuityParams): number {\n const { paymentPerPeriod, ratePerPeriod, periods, timing = \"end\" } = params;\n assertNonNegative(paymentPerPeriod, \"paymentPerPeriod\");\n assertNonNegative(periods, \"periods\");\n\n if (periods === 0) return 0;\n if (ratePerPeriod === 0) {\n const pv = paymentPerPeriod * periods;\n return timing === \"begin\" ? pv * (1 + ratePerPeriod) : pv;\n }\n const pv = (paymentPerPeriod * (1 - (1 + ratePerPeriod) ** -periods)) / ratePerPeriod;\n return timing === \"begin\" ? pv * (1 + ratePerPeriod) : pv;\n}\n","import { assertNonNegative, assertPositive } from \"../utils/assertions.js\";\n\nexport type RateToReachGoalParams = {\n principal: number;\n targetFutureValue: number;\n periods: number;\n contributionPerPeriod?: number;\n contributionTiming?: \"end\" | \"begin\";\n};\n\n/**\n * Rate per period required to reach target future value in given periods.\n * Uses Newton-Raphson when contributions are present; closed form for lump sum.\n */\nexport function rateToReachGoal(params: RateToReachGoalParams): number {\n const {\n principal,\n targetFutureValue,\n periods,\n contributionPerPeriod = 0,\n contributionTiming = \"end\",\n } = params;\n\n assertNonNegative(principal, \"principal\");\n assertNonNegative(targetFutureValue, \"targetFutureValue\");\n assertPositive(periods, \"periods\");\n assertNonNegative(contributionPerPeriod, \"contributionPerPeriod\");\n\n if (periods === 0) return 0;\n if (targetFutureValue <= principal && contributionPerPeriod === 0) return 0;\n\n if (contributionPerPeriod === 0) {\n if (targetFutureValue <= principal) return 0;\n return (targetFutureValue / principal) ** (1 / periods) - 1;\n }\n\n // FV = principal*(1+r)^n + PMT * ((1+r)^n - 1)/r (ordinary) or PMT * (1+r) * ((1+r)^n - 1)/r (due)\n // Solve for r with Newton-Raphson\n const dueFactor = contributionTiming === \"begin\" ? 1 : 0;\n let r = (targetFutureValue / (principal + contributionPerPeriod * periods)) ** (1 / periods) - 1;\n if (r <= -1) r = 0.01;\n\n for (let i = 0; i < 100; i++) {\n const onePlusR = 1 + r;\n const onePlusRN = onePlusR ** periods;\n const fvLump = principal * onePlusRN;\n const annuity = contributionPerPeriod * ((onePlusRN - 1) / (r || 1e-14)) * (1 + dueFactor * r);\n const fv = fvLump + annuity;\n const err = fv - targetFutureValue;\n if (Math.abs(err) < 1e-10) return r;\n // Derivative approximation\n const dr = r * 1e-6 || 1e-10;\n const r2 = r + dr;\n const onePlusR2 = 1 + r2;\n const onePlusR2N = onePlusR2 ** periods;\n const fv2 =\n principal * onePlusR2N +\n contributionPerPeriod * ((onePlusR2N - 1) / r2) * (1 + dueFactor * r2);\n const dFvDr = (fv2 - fv) / dr;\n r = r - err / dFvDr;\n if (r <= -1) r = 0.01;\n if (r > 10) r = 10;\n }\n return r;\n}\n","import { assertFiniteNumber } from \"../utils/assertions.js\";\n\nexport type RealReturnParams = { nominalReturn: number; inflationRate: number };\n\nexport function realReturn(params: RealReturnParams): number {\n const { nominalReturn, inflationRate } = params;\n assertFiniteNumber(nominalReturn, \"nominalReturn\");\n assertFiniteNumber(inflationRate, \"inflationRate\");\n if (inflationRate <= -1) return NaN;\n return (1 + nominalReturn) / (1 + inflationRate) - 1;\n}\n","import { assertFiniteNumber, assertPositive } from \"../utils/assertions.js\";\n\nexport type RuleOf72Params = {\n rate: number;\n /** Use 69 for continuous-ish approximation (e.g. daily compounding). Default 72. */\n constant?: 72 | 69;\n};\n\n/**\n * Approximate years to double a lump sum at the given annual rate.\n * rate is decimal (e.g. 0.07 for 7%). Uses rule of 72 by default, or 69 if specified.\n */\nexport function ruleOf72(params: RuleOf72Params): number {\n const { rate, constant = 72 } = params;\n assertFiniteNumber(rate, \"rate\");\n assertPositive(rate, \"rate\");\n return constant / 100 / rate;\n}\n","import { assertNonNegative, assertPositive } from \"../utils/assertions.js\";\n\nexport type LoanPaymentParams = {\n principal: number;\n annualRate: number;\n paymentsPerYear: number;\n years: number;\n};\n\n/**\n * Fixed periodic payment for an amortizing loan.\n *\n * - `annualRate` is nominal annual rate (e.g. 0.06 for 6%)\n * - payment returned is per period (`paymentsPerYear`)\n */\nexport function loanPayment(params: LoanPaymentParams): number {\n const { principal, annualRate, paymentsPerYear, years } = params;\n assertNonNegative(principal, \"principal\");\n assertPositive(paymentsPerYear, \"paymentsPerYear\");\n assertNonNegative(years, \"years\");\n\n const n = Math.round(paymentsPerYear * years);\n if (n === 0) return 0;\n\n const r = annualRate / paymentsPerYear;\n if (r === 0) return principal / n;\n\n return (r * principal) / (1 - (1 + r) ** -n);\n}\n","import { assertNonNegative, assertPositive } from \"../utils/assertions.js\";\nimport { loanPayment } from \"./loanPayment.js\";\n\nexport type AmortizationScheduleParams = {\n principal: number;\n annualRate: number;\n paymentsPerYear: number;\n years: number;\n extraPaymentPerPeriod?: number;\n};\n\nexport type AmortizationRow = {\n period: number;\n payment: number;\n principalPayment: number;\n interestPayment: number;\n balance: number;\n};\n\nexport type AmortizationScheduleResult = {\n paymentPerPeriod: number;\n schedule: AmortizationRow[];\n totalPaid: number;\n totalInterest: number;\n};\n\nexport function amortizationSchedule(\n params: AmortizationScheduleParams,\n): AmortizationScheduleResult {\n const { principal, annualRate, paymentsPerYear, years, extraPaymentPerPeriod = 0 } = params;\n assertNonNegative(principal, \"principal\");\n assertPositive(paymentsPerYear, \"paymentsPerYear\");\n assertNonNegative(years, \"years\");\n assertNonNegative(extraPaymentPerPeriod, \"extraPaymentPerPeriod\");\n\n const scheduledN = Math.round(paymentsPerYear * years);\n if (scheduledN === 0 || principal === 0) {\n return { paymentPerPeriod: 0, schedule: [], totalPaid: 0, totalInterest: 0 };\n }\n\n const basePayment = loanPayment({ principal, annualRate, paymentsPerYear, years });\n const r = annualRate / paymentsPerYear;\n\n const schedule: AmortizationRow[] = [];\n let balance = principal;\n let totalPaid = 0;\n let totalInterest = 0;\n\n for (let period = 1; period <= scheduledN && balance > 0; period++) {\n const interestPayment = r === 0 ? 0 : balance * r;\n let payment = basePayment + extraPaymentPerPeriod;\n let principalPayment = payment - interestPayment;\n\n if (principalPayment > balance) {\n principalPayment = balance;\n payment = principalPayment + interestPayment;\n }\n\n balance = balance - principalPayment;\n if (Math.abs(balance) < 1e-12) balance = 0;\n\n totalPaid += payment;\n totalInterest += interestPayment;\n\n schedule.push({\n period,\n payment,\n principalPayment,\n interestPayment,\n balance,\n });\n }\n\n return {\n paymentPerPeriod: basePayment,\n schedule,\n totalPaid,\n totalInterest,\n };\n}\n","import { assertFiniteNumber, assertNonNegative, assertPositive } from \"../utils/assertions.js\";\n\nexport type PayoffPeriodWithExtraParams = {\n principal: number;\n annualRate: number;\n paymentsPerYear: number;\n basePaymentPerPeriod: number;\n extraPaymentPerPeriod: number;\n};\n\n/**\n * Number of periods until the loan is paid off with the given base + extra payment.\n */\nexport function payoffPeriodWithExtra(params: PayoffPeriodWithExtraParams): number {\n const { principal, annualRate, paymentsPerYear, basePaymentPerPeriod, extraPaymentPerPeriod } =\n params;\n\n assertNonNegative(principal, \"principal\");\n assertFiniteNumber(annualRate, \"annualRate\");\n assertPositive(paymentsPerYear, \"paymentsPerYear\");\n assertNonNegative(basePaymentPerPeriod, \"basePaymentPerPeriod\");\n assertNonNegative(extraPaymentPerPeriod, \"extraPaymentPerPeriod\");\n\n if (principal <= 0) return 0;\n const payment = basePaymentPerPeriod + extraPaymentPerPeriod;\n if (payment <= 0) return Number.POSITIVE_INFINITY;\n\n const r = annualRate / paymentsPerYear;\n let balance = principal;\n let period = 0;\n\n while (balance > 1e-12 && period < 100000) {\n const interest = r > 0 ? balance * r : 0;\n if (payment <= interest) return Number.POSITIVE_INFINITY;\n const principalPaid = Math.min(payment - interest, balance);\n balance -= principalPaid;\n period++;\n }\n return balance <= 1e-12 ? period : Number.POSITIVE_INFINITY;\n}\n","import { assertNonNegative, assertPositive } from \"../utils/assertions.js\";\nimport { loanPayment } from \"./loanPayment.js\";\n\nexport type RemainingBalanceParams = {\n principal: number;\n annualRate: number;\n paymentsPerYear: number;\n years: number;\n afterPeriodNumber: number;\n};\n\n/**\n * Remaining balance after a given number of payments on an amortizing loan.\n */\nexport function remainingBalance(params: RemainingBalanceParams): number {\n const { principal, annualRate, paymentsPerYear, years, afterPeriodNumber } = params;\n assertNonNegative(principal, \"principal\");\n assertPositive(paymentsPerYear, \"paymentsPerYear\");\n assertNonNegative(years, \"years\");\n assertNonNegative(afterPeriodNumber, \"afterPeriodNumber\");\n\n const n = Math.round(paymentsPerYear * years);\n if (n === 0 || afterPeriodNumber >= n) return 0;\n if (afterPeriodNumber <= 0) return principal;\n\n const payment = loanPayment({ principal, annualRate, paymentsPerYear, years });\n const r = annualRate / paymentsPerYear;\n const k = afterPeriodNumber;\n\n if (r === 0) return Math.max(0, principal - payment * k);\n const balance = principal * (1 + r) ** k - payment * (((1 + r) ** k - 1) / r);\n return Math.max(0, balance);\n}\n","import { assertFiniteNumber } from \"./assertions.js\";\n\nexport type RoundToCurrencyParams = {\n value: number;\n /** Decimal places. Default 2. */\n decimals?: number;\n /** 'half-up': 0.5 rounds up. 'half-even': banker's rounding. Default 'half-up'. */\n mode?: \"half-up\" | \"half-even\";\n};\n\n/**\n * Rounds a number to currency (default 2 decimals). half-up: 2.125 -> 2.13. half-even: 2.125 -> 2.12.\n */\nexport function roundToCurrency(params: RoundToCurrencyParams): number {\n const { value, decimals = 2, mode = \"half-up\" } = params;\n assertFiniteNumber(value, \"value\");\n const d = Math.max(0, Math.floor(decimals));\n const factor = 10 ** d;\n if (mode === \"half-even\") {\n const scaled = value * factor;\n const rounded = Math.round(scaled);\n const remainder = Math.abs(scaled - rounded);\n if (remainder === 0.5) {\n const down = Math.floor(scaled);\n return (down % 2 === 0 ? down : down + 1) / factor;\n }\n return rounded / factor;\n }\n return Math.round(value * factor) / factor;\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pyverret/ratejs",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Lightweight, dependency-free TypeScript financial math library providing pure calculation utilities.",
|
|
5
|
+
"author": "Pierre-Yves Verret",
|
|
6
|
+
"license": "ISC",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"finance",
|
|
9
|
+
"financial-math",
|
|
10
|
+
"interest",
|
|
11
|
+
"loan",
|
|
12
|
+
"amortization",
|
|
13
|
+
"irr",
|
|
14
|
+
"cagr",
|
|
15
|
+
"time-value-of-money",
|
|
16
|
+
"typescript"
|
|
17
|
+
],
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/pyverret/ratejs.git"
|
|
21
|
+
},
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/pyverret/ratejs/issues"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/pyverret/ratejs#readme",
|
|
26
|
+
"type": "module",
|
|
27
|
+
"main": "./dist/index.cjs",
|
|
28
|
+
"module": "./dist/index.js",
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"sideEffects": false,
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"import": "./dist/index.js",
|
|
35
|
+
"require": "./dist/index.cjs"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"dist"
|
|
40
|
+
],
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsup",
|
|
43
|
+
"lint": "biome check .",
|
|
44
|
+
"lint:fix": "biome check . --write",
|
|
45
|
+
"format": "biome format . --write",
|
|
46
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
47
|
+
"test": "vitest run",
|
|
48
|
+
"test:watch": "vitest",
|
|
49
|
+
"prepublishOnly": "npm run lint && npm run typecheck && npm test && npm run build"
|
|
50
|
+
},
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"access": "public",
|
|
53
|
+
"provenance": true
|
|
54
|
+
},
|
|
55
|
+
"engines": {
|
|
56
|
+
"node": ">=20.19.0"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@biomejs/biome": "^2.4.4",
|
|
60
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
61
|
+
"tsup": "^8.5.0",
|
|
62
|
+
"typescript": "^5.9.2",
|
|
63
|
+
"vitest": "^3.2.4"
|
|
64
|
+
}
|
|
65
|
+
}
|