diff --git a/README.md b/README.md index 17f22a2..a2aa68b 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ For example, if you invest $1,000 today at a 7% annual interest rate, how much w #### Compound Interest - [x] 1. calculate compound interest of a lump sum over time -- [x] 2. calculate compound interest with additional contributions +- [x] 2. calculate compound interest with additional contributions and annual contribution adjustments - [x] 3. calculate compound interest with interest only payments towards the principal borrowed - [x] 4. calculate compound interest with repayments towards the principal @@ -74,7 +74,7 @@ const lumpSum = compoundInterestPerPeriod({ paymentsPerAnnum: 12 // displays monthly interest balance }); -// calculate a lump sum over 2 years with additional contributions of 500 per month + // calculate a lump sum over 2 years with additional contributions of 500 per month with 2% adjustment every year const additionalContributions = compoundInterestPerPeriod({ type: "contribution", principal: 500, @@ -82,6 +82,7 @@ const additionalContributions = compoundInterestPerPeriod({ years: 2, paymentsPerAnnum: 12, amountPerAnnum: 6_000, + contributionPerAnnumChange: 2, accrualOfPaymentsPerAnnum: true }); @@ -126,7 +127,8 @@ const repayment = compoundInterestPerPeriod({ ###### Contribution Options - `amountPerAnnum: number` The amount of contributions per annum (eg 6_000 for 500 per month) -- `accrualOfPaymentsPerAnnum: number` If provided payments accrue interest per annum; Otherwise interest is only accrued on the principal payment. +- `accrualOfPaymentsPerAnnum: boolean` If provided payments accrue interest per annum; Otherwise interest is only accrued on the principal payment. +- `contributionPerAnnumChange: number` Changes of annual contribution in percents (to adjust contribution according inflation rates, good for long investments) ###### Debt Repayment Options diff --git a/calc/compoundInterest.test.ts b/calc/compoundInterest.test.ts index d624f9f..5eb08ea 100644 --- a/calc/compoundInterest.test.ts +++ b/calc/compoundInterest.test.ts @@ -592,4 +592,30 @@ describe("compoundInterestPerPeriod", () => { }); }); }); + + describe("contributionPerAnnumChange", () => { + it("when contributionPerAnnumChange is supplied it increases annual additional contribution amountPerAnnum", () => { + const options: IOptions = { + type: "contribution", + principal: 250_000, + rate: 7.8, + years: 25, + paymentsPerAnnum: 1, + amountPerAnnum: 12_000, + contributionPerAnnumChange: 3, + currentPositionInYears: 1, + accrualOfPaymentsPerAnnum: true + }; + const result = compoundInterestPerPeriod(options); + expect(result).toMatchObject( + expect.objectContaining({ + currentBalance: 282436, + totalInterest: 2144895.2679852117, + endBalance: 2832406.46, + accrualOfPaymentsPerAnnum: true, + investmentType: "contribution" + }) + ); + }); + }); }); diff --git a/calc/compoundInterest.ts b/calc/compoundInterest.ts index b1dc317..f93b746 100644 --- a/calc/compoundInterest.ts +++ b/calc/compoundInterest.ts @@ -41,7 +41,15 @@ export const calcTotalInvestment = (options: IOptions, investmentType: Investmen const { principal, years, paymentsPerAnnum = 1 } = options; if (investmentType === "contribution" && "amountPerAnnum" in options) { - const { amountPerAnnum = 0 } = options; + const { amountPerAnnum = 0, contributionPerAnnumChange = 0 } = options; + // Adjust annual contributions if contributionPerAnnumChange rate provided + if (contributionPerAnnumChange > 0) { + let repaymentWithAnnualChange = amountPerAnnum; + for (let i = 1; i < years; i++) { + repaymentWithAnnualChange += (repaymentWithAnnualChange * contributionPerAnnumChange) / 100 + amountPerAnnum; + } + return principal * years + repaymentWithAnnualChange; + } return principal + amountPerAnnum * years; } @@ -94,6 +102,18 @@ export const calcInterestPayments = (principal: number, interestRate: number, pa * amountPerAnnum: 6_000, * accrualOfPaymentsPerAnnum: true * }); + * @example + * // calculate a lump sum over 2 years with additional contributions of 500 per month with 2% adjustment every year + * const additionalContributions = compoundInterestPerPeriod({ + * type: "contribution", + * principal: 500, + * rate: 3.4, + * years: 2, + * paymentsPerAnnum: 12, + * amountPerAnnum: 6_000, + * contributionPerAnnumChange: 2, + * accrualOfPaymentsPerAnnum: true + * }); * * @example * // example interest only payment that compounds at 4% per annum @@ -115,6 +135,7 @@ export const compoundInterestPerPeriod = (options: IOptions): CompoundInterestRe const { type: investmentType, principal, years, paymentsPerAnnum = 1, currentPositionInYears } = options; let amountPerAnnum = 0; + let contributionPerAnnumChange; let accrualOfPaymentsPerAnnum = false; if ("amountPerAnnum" in options && options.amountPerAnnum && options.amountPerAnnum > 0) { @@ -125,6 +146,10 @@ export const compoundInterestPerPeriod = (options: IOptions): CompoundInterestRe accrualOfPaymentsPerAnnum = options.accrualOfPaymentsPerAnnum; } + if ("contributionPerAnnumChange" in options && options.contributionPerAnnumChange !== undefined) { + contributionPerAnnumChange = options.contributionPerAnnumChange; + } + // if rate is provided as a percentage, convert to decimal if (rate >= 1) { rate = rate / 100; @@ -170,6 +195,10 @@ export const compoundInterestPerPeriod = (options: IOptions): CompoundInterestRe for (let p = 0; p < paymentsPerAnnum; p++) { if (accrualOfPaymentsPerAnnum) { + // Adjust contributions only from the 2nd year + if (i >= 1 && contributionPerAnnumChange) { + amountPerAnnum = amountPerAnnum * (1 + contributionPerAnnumChange / 100); + } const newBalanceWithAccrual = prevBalance + amountPerAnnum / paymentsPerAnnum; const interest = newBalanceWithAccrual * ratePerPeriod; prevBalance = prevBalance + interest + amountPerAnnum / paymentsPerAnnum; diff --git a/types/calculator.ts b/types/calculator.ts index eb3dabe..e642fad 100644 --- a/types/calculator.ts +++ b/types/calculator.ts @@ -46,6 +46,7 @@ export type ContributionOptions = { type: "contribution"; paymentsPerAnnum?: number; amountPerAnnum?: number; + contributionPerAnnumChange?: number; accrualOfPaymentsPerAnnum?: boolean; };