financial-calcs
Version:
Reusable financial calculation library for FERS, Social Security, retirement savings, and mortgage amortization
75 lines • 3.01 kB
JavaScript
export function validateMortgageInput(input) {
const errors = [];
const { loanAmount, annualRate, termYears, extraPayment } = input;
if (loanAmount <= 0)
errors.push({ field: "loanAmount", message: "Loan amount must be greater than 0" });
if (annualRate < 0)
errors.push({ field: "annualRate", message: "Annual interest rate cannot be negative" });
if (termYears <= 0)
errors.push({ field: "termYears", message: "Loan term must be greater than 0" });
if (extraPayment !== undefined && extraPayment < 0)
errors.push({ field: "extraPayment", message: "Extra payment cannot be negative" });
return errors;
}
// --- Pure calculation: monthly amortization ---
export function calculateMortgageAmortization(input) {
const { loanAmount, annualRate, termYears, startDate = new Date(), extraPayment = 0, } = input;
const errors = validateMortgageInput(input);
if (errors.length > 0) {
const err = new Error("Mortgage Amortization input validation failed");
err.validationErrors = errors;
throw err;
}
const monthlyRate = annualRate / 100 / 12;
const totalMonths = termYears * 12;
const basePayment = monthlyRate === 0
? loanAmount / totalMonths
: loanAmount *
(monthlyRate * Math.pow(1 + monthlyRate, totalMonths)) /
(Math.pow(1 + monthlyRate, totalMonths) - 1);
let balance = loanAmount;
const data = [];
for (let i = 1; balance > 0.01 && i <= totalMonths; i++) {
const interest = balance * monthlyRate;
const principal = Math.min(basePayment + extraPayment - interest, balance);
balance -= principal;
const paymentDate = new Date(startDate.getFullYear(), startDate.getMonth() + i);
data.push({
year: Math.ceil(i / 12),
month: i,
date: paymentDate.toLocaleDateString(),
payment: principal + interest,
principal,
interest,
balance: Math.max(balance, 0),
});
}
return data;
}
// --- Pure calculation: yearly aggregation ---
export function groupByYear(rows) {
const yearlyMap = {};
rows.forEach((row) => {
const year = Math.floor((row.month - 1) / 12) + 1; // Loan year #1, #2, etc.
if (!yearlyMap[year]) {
yearlyMap[year] = {
year,
month: row.month,
date: row.date,
payment: 0,
principal: 0,
interest: 0,
balance: row.balance,
};
}
yearlyMap[year].payment += row.payment;
yearlyMap[year].principal += row.principal;
yearlyMap[year].interest += row.interest;
// overwrite with last month’s values
yearlyMap[year].month = row.month;
yearlyMap[year].date = row.date;
yearlyMap[year].balance = row.balance;
});
return Object.values(yearlyMap);
}
//# sourceMappingURL=amortization.js.map