Python 'Cashflow' object

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
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)

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.

• Using the strategy pattern from the GOF would be a solution, but I don't speak python... See sourcemaking.com/design_patterns/strategy/python/1 as an example Commented Jan 22, 2019 at 13:50
• Hi, cheers for your response. Unfortunately, the strategy described there goes somewhat over my head, on account of my current lack of knowledge of python. I'll try deciphering it with the help of Google though! Commented Jan 22, 2019 at 23:20

Using the strategy pattern from the GOF would be a solution, but I don't speak python...

1. create an abstract class ValueCalculator with the functions calculate_value_to_time(self, time)and calculate_value(self)
2. create three different classes that implement the interface: ValueCalculatorWithContinuous, ValueCalculatorWithDue and ValueCalculatorWithImmediate
3. each class offers the special implementation of the formula
4. create a self.valueCalculator-property for the Annuity-class
5. instantiate the correct ValueCalculator, decided from the annuity_type, and set the self.valueCalculator in the __init__-part to this
6. then you can shorten the functions to
#Functions
def present_value_to_time(self, time):
if time >= self.term:
return self.present_value
else:
return self.valueCalculator.calculate_value_to_time(self, time)


Hope that helps. Watch out, this is not REAL Python code!