This class can be used to do interest rate calculations much like a financial calculator, and demonstrates the use of Python properties.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | '''\
An experiment with python properties using TVM equations as an example
A TVM object works like a financial calculator.
Given any four of (n, i, pmt, pv, fv) TVM object can calculate the fifth.
This version assumes payments are at end of compounding period.
Example:
>>> loan=TVM()
>>> loan.n=3*12 # set number of payments to 36
>>> loan.pv=6000 # set present value to 6000
>>> loan.i=6/12. # set interest rate to 6% annual
>>> loan.fv=0 # set future value to zero
>>> loan.pmt # ask for payment amount
-182.5316247083343 # payment (note sign)
Alternative style:
>>> TVM(n=36, pv=6000, pmt=-200, fv=0).i*12
12.2489388032796
'''
from math import log
from __future__ import division
class TVM (object):
'''A Time Value of Money class using properties'''
def __init__(self, n=None, i=None, pv=None, pmt=None, fv=None):
self.__n=n
self.__i=i
self.__pv=pv
self.__pmt=pmt
self.__fv=fv
def __repr__(self):
return "TVM(n=%s, i=%s, pv=%s, pmt=%s, fv=%s)" % \
(self.__n, self.__i, self.__pv, self.__pmt, self.__fv)
def __get_a(self):
'''calculate 'a' intermediate value'''
return (1+self.__i/100)**self.__n-1
def __get_b(self):
'''calculate 'b' intermediate value'''
return 100/self.__i
def __get_c(self):
'''calculate 'c' intermediate value'''
return self.__get_b()*self.__pmt
def get_n(self):
c=self.__get_c()
self.__n = log((c-self.__fv)/(c+self.__pv))/log(1+self.__i/100)
return self.__n
def set_n(self,value):
self.__n=value
n = property(get_n, set_n, None, 'number of payments')
def get_i(self):
# need to do an iterative solution - no closed form solution exists
# trying Newton's method
INTTOL=0.0000001 # tolerance
ITERLIMIT=1000 # iteration limit
# initial guess for interest
if self.__i:
i0=self.__i
else:
i0=1.0
# 10% higher interest rate - to get a slope calculation
i1=1.1*i0
def f(tvm,i):
'''function used in Newton's method; pmt(i)-pmt'''
a = (1+i/100)**self.__n-1
b = 100/i
out = -(tvm.__fv+tvm.__pv*(a+1))/(a*b)-tvm.__pmt
return out
fi0 = f(self,i0)
if abs(fi0)<INTTOL:
self.__i=i0
return i0
else:
n=0
while 1: # Newton's method loop here
fi1 = f(self,i1)
if abs(fi1)<INTTOL:
break
if n>ITERLIMIT:
print "Failed to converge; exceeded iteration limit"
break
slope=(fi1-fi0)/(i1-i0)
i2=i0-fi0/slope # New 'i1'
fi0 = fi1
i0=i1
i1=i2
n+=1
self.__i = i1
return self.__i
def set_i(self,value):
self.__i=value
i = property(get_i, set_i, None, 'interest rate')
def get_pv(self):
a=self.__get_a()
c=self.__get_c()
self.__pv = -(self.__fv+a*c)/(a+1)
return self.__pv
def set_pv(self,value):
self.__pv=value
pv = property(get_pv, set_pv, None, 'present value')
def get_pmt(self):
a=self.__get_a()
b=self.__get_b()
self.__pmt = -(self.__fv+self.__pv*(a+1))/(a*b)
return self.__pmt
def set_pmt(self,value):
self.__pmt=value
pmt = property(get_pmt, set_pmt, None, 'payment')
def get_fv(self):
a=self.__get_a()
c=self.__get_c()
self.__fv = -(self.__pv+a*(self.__pv+c))
return self.__fv
def set_fv(self,value):
self.__fv=value
fv = property(get_fv, set_fv, None, 'future value')
|
This is a handy tool to have around - and shows how properties can be used to make a nice interface for an certain types of problems. This class presents a uniform face on solving the equation for each of the variables, rather than having a set of five functions which each solve for a particular variable.
Data type. You should really be using Decimal for this kind of math. See PEP 327 for further info.