library(tidyverse)
library(FinancialMath)
set.seed(123)McAuliffe Home Purchase
Introduction
The goal is to anticipate how much our costs will be for purchasing a house and servicing the mortgage.
We’ll start by setting a randomization seed and uploading packages.
Now we also create some functions we will use throughout. In all cases, we will sample from a distributions composed of 100,000 samples. This will help us capture uncertainty in how much things will cost.
# sample from lognormal distribution
sample_logn <- function(mean, sd) {
logn_dist <- rlnorm(n = 100000,
meanlog = log(mean^2 / sqrt(mean^2 + sd^2)),
sdlog = sqrt(log(1 + (sd^2/mean^2))))
return(logn_dist)
}
#sample from the beta distribution by specifying the mean proportion an its standard deviation
sample_beta <- function(mean_val, sd_val) {
n <- 100000
# calculate alpha and beta parameters
var_val <- sd_val^2
alpha <- ((1 - mean_val) / var_val - 1 / mean_val) * mean_val^2
beta <- alpha * (1 / mean_val - 1)
# Check and adjust alpha and beta to ensure they are valid for the beta distribution
if (alpha <= 0 | beta <= 0) {
stop("Invalid shape parameters: alpha and beta must be greater than 0.")
}
# sample from beta distribution
return(rbeta(n, alpha, beta))
}
#Summary statistics
calculate_summary_stats <- function(data) {
result <- data.frame(
Value = c(mean(data), median(data), quantile(data, 0.05), quantile(data, 0.95))
)
# Set row names
row.names(result) <- c("Mean", "Median", "5_Percentile", "95_Percentile")
return(result)
}
#I modified a tutorial on the FinancialMath package to generate a amortization table for a given loan. See:
# https://medium.com/analytics-vidhya/calculating-a-fixed-rate-mortgage-payment-in-r-2ce58b2d929e
# i = annual interest rate. it is divided by 12 to get monthly rate
# n = 12*30 monthly payments
generate_amort_table <- function(principal, interest_rate) {
amortization_table<-amort.table(Loan = principal, i = interest_rate/12, n = 12*30)
# Extract relevant values
monthly_payment <- amortization_table$Schedule[1, "Payment"]
total_paid <- amortization_table$Other["Total Paid", "Details"]
total_interest <- amortization_table$Other["Total Interest", "Details"]
return(c(monthly_payment = monthly_payment, total_paid = total_paid, total_interest = total_interest))
}Home Cost
We can either:
(1) choose a home price that we want and see what we would need to obtain it, or
(2) choose a savings strategy and a purchase date and see how expensive of a home we could afford.
I’ll start with (1) for now. The home price determines everything else. I created a distribution centered on $500,000. It is slightly positively skewed to reflect that we’re more likely to spend less than $500,000. On this model, there is a 90% chance our costs between ~$353,000 and ~$679,000.
# Sample from lognormal distribution
house_cost_samples <- sample_logn(500000, 100000)
#median, 90% of samples
calculate_summary_stats(house_cost_samples) Value
Mean 500088.6
Median 490382.2
5_Percentile 353830.0
95_Percentile 678937.6
#plot distribution
ggplot(data.frame(x = house_cost_samples), aes(x)) +
geom_histogram(binwidth = 20000, fill = "blue", color = "black", alpha = 0.7) + labs(title = "Housing Cost",x = "USD",y = "Number of Samples") + scale_x_continuous(labels = scales::comma) +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5)) # Center the titleAssumptions
The calculations are based on the following assumptions:
30-year fixed mortgage. This is the most common choice, although it means that we pay a lot more in interest over the long-run than, say, a 15-year loan.
No broker fees. In Florida, the seller usually pays the broker.
No refinancing the mortgage. This is a conservative assumption. If interest rates did substantially decline after we bought the house, we could lock in a lower rate, lowering our monthly bill.
No earmarked reserves. Lenders want to see that we have 2+ months of mortgage payments in savings; however, we can use funds set aside for other purposes to demonstrate adequate reserves.
No change in property value. This is also highly conservative, homes typically appreciate in value over time. That said, assuming a constant price will actually underestimate how much we pay in property taxes over time.
Upfront Costs
Down Payment
I assume a down payment of 20%, which will allow us to avoid paying for mortgage insurance. There is a 90% chance the down payment will cost between ~$70,000 and ~$136,000.
down_payment<- house_cost_samples*.20
#median, 90% of samples
calculate_summary_stats(down_payment) Value
Mean 100017.72
Median 98076.45
5_Percentile 70766.00
95_Percentile 135787.52
#plot distribution
ggplot(data.frame(x = down_payment), aes(x)) +
geom_histogram(binwidth = 5000, fill = "orange", color = "black", alpha = 0.7) + labs(title = "Down Payment",x = "USD",y = "Number of Samples") + scale_x_continuous(labels = scales::comma) +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5)) # Center the titleClosing Costs
I assume the total closing cost is somewhere between 2 and 5%of the loan principal (i.e., sticker price minus down payment). These costs are due to the appraisal, home inspection, etc.
There is a 90% chance the costs fall between ~$7,600 and ~$22,000.
#uniform distribution between 2 and 5%, expressed as a proportion
cc_prop<-runif(n = 100000, min = .02, max = .05)
closing_costs<- cc_prop*(house_cost_samples*.80)
#median, 90% of samples
calculate_summary_stats(closing_costs) Value
Mean 14011.955
Median 13519.296
5_Percentile 7630.957
95_Percentile 22158.010
#plot distribution
ggplot(data.frame(x = closing_costs), aes(x)) +
geom_histogram(binwidth = 1000, fill = "yellow", color = "black", alpha = 0.7) + labs(title = "Closing Costs",x = "USD",y = "Number of Samples") + scale_x_continuous(labels = scales::comma) +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5)) # Center the titleMoving
I assume moving within Miami will cost somewhere between $1,500 and $3000.
moving_costs<-runif(n = 100000, min = 1500, max = 3000) Total Upfront
total_upfront<-down_payment+closing_costs+moving_costs
#median, 90% of samples
calculate_summary_stats(total_upfront) Value
Mean 116280.21
Median 114021.14
5_Percentile 82535.56
95_Percentile 157606.81
#plot distribution
ggplot(data.frame(x = total_upfront), aes(x)) +
geom_histogram(binwidth = 1000, fill = "green", color = "black", alpha = 0.7) + labs(title = "Total Upfront Cost",x = "USD",y = "Number of Samples") + scale_x_continuous(labels = scales::comma) +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5)) # Center the titleOngoing Costs
Mortgage Payment
The average annual interest rate for the top credit score category is currently 6.73%. Based on recent variation I’ll assume a standard deviation of 0.75%. That is, there is a 90% chance that the mortgage rate will be between 5.5% and 8%.
interest_rate_samples<-sample_beta(.0673, .0075)
#median, 90% of samples
calculate_summary_stats(interest_rate_samples) Value
Mean 0.06728094
Median 0.06705330
5_Percentile 0.05539785
95_Percentile 0.08007114
ggplot(data.frame(x = interest_rate_samples), aes(x)) +
geom_histogram(binwidth = 0.001, fill = "red", color = "black", alpha = 0.7) +
labs(title = "Mortgage Annual Interest Rate",
x = "Value",
y = "Frequency") +
theme_minimal()+
theme(plot.title = element_text(hjust = 0.5)) # Center the titleEven though the interest rate remains fixed across years, the amount we pay in interest decreases over time. This is because the interest rate applies to the outstanding loan balance, which decreases as we pay off more and more of the principal. Nevertheless, the loan is amortized, so the actual amount we pay month-to-month remains constant over time.
# Use mapply to apply the function to all combinations of principals and interest rates
mortgage_payment_samples <- mapply(generate_amort_table, (house_cost_samples*.80), interest_rate_samples)
monthly_payment<-calculate_summary_stats(mortgage_payment_samples["monthly_payment",])
total_paid<-calculate_summary_stats(mortgage_payment_samples["total_paid",])
total_interest<-calculate_summary_stats(mortgage_payment_samples["total_interest",])
mortgage_summary<-cbind(monthly_payment, total_paid, total_interest)
colnames(mortgage_summary) <- c("Monthly Payment", "Total Paid", "Total Interest")
mortgage_summary Monthly Payment Total Paid Total Interest
Mean 2592.220 933199.1 533128.2
Median 2534.480 912413.3 518646.3
5_Percentile 1789.590 644254.1 349259.7
95_Percentile 3591.662 1292998.8 767727.1
#plot distribution
ggplot(data.frame(x = mortgage_payment_samples["monthly_payment",]), aes(x)) +
geom_histogram(binwidth = 100, fill = "purple", color = "black", alpha = 0.7) + labs(title = "Monthly Mortgage Payment",x = "USD",y = "Number of Samples") + scale_x_continuous(labels = scales::comma) +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5)) # Center the titleProperty Taxes
The median property tax rate in Miami-Dade county is 1.02%. I am not sure what types of uncertainty the website I linked to accounted for, so I will assume the true percentage could be 10 basis points higher or lower. We use the cost of the home as a proxy for the property value, which should be realistic for at least the first few years of home ownership.
#expressed as a proportion of the home value.
property_tax_percentage_samples<- runif(100000, min = 0.0092, max = .0112)
property_tax_samples<-(property_tax_percentage_samples*house_cost_samples)/12
#monthly payment
monthly_property_tax<-calculate_summary_stats(property_tax_samples)
colnames(monthly_property_tax)[colnames(monthly_property_tax) == "Value"] <- "Monthly Property Tax Payment"
monthly_property_tax Monthly Property Tax Payment
Mean 425.1350
Median 416.1098
5_Percentile 296.4351
95_Percentile 584.1514
Homeowners Insurance
The average annual cost in Miami depends on the coverage amount:
$3,572 for $300,000 in coverage
$4,717 for $400,000 in coverage
$5,961 for $500,000 in coverage
In each case, the cost is ~1.2% of the amount of the dwelling coverage, the amount needed to rebuild your home. Rebuilding your home costs somewhere around $125 per square foot in Miami right now. How many square feet we get depends a lot on whether we get a house or condo. Based on places we have looked at so far, I’ll create a positively skewed distribution centered on 1,500 square feet.
square_footage <- sample_logn(1500, 250)
homeowners_monthly_samples<- ((square_footage*125)*.012)/12
#monthly payment
homeowners_insurance<-calculate_summary_stats(homeowners_monthly_samples)
colnames(homeowners_insurance)[colnames(homeowners_insurance) == "Value"] <- "Monthly Homeowner's Insurance Payment"
homeowners_insurance Monthly Homeowner's Insurance Payment
Mean 187.3990
Median 184.8355
5_Percentile 140.8737
95_Percentile 242.3965
Flood Insurance
In Miami-Dade county, the average cost of flood insurance per year is $477. The minimum in the Florida range is $261, so we’ll go with a standard deviation of $100.
yearly_flood_samples<-rnorm(100000, mean = 477, sd = 100)
monthly_flood_samples<-yearly_flood_samples/12
monthly_flood_summary<-calculate_summary_stats(monthly_flood_samples)
colnames(monthly_flood_summary)[colnames(monthly_flood_summary) == "Value"] <- "Monthly Flood Insurance Payment"
monthly_flood_summary Monthly Flood Insurance Payment
Mean 39.76385
Median 39.76488
5_Percentile 26.14220
95_Percentile 53.43399
Maintenance
A rule of thumb is spending 1% of a home’s cost per year on maintenance, though 3% is safer for home if the home is “older than 30 years” or “located in a wet, humid, or stormy climate” or “built with lower-life materials.” For safety, we’ll assume we need to set 3% per year in a rainy-day fund. Below I estimate the implications for saving money on a monthly basis.
monthly_maitenance_samples<-(house_cost_samples*.03)/12
monthly_maitenance_summary<-calculate_summary_stats(monthly_maitenance_samples)
colnames(monthly_maitenance_summary)[colnames(monthly_maitenance_summary) == "Value"] <- "Monthly Savings for Maitenance"
monthly_maitenance_summary Monthly Savings for Maitenance
Mean 1250.2215
Median 1225.9556
5_Percentile 884.5749
95_Percentile 1697.3440
Utilities
We currently pay $80 for internet for month, so I’ll assume that for internet.
This website says the average payment water, gas, and electricity in Miami is $128 per month. However, we keep the house cold and pay ~$116 a month for electricity alone; assuming gas and water are about $20 a month each, it is probably safer to assume $156 per month.
The same website says while garbage collection is $484 per year (~$40 per month).
utilities_monthly_samples<-rnorm(100000, mean = (80+156+40), sd = 50)HOA Fees
Average HOA fees in Miami are ~$650. I’ll assume a standard deviation of $100.
HOA fees only apply if we live in the condo. Of course, the amenities a condo provides may save us money we would spend on a gym membership, lawn maintenance, and so on. As a guess I treat 70% of the HOA fee as an added cost.
hoa_monthly_samples<-rnorm(100000, mean = (650*.7), sd = 100)Total Ongoing Costs
The average monthly cost if we buy a house is $4,772. There is a 90% chance that it will be between $3,494 and $6,352.
house_total<- mortgage_payment_samples["monthly_payment",]+property_tax_samples+homeowners_monthly_samples+monthly_flood_samples+monthly_maitenance_samples+utilities_monthly_samples
house_summary<-calculate_summary_stats(house_total)
colnames(house_summary)[colnames(house_summary) == "Value"] <- "Monthly Ongoing Costs"
house_summary Monthly Ongoing Costs
Mean 4770.715
Median 4681.745
5_Percentile 3491.930
95_Percentile 6345.353
#plot distribution
ggplot(data.frame(x = house_total), aes(x)) +
geom_histogram(binwidth = 100, fill = "purple", color = "black", alpha = 0.7) + labs(title = "Monthly House Costs",x = "USD",y = "Number of Samples") + scale_x_continuous(labels = scales::comma) +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5)) # Center the titleThe average monthly cost if we buy a condo is $5,227. There is a 90% chance that it will be between $3,934 and $6,810.
condo_total<- mortgage_payment_samples["monthly_payment",]+property_tax_samples+homeowners_monthly_samples+monthly_flood_samples+monthly_maitenance_samples+utilities_monthly_samples+hoa_monthly_samples
condo_summary<-calculate_summary_stats(condo_total)
colnames(condo_summary)[colnames(condo_summary) == "Value"] <- "Monthly Ongoing Costs"
condo_summary Monthly Ongoing Costs
Mean 5225.154
Median 5139.731
5_Percentile 3932.807
95_Percentile 6809.459
#plot distribution
ggplot(data.frame(x = condo_total), aes(x)) +
geom_histogram(binwidth = 100, fill = "blue", color = "black", alpha = 0.7) + labs(title = "Monthly Ongoing Costs",x = "USD",y = "Number of Samples") + scale_x_continuous(labels = scales::comma) +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5)) # Center the title