moneyfunx 3.0.8 → 3.0.10

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.
@@ -32,8 +32,8 @@ export declare function determineCarryover(loan: Loan, loanPayment: number, loan
32
32
  *
33
33
  * @param {Loan} loan The loan to amortize payments for
34
34
  * @param {number} principal The amount borrowed
35
- * @param {number} payment The amount to pay to the loan's balance each period
36
- * @param {number} numPayments The number of periods to make payments to the loan
35
+ * @param {number|null} payment The amount to pay to the loan's balance each period
36
+ * @param {number|null} numPayments The number of periods to make payments to the loan
37
37
  * @param {number} startPeriod An initial offset of periods to 'fast-forward' the state of the loan to prior to calculation of each period
38
38
  * @param {number} carryover An additional amount to pay towards a loan, used when a residual amount is available from paying off the previous loan this period
39
39
  * @returns {PaymentRecord[]} The amortization schedule for the number of payments of payment made to the loan from the provided start period
@@ -1 +1 @@
1
- {"version":3,"file":"payments.d.ts","sourceRoot":"","sources":["../../../src/lib/debt/payments.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,KAAK,EACV,aAAa,EAEb,oBAAoB,EACrB,MAAM,yBAAyB,CAAC;AAEjC;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,KAAK,EAAE,EACd,OAAO,EAAE,MAAM,GACd,MAAM,CAYR;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,IAAI,EACV,WAAW,EAAE,MAAM,EACnB,gBAAgB,EAAE,MAAM,EACxB,aAAa,EAAE,OAAO,GACrB,MAAM,CAOR;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,IAAI,EACV,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GAAC,IAAI,EACpB,WAAW,EAAE,MAAM,GAAC,IAAI,EACxB,WAAW,GAAE,MAAU,EACvB,SAAS,GAAE,MAAU,GACpB,aAAa,EAAE,CA+BjB;AAED;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CACtB,KAAK,EAAE,IAAI,EAAE,EACb,OAAO,EAAE,MAAM,EACf,aAAa,GAAE,OAAe,GAC7B,oBAAoB,CAqItB"}
1
+ {"version":3,"file":"payments.d.ts","sourceRoot":"","sources":["../../../src/lib/debt/payments.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,KAAK,EACV,aAAa,EAEb,oBAAoB,EACrB,MAAM,yBAAyB,CAAC;AAEjC;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,KAAK,EAAE,EACd,OAAO,EAAE,MAAM,GACd,MAAM,CAYR;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,IAAI,EACV,WAAW,EAAE,MAAM,EACnB,gBAAgB,EAAE,MAAM,EACxB,aAAa,EAAE,OAAO,GACrB,MAAM,CAOR;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,IAAI,EACV,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GAAG,IAAI,EACtB,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,WAAW,GAAE,MAAU,EACvB,SAAS,GAAE,MAAU,GACpB,aAAa,EAAE,CA2BjB;AAED;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CACtB,KAAK,EAAE,IAAI,EAAE,EACb,OAAO,EAAE,MAAM,EACf,aAAa,GAAE,OAAe,GAC7B,oBAAoB,CA0ItB"}
@@ -4,6 +4,7 @@
4
4
  *
5
5
  */
6
6
  import * as errors from '../errors.js';
7
+ import { TOTALS } from '../constants.js';
7
8
  /**
8
9
  *
9
10
  * Calculates the extra amount in a payment after all loans' minimum payments are met
@@ -45,27 +46,24 @@ export function determineCarryover(loan, loanPayment, loanFinalPayment, reduceMi
45
46
  *
46
47
  * @param {Loan} loan The loan to amortize payments for
47
48
  * @param {number} principal The amount borrowed
48
- * @param {number} payment The amount to pay to the loan's balance each period
49
- * @param {number} numPayments The number of periods to make payments to the loan
49
+ * @param {number|null} payment The amount to pay to the loan's balance each period
50
+ * @param {number|null} numPayments The number of periods to make payments to the loan
50
51
  * @param {number} startPeriod An initial offset of periods to 'fast-forward' the state of the loan to prior to calculation of each period
51
52
  * @param {number} carryover An additional amount to pay towards a loan, used when a residual amount is available from paying off the previous loan this period
52
53
  * @returns {PaymentRecord[]} The amortization schedule for the number of payments of payment made to the loan from the provided start period
53
54
  */
54
55
  export function amortizePayments(loan, principal, payment, numPayments, startPeriod = 0, carryover = 0) {
55
- if (payment === null) {
56
- payment = loan.minPayment;
57
- }
58
- payment = loan.validatePayment(payment);
59
- if (numPayments === null) {
60
- numPayments = loan.numPaymentsToZero(payment);
61
- }
56
+ // Strict null check handling
57
+ let actualPayment = (payment !== null) ? payment : loan.minPayment;
58
+ actualPayment = loan.validatePayment(actualPayment);
59
+ let actualNumPayments = (numPayments !== null) ? numPayments : loan.numPaymentsToZero(actualPayment);
62
60
  const amortizationSchedule = [];
63
61
  let principalRemaining = principal;
64
- for (let period = 0; period < numPayments; period++) {
62
+ for (let period = 0; period < actualNumPayments; period++) {
65
63
  const interestThisPeriod = loan.accrueInterest(principalRemaining);
66
- const principalThisPeriod = Math.min((period === numPayments - 1
67
- ? payment + carryover
68
- : payment) - interestThisPeriod, principalRemaining);
64
+ const principalThisPeriod = Math.min((period === actualNumPayments - 1
65
+ ? actualPayment + carryover
66
+ : actualPayment) - interestThisPeriod, principalRemaining);
69
67
  principalRemaining -= principalThisPeriod;
70
68
  amortizationSchedule.push({
71
69
  period: startPeriod + period + 1,
@@ -110,11 +108,14 @@ export function payLoans(loans, payment, reduceMinimum = false) {
110
108
  const firstLoanPrincipalRemaining = loanPrincipalsRemaining[firstLoan.id];
111
109
  const periodsToPay = firstLoan.numPaymentsToZero(firstLoanPayment, firstLoanPrincipalRemaining);
112
110
  const firstLoanAmortizedPayments = amortizePayments(firstLoan, firstLoanPrincipalRemaining, firstLoanPayment, periodsToPay, periodsElapsed);
113
- const firstLoanFinalPayment = firstLoan.principalRemaining(periodsToPay - 1, firstLoanPayment, firstLoanPrincipalRemaining) + firstLoan.accrueInterest(firstLoan.principalRemaining(periodsToPay - 1, firstLoanPayment, firstLoanPrincipalRemaining));
111
+ // Calculate final payment logic
112
+ const finalPrincipal = firstLoan.principalRemaining(periodsToPay - 1, firstLoanPayment, firstLoanPrincipalRemaining);
113
+ const firstLoanFinalPayment = finalPrincipal + firstLoan.accrueInterest(finalPrincipal);
114
114
  paymentSchedule[firstLoan.id].amortizationSchedule = [
115
115
  ...paymentSchedule[firstLoan.id].amortizationSchedule,
116
116
  ...firstLoanAmortizedPayments
117
117
  ];
118
+ // Merge into totals
118
119
  totalAmortizationSchedule = [
119
120
  ...totalAmortizationSchedule,
120
121
  ...firstLoanAmortizedPayments
@@ -132,6 +133,7 @@ export function payLoans(loans, payment, reduceMinimum = false) {
132
133
  ...paymentSchedule[loan.id].amortizationSchedule,
133
134
  ...loanAmortizedPayments,
134
135
  ];
136
+ // Safe map for totals
135
137
  totalAmortizationSchedule = totalAmortizationSchedule.map((element) => {
136
138
  const matchedInnerElement = loanAmortizedPayments.find((innerElement) => innerElement.period === element.period);
137
139
  return (matchedInnerElement != null)
@@ -144,7 +146,11 @@ export function payLoans(loans, payment, reduceMinimum = false) {
144
146
  }
145
147
  : element;
146
148
  });
147
- loanPrincipalsRemaining[loan.id] = paymentSchedule[loan.id].amortizationSchedule[paymentSchedule[loan.id].amortizationSchedule.length - 1].principalRemaining;
149
+ // Update remaining principals for next iteration
150
+ const currentLoanSchedule = paymentSchedule[loan.id].amortizationSchedule;
151
+ if (currentLoanSchedule.length > 0) {
152
+ loanPrincipalsRemaining[loan.id] = currentLoanSchedule[currentLoanSchedule.length - 1].principalRemaining;
153
+ }
148
154
  });
149
155
  periodsElapsed += periodsToPay;
150
156
  }
@@ -155,7 +161,7 @@ export function payLoans(loans, payment, reduceMinimum = false) {
155
161
  totalLifetimeInterest += loanLifetimeInterest;
156
162
  totalLifetimePrincipal += loan.currentBalance;
157
163
  }
158
- paymentSchedule.totals = {
164
+ paymentSchedule[TOTALS] = {
159
165
  lifetimeInterest: totalLifetimeInterest,
160
166
  lifetimePrincipal: totalLifetimePrincipal,
161
167
  amortizationSchedule: totalAmortizationSchedule,
@@ -4,7 +4,7 @@
4
4
  *
5
5
  */
6
6
  import type { Instrument } from '../investment/instrument.js';
7
- import { ContributionRecord, InstrumentsContributionSchedule } from '../investment/contributionTypes.js';
7
+ import type { ContributionRecord, InstrumentsContributionSchedule } from '../investment/contributionTypes.js';
8
8
  /**
9
9
  *
10
10
  * @param {IInstrument[]} instruments The instruments to allocate maximum contributions
@@ -19,8 +19,8 @@ export declare function determineExtraContribution(instruments: Instrument[], co
19
19
  * @param {number} contribution The amount to contribute to the instrument's balance
20
20
  * @param {number[int]} startPeriod The initial offset for period values
21
21
  * @param {boolean} accrueBeforeContribution A flag for ordering operations of accrual (A) and contribution (C)
22
- * true: A -> C
23
- * false: C -> A
22
+ * true: A -> C
23
+ * false: C -> A
24
24
  * @returns {ContributionRecord} The amortized contribution
25
25
  */
26
26
  export declare function amortizeContribution(instrument: Instrument, currentBalance: number, contribution: number, startPeriod?: number, accrueBeforeContribution?: boolean): ContributionRecord;
@@ -34,8 +34,8 @@ export declare function amortizeContribution(instrument: Instrument, currentBala
34
34
  * @param {number} numContributions The number of periods to make contributions to the instrument
35
35
  * @param {number[int]} startPeriod The inital offset for period values
36
36
  * @param {boolean} accrueBeforeContribution A flag for ordering operations of accrual (A) and contribution (C)
37
- * true: A -> C
38
- * false: C -> A
37
+ * true: A -> C
38
+ * false: C -> A
39
39
  * @returns {ContributionRecord[]} The amortized contributions
40
40
  */
41
41
  export declare function amortizeContributions(instrument: Instrument, initialBalance: number, contribution: number, numContributions: number, startPeriod?: number, accrueBeforeContribution?: boolean): ContributionRecord[];
@@ -45,8 +45,8 @@ export declare function amortizeContributions(instrument: Instrument, initialBal
45
45
  * @param {number} contribution The total amount to contirbute each period
46
46
  * @param {number[int]} numContributions The number of periods to contribute
47
47
  * @param {boolean} accrueBeforeContribution A flag for ordering operations of accrual (A) and contribution (C)
48
- * true: A -> C
49
- * false: C -> A
48
+ * true: A -> C
49
+ * false: C -> A
50
50
  * @returns {InstrumentsContributionSchedule} The amortized contributions for all instruments
51
51
  */
52
52
  export declare function contributeInstruments(instruments: Instrument[], contribution: number, numContributions: number, accrueBeforeContribution?: boolean): InstrumentsContributionSchedule;
@@ -1 +1 @@
1
- {"version":3,"file":"contributions.d.ts","sourceRoot":"","sources":["../../../src/lib/investment/contributions.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EACL,kBAAkB,EAClB,+BAA+B,EAGhC,MAAM,oCAAoC,CAAC;AAE5C;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,UAAU,EAAE,EACzB,YAAY,EAAE,MAAM,GACnB,MAAM,CAMR;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,UAAU,EACtB,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,EACpB,WAAW,GAAE,MAAU,EACvB,wBAAwB,GAAE,OAAc,GACvC,kBAAkB,CAgBpB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,UAAU,EACtB,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,EACpB,gBAAgB,EAAE,MAAM,EACxB,WAAW,GAAE,MAAU,EACvB,wBAAwB,GAAE,OAAc,GACvC,kBAAkB,EAAE,CAkBtB;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,UAAU,EAAE,EACzB,YAAY,EAAE,MAAM,EACpB,gBAAgB,EAAE,MAAM,EACxB,wBAAwB,GAAE,OAAc,GACvC,+BAA+B,CA6FjC"}
1
+ {"version":3,"file":"contributions.d.ts","sourceRoot":"","sources":["../../../src/lib/investment/contributions.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EACV,kBAAkB,EAClB,+BAA+B,EAGhC,MAAM,oCAAoC,CAAC;AAE5C;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,UAAU,EAAE,EACzB,YAAY,EAAE,MAAM,GACnB,MAAM,CAMR;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,UAAU,EACtB,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,EACpB,WAAW,GAAE,MAAU,EACvB,wBAAwB,GAAE,OAAc,GACvC,kBAAkB,CAkBpB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,UAAU,EACtB,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,EACpB,gBAAgB,EAAE,MAAM,EACxB,WAAW,GAAE,MAAU,EACvB,wBAAwB,GAAE,OAAc,GACvC,kBAAkB,EAAE,CAoBtB;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,UAAU,EAAE,EACzB,YAAY,EAAE,MAAM,EACpB,gBAAgB,EAAE,MAAM,EACxB,wBAAwB,GAAE,OAAc,GACvC,+BAA+B,CA+FjC"}
@@ -3,6 +3,7 @@
3
3
  * This file containts functions for computing detailed information on contributing to investments
4
4
  *
5
5
  */
6
+ import { TOTALS } from '../constants.js';
6
7
  /**
7
8
  *
8
9
  * @param {IInstrument[]} instruments The instruments to allocate maximum contributions
@@ -21,26 +22,27 @@ export function determineExtraContribution(instruments, contribution) {
21
22
  * @param {number} contribution The amount to contribute to the instrument's balance
22
23
  * @param {number[int]} startPeriod The initial offset for period values
23
24
  * @param {boolean} accrueBeforeContribution A flag for ordering operations of accrual (A) and contribution (C)
24
- * true: A -> C
25
- * false: C -> A
25
+ * true: A -> C
26
+ * false: C -> A
26
27
  * @returns {ContributionRecord} The amortized contribution
27
28
  */
28
29
  export function amortizeContribution(instrument, currentBalance, contribution, startPeriod = 0, accrueBeforeContribution = true) {
29
30
  let interestThisPeriod;
31
+ let newBalance = currentBalance;
30
32
  if (accrueBeforeContribution) {
31
- interestThisPeriod = instrument.accrueInterest(currentBalance);
32
- currentBalance += contribution + interestThisPeriod;
33
+ interestThisPeriod = instrument.accrueInterest(newBalance);
34
+ newBalance += contribution + interestThisPeriod;
33
35
  }
34
36
  else {
35
- currentBalance += contribution;
36
- interestThisPeriod = instrument.accrueInterest(currentBalance);
37
- currentBalance += interestThisPeriod;
37
+ newBalance += contribution;
38
+ interestThisPeriod = instrument.accrueInterest(newBalance);
39
+ newBalance += interestThisPeriod;
38
40
  }
39
41
  return {
40
42
  period: startPeriod + 1,
41
43
  contribution: contribution,
42
44
  growth: interestThisPeriod,
43
- currentBalance,
45
+ currentBalance: newBalance,
44
46
  };
45
47
  }
46
48
  ;
@@ -54,8 +56,8 @@ export function amortizeContribution(instrument, currentBalance, contribution, s
54
56
  * @param {number} numContributions The number of periods to make contributions to the instrument
55
57
  * @param {number[int]} startPeriod The inital offset for period values
56
58
  * @param {boolean} accrueBeforeContribution A flag for ordering operations of accrual (A) and contribution (C)
57
- * true: A -> C
58
- * false: C -> A
59
+ * true: A -> C
60
+ * false: C -> A
59
61
  * @returns {ContributionRecord[]} The amortized contributions
60
62
  */
61
63
  export function amortizeContributions(instrument, initialBalance, contribution, numContributions, startPeriod = 0, accrueBeforeContribution = true) {
@@ -66,6 +68,8 @@ export function amortizeContributions(instrument, initialBalance, contribution,
66
68
  const periodicContribution = instrument.validateContribution(contribution, ytd);
67
69
  const record = amortizeContribution(instrument, currentBalance, periodicContribution, period + startPeriod, accrueBeforeContribution);
68
70
  currentBalance = record.currentBalance;
71
+ // Reset YTD every 12 periods, assuming monthly periodicity matching periodsPerYear
72
+ // NOTE: This logic assumes period 0 is the start of a year.
69
73
  period % 12 === 0 ? ytd = 0 : ytd += periodicContribution;
70
74
  contributionSchedule.push(record);
71
75
  }
@@ -78,8 +82,8 @@ export function amortizeContributions(instrument, initialBalance, contribution,
78
82
  * @param {number} contribution The total amount to contirbute each period
79
83
  * @param {number[int]} numContributions The number of periods to contribute
80
84
  * @param {boolean} accrueBeforeContribution A flag for ordering operations of accrual (A) and contribution (C)
81
- * true: A -> C
82
- * false: C -> A
85
+ * true: A -> C
86
+ * false: C -> A
83
87
  * @returns {InstrumentsContributionSchedule} The amortized contributions for all instruments
84
88
  */
85
89
  export function contributeInstruments(instruments, contribution, numContributions, accrueBeforeContribution = true) {
@@ -126,6 +130,7 @@ export function contributeInstruments(instruments, contribution, numContribution
126
130
  contributionSchedules[instrument.id].lifetimeGrowth = instrumentLifetimeGrowth;
127
131
  totalLifetimeContribution += instrumentLifetimeContribution;
128
132
  totalLifetimeGrowth += instrumentLifetimeGrowth;
133
+ // Merge totals safely
129
134
  totalAmortizationSchedule = totalAmortizationSchedule.length
130
135
  ? (totalAmortizationSchedule.map((element) => {
131
136
  const matchedInnerElement = instrumentSchedule.find((innerElement) => innerElement.period === element.period);
@@ -141,7 +146,7 @@ export function contributeInstruments(instruments, contribution, numContribution
141
146
  }))
142
147
  : instrumentSchedule;
143
148
  }
144
- contributionSchedules.totals = {
149
+ contributionSchedules[TOTALS] = {
145
150
  lifetimeContribution: totalLifetimeContribution,
146
151
  lifetimeGrowth: totalLifetimeGrowth,
147
152
  amortizationSchedule: totalAmortizationSchedule,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "moneyfunx",
3
- "version": "3.0.8",
3
+ "version": "3.0.10",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {