Welcome, guest | Sign In | My Account | Store | Cart
#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>

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

History

  • revision 14 (14 years ago)
  • previous revisions are not available