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 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
- amortizationSchedule: Array<AmortizationRecord>;
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>;
@@ -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: Array<ILoan>, payment: number): number;
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): Array<AmortizationRecord>;
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
@@ -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
- if (totalMinPayment > payment) {
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 ? payment + carryover : payment) - interestThisPeriod, principalRemaining);
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: 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.map((loan) => {
72
- paymentData[loan.id] = { lifetimeInterest: 0, amortizationSchedule: [] };
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["totals"] = {
138
+ paymentData.totals = {
129
139
  lifetimeInterest: totalLifetimeInterest,
140
+ lifetimePrincipal: totalLifetimePrincipal,
130
141
  amortizationSchedule: totalAmortizationSchedule
131
142
  };
132
143
  return paymentData;
@@ -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: Array<ILoan>, sortFunction: sortFunction): ILoan[];
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.39",
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";
@@ -7,13 +7,10 @@ export interface AmortizationRecord {
7
7
 
8
8
  export interface PaymentSummary {
9
9
  lifetimeInterest: number;
10
- amortizationSchedule: Array<AmortizationRecord>;
10
+ lifetimePrincipal: number;
11
+ amortizationSchedule: AmortizationRecord[];
11
12
  }
12
13
 
13
- export interface LoansPaymentSummary {
14
- [id: string]: PaymentSummary;
15
- }
14
+ export type LoansPaymentSummary = Record<string, PaymentSummary>;
16
15
 
17
- export interface LoanPrincipals {
18
- [id: string]: number;
19
- }
16
+ export type LoanPrincipals = Record<string, number>;
@@ -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: Array<ILoan>,
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
- if (totalMinPayment > payment) {
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
- ): Array<AmortizationRecord> {
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 ? payment + carryover : payment) - interestThisPeriod,
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: 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.map((loan) => {
108
- paymentData[loan.id] = { lifetimeInterest: 0, amortizationSchedule: [] };
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
- period: element.period,
186
- principal: element.principal + matchedInnerElement.principal,
187
- interest: element.interest + matchedInnerElement.interest,
188
- principalRemaining:
189
- element.principalRemaining +
190
- matchedInnerElement.principalRemaining
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 = paymentData[loan.id].amortizationSchedule.reduce((acc, curval) => acc + curval.interest, 0);
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["totals"] = {
222
+ paymentData.totals = {
209
223
  lifetimeInterest: totalLifetimeInterest,
224
+ lifetimePrincipal: totalLifetimePrincipal,
210
225
  amortizationSchedule: totalAmortizationSchedule
211
226
  };
212
227
 
@@ -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: Array<ILoan>, sortFunction: sortFunction) {
39
+ export function sortLoans(loans: ILoan[], sortFunction: sortFunction) {
40
40
  return loans.sort(sortFunction);
41
41
  }