Featured Post

Set up machine learning and deep learning on AWS

Here is the simple instructions to set up a EC2 instance to run machine learning and deep learning on AWS 1.  Run an EC2 instance from ...

Showing posts with label Finance. Show all posts
Showing posts with label Finance. Show all posts

Jan 11, 2021

The buy vs. rent decision - value of money

 


The buy vs. rent decision problem is a classical project to illustrate the value of money in the financial courses. There are different versions of this project. The context of the various versions are pretty much the same with the change only on the numbers - interest rate, price of the condo, etc. Here a solution based on the Python functions is provided using the functionalities implemented in this article.

The story started with the consideration to buy a property. In May 2013, Rebecca Young completed her MBA and moved to Toronto for a new job in investment banking. There, she rented a spacious, two-bedroom condominium for $3,000 per month, which included parking but not utilities or cable television. In July 2014, the virtually identical unit next door became available for sale with an asking price of $620,000, and Young believed she could purchase it for $600,000. She realized she was facing the classic buy-versus-rent decision. It was time for her to apply some of the analytical tools she had acquired in business school — including “time value of money” concepts — to her personal life...

Summary of the financial details

  • Purchase price of the property: $600,000
  • Down payment: 20% of property price
  • Mortgage interest (Semi-annual compound): 4% 
  • Mortgage periods: 25 years 
  • Owner monthly fees: Condo fee ($1055) + property tax ($300) + maintenance ($50)
  • Purchase closing cost: 1.5% + 1.5% + $2000
  • Rent: $3000 
Summary of scenarios
  1. The condo price remains unchanged
  2. The condo price drops 10% over the next two years, then increases back to it's purchase price by the end of 5 years
  3. The condo price increases annually by the annual rate of inflation of 2% over the next 10 years
  4. The condo price increases annually by an annual rate of 5% over the next 10 years.
Summary of selling fees
  • 5% of selling price
  • $2000 flat fee
The loan amount is the $600k * 80% = $480k. The monthly rate can be calculated from the nominal interest as r = (1 + 0.04/2)^(1/6) - 1. The total number of payments is 25x12 = 300. The monthly payment of loan can be calculated as below. The number is $2524.9. The opportunity cost is the extra payment comparing to rent - 2524.9 + 1055 + 300 + 50 - 3000 = 929.91.

def get_monthly_pmt(loan_amt, r, n):
    """ calculate monthly payment
    loan_amt: inital loan amount
    r: monthly interest
    n: total number of payments
    """
    return loan_amt*r*(1 + r)**n / ((1 + r)**n - 1)


def get_principle(loan_amt, A, r, t):
    """ calculate principle for month t
    loan_amt: intial loan amount
    A: monthly payment
    r: monthly intrest
    t: number of payments
    """
    return loan_amt*((1 + r)**t) - A*((1 + r)**t - 1)/r

"""
r = (1 + 0.04/2)^(1/6) - 1 = 0.00330589

>>> get_monthly_pmt(loan_amt=480000, r=0.00330589, n=300)

2524.905347597944
"""

Considering the value of money in the future, the opportunity cost is contributed by two parts: 
  • The initial out of pocket - the value is expected to increase in the future with the initial deposit. The initial out of pocket is down payment plus closing cost: 600000*20% + 600000*3% + 2000 = $140,000.
  • The savings of rent - the accumulative value is expected to increase in the future by continuous contribution of $929.91.
The future value of the opportunity cost in 2, 5 and 10 years can be calculated as below. The interesting observation here is that the decision of bur or rent is highly dependent on the housing price variation.

def get_future_value_init(deposit, r, n):
    """ Calculate future value
    deposit: monthly deposit
    r: monthly interest
    n: total number of months
    """
    return deposit*(1 + r)**n


def get_future_value(deposit, r, n):
    """ Calculate future value
    deposit: monthly deposit
    r: monthly interest
    n: total number of deposit
    """
    return deposit*((1 + r)**n - 1)/r

for i in [24, 60, 120]:
    a = get_future_value_init(140000, r=0.00330589, n=i)
    b = get_future_value(929.91, r=0.00330589, n=i)
    print(a, b, a+b)
 

"""
151540.5012231941 23187.245216568885 174727.74643976297
170659.2154860795 61600.682143214086 232259.8976292936
208032.62735945804 136691.56848584456 344724.1958453026
"""

Scenario analysis

The principle balance after 2, 5, and 10 years can be calculated as follows. The take-home amount is the selling price subtracted by the selling fee and principle balance. And the future value can be derived by the take-home amount subtracted by the future value of the opportunity.

def get_principle(loan_amt, A, r, t):
    """ calculate principle for month t
    loan_amt: intial loan amount
    A: monthly payment
    r: monthly intrest
    t: number of payments
    """
    return loan_amt*((1 + r)**t) - A*((1 + r)**t - 1)/r


for i in [24, 60, 120]:
    print(get_principle(480000, A=2524.91, r=0.00330589, t=i))
    
"""
456608.96654832666
417857.9213183897
342107.0756292622
"""

The results of the 4 different scenarios are summarized as below. It's an interesting observation that the final net future value is highly dependent on the property price. The decision to buy or rent the property is based on the housing market.

Scenario 1:

OpportunityPrincipleSelling-priceSelling-feeTake-homeNet-future-value
174,728456,609600,00032,000111,391-63,337
232,260417,858600,00032,000150,142-82,118
344,724342,107600,00032,000225,893-118,831

Scenario 2:

OpportunityPrincipleSelling-priceSelling-feeTake-homeNet-future-value
174,728456,609540,00029,00054,391-120,337
232,260417,858600,00032,000150,142-82,118
344,724342,107660,00035,000282,893-61,831

Scenario 3:

OpportunityPrincipleSelling-priceSelling-feeTake-homeNet-future-value
174,728456,609624,24033,212134,419-40,309
232,260417,858662,44835,122209,468-22,792
344,724342,107731,39738,570350,7205,996

Scenario 4:

OpportunityPrincipleSelling-priceSelling-feeTake-homeNet-future-value
174,728456,609661,50035,075169,816-4,912
232,260417,858765,76940,288307,62375,363
344,724342,107977,33750,867584,363239,639

Jan 10, 2021

Loan or Mortgage amortization function with Python


After some search on the internet, I'm not able to find a simple implementation of the loan or mortgage amortization schedule. This is usually achieved with the excel template in the financial world. One can simply use the formula to compute the monthly payment and the template to work on the schedule. There are more complex tasks that are not easily to refer to the excel sheet. For instance, we might want to perform a task to look up the balance on the amortization schedule to make the decision when to pay off the balance. This could be simply implemented in Python and it's a nice illustration of how to use Python in Finance. I decided to take a shot to the loan amortization function as the start of a financial calculator. The results has been cross checked with the financial calculators. 

There are generally tree steps. 

  • Calculate the monthly payment for the loan
  • Calculate the principle for amortization schedule
  • Calculate the payment schedule

The monthly payment can be easily calculated with the following formula. The detailed loan amortization derivation can be found on wikipedia.


where

  • A = payment amount per period
  • P = initial principal (loan amount)
  • r = interest rate per period
  • n = total number of payments or periods


The first step is to calculate the monthly payment with the loan amortization formula. The input is the initial loan amount, the month interest and the total length of loan in months. The interest published is usually the nominal annual interest. The monthly interest can be simply derived by nominal interest/12. The Python function is listed as below.

def get_monthly_pmt(loan_amt, r, n):
    """ calculate monthly payment
    loan_amt: initial loan amount
    r: monthly interest
    n: total number of payments
    """
    return loan_amt*r*(1 + r)**n / ((1 + r)**n - 1)

"""
loan_amt = 500,000
r = 0.04/12
n = 300

>>> get_monthly_pmt(loan_amt=500000, r=0.04/12, n=300)

2639.1842014888507
"""

The second step is to calculate the principle of any month t.

def get_principle(loan_amt, A, r, t):
    """ calculate principle for month t
    loan_amt: intial loan amount
    A: monthly payment
    r: monthly intrest
    t: number of payments
    """
    return loan_amt*((1 + r)**t) - A*((1 + r)**t - 1)/r

The final step is to calculate the monthly amortization schedule. 
def get_schedule(loan_amt, r, n):
    """ calculate the amortization schedule
    loan_amt: intial loan amount
    r: monthly intrest
    n: total number of payments
    """
    monthly = []
    A = get_monthly_pmt(loan_amt, r, n)
    last = loan_amt
    for i in range(1, n+1):
        curr = get_principle(loan_amt, A, r, i)
        p = last - curr
        monthly.append([i, p, A - p, curr])
        last = curr
    return pd.DataFrame(monthly, columns=['month', 'principle_pmt', 'interest_pmt', 'principle'])

To put everything in the same place with a real time example. The output is a pandas data frame containing the amortization schedule. The balance of any month can be simply looked up from the data frame.
"""
Loan/Mortgage Amortization schedule
Financial calculator
"""

import pandas as pd


def get_monthly_pmt(loan_amt, r, n):
    """ calculate monthly payment
    loan_amt: initial loan amount
    r: monthly interest
    n: total number of payments
    """
    return loan_amt*r*(1 + r)**n / ((1 + r)**n - 1)
    
def get_principle(loan_amt, A, r, t):
    """ calculate principle for month t
    loan_amt: intial loan amount
    A: monthly payment
    r: monthly intrest
    t: number of payments
    """
    return loan_amt*((1 + r)**t) - A*((1 + r)**t - 1)/r

def get_schedule(loan_amt, r, n):
    """ calculate the amortization schedule
    loan_amt: intial loan amount
    r: monthly intrest
    n: total number of payments
    """
    monthly = []
    A = get_monthly_pmt(loan_amt, r, n)
    last = loan_amt
    for i in range(1, n+1):
        curr = get_principle(loan_amt, A, r, i)
        p = last - curr
        monthly.append([i, p, A - p, curr])
        last = curr
    return pd.DataFrame(monthly, columns=['month', 'principle_pmt', 'interest_pmt', 'principle'])

"""
loan_amt = 500,000
r = 0.04/12
n = 300

>>> get_monthly_pmt(loan_amt=500000, r=0.04/12, n=300)

2639.1842014888507

>>> df = show_schedule(loan_amt=500000, r=0.04/12, n=300)

month	principle_pmt	interest_pmt	principle
0	1	972.517535	1666.666667	499027.482465
1	2	975.759260	1663.424942	498051.723205
2	3	979.011791	1660.172411	497072.711414
3	4	982.275163	1656.909038	496090.436251
4	5	985.549414	1653.634788	495104.886837
...	...	...	...	...
295	296	2595.634264	43.549938	10469.347082
296	297	2604.286378	34.897824	7865.060704
297	298	2612.967332	26.216869	5252.093371
298	299	2621.677224	17.506978	2630.416148
299	300	2630.416148	8.768054	0.000000
"""