moneyfunx 3.0.4 → 3.0.8

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.
Files changed (86) hide show
  1. package/build/index.d.ts +64 -0
  2. package/build/index.d.ts.map +1 -0
  3. package/build/index.js +27 -0
  4. package/build/lib/constants.d.ts.map +1 -0
  5. package/build/{src/lib → lib}/debt/loan.d.ts +1 -1
  6. package/build/lib/debt/loan.d.ts.map +1 -0
  7. package/build/{src/lib → lib}/debt/loan.js +2 -2
  8. package/build/lib/debt/paymentTypes.d.ts.map +1 -0
  9. package/build/{src/lib → lib}/debt/payments.d.ts +2 -2
  10. package/build/lib/debt/payments.d.ts.map +1 -0
  11. package/build/{src/lib → lib}/debt/payments.js +1 -1
  12. package/build/lib/errors.d.ts.map +1 -0
  13. package/build/lib/investment/contributionTypes.d.ts.map +1 -0
  14. package/build/{src/lib → lib}/investment/contributions.d.ts +2 -2
  15. package/build/lib/investment/contributions.d.ts.map +1 -0
  16. package/build/{src/lib → lib}/investment/instrument.d.ts +1 -1
  17. package/build/lib/investment/instrument.d.ts.map +1 -0
  18. package/build/{src/lib → lib}/investment/instrument.js +2 -2
  19. package/build/{src/lib → lib}/investment/strategies.d.ts +2 -2
  20. package/build/lib/investment/strategies.d.ts.map +1 -0
  21. package/build/{src/lib → lib}/investment/strategies.js +2 -2
  22. package/build/lib/investment/withdrawalTypes.d.ts.map +1 -0
  23. package/build/{src/lib → lib}/investment/withdrawals.d.ts +2 -2
  24. package/build/lib/investment/withdrawals.d.ts.map +1 -0
  25. package/build/{src/lib → lib}/investment/withdrawals.js +2 -2
  26. package/build/lib/shared/primitives.d.ts.map +1 -0
  27. package/build/lib/shared/sorting.d.ts.map +1 -0
  28. package/package.json +3 -3
  29. package/build/src/index.d.ts +0 -18
  30. package/build/src/index.d.ts.map +0 -1
  31. package/build/src/index.js +0 -14
  32. package/build/src/lib/constants.d.ts.map +0 -1
  33. package/build/src/lib/debt/loan.d.ts.map +0 -1
  34. package/build/src/lib/debt/paymentTypes.d.ts.map +0 -1
  35. package/build/src/lib/debt/payments.d.ts.map +0 -1
  36. package/build/src/lib/errors.d.ts.map +0 -1
  37. package/build/src/lib/investment/contributionTypes.d.ts.map +0 -1
  38. package/build/src/lib/investment/contributions.d.ts.map +0 -1
  39. package/build/src/lib/investment/instrument.d.ts.map +0 -1
  40. package/build/src/lib/investment/strategies.d.ts.map +0 -1
  41. package/build/src/lib/investment/withdrawalTypes.d.ts.map +0 -1
  42. package/build/src/lib/investment/withdrawals.d.ts.map +0 -1
  43. package/build/src/lib/shared/primitives.d.ts.map +0 -1
  44. package/build/src/lib/shared/sorting.d.ts.map +0 -1
  45. package/build/tests/debt/loan.test.d.ts +0 -2
  46. package/build/tests/debt/loan.test.d.ts.map +0 -1
  47. package/build/tests/debt/loan.test.js +0 -87
  48. package/build/tests/debt/payments.test.d.ts +0 -2
  49. package/build/tests/debt/payments.test.d.ts.map +0 -1
  50. package/build/tests/debt/payments.test.js +0 -91
  51. package/build/tests/debt/sorting.test.d.ts +0 -2
  52. package/build/tests/debt/sorting.test.d.ts.map +0 -1
  53. package/build/tests/debt/sorting.test.js +0 -43
  54. package/build/tests/index.test.d.ts +0 -2
  55. package/build/tests/index.test.d.ts.map +0 -1
  56. package/build/tests/index.test.js +0 -66
  57. package/build/tests/investment/contributions.test.d.ts +0 -2
  58. package/build/tests/investment/contributions.test.d.ts.map +0 -1
  59. package/build/tests/investment/contributions.test.js +0 -113
  60. package/build/tests/investment/instrument.test.d.ts +0 -2
  61. package/build/tests/investment/instrument.test.d.ts.map +0 -1
  62. package/build/tests/investment/instrument.test.js +0 -57
  63. package/build/tests/investment/strategies.test.d.ts +0 -2
  64. package/build/tests/investment/strategies.test.d.ts.map +0 -1
  65. package/build/tests/investment/strategies.test.js +0 -15
  66. package/build/tests/investment/withdrawals.test.d.ts +0 -2
  67. package/build/tests/investment/withdrawals.test.d.ts.map +0 -1
  68. package/build/tests/investment/withdrawals.test.js +0 -66
  69. package/build/tests/shared/primitives.test.d.ts +0 -2
  70. package/build/tests/shared/primitives.test.d.ts.map +0 -1
  71. package/build/tests/shared/primitives.test.js +0 -76
  72. /package/build/{src/lib → lib}/constants.d.ts +0 -0
  73. /package/build/{src/lib → lib}/constants.js +0 -0
  74. /package/build/{src/lib → lib}/debt/paymentTypes.d.ts +0 -0
  75. /package/build/{src/lib → lib}/debt/paymentTypes.js +0 -0
  76. /package/build/{src/lib → lib}/errors.d.ts +0 -0
  77. /package/build/{src/lib → lib}/errors.js +0 -0
  78. /package/build/{src/lib → lib}/investment/contributionTypes.d.ts +0 -0
  79. /package/build/{src/lib → lib}/investment/contributionTypes.js +0 -0
  80. /package/build/{src/lib → lib}/investment/contributions.js +0 -0
  81. /package/build/{src/lib → lib}/investment/withdrawalTypes.d.ts +0 -0
  82. /package/build/{src/lib → lib}/investment/withdrawalTypes.js +0 -0
  83. /package/build/{src/lib → lib}/shared/primitives.d.ts +0 -0
  84. /package/build/{src/lib → lib}/shared/primitives.js +0 -0
  85. /package/build/{src/lib → lib}/shared/sorting.d.ts +0 -0
  86. /package/build/{src/lib → lib}/shared/sorting.js +0 -0
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=sorting.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sorting.test.d.ts","sourceRoot":"","sources":["../../../tests/debt/sorting.test.ts"],"names":[],"mappings":""}
@@ -1,43 +0,0 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- import { describe, expect, it } from 'vitest';
11
- import { Loan } from '@/lib/debt/loan';
12
- import * as sorting from '@/lib/shared/sorting';
13
- const Loans = () => [
14
- new Loan(7500, 0.068, 12, 10, 'uno'),
15
- new Loan(4500, 0.0429, 12, 10, 'dos'),
16
- new Loan(7500, 0.0368, 12, 10, 'tres'),
17
- ];
18
- describe('sorting module', () => {
19
- const loans = Loans();
20
- const [loan2, loan3, loan1] = loans;
21
- it('avalanche compares loans correctly', () => __awaiter(void 0, void 0, void 0, function* () {
22
- expect(sorting.avalanche(loan2, loan1)).toBeCloseTo(-0.031200, 5);
23
- expect(sorting.avalanche(loan3, loan1)).toBeCloseTo(-0.006100, 5);
24
- }));
25
- it('snowball compares loans correctly', () => __awaiter(void 0, void 0, void 0, function* () {
26
- expect(sorting.snowball(loan2, loan3)).toBe(3000);
27
- expect(sorting.snowball(loan2, loan1)).toBe(0);
28
- }));
29
- it('orders loans correctly using avalanche sorting', () => __awaiter(void 0, void 0, void 0, function* () {
30
- expect(sorting.sortWith(loans, sorting.avalanche)).toStrictEqual([
31
- loan2,
32
- loan3,
33
- loan1,
34
- ]);
35
- }));
36
- it('orders loans correctly using snowball sorting', () => __awaiter(void 0, void 0, void 0, function* () {
37
- expect(sorting.sortWith(loans, sorting.snowball)).toStrictEqual([
38
- loan3,
39
- loan2,
40
- loan1,
41
- ]);
42
- }));
43
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=index.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../tests/index.test.ts"],"names":[],"mappings":""}
@@ -1,66 +0,0 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- import { describe, expect, it } from 'vitest';
11
- import * as moneyfunx from '@/index';
12
- /**
13
- * Ensures all intended public members are exported from the library.
14
- * This test acts as a safeguard against accidental omission of core functionality
15
- * during refactors or version bumps.
16
- */
17
- describe('moneyfunx module', () => {
18
- it('exports expected members matching the new descriptive naming standard', () => __awaiter(void 0, void 0, void 0, function* () {
19
- const expectedExports = [
20
- // Constants
21
- 'MAX_DURATION_YEARS',
22
- 'TOTALS',
23
- // Debt / Loans
24
- 'ILoan',
25
- 'Loan',
26
- 'determineExtraPayment',
27
- 'amortizePayments',
28
- 'payLoans',
29
- 'LoansPaymentSchedule',
30
- 'LoanPrincipals',
31
- 'PaymentSchedule',
32
- 'PaymentRecord',
33
- // Investment / Accumulation
34
- 'IInstrument',
35
- 'Instrument',
36
- 'amortizeContributions',
37
- 'contributeInstruments',
38
- 'determineExtraContribution',
39
- 'ContributionRecord',
40
- 'ContributionSchedule',
41
- 'InstrumentsContributionSchedule',
42
- // Drawdown / Distribution
43
- 'calculateAmortizedWithdrawal',
44
- 'drawdownInstruments',
45
- 'WithdrawalRecord',
46
- 'WithdrawalSchedule',
47
- 'InstrumentsWithdrawalSchedule',
48
- 'performWaterfallDrawdown',
49
- // Shared Math Primitives
50
- 'calculatePeriodicAmount',
51
- 'calculateBalanceRemaining',
52
- 'calculatePeriodsToZero',
53
- 'calculateInterestOverPeriods',
54
- // Errors
55
- 'NegativeContributionError',
56
- 'PaymentTooLowError',
57
- 'NegativeWithdrawalError',
58
- // Shared Utilities
59
- 'HasRateAndBalance',
60
- 'snowball',
61
- 'avalanche',
62
- 'sortWith',
63
- ].sort();
64
- expect(Object.keys(moneyfunx).sort()).toStrictEqual(expectedExports);
65
- }));
66
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=contributions.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"contributions.test.d.ts","sourceRoot":"","sources":["../../../tests/investment/contributions.test.ts"],"names":[],"mappings":""}
@@ -1,113 +0,0 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- import { describe, expect, it } from 'vitest';
11
- import * as constants from '@/lib/constants';
12
- import { Instrument } from '@/lib/investment/instrument';
13
- import * as contributions from '@/lib/investment/contributions';
14
- const Instruments = () => [
15
- new Instrument(10000, 0.11, 12, 'IRA', 6500),
16
- new Instrument(45000, 0.085, 12, '401(K)', 23500),
17
- new Instrument(0, 0.042666667, 12, 'ABC'),
18
- ];
19
- describe('contributions module', () => {
20
- const instruments = Instruments();
21
- const [inst1, inst2, inst3] = instruments;
22
- it('amortizes a single instrument', () => __awaiter(void 0, void 0, void 0, function* () {
23
- const iraAmortizationSchedulePre = contributions.amortizeContributions(inst1, inst1.currentBalance, inst1.periodicContribution(), 60, 0, true);
24
- expect(iraAmortizationSchedulePre.length).toBe(60);
25
- expect(iraAmortizationSchedulePre[3].period).toBe(4);
26
- expect(iraAmortizationSchedulePre[3].contribution).toBeCloseTo(541.666666, 5);
27
- expect(iraAmortizationSchedulePre[3].growth).toBeCloseTo(109.243473, 5);
28
- expect(iraAmortizationSchedulePre[3].currentBalance).toBeCloseTo(12568.380024, 5);
29
- expect(iraAmortizationSchedulePre[44].period).toBe(45);
30
- expect(iraAmortizationSchedulePre[44].contribution).toBeCloseTo(541.666666, 5);
31
- expect(iraAmortizationSchedulePre[44].growth).toBeCloseTo(404.570099, 5);
32
- expect(iraAmortizationSchedulePre[44].currentBalance).toBeCloseTo(45081.156667, 5);
33
- const iraAmortizationSchedulePost = contributions.amortizeContributions(inst1, inst1.currentBalance, inst1.periodicContribution(), 60, 0, false);
34
- expect(iraAmortizationSchedulePost.length).toBe(60);
35
- expect(iraAmortizationSchedulePost[3].period).toBe(4);
36
- expect(iraAmortizationSchedulePost[3].contribution).toBeCloseTo(541.666666, 5);
37
- expect(iraAmortizationSchedulePost[3].growth).toBeCloseTo(114.346552, 5);
38
- expect(iraAmortizationSchedulePost[3].currentBalance).toBeCloseTo(12588.515898, 5);
39
- expect(iraAmortizationSchedulePost[44].period).toBe(45);
40
- expect(iraAmortizationSchedulePost[44].contribution).toBeCloseTo(541.666666, 5);
41
- expect(iraAmortizationSchedulePost[44].growth).toBeCloseTo(411.988512, 5);
42
- expect(iraAmortizationSchedulePost[44].currentBalance).toBeCloseTo(45356.189858, 5);
43
- }));
44
- it('uses annualLimits on instruments', () => __awaiter(void 0, void 0, void 0, function* () {
45
- const workAcctAmortizationSchedule = contributions.amortizeContributions(inst2, inst2.currentBalance, 2000, 24);
46
- expect(workAcctAmortizationSchedule.length).toBe(24);
47
- expect(workAcctAmortizationSchedule[3].period).toBe(4);
48
- expect(workAcctAmortizationSchedule[3].contribution).toBe(23500 / 12);
49
- expect(workAcctAmortizationSchedule[3].growth).toBeCloseTo(367.481578, 5);
50
- expect(workAcctAmortizationSchedule[3].currentBalance).toBeCloseTo(54205.567182, 5);
51
- expect(workAcctAmortizationSchedule[23].period).toBe(24);
52
- expect(workAcctAmortizationSchedule[23].contribution).toBe(23500 / 12);
53
- expect(workAcctAmortizationSchedule[23].growth).toBeCloseTo(720.115315, 5);
54
- expect(workAcctAmortizationSchedule[23].currentBalance).toBeCloseTo(104341.787377, 5);
55
- }));
56
- it('determines extra contributions', () => __awaiter(void 0, void 0, void 0, function* () {
57
- expect(contributions.determineExtraContribution(instruments, 2250)).toBe(0);
58
- expect(contributions.determineExtraContribution(instruments, 3000)).toBe(500);
59
- }));
60
- it('amortizes contributions for multiple instruments', () => __awaiter(void 0, void 0, void 0, function* () {
61
- const instrumentsContributionSummary = contributions.contributeInstruments(instruments, 2250, 300);
62
- expect(Object.keys(instrumentsContributionSummary).length).toBe(4);
63
- expect(instrumentsContributionSummary[inst1.id].lifetimeContribution).toBeCloseTo(162499.999999, 5);
64
- expect(instrumentsContributionSummary[inst1.id].lifetimeGrowth).toBeCloseTo(835717.757059, 5);
65
- expect(instrumentsContributionSummary[inst1.id].amortizationSchedule.length).toBe(300);
66
- // correct below given current code
67
- // but need to write ways to default/spread a single contribution across instruments
68
- expect(instrumentsContributionSummary[inst3.id].lifetimeContribution).toBe(0);
69
- expect(instrumentsContributionSummary[inst3.id].lifetimeGrowth).toBe(0);
70
- expect(instrumentsContributionSummary[inst3.id].amortizationSchedule.length).toBe(300);
71
- expect(instrumentsContributionSummary[constants.TOTALS].lifetimeContribution).toBeCloseTo(674999.999999, 5);
72
- expect(instrumentsContributionSummary[constants.TOTALS].lifetimeGrowth).toBeCloseTo(2415285.956132, 5);
73
- expect(instrumentsContributionSummary[constants.TOTALS].amortizationSchedule.length).toBe(300);
74
- // period 28
75
- expect(instrumentsContributionSummary[inst1.id].amortizationSchedule[27].period).toBe(28);
76
- expect(instrumentsContributionSummary[inst1.id].amortizationSchedule[27].contribution).toBeCloseTo(541.666666, 5);
77
- expect(instrumentsContributionSummary[inst1.id].amortizationSchedule[27].growth).toBeCloseTo(268.604841, 5);
78
- expect(instrumentsContributionSummary[inst1.id].amortizationSchedule[27].currentBalance).toBeCloseTo(30112.617826, 5);
79
- expect(instrumentsContributionSummary[inst2.id].amortizationSchedule[27].period).toBe(28);
80
- expect(instrumentsContributionSummary[inst2.id].amortizationSchedule[27].contribution).toBeCloseTo(1708.333333, 5);
81
- expect(instrumentsContributionSummary[inst2.id].amortizationSchedule[27].growth).toBeCloseTo(744.328300, 5);
82
- expect(instrumentsContributionSummary[inst2.id].amortizationSchedule[27].currentBalance).toBeCloseTo(107534.304119, 5);
83
- // correct below given current code
84
- // but need to write ways to default/spread a single contribution across instruments
85
- expect(instrumentsContributionSummary[inst3.id].amortizationSchedule[27].period).toBe(28);
86
- expect(instrumentsContributionSummary[inst3.id].amortizationSchedule[27].contribution).toBe(0);
87
- expect(instrumentsContributionSummary[inst3.id].amortizationSchedule[27].growth).toBe(0);
88
- expect(instrumentsContributionSummary[inst3.id].amortizationSchedule[27].currentBalance).toBe(0);
89
- expect(instrumentsContributionSummary[constants.TOTALS].amortizationSchedule[27].period).toBe(28);
90
- expect(instrumentsContributionSummary[constants.TOTALS].amortizationSchedule[27].contribution).toBe(2250);
91
- expect(instrumentsContributionSummary[constants.TOTALS].amortizationSchedule[27].growth).toBeCloseTo(1012.933142, 5);
92
- expect(instrumentsContributionSummary[constants.TOTALS].amortizationSchedule[27].currentBalance).toBeCloseTo(137646.921945, 5);
93
- // period 272
94
- expect(instrumentsContributionSummary[inst1.id].amortizationSchedule[271].period).toBe(272);
95
- expect(instrumentsContributionSummary[inst1.id].amortizationSchedule[271].contribution).toBeCloseTo(541.666666, 5);
96
- expect(instrumentsContributionSummary[inst1.id].amortizationSchedule[271].growth).toBeCloseTo(6967.253017, 5);
97
- expect(instrumentsContributionSummary[inst1.id].amortizationSchedule[271].currentBalance).toBeCloseTo(767572.885191, 5);
98
- expect(instrumentsContributionSummary[inst2.id].amortizationSchedule[271].period).toBe(272);
99
- expect(instrumentsContributionSummary[inst2.id].amortizationSchedule[271].contribution).toBeCloseTo(1708.333333, 5);
100
- expect(instrumentsContributionSummary[inst2.id].amortizationSchedule[271].growth).toBeCloseTo(12019.353385, 5);
101
- expect(instrumentsContributionSummary[inst2.id].amortizationSchedule[271].currentBalance).toBeCloseTo(1710577.576386, 5);
102
- // correct below given current code
103
- // but need to write ways to default/spread a single contribution across instruments
104
- expect(instrumentsContributionSummary[inst3.id].amortizationSchedule[271].period).toBe(272);
105
- expect(instrumentsContributionSummary[inst3.id].amortizationSchedule[271].contribution).toBe(0);
106
- expect(instrumentsContributionSummary[inst3.id].amortizationSchedule[271].growth).toBe(0);
107
- expect(instrumentsContributionSummary[inst3.id].amortizationSchedule[271].currentBalance).toBe(0);
108
- expect(instrumentsContributionSummary[constants.TOTALS].amortizationSchedule[271].period).toBe(272);
109
- expect(instrumentsContributionSummary[constants.TOTALS].amortizationSchedule[271].contribution).toBe(2250);
110
- expect(instrumentsContributionSummary[constants.TOTALS].amortizationSchedule[271].growth).toBeCloseTo(18986.606402, 5);
111
- expect(instrumentsContributionSummary[constants.TOTALS].amortizationSchedule[271].currentBalance).toBeCloseTo(2478150.461577, 5);
112
- }));
113
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=instrument.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"instrument.test.d.ts","sourceRoot":"","sources":["../../../tests/investment/instrument.test.ts"],"names":[],"mappings":""}
@@ -1,57 +0,0 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- import { describe, expect, it } from 'vitest';
11
- import { Instrument } from '@/lib/investment/instrument';
12
- const Instruments = () => [
13
- new Instrument(10000, 0.11, 12, 'IRA', 6500),
14
- new Instrument(45000, 0.085, 12, '401(K)', 23500),
15
- new Instrument(0, 0.042666667, 12, 'ABC'),
16
- ];
17
- describe('instrument module', () => {
18
- const [inst1, inst2, inst3] = Instruments();
19
- it('creates an Instrument with proper attributes', () => __awaiter(void 0, void 0, void 0, function* () {
20
- expect(inst1.name).toBe('IRA');
21
- expect(inst1.currentBalance).toBe(10000);
22
- expect(inst1.annualRate).toBe(0.11);
23
- expect(inst1.periodicRate).toBeCloseTo(0.009166, 5);
24
- expect(inst1.periodicContribution()).toBeCloseTo(541.666666, 5);
25
- expect(inst1.annualLimit).toBe(6500);
26
- }));
27
- it('defaults attributes appropriately', () => __awaiter(void 0, void 0, void 0, function* () {
28
- expect(inst3.annualLimit).toBe(0);
29
- expect(inst3.periodicContribution()).toBe(0);
30
- }));
31
- it('validates contributions', () => __awaiter(void 0, void 0, void 0, function* () {
32
- const negativeContribution = -177;
33
- expect(inst2.validateContribution(2000, 22400)).toBe(1100);
34
- expect(inst3.validateContribution(2000, 22400)).toBe(2000);
35
- expect(inst2.validateContribution(2000, 24400)).toBe(0);
36
- expect(() => inst1.validateContribution(negativeContribution, 2000)).toThrow(`contribution of ${negativeContribution} must be greater than/equal to zero`);
37
- }));
38
- it('accrues interest', () => __awaiter(void 0, void 0, void 0, function* () {
39
- expect(inst2.accrueInterest()).toBe(318.75);
40
- expect(inst2.accrueInterest(1000)).toBeCloseTo(7.083333, 5);
41
- expect(inst3.accrueInterest()).toBe(0);
42
- expect(inst3.accrueInterest(200)).toBeCloseTo(0.711111, 5);
43
- }));
44
- describe('instrument module - drawdown methods', () => {
45
- const testInstrument = new Instrument(10000, 0.06, 12, 'Test Acc');
46
- it('calculates periods to zero (numWithdrawalsToZero)', () => {
47
- // Covers instrument.ts lines 87-95
48
- const periods = testInstrument.numWithdrawalsToZero(500);
49
- expect(periods).toBe(22);
50
- });
51
- it('calculates maximum sustainable withdrawal (calculateMaxWithdrawal)', () => {
52
- // Covers instrument.ts lines 105-113
53
- const maxWithdrawal = testInstrument.calculateMaxWithdrawal(12);
54
- expect(maxWithdrawal).toBeCloseTo(860.66, 2);
55
- });
56
- });
57
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=strategies.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"strategies.test.d.ts","sourceRoot":"","sources":["../../../tests/investment/strategies.test.ts"],"names":[],"mappings":""}
@@ -1,15 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { Instrument } from '@/lib/investment/instrument';
3
- import { performWaterfallDrawdown } from '@/lib/investment/strategies';
4
- import * as constants from '@/lib/constants';
5
- describe('strategies module', () => {
6
- it('executes a waterfall drawdown and handles depletion', () => {
7
- const smallAccount = new Instrument(1000, 0.05, 12, 'Small');
8
- const largeAccount = new Instrument(100000, 0.05, 12, 'Large');
9
- // Request more than Small has in one period
10
- const results = performWaterfallDrawdown([smallAccount, largeAccount], 5000, 1, 0);
11
- expect(results[smallAccount.id].amortizationSchedule[0].currentBalance).toBe(0);
12
- expect(results[largeAccount.id].amortizationSchedule[0].withdrawal).toBeGreaterThan(0);
13
- expect(results[constants.TOTALS].lifetimeWithdrawal).toBe(5000);
14
- });
15
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=withdrawals.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"withdrawals.test.d.ts","sourceRoot":"","sources":["../../../tests/investment/withdrawals.test.ts"],"names":[],"mappings":""}
@@ -1,66 +0,0 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- import { describe, expect, it } from 'vitest';
11
- import * as constants from '@/lib/constants';
12
- import { Instrument } from '@/lib/investment/instrument';
13
- import { calculateAmortizedWithdrawal, drawdownInstruments } from '@/lib/investment/withdrawals';
14
- const Instruments = () => [
15
- new Instrument(50000, 0.06, 12, 'Taxable Account'),
16
- new Instrument(100000, 0.08, 12, 'IRA Account'),
17
- ];
18
- describe('withdrawals module', () => {
19
- const [taxableAccount, iraAccount] = Instruments();
20
- it('calculates a single period amortized withdrawal (Accrue Before)', () => __awaiter(void 0, void 0, void 0, function* () {
21
- const record = calculateAmortizedWithdrawal(taxableAccount, 50000, 2000, 0, 0.15, true);
22
- // 50,000 * (0.06/12) = 250 growth
23
- // 50,250 - 2000 withdrawal = 48,250
24
- expect(record.growth).toBe(250);
25
- expect(record.currentBalance).toBe(48250);
26
- expect(record.netAmount).toBe(2000 * 0.85);
27
- }));
28
- it('calculates a single period amortized withdrawal (Withdraw Before)', () => __awaiter(void 0, void 0, void 0, function* () {
29
- // This covers withdrawals.ts lines 51-63
30
- const record = calculateAmortizedWithdrawal(taxableAccount, 50000, 2000, 0, 0.15, false);
31
- // 50,000 - 2000 = 48,000
32
- // 48,000 * (0.06/12) = 240 growth
33
- // 48,000 + 240 = 48,240
34
- expect(record.growth).toBe(240);
35
- expect(record.currentBalance).toBe(48240);
36
- expect(record.netAmount).toBe(1700);
37
- }));
38
- it('draws down multiple instruments and validates comprehensive schedules', () => __awaiter(void 0, void 0, void 0, function* () {
39
- const targetNetIncome = 3000;
40
- const simulationDuration = 36;
41
- const taxRate = 0.10;
42
- const summary = drawdownInstruments([taxableAccount, iraAccount], targetNetIncome, simulationDuration, taxRate);
43
- // Verify lifetime totals match the sum of individual schedules (style of payments.test.ts)
44
- for (const instrument of [taxableAccount, iraAccount]) {
45
- const computedLifetimeGrowth = summary[instrument.id].amortizationSchedule
46
- .reduce((sum, record) => sum + record.growth, 0);
47
- expect(summary[instrument.id].lifetimeGrowth).toBeCloseTo(computedLifetimeGrowth, 5);
48
- }
49
- // Verify global totals structure
50
- expect(Object.keys(summary).length).toBe(3); // 2 instruments + TOTALS
51
- expect(summary[constants.TOTALS].amortizationSchedule.length).toBe(36);
52
- // Check final period balance
53
- const lastRecord = summary[constants.TOTALS].amortizationSchedule[35];
54
- expect(lastRecord.period).toBe(36);
55
- expect(lastRecord.currentBalance).toBeLessThan(150000);
56
- }));
57
- describe('withdrawals module - validation', () => {
58
- const testAccount = new Instrument(10000, 0.05, 12, 'Test Account');
59
- it('throws a NegativeWithdrawalError when the withdrawal amount is less than zero', () => {
60
- const negativeWithdrawalAmount = -500;
61
- expect(() => {
62
- calculateAmortizedWithdrawal(testAccount, testAccount.currentBalance, negativeWithdrawalAmount, 0, 0.15);
63
- }).toThrow(`withdrawal of ${negativeWithdrawalAmount} must be greater than/equal to zero`);
64
- });
65
- });
66
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=primitives.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"primitives.test.d.ts","sourceRoot":"","sources":["../../../tests/shared/primitives.test.ts"],"names":[],"mappings":""}
@@ -1,76 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import * as sharedFinancialPrimitives from '@/lib/shared/primitives';
3
- /**
4
- * Unit tests for shared financial primitives.
5
- * These tests ensure the core mathematical engine for the library is accurate
6
- * across various interest rates and durations.
7
- */
8
- describe('shared financial primitives', () => {
9
- // Shared test constants for consistency across test cases
10
- const initialPrincipalBalance = 10000;
11
- const annualInterestRate = 0.05;
12
- const periodicInterestRate = annualInterestRate / 12;
13
- const totalLoanPeriods = 120; // 10 years in months
14
- describe('calculatePeriodicAmount (PMT)', () => {
15
- it('should calculate the standard amortization payment correctly', () => {
16
- const calculatedPeriodicAmount = sharedFinancialPrimitives.calculatePeriodicAmount(initialPrincipalBalance, periodicInterestRate, totalLoanPeriods);
17
- // Expected: $10,000 at 5% for 120 periods is ~$106.0655
18
- expect(calculatedPeriodicAmount).toBeCloseTo(106.0655, 4);
19
- });
20
- it('should handle zero percent interest rates via simple division', () => {
21
- const simplePrincipalBalance = 12000;
22
- const zeroInterestRate = 0;
23
- const twelveMonths = 12;
24
- const calculatedPeriodicAmount = sharedFinancialPrimitives.calculatePeriodicAmount(simplePrincipalBalance, zeroInterestRate, twelveMonths);
25
- expect(calculatedPeriodicAmount).toBe(1000);
26
- });
27
- });
28
- describe('calculateBalanceRemaining (FV)', () => {
29
- it('should calculate the correct remaining balance after a specific number of periods', () => {
30
- const fixedPeriodicPayment = 150;
31
- const periodsElapsed = 12;
32
- const remainingBalance = sharedFinancialPrimitives.calculateBalanceRemaining(initialPrincipalBalance, fixedPeriodicPayment, periodicInterestRate, periodsElapsed);
33
- // Corrected math expectation for 1 year of $150 payments at 5%
34
- expect(remainingBalance).toBeCloseTo(8669.7907, 4);
35
- });
36
- it('should return zero if the balance is overpaid within the specified periods', () => {
37
- const smallInitialBalance = 1000;
38
- const largePeriodicPayment = 1200;
39
- const singlePeriod = 1;
40
- const remainingBalance = sharedFinancialPrimitives.calculateBalanceRemaining(smallInitialBalance, largePeriodicPayment, periodicInterestRate, singlePeriod);
41
- expect(remainingBalance).toBe(0);
42
- });
43
- it('should calculate the correct remaining balance for zero percent interest scenarios', () => {
44
- const zeroInterestRate = 0;
45
- const fixedPeriodicPayment = 100;
46
- const fivePeriodsElapsed = 5;
47
- const remainingBalance = sharedFinancialPrimitives.calculateBalanceRemaining(1000, fixedPeriodicPayment, zeroInterestRate, fivePeriodsElapsed);
48
- expect(remainingBalance).toBe(500);
49
- });
50
- });
51
- describe('calculatePeriodsToZero (NPER)', () => {
52
- it('should calculate the total number of periods required to reach a zero balance', () => {
53
- const fixedPeriodicPayment = 200;
54
- const totalPeriodsRequired = sharedFinancialPrimitives.calculatePeriodsToZero(initialPrincipalBalance, fixedPeriodicPayment, periodicInterestRate);
55
- // $10,000 at 5% with $200 payments rounds up to 57 months
56
- expect(totalPeriodsRequired).toBe(57);
57
- });
58
- });
59
- describe('calculateInterestOverPeriods', () => {
60
- it('should calculate the total interest paid during a set duration', () => {
61
- // Using a slightly more precise payment to match amortization curve
62
- const fixedPeriodicPayment = 106.07;
63
- const oneYearInMonths = 12;
64
- const totalInterestPaid = sharedFinancialPrimitives.calculateInterestOverPeriods(initialPrincipalBalance, fixedPeriodicPayment, periodicInterestRate, oneYearInMonths);
65
- // Total interest for the first year should be approximately $482.04
66
- expect(totalInterestPaid).toBeCloseTo(482.04, 2);
67
- });
68
- it('should return zero interest for loans with a zero percent interest rate', () => {
69
- const zeroInterestRate = 0;
70
- const fixedPeriodicPayment = 100;
71
- const fivePeriods = 5;
72
- const totalInterestPaid = sharedFinancialPrimitives.calculateInterestOverPeriods(1000, fixedPeriodicPayment, zeroInterestRate, fivePeriods);
73
- expect(totalInterestPaid).toBe(0);
74
- });
75
- });
76
- });
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes