I would love to own a house one day. Will it ever happen? Who knows! This calculator was built to weight the many different options for loans, as well as the impact of other necessary and potential payments when buying a house or property.

The calculator can handle the following:

To use the calculator, you input the following costs into the mortgage() class, which will calculate a list of values potentially relevant to deciding on whether or not to buy a house.

mortgage(House Cost, Down Payment, Start Date of Loan, Loan Length (years), 
Interest Rate, Property Tax Rate, Points Bought, Closing Cost Percentage, 
HOA (monthly), Utilities (monthly), Home Insurance (monthly), FHA (1-Yes, 0-No)

The calculator can return the following:

  • A DataFrame of Principle and Interest Paid Over Time
  • A DataFrame list of Relevant Values
  • A Sentence on the Potential Mortgage Loan
  • A Short Sentence on Monthly and Upfront Costs

See below for examples.

This tool was intended to be a fully developed app, but I have not learned any front-end tools, so it will live in a notebook for now 🙃.

# Libraries
import datetime
import pytz
from dateutil import parser
from datetime import timezone
from dateutil.relativedelta import relativedelta
## String manipulation
import string
import re #regex
## Data Generation
import random
## Data Manipulation
import pandas as pd
import numpy as np
## Vector Math
from numpy.linalg import norm
## Math
import math
import matplotlib as mpl

class mortgage:
    def __init__ (self,cost,down,sdate,yr_length,intr,prop_tax,pts,cc_pct,hoa,util,ins,fha):
        # load from class
        self.cost = cost
        self.down = down
        self.yrs = yr_length
        self.length = yr_length * 12
        self.fha = fha
        self.sdate = pd.to_datetime(sdate)
        self.intr_curr = intr
        self.pts = pts
        self.cc_pct = cc_pct
        self.hoa = hoa
        self.util = util
        self.ins = ins
        self.proptax = prop_tax
        
        # general functions
        self.mortgage = self.cost - self.down
        self.ltv = self.down/self.mortgage
        self.months = list(range(self.length+1))
        self.intr_12mon = intr/12
        self.savings = self.cost * 0.02
        
        # property tax
        self.ann_proptax = self.cost * self.proptax
        self.mon_proptax = self.ann_proptax / 12
        
        # closing
        self.c_costs = self.mortgage * self.cc_pct
        
        # points
        self.pts_cost = self.mortgage * 0.01 * self.pts
        self.intr = self.intr_curr - (0.0025*self.pts)
        self.mon_intr = self.intr/12
        self.mon_intr_curr = self.intr_curr/12
        
        # pmi
        if self.ltv < 0.2:
            self.ann_pmi =  self.mortgage * 0.01
            self.mon_pmi = self.ann_pmi/12
        else:
            self.ann_pmi = 0
            self.mon_pmi = 0
            self.payoff_pmi = 0
        
        # fha
        if self.fha != 0:
            self.fha_pct = 0.0175
            self.ann_fha = self.mortgage * self.fha_pct
            self.mon_fha = self.ann_fha / 12
        else: 
            self.ann_fha = 0
            self.mon_fha = 0
    
        # end stats
        self.money_on_hand = self.down + self.c_costs + self.pts_cost
        self.mon_mortgage = self.mortgage*((self.mon_intr)*(1+self.mon_intr)**(self.length))/((1+self.mon_intr)**(self.length)-1)
        self.tot_mortgage = self.mon_mortgage * self.length
        self.tot_interest = self.tot_mortgage - self.mortgage
        self.tot_mon_cost = self.mon_mortgage + self.hoa + self.mon_proptax + self.mon_pmi + self.mon_fha + self.ins
        self.tot_upfront_cost = self.down + self.c_costs
        
        if self.ltv < 0.2:
            self.payoff_pmi = round(math.log((((self.cost*0.2)-self.down)*(self.mon_intr))/((self.mon_mortgage-(self.mon_intr*self.mortgage)))+1)/math.log(1+(self.mon_intr))/12,2)
        else: self.payoff_pmi = 0
        
        if self.pts > 0:
            self.self.mon_mortgage = self.mortgage*((self.mon_intr_curr)*(1+self.mon_intr_curr)**(self.length))/((1+self.mon_intr_curr)**(self.length)-1)
            self.mon_mortgage_p_diff = self.self.mon_mortgage - self.mon_mortgage
            self.payoff_pts = round(self.pts_cost/(self.mon_mortgage_p_diff*12),2)
        else: 
            self.mon_mortgage_nop = self.mon_mortgage
            self.mon_mortgage_p_diff = 0
            self.payoff_pts = 0
        
        self.tot_mortgage_cost = self.tot_mortgage + (self.ann_fha * 11) + (self.ann_pmi * self.payoff_pmi)
        self.tot_cost = self.tot_mortgage_cost + self.tot_upfront_cost
        
#         math.log((((self.cost*0.2)-self.down)*(self.mon_intr))/((self.mon_mortgage-(self.mon_intr*self.mortgage))+1))
#         /math.log(1+(self.mon_intr))/12
        
        # data frame
        self.df = pd.DataFrame({'month':self.months})
        
        months = list(range(self.length+1))
        months_real = []
        months_princ = []
        months_intr = []
        for n in months:
            date_real = pd.to_datetime(sdate) + relativedelta(months=n)
            months_real.append(date_real.date().strftime('%m-%d-%Y'))
        self.df['real_date'] = months_real
        
        for n in months:
            princ = (self.mon_mortgage-(self.mon_intr*self.mortgage))*((1+self.mon_intr)**(n)-1)/(self.mon_intr)
            intr = (self.mon_mortgage*n)-princ
            months_princ.append(princ)
            months_intr.append(round(intr,2))
        self.df['principal_paid'] = months_princ
        self.df['interest_paid'] = months_intr
        self.df['short_date'] = self.df['real_date'].apply(lambda x: pd.to_datetime(x).strftime('%B-%Y'))
        
    def mortgage_sentence(self):
        numb = [self.cost, self.down, self.mon_mortgage, self.tot_mortgage, self.tot_interest, self.mon_pmi, self.mon_fha,
                (self.ann_fha*11), self.hoa, self.mon_proptax, self.util, self.tot_mon_cost, self.pts_cost, self.c_costs,
                self.savings, self.tot_upfront_cost, self.ins]
        numb_list = []
        for z in numb:
            z = ('{:,.2f}'.format(round(z,2)))
            numb_list.append('$'+z)
        string = 'For a %s year mortgage on a %s with a %s down payment and a %.2f interest rate, your monthly mortgage payment will be %s. ' %(self.yrs, numb_list[0], numb_list[1], self.intr, numb_list[2])
        string2 = 'The total mortgage cost over %s years is %s, of which %s is interest. ' %(self.yrs, numb_list[3],numb_list[4])
        if self.ltv < 0.2 and self.fha == 0:
            string3 = 'Because your Loan-to-value (LTV) ratio is %.2f, which is lower than 0.2 (20%% downpayment), an additional Premium Mortgage Insurance (PMI) monthly payment of %s is needed. The additional PMI payment will end when the LTV  reaches 0.2. This will happen after %.2f years, assuming no re-financing and on-time payments. ' \
            %(float(self.ltv),numb_list[5], float(self.payoff_pmi))
        elif self.fha == 1:
            string3 = 'Because you have chosen an FHA loan, you will be paying an additional monthly Mortgage Insurance Premium (MIP) of %s for 11 years of the loan. This will add an additional %s to the total cost. ' \
            %(numb_list[6], numb_list[7])
        else: string3 = ''
        if self.hoa > 0:
            string4_1 = 'a monthly HOA payment of %s' %(numb_list[8])
        else: string4_1 = ''
        if self.mon_proptax > 0:
            string4_2 = 'the expected property tax of %s' %(numb_list[9])
        else: string4_2 = ''
        if self.util > 0:
            string4_3 = 'the expected monthly utility bill of %s' %( numb_list[10])
        else: string4_3 = ''
        if self.ins > 0:
            string4_4 = 'the expected monthly insurance of %s' %(numb_list[16])
        else: string4_4 = ''
        if (string4_1 == '' and string4_2 == '' and string4_3 == '' and string4_4 == ''):
            string4 = 'The total monthly payment is %s' %(numb_list[11])
        else:
            string4 = 'Additionally,'
            for x in [string4_1,string4_2,string4_3,string4_4]:
                if x != '':
                    string4 = string4 + ' ' + x + ', and'
            string4 = string4[:-4] + ' will make the monthly payment %s. ' %(numb_list[11])
        if self.pts > 0:
            string5 = 'In opting to buy %s Interest Point(s) for %s (-0.025 interest point per point) to bring down the current interest rate of %.2f to %.2f, it will take %.2f years of monthly mortgage payments to make up the upfront cost. ' %(self.pts, numb_list[12], self.intr_curr, self.intr, self.payoff_pts)
        else: string5 = ''
        string6 = 'You will pay %s upfront, %s of which are closing costs, and should have around %s of savings. ' \
        %(numb_list[15], numb_list[13], numb_list[14])
        return string + string2 + string3 + string4 + string5 + string6
    
    def mortgage_short(self):
        numb = [self.tot_mon_cost,
                self.tot_upfront_cost]
        numb_list = []
        for z in numb:
            z = ('{:,.2f}'.format(round(z,2)))
            numb_list.append('$'+z)
        string = 'Your monthly payment will be %s. Your total upfront costs will be around %s' %(numb_list[0], numb_list[1])
        return string
    
    def mortgage_list(self):
        list_df = pd.DataFrame(columns=['metric', 'value'])
        metric = ['Loan Cost', 'Downpayment', 'Monthly Mortgage', 'Total Mortgage Cost', 'Total Interest', 'Monthly PMI', \
                'Monthly FHA', 'Annual FHA', 'HOA', 'Monthly Property Tax', 'Utilities', 'Insurance', 'Total Monthly Cost', \
                'Points Upfront Cost', 'Closing Costs', 'Savings', 'Total Upfront Costs']
        numb = [self.cost, self.down, self.mon_mortgage, self.tot_mortgage, self.tot_interest, self.mon_pmi, \
                self.mon_fha, (self.ann_fha*11), self.hoa, self.mon_proptax, self.util, self.ins, self.tot_mon_cost, \
                self.pts_cost, self.c_costs, self.savings, self.tot_upfront_cost]
        numb_list = []
        for z in numb:
            z = ('{:,.2f}'.format(round(z,2)))
            numb_list.append('$'+z)
        list_df = pd.DataFrame({'metric':metric, 'value':numb_list})
        return list_df
        

    def mortgage_year(self,yr):
        dte = self.sdate + relativedelta(years=yr)
#         closest_mon = 
#         string = 
        return dte
# (cost,down,sdate,length,intr,prop_tax,pts,cc_pct,hoa,util,fha)
import plotly.express as px
import plotly.graph_objects as go

test = mortgage(450000,50000,'2023-05-01',30,0.04,0.054,0,0.04,0,0,200,0)
test.df
month real_date principal_paid interest_paid short_date
0 0 05-01-2023 0.000000 0.00 May-2023
1 1 06-01-2023 576.327849 1333.33 June-2023
2 2 07-01-2023 1154.576790 2664.75 July-2023
3 3 08-01-2023 1734.753228 3994.23 August-2023
4 4 09-01-2023 2316.863587 5321.78 September-2023
... ... ... ... ... ...
356 356 01-01-2053 392424.588738 287414.79 January-2053
357 357 02-01-2053 394308.998549 287440.04 February-2053
358 358 03-01-2053 396199.689726 287459.01 March-2053
359 359 04-01-2053 398096.683207 287471.68 April-2053
360 360 05-01-2053 400000.000000 287478.03 May-2053

361 rows × 5 columns

test = mortgage(450000,50000,'2023-05-01',30,0.04,0.054,0,0.04,0,0,200,0)

fig = go.Figure(layout=go.Layout(
        title=go.layout.Title(text="Principal and Interst Over Time")))
fig.add_trace(go.Scatter(x=test.df['short_date'], y=test.df['principal_paid'],
                    mode='lines',
                    name='Principal',
                    stackgroup='one'))
fig.add_trace(go.Scatter(x=test.df['short_date'], y=test.df['interest_paid'],
                    mode='lines',
                    name='Interest',
                    stackgroup='one'))
fig = fig.update_xaxes(showgrid=False, nticks=8)
fig = fig.update_layout(yaxis_tickprefix = '$', yaxis_tickformat = ',')
fig.show()

There was a plotly plot here!

test = mortgage(450000,50000,'2023-05-01',30,0.04,0.054,0,0.04,0,0,200,0)
test.mortgage_list()
metric value
0 Loan Cost $450,000.00
1 Downpayment $50,000.00
2 Monthly Mortgage $1,909.66
3 Total Mortgage Cost $687,478.03
4 Total Interest $287,478.03
5 Monthly PMI $333.33
6 Monthly FHA $0.00
7 Annual FHA $0.00
8 HOA $0.00
9 Monthly Property Tax $2,025.00
10 Utilities $0.00
11 Insurance $200.00
12 Total Monthly Cost $4,467.99
13 Points Upfront Cost $0.00
14 Closing Costs $16,000.00
15 Savings $9,000.00
16 Total Upfront Costs $66,000.00
# (cost,down,sdate,length,intr,prop_tax,pts,cc_pct,hoa,util,fha)
mortgage(450000,50000,'2023-05-01',30,0.04,0.054,0,0.04,0,0,200,0).mortgage_sentence()
For a 30 year mortgage on a $450,000.00 with a $50,000.00 down payment and a 0.04 
interest rate, your monthly mortgage payment will be $1,909.66. The total mortgage 
cost over 30 years is $687,478.03, of which $287,478.03 is interest. Because your 
Loan-to-value (LTV) ratio is 0.12, which is lower than 0.2 (20% downpayment), an 
additional Premium Mortgage Insurance (PMI) monthly payment of $333.33 is needed. 
The additional PMI payment will end when the LTV  reaches 0.2. This will happen 
after 5.21 years, assuming no re-financing and on-time payments. Additionally, 
the expected property tax of $2,025.00, and the expected monthly insurance of 
$200.00, will make the monthly payment $4,467.99. You will pay $66,000.00 upfront, 
$16,000.00 of which are closing costs, and should have around $9,000.00 of savings. 
mortgage(450000,50000,'2023-05-01',30,0.04,0.054,0,0.04,0,0,200,0).mortgage_short()
Your monthly payment will be $4,467.99. 
Your total upfront costs will be around $66,000.00