moneyfunx 0.0.39 → 0.0.41
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/build/index.d.ts +2 -2
- package/build/lib/paymentTypes.d.ts +4 -7
- package/build/lib/payments.d.ts +4 -4
- package/build/lib/payments.js +19 -8
- package/build/lib/sorting.d.ts +2 -2
- package/package.json +1 -1
- package/src/index.ts +4 -4
- package/src/lib/paymentTypes.ts +4 -7
- package/src/lib/payments.ts +35 -20
- package/src/lib/sorting.ts +2 -2
package/build/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { PaymentTooLowError } from "./lib/errors";
|
|
2
2
|
export { calculateMinPayment, numPaymentsToZero, principalRemaining, interestPaid, } from "./lib/helperFunctions";
|
|
3
|
-
export { ILoan, Loan } from "./lib/loan";
|
|
3
|
+
export { type ILoan, Loan } from "./lib/loan";
|
|
4
4
|
export { determineExtraPayment, amortizePayments, payLoans, } from "./lib/payments";
|
|
5
|
-
export { AmortizationRecord, LoansPaymentSummary, PaymentSummary, } from "./lib/paymentTypes";
|
|
5
|
+
export { type AmortizationRecord, type LoansPaymentSummary, type PaymentSummary, } from "./lib/paymentTypes";
|
|
6
6
|
export { snowball, avalanche, sortLoans } from "./lib/sorting";
|
|
@@ -6,11 +6,8 @@ export interface AmortizationRecord {
|
|
|
6
6
|
}
|
|
7
7
|
export interface PaymentSummary {
|
|
8
8
|
lifetimeInterest: number;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
export interface LoansPaymentSummary {
|
|
12
|
-
[id: string]: PaymentSummary;
|
|
13
|
-
}
|
|
14
|
-
export interface LoanPrincipals {
|
|
15
|
-
[id: string]: number;
|
|
9
|
+
lifetimePrincipal: number;
|
|
10
|
+
amortizationSchedule: AmortizationRecord[];
|
|
16
11
|
}
|
|
12
|
+
export type LoansPaymentSummary = Record<string, PaymentSummary>;
|
|
13
|
+
export type LoanPrincipals = Record<string, number>;
|
package/build/lib/payments.d.ts
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* This file contains functions for computing detailed information on paying loans
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
|
-
import { ILoan, Loan } from "./loan";
|
|
7
|
-
import { AmortizationRecord, LoansPaymentSummary } from "./paymentTypes";
|
|
6
|
+
import type { ILoan, Loan } from "./loan";
|
|
7
|
+
import type { AmortizationRecord, LoansPaymentSummary } from "./paymentTypes";
|
|
8
8
|
/**
|
|
9
9
|
*
|
|
10
10
|
* Calculates the extra amount in a payment after all loans' minimum payments are met
|
|
@@ -14,7 +14,7 @@ import { AmortizationRecord, LoansPaymentSummary } from "./paymentTypes";
|
|
|
14
14
|
* @param {number} payment The amount to pay across all loans
|
|
15
15
|
* @returns {number} The extra amount of payment
|
|
16
16
|
*/
|
|
17
|
-
export declare function determineExtraPayment(loans:
|
|
17
|
+
export declare function determineExtraPayment(loans: ILoan[], payment: number): number;
|
|
18
18
|
/**
|
|
19
19
|
*
|
|
20
20
|
* Calculates the amortization schedule for a loan paid with a payment
|
|
@@ -25,7 +25,7 @@ export declare function determineExtraPayment(loans: Array<ILoan>, payment: numb
|
|
|
25
25
|
* @param {number} startPeriod An initial offset of periods to "fast-forward" the state of the loan to prior to calculation of each period
|
|
26
26
|
* @returns {Array<AmortizationRecord>} The amortization schdule for the number of payments of payment made to the loan from the provided start period
|
|
27
27
|
*/
|
|
28
|
-
export declare function amortizePayments(loan: Loan, principal: number, payment: number, numPayments: number, startPeriod?: number, carryover?: number):
|
|
28
|
+
export declare function amortizePayments(loan: Loan, principal: number, payment: number, numPayments: number, startPeriod?: number, carryover?: number): AmortizationRecord[];
|
|
29
29
|
/**
|
|
30
30
|
*
|
|
31
31
|
* Calculates a wealth of information about paying of a set of loans with a total payment amount
|
package/build/lib/payments.js
CHANGED
|
@@ -16,7 +16,8 @@ import * as helpers from "./helperFunctions";
|
|
|
16
16
|
*/
|
|
17
17
|
export function determineExtraPayment(loans, payment) {
|
|
18
18
|
const totalMinPayment = loans.reduce((previousValue, currentValue) => previousValue + currentValue.minPayment, 0);
|
|
19
|
-
|
|
19
|
+
// hack to get around floating precision adjustments
|
|
20
|
+
if (parseFloat(totalMinPayment.toFixed(2)) > parseFloat(payment.toFixed(2))) {
|
|
20
21
|
throw new errors.PaymentTooLowError(`Payment amount of ${payment} must be greater than ${totalMinPayment}`);
|
|
21
22
|
}
|
|
22
23
|
return payment - totalMinPayment;
|
|
@@ -43,13 +44,15 @@ export function amortizePayments(loan, principal, payment, numPayments, startPer
|
|
|
43
44
|
const amortizationSchedule = [];
|
|
44
45
|
for (let period = 0; period < numPayments; period++) {
|
|
45
46
|
const interestThisPeriod = loan.accrueInterest(principalRemaining);
|
|
46
|
-
const principalThisPeriod = Math.min((period === numPayments - 1
|
|
47
|
+
const principalThisPeriod = Math.min((period === numPayments - 1
|
|
48
|
+
? payment + carryover
|
|
49
|
+
: payment) - interestThisPeriod, principalRemaining);
|
|
47
50
|
principalRemaining -= principalThisPeriod;
|
|
48
51
|
amortizationSchedule.push({
|
|
49
52
|
period: startPeriod + period + 1,
|
|
50
53
|
principal: principalThisPeriod,
|
|
51
54
|
interest: interestThisPeriod,
|
|
52
|
-
principalRemaining
|
|
55
|
+
principalRemaining,
|
|
53
56
|
});
|
|
54
57
|
}
|
|
55
58
|
return amortizationSchedule;
|
|
@@ -68,13 +71,18 @@ export function payLoans(loans, payment, reduceMinimum = false) {
|
|
|
68
71
|
let monthlyPayment = payment;
|
|
69
72
|
const paymentData = {};
|
|
70
73
|
const loanPrincipalsRemaining = {};
|
|
71
|
-
loans.
|
|
72
|
-
paymentData[loan.id] = {
|
|
74
|
+
loans.forEach((loan) => {
|
|
75
|
+
paymentData[loan.id] = {
|
|
76
|
+
lifetimeInterest: 0,
|
|
77
|
+
lifetimePrincipal: loan.principal,
|
|
78
|
+
amortizationSchedule: []
|
|
79
|
+
};
|
|
73
80
|
loanPrincipalsRemaining[loan.id] = loan.principal;
|
|
74
81
|
});
|
|
75
82
|
let periodsElapsed = 0;
|
|
76
83
|
let paidLoans = 0;
|
|
77
84
|
let totalLifetimeInterest = 0;
|
|
85
|
+
let totalLifetimePrincipal = 0;
|
|
78
86
|
let totalAmortizationSchedule = [];
|
|
79
87
|
while (paidLoans < loans.length) {
|
|
80
88
|
const firstLoan = loans.slice(paidLoans)[0];
|
|
@@ -104,7 +112,7 @@ export function payLoans(loans, payment, reduceMinimum = false) {
|
|
|
104
112
|
loanPrincipalsRemaining[loan.id] = paymentData[loan.id].amortizationSchedule[paymentData[loan.id].amortizationSchedule.length - 1].principalRemaining;
|
|
105
113
|
totalAmortizationSchedule = totalAmortizationSchedule.map((element) => {
|
|
106
114
|
const matchedInnerElement = paidPeriods.find((innerElement) => innerElement.period === element.period);
|
|
107
|
-
return matchedInnerElement
|
|
115
|
+
return (matchedInnerElement != null)
|
|
108
116
|
? {
|
|
109
117
|
period: element.period,
|
|
110
118
|
principal: element.principal + matchedInnerElement.principal,
|
|
@@ -121,12 +129,15 @@ export function payLoans(loans, payment, reduceMinimum = false) {
|
|
|
121
129
|
periodsElapsed += periodsToPay;
|
|
122
130
|
}
|
|
123
131
|
for (const loan of loans) {
|
|
124
|
-
const loanLifetimeInterest = paymentData[loan.id].amortizationSchedule.reduce((acc, curval) => acc + curval.interest, 0);
|
|
132
|
+
const loanLifetimeInterest = (paymentData[loan.id].amortizationSchedule.reduce((acc, curval) => acc + curval.interest, 0));
|
|
125
133
|
paymentData[loan.id].lifetimeInterest = loanLifetimeInterest;
|
|
134
|
+
paymentData[loan.id].lifetimePrincipal = loan.principal;
|
|
126
135
|
totalLifetimeInterest += loanLifetimeInterest;
|
|
136
|
+
totalLifetimePrincipal += loan.principal;
|
|
127
137
|
}
|
|
128
|
-
paymentData
|
|
138
|
+
paymentData.totals = {
|
|
129
139
|
lifetimeInterest: totalLifetimeInterest,
|
|
140
|
+
lifetimePrincipal: totalLifetimePrincipal,
|
|
130
141
|
amortizationSchedule: totalAmortizationSchedule
|
|
131
142
|
};
|
|
132
143
|
return paymentData;
|
package/build/lib/sorting.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* This file contains functions for sorting Arrays of Loans on certain attributes
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
|
-
import { ILoan } from "./loan";
|
|
6
|
+
import { type ILoan } from "./loan";
|
|
7
7
|
type avalanche = (loan1: ILoan, loan2: ILoan) => number;
|
|
8
8
|
type snowball = (loan1: ILoan, loan2: ILoan) => number;
|
|
9
9
|
type sortFunction = avalanche | snowball;
|
|
@@ -27,5 +27,5 @@ export declare function snowball(loan1: ILoan, loan2: ILoan): number;
|
|
|
27
27
|
* @param {function} sortFunc The algorithm to sort the loans with
|
|
28
28
|
* @returns The sorted array loans
|
|
29
29
|
*/
|
|
30
|
-
export declare function sortLoans(loans:
|
|
30
|
+
export declare function sortLoans(loans: ILoan[], sortFunction: sortFunction): ILoan[];
|
|
31
31
|
export {};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "moneyfunx",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.41",
|
|
5
5
|
"description": "MoneyFunx is a small library of functions for financial computations, with a focus on personal finance",
|
|
6
6
|
"main": "build/index.js",
|
|
7
7
|
"types": "build/index.d.ts",
|
package/src/index.ts
CHANGED
|
@@ -5,15 +5,15 @@ export {
|
|
|
5
5
|
principalRemaining,
|
|
6
6
|
interestPaid,
|
|
7
7
|
} from "./lib/helperFunctions";
|
|
8
|
-
export { ILoan, Loan } from "./lib/loan";
|
|
8
|
+
export { type ILoan, Loan } from "./lib/loan";
|
|
9
9
|
export {
|
|
10
10
|
determineExtraPayment,
|
|
11
11
|
amortizePayments,
|
|
12
12
|
payLoans,
|
|
13
13
|
} from "./lib/payments";
|
|
14
14
|
export {
|
|
15
|
-
AmortizationRecord,
|
|
16
|
-
LoansPaymentSummary,
|
|
17
|
-
PaymentSummary,
|
|
15
|
+
type AmortizationRecord,
|
|
16
|
+
type LoansPaymentSummary,
|
|
17
|
+
type PaymentSummary,
|
|
18
18
|
} from "./lib/paymentTypes";
|
|
19
19
|
export { snowball, avalanche, sortLoans } from "./lib/sorting";
|
package/src/lib/paymentTypes.ts
CHANGED
|
@@ -7,13 +7,10 @@ export interface AmortizationRecord {
|
|
|
7
7
|
|
|
8
8
|
export interface PaymentSummary {
|
|
9
9
|
lifetimeInterest: number;
|
|
10
|
-
|
|
10
|
+
lifetimePrincipal: number;
|
|
11
|
+
amortizationSchedule: AmortizationRecord[];
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
export
|
|
14
|
-
[id: string]: PaymentSummary;
|
|
15
|
-
}
|
|
14
|
+
export type LoansPaymentSummary = Record<string, PaymentSummary>;
|
|
16
15
|
|
|
17
|
-
export
|
|
18
|
-
[id: string]: number;
|
|
19
|
-
}
|
|
16
|
+
export type LoanPrincipals = Record<string, number>;
|
package/src/lib/payments.ts
CHANGED
|
@@ -6,12 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
import * as errors from "./errors";
|
|
8
8
|
import * as helpers from "./helperFunctions";
|
|
9
|
-
import { ILoan, Loan } from "./loan";
|
|
10
|
-
import {
|
|
9
|
+
import type { ILoan, Loan } from "./loan";
|
|
10
|
+
import type {
|
|
11
11
|
AmortizationRecord,
|
|
12
12
|
LoanPrincipals,
|
|
13
13
|
LoansPaymentSummary,
|
|
14
|
-
PaymentSummary,
|
|
15
14
|
} from "./paymentTypes";
|
|
16
15
|
|
|
17
16
|
/**
|
|
@@ -24,14 +23,15 @@ import {
|
|
|
24
23
|
* @returns {number} The extra amount of payment
|
|
25
24
|
*/
|
|
26
25
|
export function determineExtraPayment(
|
|
27
|
-
loans:
|
|
26
|
+
loans: ILoan[],
|
|
28
27
|
payment: number
|
|
29
28
|
): number {
|
|
30
29
|
const totalMinPayment = loans.reduce(
|
|
31
30
|
(previousValue, currentValue) => previousValue + currentValue.minPayment,
|
|
32
31
|
0
|
|
33
32
|
);
|
|
34
|
-
|
|
33
|
+
// hack to get around floating precision adjustments
|
|
34
|
+
if (parseFloat(totalMinPayment.toFixed(2)) > parseFloat(payment.toFixed(2))) {
|
|
35
35
|
throw new errors.PaymentTooLowError(
|
|
36
36
|
`Payment amount of ${payment} must be greater than ${totalMinPayment}`
|
|
37
37
|
);
|
|
@@ -56,7 +56,7 @@ export function amortizePayments(
|
|
|
56
56
|
numPayments: number,
|
|
57
57
|
startPeriod: number = 0,
|
|
58
58
|
carryover: number = 0
|
|
59
|
-
):
|
|
59
|
+
): AmortizationRecord[] {
|
|
60
60
|
if (payment === null) {
|
|
61
61
|
payment = loan.minPayment;
|
|
62
62
|
}
|
|
@@ -72,7 +72,9 @@ export function amortizePayments(
|
|
|
72
72
|
for (let period = 0; period < numPayments; period++) {
|
|
73
73
|
const interestThisPeriod = loan.accrueInterest(principalRemaining);
|
|
74
74
|
const principalThisPeriod = Math.min(
|
|
75
|
-
(period === numPayments - 1
|
|
75
|
+
(period === numPayments - 1
|
|
76
|
+
? payment + carryover
|
|
77
|
+
: payment) - interestThisPeriod,
|
|
76
78
|
principalRemaining
|
|
77
79
|
);
|
|
78
80
|
principalRemaining -= principalThisPeriod;
|
|
@@ -80,7 +82,7 @@ export function amortizePayments(
|
|
|
80
82
|
period: startPeriod + period + 1,
|
|
81
83
|
principal: principalThisPeriod,
|
|
82
84
|
interest: interestThisPeriod,
|
|
83
|
-
principalRemaining
|
|
85
|
+
principalRemaining,
|
|
84
86
|
});
|
|
85
87
|
}
|
|
86
88
|
return amortizationSchedule;
|
|
@@ -104,14 +106,19 @@ export function payLoans(
|
|
|
104
106
|
let monthlyPayment = payment;
|
|
105
107
|
const paymentData: LoansPaymentSummary = {};
|
|
106
108
|
const loanPrincipalsRemaining: LoanPrincipals = {};
|
|
107
|
-
loans.
|
|
108
|
-
paymentData[loan.id] = {
|
|
109
|
+
loans.forEach((loan) => {
|
|
110
|
+
paymentData[loan.id] = {
|
|
111
|
+
lifetimeInterest: 0,
|
|
112
|
+
lifetimePrincipal: loan.principal,
|
|
113
|
+
amortizationSchedule: []
|
|
114
|
+
};
|
|
109
115
|
loanPrincipalsRemaining[loan.id] = loan.principal;
|
|
110
116
|
});
|
|
111
117
|
|
|
112
118
|
let periodsElapsed = 0;
|
|
113
119
|
let paidLoans = 0;
|
|
114
120
|
let totalLifetimeInterest = 0;
|
|
121
|
+
let totalLifetimePrincipal = 0;
|
|
115
122
|
let totalAmortizationSchedule: AmortizationRecord[] = [];
|
|
116
123
|
|
|
117
124
|
while (paidLoans < loans.length) {
|
|
@@ -180,15 +187,15 @@ export function payLoans(
|
|
|
180
187
|
const matchedInnerElement = paidPeriods.find(
|
|
181
188
|
(innerElement) => innerElement.period === element.period
|
|
182
189
|
);
|
|
183
|
-
return matchedInnerElement
|
|
190
|
+
return (matchedInnerElement != null)
|
|
184
191
|
? {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
+
period: element.period,
|
|
193
|
+
principal: element.principal + matchedInnerElement.principal,
|
|
194
|
+
interest: element.interest + matchedInnerElement.interest,
|
|
195
|
+
principalRemaining:
|
|
196
|
+
element.principalRemaining +
|
|
197
|
+
matchedInnerElement.principalRemaining
|
|
198
|
+
}
|
|
192
199
|
: element;
|
|
193
200
|
});
|
|
194
201
|
});
|
|
@@ -200,13 +207,21 @@ export function payLoans(
|
|
|
200
207
|
}
|
|
201
208
|
|
|
202
209
|
for (const loan of loans) {
|
|
203
|
-
const loanLifetimeInterest =
|
|
210
|
+
const loanLifetimeInterest = (
|
|
211
|
+
paymentData[loan.id].amortizationSchedule.reduce(
|
|
212
|
+
(acc, curval) => acc + curval.interest,
|
|
213
|
+
0
|
|
214
|
+
)
|
|
215
|
+
);
|
|
204
216
|
paymentData[loan.id].lifetimeInterest = loanLifetimeInterest;
|
|
217
|
+
paymentData[loan.id].lifetimePrincipal = loan.principal;
|
|
205
218
|
totalLifetimeInterest += loanLifetimeInterest;
|
|
219
|
+
totalLifetimePrincipal += loan.principal;
|
|
206
220
|
}
|
|
207
221
|
|
|
208
|
-
paymentData
|
|
222
|
+
paymentData.totals = {
|
|
209
223
|
lifetimeInterest: totalLifetimeInterest,
|
|
224
|
+
lifetimePrincipal: totalLifetimePrincipal,
|
|
210
225
|
amortizationSchedule: totalAmortizationSchedule
|
|
211
226
|
};
|
|
212
227
|
|
package/src/lib/sorting.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { ILoan } from "./loan";
|
|
7
|
+
import { type ILoan } from "./loan";
|
|
8
8
|
|
|
9
9
|
type avalanche = (loan1: ILoan, loan2: ILoan) => number;
|
|
10
10
|
type snowball = (loan1: ILoan, loan2: ILoan) => number;
|
|
@@ -36,6 +36,6 @@ export function snowball(loan1: ILoan, loan2: ILoan) {
|
|
|
36
36
|
* @param {function} sortFunc The algorithm to sort the loans with
|
|
37
37
|
* @returns The sorted array loans
|
|
38
38
|
*/
|
|
39
|
-
export function sortLoans(loans:
|
|
39
|
+
export function sortLoans(loans: ILoan[], sortFunction: sortFunction) {
|
|
40
40
|
return loans.sort(sortFunction);
|
|
41
41
|
}
|