I decided to tune it up for myself. So he is the last version i came up with, which now includes a "amortization_tab" function for printing an amortization table (PrettyTable and CSV formats) and another useful functions: "rest_amount", "amount_can_be_payed_in_n_years", "years_to_pay_off".
I have added CSV format, so one can generate an initial calculation, import it into Excel and continue there.
So now it's a more or less complete library for annuity loan/mortgage math.
PS it's been tested with Python v3.5.1, but it should also theoretically work with another Python versions.
from __future__ import print_function
import sys
import math
import io
import csv
import prettytable
# Formulas: http://mathforum.org/dr.math/faq/faq.interest.html
description = {
'P': 'amount of the principal',
'i': 'annual interest rate',
'n': 'number of years',
'q': 'number of times per year that the interest is compounded',
'M': 'fixed monthly payment',
'k': 'number of "payed" payments',
}
def pr_debug(P=None, i=None, n=None, q=None, M=None, k=None, debug=False):
if not debug:
return
columns = ['var','value', 'description']
t = prettytable.PrettyTable(columns)
t.align['var'] = 'l'
t.align['value'] = 'r'
t.align['description'] = 'l'
t.padding_width = 1
t.float_format = '.2'
if P:
t.add_row(['P', P, description['P']])
if i:
t.add_row(['i', i, description['i']])
if n:
t.add_row(['n', n, description['n']])
if q:
t.add_row(['q', q, description['q']])
if M:
t.add_row(['M', M, description['M']])
if k:
t.add_row(['k', k, description['k']])
print(t.get_string() + '\n')
def annuity_monthly_payment(P, n, q, i, debug = False):
"""
Calculates fixed monthly annuity payment
P - amount of the principal
n - number of years
q - number of times per year that the interest is compounded
i - yearly rate of interest (for example: 0.045 for 4.5% interest)
"""
pr_debug(P=P, n=n, q=q, i=i, debug=debug)
i /= 100
return round(P*i/( q*(1 - pow(1 + i/q, -n*q)) ), 2)
def rest_amount(P, M, k, q, i, debug = False):
"""
Calculates rest amount after 'k' payed payments
P - Principal amount
M - fixed amount that have been payed 'k' times
k - # of payments
q - # of periods (12 times per year)
i - yearly interest rate (for example: 0.04 for 4%)
"""
pr_debug(P=P, M=M, k=k, q=q, i=i, debug=debug)
i /= 100
return round((P - M*q/i) * pow(1 + i/q, k) + M*q/i, 2)
def amount_can_be_payed_in_n_years(M, n, q, i, debug = False):
"""
Returns the amount of principal that can be paid off in n years
M - fixed amount that have been payed 'k' times
n - Number of years
q - # of periods (12 times per year)
i - yearly interest rate (for example: 0.04 for 4%)
"""
pr_debug(M=M, n=n, q=q, i=i, debug=debug)
i /= 100
return round( M*(1 - pow(1 + i/q, -n*q) )*q/i, 2)
def years_to_pay_off(P, M, q, i, debug = False):
"""
Returns number of years needed to pay off the loan
P - Principal amount
M - fixed amount that have been payed 'k' times
q - # of periods (12 times per year)
i - yearly interest rate (for example: 0.04 for 4%)
"""
pr_debug(P=P, M=M, q=q, i=i, debug=debug)
i /= 100
return round(-math.log(1 - (P*i/M/q)) / (q*math.log(1 + i/q)), 2)
def amortization_tab(P, n, q, i, M=None, fmt='txt', debug=False):
"""
Generates amortization table
P - Principal amount
M - fixed amount that have been payed 'k' times
q - # of periods (12 times per year)
i - yearly interest rate (for example: 0.04 for 4%)
"""
assert any(fmt in x for x in ['txt', 'csv'])
# calculate monthly payment if it's not given
if not M:
M = annuity_monthly_payment(P=P, n=n, q=q, i=i)
pr_debug(P=P, M=M, n=n, q=q, i=i, debug=debug)
# column headers for the output table
columns=['pmt#','beg_bal','pmt','interest','applied', 'end_bal']
i /= 100
beg_bal = P
term = n*q
if fmt.lower() == 'txt':
t = prettytable.PrettyTable(columns)
t.align = 'r'
t.padding_width = 1
t.float_format = '.2'
elif fmt.lower() == 'csv':
if sys.version_info >= (2,7,0):
out = io.StringIO()
else:
out = io.BytesIO()
t = csv.writer(out, quoting=csv.QUOTE_NONNUMERIC)
t.writerow(columns)
for num in range(1, term+1):
interest = round(beg_bal*i/q , 2)
applied = round(M - interest, 2)
end_bal = round(beg_bal - applied,2)
row = [num, beg_bal, M, interest, applied, end_bal]
if fmt.lower() == 'txt':
t.add_row(row)
elif fmt.lower() == 'csv':
t.writerow(row)
beg_bal = end_bal
if fmt.lower() == 'txt':
return t.get_string()
elif fmt.lower() == 'csv':
return out.getvalue()
############################
P = 7692.0
n = 1
q = 12
i = 18
print(amortization_tab(P, n, q, i, debug=True))
print('#' * 80)
print('#' * 80)
############################
# another example
P = 100000.0
n = 5
q = 12
i = 3.5
k = 36
M = 1200
print(amortization_tab(P, n, q, i, M, fmt='csv', debug=True))
print('*' * 80)
print('Rest amount after %s payments:\t%s' %(k, rest_amount(P=P, M=M, k=k, q=q, i=i)))
Output:
+-----+---------+----------------------------------------------------------+
| var | value | description |
+-----+---------+----------------------------------------------------------+
| P | 7692.00 | amount of the principal |
| i | 18 | annual interest rate |
| n | 1 | number of years |
| q | 12 | number of times per year that the interest is compounded |
| M | 705.20 | fixed monthly payment |
+-----+---------+----------------------------------------------------------+
+------+---------+--------+----------+---------+---------+
| pmt# | beg_bal | pmt | interest | applied | end_bal |
+------+---------+--------+----------+---------+---------+
| 1 | 7692.00 | 705.20 | 115.38 | 589.82 | 7102.18 |
| 2 | 7102.18 | 705.20 | 106.53 | 598.67 | 6503.51 |
| 3 | 6503.51 | 705.20 | 97.55 | 607.65 | 5895.86 |
| 4 | 5895.86 | 705.20 | 88.44 | 616.76 | 5279.10 |
| 5 | 5279.10 | 705.20 | 79.19 | 626.01 | 4653.09 |
| 6 | 4653.09 | 705.20 | 69.80 | 635.40 | 4017.69 |
| 7 | 4017.69 | 705.20 | 60.27 | 644.93 | 3372.76 |
| 8 | 3372.76 | 705.20 | 50.59 | 654.61 | 2718.15 |
| 9 | 2718.15 | 705.20 | 40.77 | 664.43 | 2053.72 |
| 10 | 2053.72 | 705.20 | 30.81 | 674.39 | 1379.33 |
| 11 | 1379.33 | 705.20 | 20.69 | 684.51 | 694.82 |
| 12 | 694.82 | 705.20 | 10.42 | 694.78 | 0.04 |
+------+---------+--------+----------+---------+---------+
################################################################################
################################################################################
+-----+-----------+----------------------------------------------------------+
| var | value | description |
+-----+-----------+----------------------------------------------------------+
| P | 100000.00 | amount of the principal |
| i | 3.50 | annual interest rate |
| n | 5 | number of years |
| q | 12 | number of times per year that the interest is compounded |
| M | 1200 | fixed monthly payment |
+-----+-----------+----------------------------------------------------------+
"pmt#","beg_bal","pmt","interest","applied","end_bal"
1,100000.0,1200,291.67,908.33,99091.67
2,99091.67,1200,289.02,910.98,98180.69
3,98180.69,1200,286.36,913.64,97267.05
4,97267.05,1200,283.7,916.3,96350.75
5,96350.75,1200,281.02,918.98,95431.77
6,95431.77,1200,278.34,921.66,94510.11
7,94510.11,1200,275.65,924.35,93585.76
8,93585.76,1200,272.96,927.04,92658.72
9,92658.72,1200,270.25,929.75,91728.97
10,91728.97,1200,267.54,932.46,90796.51
11,90796.51,1200,264.82,935.18,89861.33
12,89861.33,1200,262.1,937.9,88923.43
13,88923.43,1200,259.36,940.64,87982.79
14,87982.79,1200,256.62,943.38,87039.41
15,87039.41,1200,253.86,946.14,86093.27
16,86093.27,1200,251.11,948.89,85144.38
17,85144.38,1200,248.34,951.66,84192.72
18,84192.72,1200,245.56,954.44,83238.28
19,83238.28,1200,242.78,957.22,82281.06
20,82281.06,1200,239.99,960.01,81321.05
21,81321.05,1200,237.19,962.81,80358.24
22,80358.24,1200,234.38,965.62,79392.62
23,79392.62,1200,231.56,968.44,78424.18
24,78424.18,1200,228.74,971.26,77452.92
25,77452.92,1200,225.9,974.1,76478.82
26,76478.82,1200,223.06,976.94,75501.88
27,75501.88,1200,220.21,979.79,74522.09
28,74522.09,1200,217.36,982.64,73539.45
29,73539.45,1200,214.49,985.51,72553.94
30,72553.94,1200,211.62,988.38,71565.56
31,71565.56,1200,208.73,991.27,70574.29
32,70574.29,1200,205.84,994.16,69580.13
33,69580.13,1200,202.94,997.06,68583.07
34,68583.07,1200,200.03,999.97,67583.1
35,67583.1,1200,197.12,1002.88,66580.22
36,66580.22,1200,194.19,1005.81,65574.41
37,65574.41,1200,191.26,1008.74,64565.67
38,64565.67,1200,188.32,1011.68,63553.99
39,63553.99,1200,185.37,1014.63,62539.36
40,62539.36,1200,182.41,1017.59,61521.77
41,61521.77,1200,179.44,1020.56,60501.21
42,60501.21,1200,176.46,1023.54,59477.67
43,59477.67,1200,173.48,1026.52,58451.15
44,58451.15,1200,170.48,1029.52,57421.63
45,57421.63,1200,167.48,1032.52,56389.11
46,56389.11,1200,164.47,1035.53,55353.58
47,55353.58,1200,161.45,1038.55,54315.03
48,54315.03,1200,158.42,1041.58,53273.45
49,53273.45,1200,155.38,1044.62,52228.83
50,52228.83,1200,152.33,1047.67,51181.16
51,51181.16,1200,149.28,1050.72,50130.44
52,50130.44,1200,146.21,1053.79,49076.65
53,49076.65,1200,143.14,1056.86,48019.79
54,48019.79,1200,140.06,1059.94,46959.85
55,46959.85,1200,136.97,1063.03,45896.82
56,45896.82,1200,133.87,1066.13,44830.69
57,44830.69,1200,130.76,1069.24,43761.45
58,43761.45,1200,127.64,1072.36,42689.09
59,42689.09,1200,124.51,1075.49,41613.6
60,41613.6,1200,121.37,1078.63,40534.97
********************************************************************************
Rest amount after 36 payments: 65574.41