Pages

Showing posts with label finance. Show all posts
Showing posts with label finance. Show all posts

Thursday, March 29, 2012

Create a mortgage amortization table in Python

Home ownership is considered one piece of the American Dream by many. Today we'll look at another seemingly integral part of the American Dream: A home mortgage.

Like it or not, most people don't buy their houses with cash, instead having to take out a loan through a bank. A key part of that loan is the amortization schedule, which is a schedule of payments over the life of the loan, showing what portion of each payment is going towards the balance of the loan, and what portion is paying interest on the loan.

For this I created two functions: amortization_table prints out the amortization schedule, while pmt calculates the monthly payment amount.

Note that due to the nature of the beast, getting the numbers to be exactly right could be nigh unto impossible, due to rounding and such. But the idea's the important thing!

Let's put the following in mortgage.py:

# Mortgage amortization

from decimal import *

def amortization_table(principal, rate, term):
    ''' Prints the amortization table for a loan.

    Prints the amortization table for a loan given
    the principal, the interest rate (as an APR), and
    the term (in months).'''

    payment = pmt(principal, rate, term)
    begBal = principal

    # Print headers
    print 'Pmt no'.rjust(6), ' ', 'Beg. bal.'.ljust(13), ' ',
    print 'Payment'.ljust(9), ' ', 'Principal'.ljust(9), ' ',
    print 'Interest'.ljust(9), ' ', 'End. bal.'.ljust(13)
    print ''.rjust(6, '-'), ' ', ''.ljust(13, '-'), ' ',
    print ''.rjust(9, '-'), ' ', ''.ljust(9, '-'), ' ',
    print ''.rjust(9, '-'), ' ', ''.ljust(13, '-'), ' '
    # Print data
    for num in range(1, term + 1):
        
        interest = round(begBal * (rate / (12 * 100.0)), 2)
        applied = round(payment - interest, 2)
        endBal = round(begBal - applied, 2)
        
        print str(num).center(6), ' ',
        print '{0:,.2f}'.format(begBal).rjust(13), ' ',
        print '{0:,.2f}'.format(payment).rjust(9), ' ',
        print '{0:,.2f}'.format(applied).rjust(9), ' ',
        print '{0:,.2f}'.format(interest).rjust(9), ' ',
        print '{0:,.2f}'.format(endBal).rjust(13)

        begBal = endBal
    
def pmt(principal, rate, term):
    '''Calculates the payment on a loan.

    Returns the payment amount on a loan given
    the principal, the interest rate (as an APR),
    and the term (in months).'''
    
    ratePerTwelve = rate / (12 * 100.0)
    
    result = principal * (ratePerTwelve / (1 - (1 + ratePerTwelve) ** (-term)))

    # Convert to decimal and round off to two decimal
    # places.
    result = Decimal(result)
    result = round(result, 2)
    return result

What happens if we run this? To use the amortization_table function, we call it with the loan amount, the interest rate (as an APR), and the term of the loan in months. Let's try a loan for $150,000 at 4% for fifteen years (180 months):

>>> import mortgage
>>> mortgage.amortization_table(150000, 4, 180)
Pmt no   Beg. bal.       Payment     Principal   Interest    End. bal.    
------   -------------   ---------   ---------   ---------   -------------  
  1         150,000.00    1,109.53      609.53      500.00      149,390.47
  2         149,390.47    1,109.53      611.56      497.97      148,778.91
  3         148,778.91    1,109.53      613.60      495.93      148,165.31
  4         148,165.31    1,109.53      615.65      493.88      147,549.66
  5         147,549.66    1,109.53      617.70      491.83      146,931.96
...output deleted...
 175          6,580.75    1,109.53    1,087.59       21.94        5,493.16
 176          5,493.16    1,109.53    1,091.22       18.31        4,401.94
 177          4,401.94    1,109.53    1,094.86       14.67        3,307.08
 178          3,307.08    1,109.53    1,098.51       11.02        2,208.57
 179          2,208.57    1,109.53    1,102.17        7.36        1,106.40
 180          1,106.40    1,109.53    1,105.84        3.69            0.56

And if we check the handy-dandy calculator at BankRate.com, we find that our numbers are quite nearly the same!

But why aren't they the same? Well, I think it's because I'm switching back and forth between Decimal and float. I need to learn how to use Decimal exclusively, as it is more accurate...

What does this mean? We'll probably revisit this later.

Tuesday, March 27, 2012

Present and future values of a sum with Python

Not only am I interested in science and computer stuff, but I'm also interested in the worlds of business and finance. That's why today I'll look at computing the present and future values of a lump sum using Python.

Present and future values revolve around the idea of time-value of money: the idea that I'd rather have money now than later. Because of this, when someone says they will give me $1 a year from now, I have to discount that dollar to the present, or calculate its present value, to see how much it's worth right now to me.

Likewise, when calculating the future value of a sum, I'm seeing how much it's worth in the future.

Future value is useful when dealing with compound interest. For example, if I put $1,000 in a savings account right now that's paying 0.8% interest, how much money will be in the account in ten years?

While we could use a financial calculator (like an HP 12C financial calculator [affiliate link]), or one of the plethora of online calculators to solve this, I think it'd be more fun to write a Python function to calculate it for us!

import decimal

def fv(pv, rate, n):
    '''Calculates future value of present value.

    Returns the future value of a sum given the
    present value, a rate, and the number of periods.'''

    result = Decimal(pv * (1 + rate) ** n)

    return result

Note that I used some abbreviations for these variables: fv is the future value, pv is the present value, rate is for the interest rate per period (as a decimal), and n is the number of periods.

The function is very simple, and all it's doing is implementing the equation for the future value of a lump sum: one plus the interest rate, all raised to the number of periods, times the present value.

If we try this out in Python, we get the following (assuming you had the above code in finance.py):

>>> import finance
>>> from decimal import *
>>> finance.fv(Decimal(1000), Decimal(0.008), Decimal(10))
Decimal('1082.942308472838656347119069')

$1,083 isn't a lot of money, so we'll probably want to consider putting our money elsewhere, but the function is the important thing.

Now let's write the function for calculating the present value given a future value:

import decimal

def pv(fv, rate, n):
    '''Calculates present value of future value.

    Returns the present value of a sum given the
    future value, a rate, and the number of periods.'''

    result = Decimal(fv * (1 + rate) ** -n)

    return result

You may notice that this function is basically the inverse of the above function. Instead of raising to the number of periods, we raise to the number of periods as a negative number (thus working backwards), and we multiply it all by the future value as opposed to the present value.

Does it work? Let's try the inverse of the problem we just did. If we have $1,082.94 ten years from now, after compounding at 0.8% annual interest, how much did we start with? Note that due to rounding there may be a slight error.

>>> import finance
>>> from decimal import *
>>> finance.pv(Decimal(1082.94), Decimal(0.008), Decimal(10))
Decimal('999.9978683325782541661697781')

As I rounded the future value to two decimal places when entering it into the present value function, that may have introduced a small amount of error.

These functions only allow us to calculate the value of a lump sum. Later we'll create functions that allow us to calculate the value of a sum plus some stream of payments.