In the code shown below, I have created 3 classes. The first of these are an 'Payment' class and a 'Annuity' class, which allow the user to create objects representing individual or recurring payments respectively.
The third of these classes is a 'Cashflow' class, which takes a combination of Payment and Annuity object to construct a cashflow, and returns allows the user to calculate various related properties. One example of such a property is the discounted payback period, which is the earliest point in time that the net present value of the cashflows given is equal to zero.
import math
class Annuity():
#Initialise function
def __init__(self, annuity_type, effective_interest_rate, term, payment_rate = 1, payment_frequency = 1):
self.annuity_type = annuity_type
self.effective_interest_rate = effective_interest_rate
self.term = term
self.payment_rate = payment_rate
self.payment_frequency = payment_frequency
#Properties
@property
def present_value(self):
if self.annuity_type == 'continuous':
return self.payment_rate * (1 - (1 + self.effective_interest_rate) ** -self.term) / math.log(1 + self.effective_interest_rate)
elif self.annuity_type == 'due':
return self.payment_rate * (1 - (1 + self.effective_interest_rate) ** -self.term) / (self.effective_interest_rate / (1 + self.effective_interest_rate))
elif self.annuity_type == 'immediate':
return self.payment_rate * (1 - (1 + self.effective_interest_rate) ** -self.term) / self.effective_interest_rate
@property
def time_payable(self):
return self.term
#Functions
def present_value_to_time(self, time):
if time >= self.term:
return self.present_value
elif self.annuity_type == 'continuous':
return self.payment_rate * (1 - (1 + self.effective_interest_rate) ** -time) / math.log(1 + self.effective_interest_rate)
elif self.annuity_type == 'due':
return self.payment_rate * (1 - (1 + self.effective_interest_rate) ** -time) / (self.effective_interest_rate / (1 + self.effective_interest_rate))
elif self.annuity_type == 'immediate':
return self.payment_rate * (1 - (1 + self.effective_interest_rate) ** -time) / self.effective_interest_rate
class Payment():
#Initialise function
def __init__(self, effective_interest_rate, time_payable, ammount):
self.effective_interest_rate = effective_interest_rate
self.time_payable = time_payable
self.ammount = ammount
#Properties
@property
def present_value(self):
return self.ammount * (1 + self.effective_interest_rate) ** -self.time_payable
#Functions
def present_value_to_time(self, time):
if time < self.time_payable:
return 0
else:
return self.present_value
class Cashflow(object):
#Initialise function
def __init__(self, cashflow = []):
self.cashflow = cashflow
#Properties
@property
def net_present_value(self):
npv = 0
for cf in self.cashflow:
npv += cf.present_value
return npv
@property
def cashflow_timings(self):
cashflow_timings = []
for cf in self.cashflow:
cashflow_timings.append(cf.time_payable)
cashflow_timings.sort()
return cashflow_timings
@property
def discounted_payback_period(self):
n1 = min(self.cashflow_timings)
n2 = max(self.cashflow_timings) + 1
discounted_payback_period = 0
for t in range(0, 6):
for n in range(n1, n2):
n /= 10 ** t
n += discounted_payback_period
if self.net_present_value_to_time(n) >= 0:
discounted_payback_period = n - (1 / 10 ** t)
n1 = 0
n2 = 11
break
return round(discounted_payback_period, 5)
#Functions
def add_to_cashflow(self, cashflow_item):
self.cashflow.append(cashflow_item)
def net_present_value_to_time(self, time):
net_present_value_to_time = 0
for cf in self.cashflow:
net_present_value_to_time += cf.present_value_to_time(time)
return net_present_value_to_time
An example of how one might use the above classes is as follows:
my_cashflow = Cashflow()
a1 = Annuity('continuous', 0.1, 5, 10000)
a2 = Annuity('continuous', 0.1, 5, -2000)
p1 = Payment(0.1, 0, -25000)
p2 = Payment(0.1, 6, -5000)
my_cashflow.add_to_cashflow(a1)
my_cashflow.add_to_cashflow(a2)
my_cashflow.add_to_cashflow(p1)
my_cashflow.add_to_cashflow(p2)
print(my_cashflow.discounted_payback_period)
Does anyone have any suggestions as to how the above code might be improved? In particular, I was hoping that there might be a more 'elegant' solution than the use of if / elif statements used in the Annuity class.