/** * Student Loan Matching Calculator Logic * Calculates net worth over time with and without SECURE Act 2.0 matching */ const INVESTMENT_RETURN_RATE = 0.07; // 7% annual return const LOAN_INTEREST_RATE = 0.05; // 5% annual interest (federal student loan average) const RETIREMENT_AGE = 65; const MAX_CALCULATION_AGE = 70; // Calculate up to age 70 for complete data /** * Calculate loan payoff time in years * @param {number} totalDebt - Total loan balance * @param {number} monthlyPayment - Monthly payment amount * @returns {number} Years to pay off loan */ function calculatePayoffYears(totalDebt, monthlyPayment) { if (!monthlyPayment || monthlyPayment <= 0) return 0; const monthlyRate = LOAN_INTEREST_RATE / 12; const minPayment = totalDebt * monthlyRate; if (monthlyPayment <= minPayment) { return 50; // Loan will never be paid off at this rate } const months = Math.log(monthlyPayment / (monthlyPayment - totalDebt * monthlyRate)) / Math.log(1 + monthlyRate); return Math.ceil(months / 12); } /** * Calculate remaining loan balance after a number of months * @param {number} originalBalance - Original loan amount * @param {number} monthlyPayment - Monthly payment * @param {number} monthsElapsed - Months since start of loan * @param {number} totalMonths - Total months for loan term * @returns {number} Remaining balance */ function calculateRemainingBalance(originalBalance, monthlyPayment, monthsElapsed, totalMonths) { if (monthsElapsed <= 0) return originalBalance; if (monthsElapsed >= totalMonths) return 0; const monthlyRate = LOAN_INTEREST_RATE / 12; const factor = Math.pow(1 + monthlyRate, monthsElapsed); const paymentsValue = monthlyPayment * ((factor - 1) / monthlyRate); const remainingBalance = originalBalance * factor - paymentsValue; return Math.max(0, remainingBalance); } /** * Calculate future value of retirement contributions with compound growth * @param {number} annualContribution - Annual contribution amount * @param {number} yearsContributing - Number of years contributing * @param {number} yearsToGrow - Total years for growth (from first contribution to retirement) * @returns {number} Future value */ function calculateRetirementValue(annualContribution, yearsContributing, yearsToGrow) { let totalValue = 0; for (let year = 0; year < yearsContributing; year++) { const growthYears = yearsToGrow - year; totalValue += annualContribution * Math.pow(1 + INVESTMENT_RETURN_RATE, growthYears); } return totalValue; } /** * Main calculation function for net worth comparison * @param {Object} params - Input parameters * @returns {Object} Chart data with ages and net worth values */ function calculateNetWorthComparison(params) { const { salary = 0, totalDebt = 0, monthlyPayment = 0, matchRate = 0, currentAge = 28 } = params; // Calculate derived values const payoffYears = calculatePayoffYears(totalDebt, monthlyPayment); const annualMatch = Math.min(monthlyPayment * 12, salary * (matchRate / 100)); const payoffAge = currentAge + payoffYears; // Arrays for full calculation data (yearly) const allAges = []; const allWithProgram = []; const allWithoutProgram = []; // Calculate for each year from current to age 70 for (let age = currentAge; age <= MAX_CALCULATION_AGE; age++) { allAges.push(age); const yearsElapsed = age - currentAge; const monthsElapsed = yearsElapsed * 12; // Calculate remaining loan balance const totalMonths = payoffYears * 12; const remainingDebt = calculateRemainingBalance(totalDebt, monthlyPayment, monthsElapsed, totalMonths); if (age <= payoffAge) { // DURING LOAN PAYMENT PERIOD // With program: Get employer match while paying loans const yearsContributing = Math.min(yearsElapsed, payoffYears); const retirementValue = calculateRetirementValue(annualMatch, yearsContributing, yearsElapsed); const netWorthWith = retirementValue - remainingDebt; allWithProgram.push(Math.round(netWorthWith)); // Without program: No retirement savings, just debt const netWorthWithout = -remainingDebt; allWithoutProgram.push(Math.round(netWorthWithout)); } else { // AFTER LOANS PAID OFF const yearsSincePayoff = age - payoffAge; const postPayoffContribution = Math.min(monthlyPayment * 12, salary * (matchRate / 100)); // With program: Initial match during loan period + continued contributions after const duringLoanValue = calculateRetirementValue(annualMatch, payoffYears, yearsElapsed); const afterLoanValue = calculateRetirementValue(postPayoffContribution, yearsSincePayoff, yearsSincePayoff); const totalWithProgram = duringLoanValue + afterLoanValue; allWithProgram.push(Math.round(totalWithProgram)); // Without program: Only start contributing after loans paid off const totalWithoutProgram = calculateRetirementValue(postPayoffContribution, yearsSincePayoff, yearsSincePayoff); allWithoutProgram.push(Math.round(totalWithoutProgram)); } } // Now filter the data for chart display (up to age 65) // Show consistent points regardless of starting age: every 2 years on even ages // Plus always include the starting age and retirement age const ages = []; const withProgram = []; const withoutProgram = []; for (let i = 0; i < allAges.length; i++) { const age = allAges[i]; // Only include ages up to 65 for chart display if (age > RETIREMENT_AGE) break; // Include if: // 1. It's the starting age (first point) // 2. It's the retirement age (last point) // 3. It's an even age and at least 2 years from start const isStartAge = (i === 0); const isRetirementAge = (age === RETIREMENT_AGE); const isEvenInterval = (age % 2 === 0 && age > currentAge); if (isStartAge || isRetirementAge || isEvenInterval) { ages.push(age); withProgram.push(allWithProgram[i]); withoutProgram.push(allWithoutProgram[i]); } } return { ages, withProgram, withoutProgram, payoffYears, annualMatch, totalRetirementWith: allWithProgram[allWithProgram.length - 1], totalRetirementWithout: allWithoutProgram[allWithoutProgram.length - 1], retirementDifference: allWithProgram[allWithProgram.length - 1] - allWithoutProgram[allWithoutProgram.length - 1], // Include full yearly data if needed allAges, allWithProgram, allWithoutProgram }; } // Export for use in browser if (typeof window !== 'undefined') { window.StudentLoanCalculator = { calculatePayoffYears, calculateRemainingBalance, calculateRetirementValue, calculateNetWorthComparison, INVESTMENT_RETURN_RATE, LOAN_INTEREST_RATE, RETIREMENT_AGE, MAX_CALCULATION_AGE }; } // Export for Node.js testing if (typeof module !== 'undefined' && module.exports) { module.exports = { calculatePayoffYears, calculateRemainingBalance, calculateRetirementValue, calculateNetWorthComparison, INVESTMENT_RETURN_RATE, LOAN_INTEREST_RATE, RETIREMENT_AGE, MAX_CALCULATION_AGE }; }