Welcome, guest | Sign In | My Account | Store | Cart

This class can be used to do interest rate calculations much like a financial calculator, and demonstrates the use of Python properties.

Python, 128 lines
  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.

1 comment

Facundo Batista 19 years, 7 months ago  # | flag

Data type. You should really be using Decimal for this kind of math. See PEP 327 for further info.