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

The precise rate of IRR( Internal Rate of Return ) is found using my program IRR-Versus-NPV, where I used the linear interpolation . However, companies decide to take or reject a project by comparing the IRR value to the minimum required return. If the IRR is greater than the cost of capital the project should go ahead, otherwise the project should be rejected. The NPV (Net Present Value) could also be computed using my program, where projects are accepted or rejected on the basis of the NPV value, if the NPV is greater than zero the project is accepted , otherwise rejected, a positive NPV demonstrate the project is attractive, thus, it will increase the shareholders’ wealth by this amount. NPV and IRR are both superior Project appraisal techniques, yet the NPV method is a better technique, since it shows an exact amount of increase in shareholders’ wealth, and yet the IRR requires a use of a computer program. My program IRR-Versus-NPV will be of a value to ACCA’s students and also to both undergraduate and postgraduate students of Corporate Finance, Accounting, and Business studies.

Python, 255 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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
#On the name of ALLAH
#Author : Fouad Teniou
#Date : 07/03/09
#version :2.6.1

""" New in python 2.6 namedtuple function is used in my program IRR-Versus-NPV
    with a PresentValue as a typename, and discount rates r_1, r_2 and
    number of periods until payments n as fieldnames (PresentValue('r_1','r_2','n'))
    IRR-Versus-NPV program provide an NPV and IRR (linear interpolation) 
    calculations by entering the outflows/inflows values for a project
    with 2 different rates r_1 and r_2, and only an NPV if r_2 is equal to zero
    The program also returns any value or a series of values from the Present Value
    Annuity Tables
"""
    
import itertools
import operator
import collections
import math as m

class MinusSignError(ArithmeticError):
    """ user attempt an operation on negative number"""
    pass

class PresentValue(collections.namedtuple('PresentValue', 'r_1,r_2,n')):
    """PresentValue a subclass of namedtuple Python class
    with two rates values (r_1,r_2) and a period number n """
    
    #set __slots__ to an empty tuple keep memory requirements low
    __slots__ = () 

    @property
    def DF(self):
        """Compute the discount factor of two values"""
        
        if self.r_1<0 or self.r_2 < 0 or self.n<0:
            raise MinusSignError,\
              "\n<Rates values and period number should be positive "
        
        try:
            discount_factor_1 = "%2.3f" % m.pow((1+self.r_1),-self.n)
            discount_factor_2 = "%2.3f" % m.pow((1+self.r_2),-self.n)
        
            return (discount_factor_1,discount_factor_2)

        #DF raises Negative number error
        except MinusSignError,exception:
            print exception

    @property
    def AF(self):
        """Compute the annuity factor of two values"""
        
        if self.r_1<0 or self.r_2 < 0 or self.n<0:
            raise MinusSignError,\
              "\n<Rates values and period number should be positive"
        try:
            
            annuity_factor_1 = "%2.3f" %((1-((m.pow((1+self.r_1),-self.n))))/self.r_1)
            annuity_factor_2 = "%2.3f" %((1-((m.pow((1+self.r_2),-self.n))))/self.r_2)

            return (annuity_factor_1,annuity_factor_2)

        #AF raises Zero division number error 
        except ZeroDivisionError:
            print "\n<Please choose a rate value greater than zero"

        #AF raises Negative number error    
        except MinusSignError,exception:
            print exception

    def npvTable(self,*args):
        """Compute the NPV and IRR values of a project with two different rates"""
        
        try:
            #You need at least one rate to compute an NPV
            assert self.r_1 !=0,"The first rate should not be equal to zero "

            res_1 = []
            res_2 = []
            item_1 =[]
            item_2 =[]
            count_1 = -1
        
            for arg in args:
     	
                count_1 +=1

                #outflows/inflows starting at year 0                 
                pv_set = PresentValue(r_1=self.r_1,r_2=self.r_2,n = count_1)
                res_1.append(pv_set.DF[0]) # Trigger the Discount factor at rate r_1 and append the res_1
                res_2.append(pv_set.DF[1]) # Trigger the Discount factor at rate r_2 and append the res_2            
            print "\n years \tCash flows \t DF at %s\t PV" %(str(int(self.r_1*100))+'%')

            count_2 = -1

            for (x_1,y) in (itertools.izip(res_1,args)):    #for loop return PV set item_1
                item_1.append((float(x_1)*y))

                count_2  +=1
            
                print "\n  %s\t%s\t\t %s\t\t%s" %(count_2,y,x_1,int(float(x_1)*y))

                npv_1 =(reduce(operator.add,item_1))    #Compute the npv_1 total
            
            print '\n\t\t\t\t NPV =  %s\n' % int(npv_1)
        
            if self.r_2 == 0:   #user attempt only one NPV calculation 
            
                print "<No NPV calculation %s=%s" % pv_set._asdict().items()[1]

            else:
            
                print "\n years \tCash flows \t DF at %s\t PV" %(str(int(self.r_2*100))+'%')

                count_3 = -1
            
                for (x_2,y) in (itertools.izip(res_2,args)):    #for loop return PV set item_2
                    item_2.append((float(x_2)*y))
                    count_3 +=1
                
                    print "\n  %s\t%s\t\t %s\t\t%s" %(count_3,y,x_2,int(float(x_2)*y))

                    npv_2 = (reduce(operator.add,item_2))   #Compute the npv_2 total 
                
                print '\n\t\t\t\t NPV =  %s\n ' % int(npv_2)

                #The IRR computation will depend of the rates and npvs (higher and lower values)               
                if self.r_1 > self.r_2:
                
                    print "<The IRR = %2.1f" % ((self.r_2+(npv_2/(npv_2-npv_1))*(self.r_1-self.r_2))*100)+' %\n'

                else:
                
                    print "<The IRR = %2.1f" % ((self.r_1+(npv_1/(npv_1-npv_2))*(self.r_2-self.r_1))*100)+' %\n'       
                print "<The project will be recommended on finanacial grounds, while "
                print "ignoring risk, if the IRR is greater than the target rate of "
                print "return, otherwise the project will be rejected.\n"

        #npvTable raises Negative number error
        except MinusSignError,exception:
            print exception
    
        
if __name__ == "__main__":
    
    p = PresentValue('r_1','r_2','n')
    
    p = p._replace(r_1=0.27,r_2 = 0.13)# n is equal to the numbers of outflows/inflows
    p.npvTable(-70000,48000,24000,22000,13000,33000,15000,17000)
    
    s = p._replace(r_1=0.07,r_2 =0.05) #You can set r_2 to zero to get only an NPV but not r_1 since you need at least one rate for the NPV

    print " years\t\tDF at%2.0f%s\tAF at%2.0f%s\t DF at%2.0f%s\t AF at%2.0f%s \n" %\
          (s.r_1*100,'%',s.r_1*100,'%',s.r_2*100,'%',s.r_2*100,'%')

    for i in range(1,11): # you can do it individualy by setting r_1,r_2 and n (eg: PresentValue(r_1 = 0.17,r_2 = 0.07,n=7)
        s = p._replace(r_1=0.07,r_2 =0.05,n=i)
        print "  %s\t\t%s\t\t %s\t\t %s\t\t %s" % \
              (i,s.DF[0],s.AF[0],s.DF[1],s.AF[1])
    
    # Another method to display the npv,for the same outflows/inflows values
    # using different sets of rates and (n = to the number of outflows/inflows )
    # It will save you time to write the same inflows/outflows every time you  
    # alter the rates 
    
    # below is just an example and you can extend your set of values as you wish.

    a = [0.07,0.02,'n']
    b = [0.04,0.05,'n']
    c = [0.04,0.07,'n']
    
    for item in a,b,c:
        p = p._make(item)
        p.npvTable(-70000,48000,24000,22000,13000,37000)
    
    #Another way to retrieve Annuity factors and Discount factors
    #individually or in sets of (r_1,r_2)
    g = [0.04,0.07,17]
    p = p._make(g)
    print p.DF
    print p.AF
    print p.DF[0]
    print p.DF[1]
    print p.AF[0]
    print p.AF[1]
    
#########################################################################################

    
# c:\Python26>python "C:Fouad Teniou\Documents\IRRvNPV.py"

# years  Cash flows       DF at 27%       PV

#  0     -70000           1.000          -70000

#  1     28000            0.787          22036

#  2     24000            0.620          14880

#  3     22500            0.488          10980

#  4     13000            0.384          4992

#  5     33000            0.303          9999

#  6     15000            0.238          3570

#  7     17000            0.188          3196

#                                 NPV =  -347


# years  Cash flows       DF at 13%       PV

#  0     -70000           1.000          -70000

#  1     28000            0.885          24780

#  2     24000            0.783          18792

#  3     22500            0.693          15592

#  4     13000            0.613          7969

#  5     33000            0.543          17919

#  6     15000            0.480          7200

#  7     17000            0.425          7225

#                                 NPV =  29477

#<The IRR = 26.8 %

#<The project will be recommended on finanacial grounds, while
#ignoring risk, if the IRR is greater than the target rate of
#return, otherwise the project will be rejected.

# years          DF at 7%        AF at 7%         DF at 5%        AF at 5%

#  1             0.935            0.935           0.952           0.952
#  2             0.873            1.808           0.907           1.859
#  3             0.816            2.624           0.864           2.723
#  4             0.763            3.387           0.823           3.546
#  5             0.713            4.100           0.784           4.329
#  6             0.666            4.767           0.746           5.076
#  7             0.623            5.389           0.711           5.786
#  8             0.582            5.971           0.677           6.463
 # 9             0.544            6.515           0.645           7.108
#  10            0.508            7.024           0.614           7.722

#c:\Python26>

###########################################################################################

I made available today 14/03/09 new methods to display the NPV and IRR without having to rewrite the inflows/outflows every time you alter the rates. However, you can extend or reduce your number of sets as you wish. And I also provided another method to retrieve individual and sets of annuity and discount factor values.

5 comments

Alia Khouri 15 years, 1 month ago  # | flag

You may be interested in my recipe ( http://code.activestate.com/recipes/576686/ ) which IMHO is simpler, and easier to reuse and understand than the above.

Fouad Teniou (author) 15 years, 1 month ago  # | flag

No thanks, I will stick to my program , because there are more interesting functions, and I am not interested in the Payback method, since it is not a very popular method due to its disadvantages, yet my display of information is the one mostly used in Financial and accounting books including the display of Annuity and Discount factors tables. I also used the advanced tools in python 2.6.1, which is only released recently. And I hope you will make a progress and try this new version. However, I know how hard it could be for people with a little knowledge in programming as yourself and as you stated in your previous comment, and I understand that there are no books yet available for this new version 2.6 released only in December 2008, but I will strongly advise you to take a step further,

PS.

I used a factory function called namedtuple( ) Python new version 2.6, from the collections module

Alia Khouri 15 years, 1 month ago  # | flag

Given your stated intention to create a program that is of value to "students of Corporate Finance, Accounting, and Business studies", one has to question your decision to use features only available in the most recently released version of python 2.6.1 (and above) when version 2.5.4 is probably most widespreadly available in current manifestations of OS X, Linux, and Windows -- especially when the use of said features apparently does not contribute to the worthy task of writing simpler and more readable code. (-:

Fouad Teniou (author) 15 years, 1 month ago  # | flag

You should know that in this life people need to progress, especially students, and time is very important, thus, ignoring tools, which were available more than three months is called ignorance, yet programmers already posted programs in this site using the new version 2.6.1 tools, and suggesting new functions for the next version 2.7. I understand how hard the new tools available in 2.6 version, but again I do have to remind you that I do write my programs for a no programmers,undergraduate, postgraduates and ACCA’s, students in Finance, Accounting and Mathematics fields and Financial Management Professionals already holding posts within firms in this field, and however, my program is running on Windows and DOS operating systems.

I would like to suggest for you to be creative and try to invent programs of your own, since it will be more helpful for others and it will free yourself to think, the fact that I was confused and chocked to see a program, which did have nearly the same title as mine and similarities in parts of the code in less than a week time, after I launched my program IRR_Versus_NPV. which is a copyright as every other program I wrote and already posted in this site…

PS.

Good luck in your next thinking ( invention!!!).

Alia Khouri 15 years, 1 month ago  # | flag

(-: You cannot imagine how amused I am by your assertion that I have somehow lifted or copied anything from your application.

If you really believe this, then by all means prove it. Please point to a single line in your program which has any semblance of similarity to mine.

From my perspective, the only thing we share are the terms "IRR" and "NPV" in our respective titles, and I am quite positive that you don't own that copyright.