import type { MortgageInput, MortgageResult } from '../types/real-estate.js';
/**
* Calculate monthly mortgage payment using standard amortization formula
* M = P [ i(1 + i)^n ] / [ (1 + i)^n – 1]
* Where:
* - M = Monthly payment
* - P = Principal (loan amount)
* - i = Monthly interest rate
* - n = Number of months
*/
export function calculateMortgage(input: MortgageInput): MortgageResult {
// Validate inputs
if (input.homePrice <= 0) {
throw new Error('Home price must be greater than 0');
}
if (input.downPayment < 0) {
throw new Error('Down payment cannot be negative');
}
if (input.downPayment >= input.homePrice) {
throw new Error('Down payment must be less than home price');
}
if (input.annualInterestRate < 0) {
throw new Error('Interest rate cannot be negative');
}
if (input.termYears <= 0) {
throw new Error('Loan term must be greater than 0');
}
// Calculate loan amount
const principal = input.homePrice - input.downPayment;
// Calculate monthly interest rate
const monthlyInterestRate = input.annualInterestRate / 100 / 12;
// Calculate number of payments
const numberOfPayments = input.termYears * 12;
// Calculate monthly P&I payment
let principalAndInterest = 0;
if (monthlyInterestRate === 0) {
// Handle 0% interest rate case
principalAndInterest = principal / numberOfPayments;
} else {
// Standard amortization formula
const factor = Math.pow(1 + monthlyInterestRate, numberOfPayments);
principalAndInterest = principal * (monthlyInterestRate * factor) / (factor - 1);
}
// Calculate monthly property tax
const monthlyTax = (input.propertyTaxAnnual || 0) / 12;
// Calculate monthly insurance
const monthlyInsurance = (input.insuranceAnnual || 0) / 12;
// HOA is already monthly
const monthlyHOA = input.hoaMonthly || 0;
// PMI - typically required if down payment < 20%
let monthlyPMI = input.pmiMonthly || 0;
if (!input.pmiMonthly && input.downPayment / input.homePrice < 0.20) {
// Estimate PMI as 0.5% of loan amount annually if not provided
monthlyPMI = principal * 0.005 / 12;
}
// Calculate total monthly payment
const totalMonthlyPayment =
principalAndInterest +
monthlyTax +
monthlyInsurance +
monthlyHOA +
monthlyPMI;
// Calculate a simple APR estimate (doesn't include all fees)
// This is a naive calculation - real APR calculation is more complex
const totalInterest = (principalAndInterest * numberOfPayments) - principal;
const averageBalance = principal / 2; // Rough approximation
const aprEstimate = (totalInterest / numberOfPayments / averageBalance) * 12 * 100;
return {
principalAndInterest: Math.round(principalAndInterest * 100) / 100,
monthlyTax: Math.round(monthlyTax * 100) / 100,
monthlyInsurance: Math.round(monthlyInsurance * 100) / 100,
monthlyHOA: Math.round(monthlyHOA * 100) / 100,
monthlyPMI: Math.round(monthlyPMI * 100) / 100,
totalMonthlyPayment: Math.round(totalMonthlyPayment * 100) / 100,
aprEstimate: Math.round(aprEstimate * 100) / 100
};
}
/**
* Calculate how much house someone can afford based on monthly payment
* @param monthlyPayment Maximum monthly payment
* @param downPayment Down payment amount
* @param annualInterestRate Annual interest rate (e.g., 6.5 for 6.5%)
* @param termYears Loan term in years (e.g., 30)
* @returns Maximum home price
*/
export function calculateAffordability(
monthlyPayment: number,
downPayment: number,
annualInterestRate: number,
termYears: number = 30
): number {
// This is the reverse of the mortgage calculation
// We'll use the payment to find the max principal
const monthlyInterestRate = annualInterestRate / 100 / 12;
const numberOfPayments = termYears * 12;
let maxPrincipal: number;
if (monthlyInterestRate === 0) {
maxPrincipal = monthlyPayment * numberOfPayments;
} else {
const factor = Math.pow(1 + monthlyInterestRate, numberOfPayments);
maxPrincipal = monthlyPayment * (factor - 1) / (monthlyInterestRate * factor);
}
const maxHomePrice = maxPrincipal + downPayment;
return Math.round(maxHomePrice);
}