You are on page 1of 26

#Annuities: a program to calculate various values of different types of

annuities
#Authors: Kate McCoy, Blake Ellerbusch, Clare Krajewski, and Lexi Hanna

import math
from graphics import*

def x(interest):
"""Calculates the present value multiplyer

Parameters:
interest: a float

Returns:
1 / (1 + interest)

Preconditions:
interest must be a number

Postconditions:
The present value multiplyer is calculated
"""
v = 1 / (1+interest)
return v

def a_angle(interest, duration):


"""Caclulates a angle, the annuity present value multiplyer

Parameters:
interest: a float
duration: an integer

Returns:
a angle duration using interest as the interest rate

Preconditions:
interest and duration must be a number

Postconditions:
a angle, the annuity present value multiplyer is calculated
"""
PV = (1-(x(interest)**duration))/interest
return PV

def s_angle(interest, duration):


"""Caclulates s angle, the future present value multiplyer

Parameters:
interest: a float
duration: an integer

Returns:
s angle duration using interest as the interest rate

Preconditions:
interest and duration must be a number
Postconditions:
s angle, the annuity future value multiplyer is calculated
"""
FV = (((1+interest)**duration)-1)/interest
return FV

def a_doubledot(interest, duration):


"""Caclulates a double dot, the annuity present value multiplyer for
annuities due

Parameters:
interest: a float
duration: an integer

Returns:
a double dot duration using interest as the interest rate

Preconditions:
interest and duration must be a number

Postconditions:
a double dot, the annuity present value multiplyer for annuities due is
calculated
"""
d = ConvertIRtoDR(interest)
PV = (1-(x(interest)**duration))/d
return PV

def s_doubledot(interest, duration):


"""Caclulates s double dot, the annuity future value multiplyer for
annuities due

Parameters:
interest: a float
duration: an integer

Returns:
s double dot duration using interest as the interest rate

Preconditions:
interest and duration must be a number

Postconditions:
s double dot, the annuity future value multiplyer for annuities due
"""
d = ConvertIRtoDR(interest)
FV= (((1+interest)**duration)-1)/d
return FV

def ConvertIRtoDR(interest):
"""Converts an interest rate to a discount rate

Parameters:
interest: a float

Returns:
the interest rate converted to a discount rate
Preconditions:
interest must be a number

Postconditions:
the interest rate is converted to a discount rate
"""
discount = (interest)/(1+interest)
return discount

def clear(win):
"""Clears the graphics window of any objects

Parameters:
win: a GraphWin object

Returns:
a blank graphics window

Preconditions:
win must be a GraphWin object

Postconditions:
the objects from the graphics window are cleared
"""
for item in win.items[:]:
item.undraw()
win.update()

class Calculator:
"""Displays screens of the calculator and gets values from the user"""

def __init__(self, win):


"""Initilizes Calculator class"""
self.win = win

def DisplayType(self):
"""Displays the first screen of the program where the user can choose
the type of annuity"""

rec = Rectangle(Point(0,0),Point(500,500))
rec.setFill("white")
rec.draw(self.win)

annuity = Rectangle(Point(15, 200), Point(240,300))


perpetuity = Rectangle(Point(485, 200), Point(260, 300))
geometric = Rectangle(Point(15, 330), Point(240, 480))
arithmatic = Rectangle(Point(485,330), Point(260,480))

annuity.draw(self.win)
perpetuity.draw(self.win)
geometric.draw(self.win)
arithmatic.draw(self.win)

prompt = Text(Point(250, 100), "Click the type of annuity you have.")


prompt.draw(self.win)
annuity_text = Text(Point(112.5, 250), "Regular")
perpetuity_text = Text(Point(372.5, 250), "Perpetuity")
geometric_text = Text(Point(112.5, 400), "Changing
Payments:\n\nGeometric")
arithmatic_text = Text(Point(372.5, 400), "Changing
Payments:\n\nArithmetic")

annuity_text.draw(self.win)
perpetuity_text.draw(self.win)
geometric_text.draw(self.win)
arithmatic_text.draw(self.win)

def DisplayRegValues(self):
"""Displays a screen where users can input values and what they are
trying to calculate"""
#specified for a regular annuity

rec = Rectangle(Point(0,0),Point(500,500))
rec.setFill("white")
rec.draw(self.win)

prompt = Text(Point(250, 25), "Type any known values of the annuity.")


prompt.draw(self.win)
prompt2 = Text(Point(250, 375), "Click the value you are trying to
calculate.")
prompt2.draw(self.win)

present = Rectangle(Point(15,50),Point(250,150))
present.draw(self.win)
pBox = Entry(Point(130,135), 20)
pBox.draw(self.win)
pBox.setTextColor("black")
self.pBox = pBox #Make input boxes instance variables to use later
ptext = Text(Point(130,90),"Present Value")
ptext.draw(self.win)

future = Rectangle(Point(250,150), Point(485,50))


future.draw(self.win)
fBox = Entry(Point(370,135), 20)
fBox.draw(self.win)
fBox.setTextColor("black")
self.fBox = fBox
ftext = Text(Point(370,90),"Future Value")
ftext.draw(self.win)

payment = Rectangle(Point(15,150), Point(250,250))


payment.draw(self.win)
pmtBox = Entry(Point(130,235), 20)
pmtBox.draw(self.win)
pmtBox.setTextColor("black")
self.pmtBox = pmtBox
paytext = Text(Point(130,190),"Payment Amount")
paytext.draw(self.win)

interest = Rectangle(Point(250,250), Point(485,150))


interest.draw(self.win)
iBox = Entry(Point(370,235), 20)
iBox.draw(self.win)
iBox.setTextColor("black")
self.iBox = iBox
itext = Text(Point(370,190),"Interest Rate\n(written as decimal)")
itext.draw(self.win)

duration = Rectangle(Point(15,250), Point(250,350))


duration.draw(self.win)
dBox = Entry(Point(130,335), 20)
dBox.draw(self.win)
dBox.setTextColor("black")
self.dBox = dBox
dtext = Text(Point(130,290),"Durarion")
dtext.draw(self.win)

frstpmt = Rectangle(Point(250,350), Point(485,250))


frstpmt.draw(self.win)
fpBox = Entry(Point(370,335), 20)
fpBox.draw(self.win)
fpBox.setTextColor("black")
self.fpBox = fpBox
fptext = Text(Point(370,290),"Immediate or Due")
fptext.draw(self.win)

PV = Rectangle(Point(10,400), Point(90,475))
PV.draw(self.win)
PVtext = Text(Point(50,435),"PV")
PVtext.draw(self.win)

FV = Rectangle(Point(90,400), Point(170,475))
FV.draw(self.win)
FVtext = Text(Point(130,435),"FV")
FVtext.draw(self.win)

PMT = Rectangle(Point(170,400), Point(250, 475))


PMT.draw(self.win)
PMTtext = Text(Point(210,435),"PMT")
PMTtext.draw(self.win)

IRR = Rectangle(Point(250,400), Point(330,475))


IRR.draw(self.win)
IRRtext = Text(Point(290,435),"IRR")
IRRtext.draw(self.win)

N = Rectangle(Point(330,400),Point(410,475))
N.draw(self.win)
Ntext = Text(Point(370,435),"N")
Ntext.draw(self.win)

IntAmt = Rectangle(Point(410,400),Point(490,475))
IntAmt.draw(self.win)
Inttext = Text(Point(450,435),"Total\nInterest")
Inttext.draw(self.win)

def GetRegValues(self):
"""Creates a dictionary with the values the user typed and its
corresponding label"""
#specified for a regular annuity

#assign text from input boxes to variables


presentvalue = self.pBox.getText()
futurevalue = self.fBox.getText()
payment = self.pmtBox.getText()
interest = self.iBox.getText()
duration = self.dBox.getText()
frstpmt = self.fpBox.getText()
self.FP = frstpmt.lower() #lowercase the first payment incase it
wasn't already

#create dictionary with label and value


values = {'PV':presentvalue, 'FV':futurevalue, 'PMT':payment,
'IRR':interest, 'N':duration}
try:
for item in values.keys(): #Had to use iteration to change
unempty values from strings to floats
if values[item] == '':
values[item] = values[item]
else:
value = values[item]
values[item] = float(value)
except:
raise ValueError("Values must be numbers")

self.values = values

def GetRegSolveFor(self):
"""Waits for user to click a solve for box, calls the get values
method, and identifies the solve for"""
#specified for a regular annuity

#Wait for user to click a solve for box


while self.win.isOpen():

try:
click = self.win.getMouse()
except:
break

#Calls get values and assigns an instance variable, SolveFor, based


on what was clicked
if 10 < click.getX() < 90 and 400 < click.getY() < 475:
self.GetRegValues()
self.SolveFor = 'PV'
break

if 90 < click.getX() < 170 and 400 < click.getY() < 475:
self.GetRegValues()
self.SolveFor = 'FV'
break

if 170 < click.getX() < 250 and 400 < click.getY() < 475:
self.GetRegValues()
self.SolveFor = 'PMT'
break
if 250 < click.getX() < 330 and 400 < click.getY() < 475:
self.GetRegValues()
self.SolveFor = 'IRR'
break

if 330 < click.getX() < 410 and 400 < click.getY() < 475:
self.GetRegValues()
self.SolveFor = 'N'
break

if 410 < click.getX() < 490 and 400 < click.getY() < 475:
self.GetRegValues()
self.SolveFor = 'IntAmt'
break

def DisplayPerpValues(self):
"""Displays a screen where users can input values and what they are
trying to calculate"""
#specified for a perpetuity

rec=Rectangle(Point(0,0), Point(500,500))
rec.setFill("white")
rec.draw(self.win)

prompt= Text(Point(250,25), "Type any known values of the perpetuity.")


prompt.draw(self.win)
prompt2=Text(Point(250,350), "Click the value you are trying to
calculate.")
prompt2.draw(self.win)

present=Rectangle(Point(15,50),Point(250,150))
present.draw(self.win)
pBox=Entry(Point(130,135),20)
pBox.draw(self.win)
pBox.setTextColor("black")
self.pBox=pBox
ptext=Text(Point(130,90), "Present Value")
ptext.draw(self.win)

frstpmt=Rectangle(Point(250,150),Point(485,50))
frstpmt.draw(self.win)
fpBox = Entry(Point(370,135), 20)
fpBox.draw(self.win)
fpBox.setTextColor("black")
self.fpBox=fpBox
fptext=Text(Point(370,90),"Immediate or Due")
fptext.draw(self.win)

payment=Rectangle(Point(15,150), Point(250,250))
payment.draw(self.win)
pmtBox=Entry(Point(130,235),20)
pmtBox.draw(self.win)
pmtBox.setTextColor("black")
self.pmtBox=pmtBox
paytext=Text(Point(130,190),"Payment Amount")
paytext.draw(self.win)
interest=Rectangle(Point(250,250), Point(485,150))
interest.draw(self.win)
iBox=Entry(Point(370,235),20)
iBox.draw(self.win)
iBox.setTextColor("black")
self.iBox=iBox
itext=Text(Point(370,190),"Interest Rate\n(written as decimal)")
itext.draw(self.win)

PV=Rectangle(Point(100,400), Point(200,475))
PV.draw(self.win)
PVtext=Text(Point(150,435), "PV")
PVtext.draw(self.win)

PMT=Rectangle(Point(200,400), Point(300,475))
PMT.draw(self.win)
PMTtext=Text(Point(250,435),"PMT")
PMTtext.draw(self.win)

IRR=Rectangle(Point(300,400),Point(400,475))
IRR.draw(self.win)
IRRtext=Text(Point(350,435),"IRR")
IRRtext.draw(self.win)

def GetPerpValues(self):
"""Creates a dictionary with the values the user typed and its
corresponding label"""
#specified for a perpetuity

presentvalue = self.pBox.getText()
payment = self.pmtBox.getText()
interest = self.iBox.getText()
frstpmt = self.fpBox.getText()
self.FP = frstpmt.lower()
values = {'PV':presentvalue, 'PMT':payment, 'IRR':interest}

try:
for item in values.keys():
if values[item] == '':
values[item] = values[item]
else:
value = values[item]
values[item] = float(value)
except:
raise ValueError("Values must be numbers")

self.values = values

def GetPerpSolveFor(self):
"""Waits for user to click a solve for box, calls the get values
method, and identifies the solve for"""
#specified for a perpetuity

while self.win.isOpen():
try:
click = self.win.getMouse()
except:
break

if 100 < click.getX() < 200 and 400 < click.getY() < 475:
self.GetPerpValues()
self.SolveFor = 'PV'
break

if 200 < click.getX() < 300 and 400 < click.getY() < 475:
self.GetPerpValues()
self.SolveFor = 'PMT'
break

if 300 < click.getX() < 400 and 400 < click.getY() < 475:
self.GetPerpValues()
self.SolveFor = 'IRR'
break

def DisplayGeoValues(self):
"""Displays a screen where users can input values and what they are
trying to calculate"""
#specified for geometric

rec=Rectangle(Point(0,0),Point(500,500))
rec.setFill("white")
rec.draw(self.win)

prompt=Text(Point(250,25), "Type any known values of the annuity.")


prompt.draw(self.win)
prompt2=Text(Point(250,375), "Click the value you are trying to
calculate.")
prompt2.draw(self.win)

present=Rectangle(Point(15,50), Point(250,150))
present.draw(self.win)
pBox=Entry(Point(130,135),20)
pBox.draw(self.win)
pBox.setTextColor("black")
self.pBox=pBox
ptext=Text(Point(130,90),"Present Value")
ptext.draw(self.win)

future=Rectangle(Point(250,150),Point(485,50))
future.draw(self.win)
fBox=Entry(Point(370,135),20)
fBox.draw(self.win)
fBox.setTextColor("black")
self.fBox=fBox
ftext=Text(Point(370,90),"Future Value")
ftext.draw(self.win)

payment=Rectangle(Point(15,150), Point(250,250))
payment.draw(self.win)
pmtBox=Entry(Point(130,235), 20)
pmtBox.draw(self.win)
pmtBox.setTextColor("black")
self.pmtBox=pmtBox
paytext=Text(Point(130,190), "Payment Amount")
paytext.draw(self.win)

interest=Rectangle(Point(250,250), Point(485,150))
interest.draw(self.win)
iBox=Entry(Point(370,235), 20)
iBox.draw(self.win)
iBox.setTextColor("black")
self.iBox=iBox
itext=Text(Point(370,190), "Interest Rate\n(written as decimal)")
itext.draw(self.win)

duration=Rectangle(Point(15,250), Point(250,350))
duration.draw(self.win)
dBox=Entry(Point(130,335), 20)
dBox.draw(self.win)
dBox.setTextColor("black")
self.dBox=dBox
dtext=Text(Point(130,290), "Duration")
dtext.draw(self.win)

K=Rectangle(Point(250,350), Point(485,250))
K.draw(self.win)
KBox=Entry(Point(370,335),20)
KBox.draw(self.win)
KBox.setTextColor("black")
self.KBox=KBox
Ktext=Text(Point(370,290),"Percent Increase\n(written as decimal)")
Ktext.draw(self.win)

PV=Rectangle(Point(10,400), Point(90,475))
PV.draw(self.win)
PVtext=Text(Point(50,435),"PV")
PVtext.draw(self.win)

FV=Rectangle(Point(90,400),Point(170,475))
FV.draw(self.win)
FVtext=Text(Point(130,435), "FV")
FVtext.draw(self.win)

PMT=Rectangle(Point(170,400),Point(250,475))
PMT.draw(self.win)
pmttext=Text(Point(210,435),"PMT")
pmttext.draw(self.win)

N=Rectangle(Point(250,400), Point(330,475))
N.draw(self.win)
Ntext= Text(Point(290,435),"N")
Ntext.draw(self.win)

IntAmt = Rectangle(Point(330,400),Point(410,475))
IntAmt.draw(self.win)
IntAmttext = Text(Point(370,435),"Total\nInterest")
IntAmttext.draw(self.win)
def GetGeoValues(self):
"""Creates a dictionary with the values the user typed and its
corresponding label"""
#specified for geometric

presentvalue = self.pBox.getText()
futurevalue = self.fBox.getText()
payment = self.pmtBox.getText()
interest = self.iBox.getText()
duration = self.dBox.getText()
K = self.KBox.getText()
values = {'PV':presentvalue, 'FV':futurevalue, 'PMT':payment,
'IRR':interest, 'N':duration, 'K':K}

try:
for item in values.keys():
if values[item] == '':
values[item] = values[item]
else:
value = values[item]
values[item] = float(value)
except:
raise ValueError("Values must be numbers")

self.values = values

def GetGeoSolveFor(self):
"""Waits for user to click a solve for box, calls the get values
method, and identifies the solve for"""
#specified for geometric

while self.win.isOpen():
try:
click = self.win.getMouse()
except:
break
if 10 < click.getX() < 90 and 400 < click.getY() < 475:
self.GetGeoValues()
self.SolveFor = 'PV'
break

if 90 < click.getX() < 170 and 400 < click.getY() < 475:
self.GetGeoValues()
self.SolveFor = 'FV'
break

if 170 < click.getX() < 250 and 400 < click.getY() < 475:
self.GetGeoValues()
self.SolveFor = 'PMT'
break

if 250 < click.getX() < 330 and 400 < click.getY() < 475:
self.GetGeoValues()
self.SolveFor = 'N'
break

if 330 < click.getX() < 410 and 400 < click.getY() < 475:
self.GetGeoValues()
self.SolveFor = 'IntAmt'
break

def DisplayArithValues(self):
"""Displays a screen where users can input values and what they are
trying to calculate"""
#specified for arithmetic

rec=Rectangle(Point(0,0),Point(500,500))
rec.setFill("white")
rec.draw(self.win)

prompt=Text(Point(250,25), "Type any known values of the annuity.")


prompt.draw(self.win)
prompt2=Text(Point(250,375), "Click the value you are trying to
calculate.")
prompt2.draw(self.win)

present=Rectangle(Point(15,50), Point(250,150))
present.draw(self.win)
pBox=Entry(Point(130,135),20)
pBox.draw(self.win)
pBox.setTextColor("black")
self.pBox=pBox
ptext=Text(Point(130,90),"Present Value")
ptext.draw(self.win)

future=Rectangle(Point(250,150),Point(485,50))
future.draw(self.win)
fBox=Entry(Point(370,135),20)
fBox.draw(self.win)
fBox.setTextColor("black")
self.fBox=fBox
ftext=Text(Point(370,90),"Future Value")
ftext.draw(self.win)

payment=Rectangle(Point(15,150), Point(250,250))
payment.draw(self.win)
pmtBox=Entry(Point(130,235), 20)
pmtBox.draw(self.win)
pmtBox.setTextColor("black")
self.pmtBox=pmtBox
paytext=Text(Point(130,190), "Payment Amount")
paytext.draw(self.win)

interest=Rectangle(Point(250,250), Point(485,150))
interest.draw(self.win)
iBox=Entry(Point(370,235), 20)
iBox.draw(self.win)
iBox.setTextColor("black")
self.iBox=iBox
itext=Text(Point(370,190), "Interest Rate\n(written as decimal)")
itext.draw(self.win)

duration=Rectangle(Point(15,250), Point(250,350))
duration.draw(self.win)
dBox=Entry(Point(130,335), 20)
dBox.draw(self.win)
dBox.setTextColor("black")
self.dBox=dBox
dtext=Text(Point(130,290), "Duration")
dtext.draw(self.win)

Q=Rectangle(Point(250,350), Point(485,250))
Q.draw(self.win)
QBox=Entry(Point(370,335),20)
QBox.draw(self.win)
QBox.setTextColor("black")
self.QBox=QBox
Qtext=Text(Point(370,290),"Payment Change")
Qtext.draw(self.win)

PV=Rectangle(Point(10,400), Point(90,475))
PV.draw(self.win)
PVtext=Text(Point(50,435),"PV")
PVtext.draw(self.win)

FV=Rectangle(Point(90,400),Point(170,475))
FV.draw(self.win)
FVtext=Text(Point(130,435), "FV")
FVtext.draw(self.win)

PMT=Rectangle(Point(170,400),Point(250,475))
PMT.draw(self.win)
pmttext=Text(Point(210,435),"PMT")
pmttext.draw(self.win)

Increase=Rectangle(Point(250,400), Point(330,475))
Increase.draw(self.win)
Increasetext= Text(Point(290,435),"Payment\nChange")
Increasetext.draw(self.win)

IntAmt=Rectangle(Point(330,400), Point(410,475))
IntAmt.draw(self.win)
IntAmttext= Text(Point(370,435),"Total\nInterest")
IntAmttext.draw(self.win)

def GetArithValues(self):
"""Creates a dictionary with the values the user typed and its
corresponding label"""
#specified for arithmetic

presentvalue = self.pBox.getText()
futurevalue = self.fBox.getText()
payment = self.pmtBox.getText()
duration = self.dBox.getText()
interest = self.iBox.getText()
Q = self.QBox.getText()
values = {'PV':presentvalue, 'FV':futurevalue, 'PMT':payment,
'IRR':interest, 'N':duration, 'Q':Q}
try:
for item in values.keys():
if values[item] == '':
values[item] = values[item]
else:
value = values[item]
values[item] = float(value)
except:
raise ValueError("Values must be numbers")

self.values = values

def GetArithSolveFor(self):
"""Waits for user to click a solve for box, calls the get values
method, and identifies the solve for"""
#specified for arithmetic

while self.win.isOpen():
try:
click = self.win.getMouse()
except:
break
if 10 < click.getX() < 90 and 400 < click.getY() < 475:
self.GetArithValues()
self.SolveFor = 'PV'
break

if 90 < click.getX() < 170 and 400 < click.getY() < 475:
self.GetArithValues()
self.SolveFor = 'FV'
break

if 170 < click.getX() < 250 and 400 < click.getY() < 475:
self.GetArithValues()
self.SolveFor = 'PMT'
break

if 250 < click.getX() < 330 and 400 < click.getY() < 475:
self.GetArithValues()
self.SolveFor = 'Q'
break

if 330 < click.getX() < 410 and 400 < click.getY() < 475:
self.GetArithValues()
self.SolveFor = 'IntAmt'
break

def DisplayAnswer(self, answer, solvefor):


"""Displays what the user was trying to calculate"""

clear(self.win)
rec = Rectangle(Point(0,0),Point(500,500))
rec.setFill("white")
rec.draw(self.win)
answer = str(answer)
final = Text(Point(250,200), "The " + solvefor + " of your investment
is " + answer)
final.draw(self.win)
click = Text(Point(250, 300), "Click to see your investment grow.")
click.draw(self.win)

def arrow(self, point):


"""Draws an upward arrow at a point"""

x = point.getX()
y = point.getY()
head = Polygon(point, Point(x+10, y+20), Point(x-10, y+20))
head.setFill("black")
head.draw(self.win)
arrow = Rectangle(Point(x-3,y+30), Point(x+2, y+20))
arrow.setFill("black")
arrow.draw(self.win)

def DisplayTimeLine(self, annuity, Type):


"""Displays a time line of the user's annuity and displays the price at
each increment of time"""

rec = Rectangle(Point(0,0),Point(500,500))
rec.setFill("white")
rec.setOutline("white")
rec.draw(self.win)
line = Line(Point(15, 250), Point(485, 250))
line.draw(self.win)

#Calculate the duration of the annuity


N = annuity.CalcN()
if N == '': #Perpetuities return an empty string for N because a
perpetuity has infitite duration
sorry = Text(Point(250,230), "The price of a perpetuity never
changes")
sorry.draw(self.win)
space = 0
N = 0
else:
#make sure N is rounded and divide it into 470 (the length of the
line)
N = round(N)
space = 470 / N

#use iteration to create dashes along the timeline


for i in range(N+1):
dash = Line(Point(15 + (space*i), 255), Point(15 + (space*i), 245))
dash.draw(self.win)
text = Text(Point(15 + (space*i), 270), i)
text.setSize(5)
text.draw(self.win)

#prompts the user


sentence = Text(Point(250, 50), "Click to see the price of your
investment grow.")
sentence.draw(self.win)

#create an empty list for the loop and calculate present value
pvalues = []
PV = annuity.CalcPV()
for i in range(N+1): #creates list of all present values at each
increment of time
presentvalue = PV * ((1+annuity.IRR)**i)
pvalues.append(presentvalue)

for i in pvalues:
#identify postion in list
pos = pvalues.index(i)

#Waits for user to click anywhere


while self.win.isOpen():

try:
click3 = self.win.getMouse()
except:
break

if 0 < click3.getX() < 500 and 0 < click3.getY() < 500:

#creates arrow
self.arrow(Point(15 + (space*pos), 300))
rec = Rectangle(Point((space*pos) - (space -5) , 300),
Point((space*pos)- (space-25), 330))
rec.draw(self.win) #cover up previous arrow with white
box
rec.setFill("white")
rec.setOutline("white")

#create the present value box and text


pvbox = Rectangle(Point(100, 120), Point(400, 200))
pvbox.draw(self.win)
pvbox.setFill("white")

price = Text(Point(250, 180), i)


price.draw(self.win)

#use position as the time


pos = str(pos)
label = Text(Point(250, 140), "Price at time " + pos + ":")
label.draw(self.win)
break

def DisplayEnd(self):
"""Displays a prompt to click to exit and closes the window when the
user clicks"""

#prompts the user


end = Text(Point(250, 400), "Click to exit")
end.draw(self.win)

#Waits until user clicks anywhere and closes graphics window


while self.win.isOpen():
try:
click4 = self.win.getMouse()
except:
break
if 0 < click4.getX() < 500 and 0 < click4.getY() < 500:
break
self.win.close()

class Annuity:
"""Calculates and returns values of a regular annuity"""

def __init__(self, presentvalue, futurevalue, duration, payment,


interestrate, firstpayment):
"""Initilizes Annuity class"""

self.PV = presentvalue
self.FV = futurevalue
self.N = duration
self.PMT = payment
self.IRR = interestrate
self.FP = firstpayment

#raise an exception if values are less than zero


lst = [presentvalue, futurevalue, duration, payment, interestrate]
for i in lst:
if i == '':
i = i
elif i <= 0:
raise ValueError('Values must be positive and non-zero')

def CalcN(self):
"""Returns the duration of the annuity"""
try:
if self.N != '': #if n has already been entered, return n
n = self.N
elif self.FV == '': #if there is no future value value, use the
present value to calculate
v = x(self.IRR)
n = (math.log(1 - ((self.PV*self.IRR)/self.PMT))) /
(math.log(v))
else:
n = (math.log(((self.FV * self.IRR)/self.PMT) + 1)) /
(math.log(1 + math.IRR))
return n

except:
raise UnboundLocalError("payment, interest, and present value or
future value is needed to calculate N")

def CalcPV(self):
"""Returns the present value of the annuity"""
try:
if self.PV != '':
PV = self.PV
elif self.FV != '':
PV = self.FV * (x(self.IRR) ** self.N)
else:
if self.FP == 'immediate':
PV = self.PMT * a_angle(self.IRR, self.N)
if self.FP == 'due':
PV = self.PMT * a_doubledot(self.IRR, self.N)
return PV
except:
raise UnboundLocalError("future value or payment, interest,
duration, and the nature of the first payment are needed to calculate PV")

def CalcFV(self):
"""Returns the future value of the annuity"""

try:
if self.FV != '':
FV = self.FV
elif self.PV != '':
FV = self.PV * ((1 + self.IRR)**self.N)
else:
if self.FP == 'immediate':
FV = self.PMT * s_angle(self.IRR, self.N)
if self.FP == 'due':
FV = self.PMT * s_doubledot(self.IRR, self.N)
return FV

except:
raise UnboundLocalError("present value or payment, interest,
duration, and the nature of the first payment are needed to calculate FV")

def CalcPMT(self):
"""Returns the payment amount of the annuity"""

try:
if self.PMT != '':
PMT = self.PMT
elif self.FV == '':
if self.FP == 'immediate':
PMT = self.PV / a_angle(self.IRR, self.N)
elif self.FP == 'due':
PMT = self.PV / a_doubledot(self.IRR, self.N)
else:
if self.FP == 'immediate':
PMT = self.FV / s_angle(self.IRR, self.N)
elif self.FP == 'due':
PMT = self.FV / s_doubledot(self.IRR, self.N)
return PMT

except:
raise UnboundLocalError("present value or future value, interest,
duration, and the nature of the first payment are needed to calculate PV")

def CalcIRR(self):
"""Returns the interest rate of the annuity"""

try:
if self.IRR != '':
i = self.IRR
else:
PV = self.CalcPV()
PMT = self.CalcPMT()
a = PV / PMT
IRR = (2*(self.N - a)) / (a * (self.N + 1))
return IRR

except:
raise UnboundLocalError("present value or future value, duration,
and the nature of the first payment are needed to calculate IRR")

def CalcTotalInterest(self):
"""Returns the total interest the investor would recieve at the end of
the annuity"""

try:
PV = self.CalcPV()
PMT = self.CalcPMT()
N = self.CalcN()
totalinterest = (PMT * N) - PV
return totalinterest

except:
raise UnboundLocalError("more values are needed to calculate Total
Interest")

class Perpetuity:
"""Calculates and returns values of a perpetuity"""

def __init__(self, presentvalue, payment, interestrate, firstpayment):


"""Initilizes Perpetuity class"""

self.PV = presentvalue
self.PMT = payment
self.IRR = interestrate
self.FP = firstpayment

#raise an exception if values are less than zero


lst = [presentvalue, payment, interestrate]
for i in lst:
if i == '':
i = i
elif i <= 0:
raise ValueError('Values must be positive and non-zero')

def CalcN(self):
"""Returns an empty string to indicate an infinite duration"""

return ''

def CalcPV(self):
"""Returns the present value of the perpetuity"""

try:
if self.PV != '':
PV = self.PV
else:
if self.FP == 'immediate':
PV = self.PMT / self.IRR
elif self.FP == 'due':
d = ConvertIRtoDR(self.IRR)
PV = self.PMT / d
return PV

except:
raise UnboundLocalError("payment, interest, and the nature of the
first payment are needed to calculate PV")

def CalcPMT(self):
"""Returns the payment amount of the perpetuity"""

try:
if self.PMT != '':
PMT = self.PMT
else:
if self.FP == 'immediate':
PMT = self.PV * self.IRR
if self.FP == 'due':
d = ConvertIRtoDR(self.IRR)
PMT = self.PV * d
return PMT

except:
raise UnboundLocalError("present value, interest, and the nature of
the first payment are needed to calculate PMT")

def CalcIRR(self):
"""Returns the interest rate of the perpetuity"""

try:
if self.IRR != '':
IRR = self.IRR
else:
if self.FP == 'immediate':
IRR = self.PMT / self.PV
if self.FP == 'due':
d = self.PMT / self.PV
IRR = d / (1-d)
return IRR

except:
raise UnboundLocalError("present value, payment, and the nature of
the first payment are needed to calculate IRR")

class Geometric:
"""Calculates and returns values of a geometric annuity"""

def __init__(self, presentvalue, futurevalue, payment, k, duration,


interestrate):
"""Initilizes Geometric class"""

self.PV = presentvalue
self.FV = futurevalue
self.PMT = payment
self.K = k
self.N = duration
self.IRR = interestrate

#raise an exception if values are less than zero


lst = [presentvalue, futurevalue, k, payment, duration, interestrate]
for i in lst:
if i == '':
i = i
elif i <= 0:
raise ValueError('Values must be positive and non-zero')

def CalcN(self):
"""Returns the duration of the geometric annuity"""

try:
if self.N != '':
n = self.N
else:
PV = self.CalcPV()
n = (math.log(1-((PV*(self.IRR-self.K)) / self.PMT))) /
(math.log((1 + self.K) / (1 + self.IRR)))
return n

except:
raise UnboundLocalError("present value or future value, duration,
payment, interest, and percent increase are needed to calculate N")

def CalcPV(self):
"""Returns the present value of the geometric annuity"""

try:
if self.PV != '':
PV = self.PV
elif self.FV != '':
PV = self.FV * (x(self.IRR) ** self.N)
else:
PV = self.PMT * ((1-(((1+self.K)/(1+self.IRR))**self.N)) /
(self.IRR - self.K))
return PV

except:
raise UnboundLocalError("payment and percent increase or future
value, interest, and duration are needed to calculate PV")

def CalcFV(self):
"""Returns the future value of the geometric annuity"""

try:
if self.FV != '':
FV = self.FV
elif self.PV != '':
FV = self.PV * ((1+self.IRR)**self.N)
else:
PV = self.CalcPV()
FV = PV * ((1+self.IRR)**self.N)
return FV

except:
raise UnboundLocalError("payment and percent increase or present
value, interest, and duration are needed to calculate FV")
def CalcPMT(self):
"""Returns the initial payment amount of the geometric annuity"""

try:
if self.PMT != '':
PMT = self.PMT
elif self.PV == '':
PV = self.CalcPV()
PMT = PV / ((1-(((1+self.K)/(1+self.IRR))**self.N)) / (self.IRR
- self.K))
return PMT

except:
raise UnboundLocalError("present value or future value, interest,
percent increase, and duration are needed to calculate PMT")

def CalcTotalInterest(self):
"""Returns the total interest the investor would recieve at the end of
the annuity"""

try:
total = 0
PMT = self.CalcPMT()
N = self.CalcN()
for i in range(self.N):
total = total + (PMT*((1+self.K)**i))
PV = self.CalcPV()
totalinterest = total - PV
return totalinterest

except:
raise UnboundLocalError("more values are needed to calculate Total
Interest")

class Arithmetic:
"""Calculates and returns values of a arithmetic annuity"""

def __init__(self, presentvalue, futurevalue, payment, Q, duration,


interestrate):
"""Initilizes Arithmetic class"""

self.PV = presentvalue
self.FV = futurevalue
self.PMT = payment
self.Q = Q
self.N = duration
self.IRR = interestrate

#raise an exception if values are less than zero


lst = [presentvalue, futurevalue, payment, Q, duration, interestrate]
for i in lst:
if i == '':
i = i
elif i <= 0:
raise ValueError('Values must be positive and non-zero')

def CalcN(self):
"""Returns the duration of the arithmetic annuity"""
try:
return self.N
except:
raise UnboundLocalError("duration cannot be calculated")

def CalcPV(self):
"""Returns the present value of the arithmetic annuity"""

try:
if self.PV != '':
PV = self.PV
elif self.FV != '':
PV = self.FV * (x(self.IRR)**self.N)
else:
PV = (self.PMT * a_angle(self.IRR, self.N)) + (self.Q *
((a_angle(self.IRR, self.N) - (self.N * (x(self.IRR)**self.N)))/self.IRR))
return PV

except:
raise UnboundLocalError("payment and payment change or future
value, interest, and duration are needed to calculate PV")

def CalcFV(self):
"""Returns the future value of the arithmetic annuity"""

try:
if self.FV != '':
FV = self.FV
elif self.PV != '':
FV = self.PV * ((1+self.IRR)**self.N)
else:
FV = (self.PMT * s_angle(self.IRR, self.N)) + (self.Q
*((s_angle(self.IRR, self.N) - self.N) / self.IRR))
return FV

except:
raise UnboundLocalError("payment and payment change or present
value, interest, and duration are needed to calculate FV")

def CalcPMT(self):
"""Returns the initial payment amount of the arithmetic annuity"""

try:
if self.PMT != '':
PMT = self.PMT
elif PV == '':
PMT = (self.FV - (self.Q *((s_angle(self.IRR, self.N) - self.N)
/ self.IRR))) / s_angle(self.IRR, self.N)
elif FV == '':
PMT = (self.PV - (self.Q * ((a_angle(self.IRR, self.N) -
(self.N * (x(self.IRR)**self.N)))/self.IRR))) / a_angle(self.IRR,self.N)
return PMT

except:
raise UnboundLocalError("present value or future value, payment
change, interest, and duration are needed to calculate PMT")

def CalcQ(self):
"""Returns the change in payment amount of the arithmetic annuity"""

try:
if self.Q != '':
Q = self.Q
elif PV == '':
Q = (self.FV - (self.PMT * s_angle(self.IRR, self.N))) /
((s_angle(self.IRR, self.N) - self.N) / self.IRR)
else:
Q = (self.PV - (self.PMT * a_angle(self.IRR, self.N))) /
((a_angle(self.IRR, self.N) - (self.N * (x(self.IRR)**self.N)))/self.IRR)
return FV

except:
raise UnboundLocalError("present value or future value, payment,
interest, and duration are needed to calculate Payment Change")

def CalcTotalInterest(self):
"""Returns the total interest the investor would recieve at the end of
the annuity"""

try:
total = 0
PMT = self.CalcPMT()
N = self.CalcN()
Q = self.CalcQ()
for i in range(self.N):
total = total + PMT + (i*Q)
PV = self.CalcPV()
totalinterest = total - PV
return totalinterest

except:
raise UnboundLocalError("more values are needed to calculate Total
Interest")

def main():
#Create window, identify calculator class, and display the first screen
win = GraphWin("Annuities",500,500)
calculator = Calculator(win)
calculator.DisplayType()

#Waits for user to click a type of annuity


while calculator.win.isOpen():

try:
click = calculator.win.getMouse()
except:
break
#Displays the right values and gets the right solve for based on what
was clicked
#indexes the values dictionary to use as the parameters for the
corresponding class
if 15 < click.getX() < 240 and 200 < click.getY() < 300:
Type = 'Annuity'
calculator.DisplayRegValues()
calculator.GetRegSolveFor()
values = calculator.values
annuity = Annuity(values['PV'], values['FV'], values['N'],
values['PMT'], values['IRR'], calculator.FP)

if 260 < click.getX() < 485 and 200 < click.getY() < 300:
Type = 'Perpetuity'
calculator.DisplayPerpValues()
calculator.GetPerpSolveFor()
values = calculator.values
annuity = Perpetuity(values['PV'], values['PMT'], values['IRR'],
calculator.FP)

if 15 < click.getX() < 240 and 330 < click.getY() < 480:
Type = 'Geometric'
calculator.DisplayGeoValues()
calculator.GetGeoSolveFor()
values = calculator.values
annuity = Geometric(values['PV'], values['FV'], values['PMT'],
values['K'], values['N'], values['IRR'])

if 260 < click.getX() < 485 and 330 < click.getY() < 480:
Type = 'Arithmetic'
calculator.DisplayArithValues()
calculator.GetArithSolveFor()
values = calculator.values
annuity = Arithmetic(values['PV'], values['FV'], values['PMT'],
values['Q'], values['N'], values['IRR'])

#Calculates a value based on what SolveFor is


if calculator.SolveFor == 'FV':
answer = annuity.CalcFV()
annuity.FV = answer
if calculator.SolveFor == 'PV':
answer = annuity.CalcPV()
annuity.PV = answer
if calculator.SolveFor == 'PMT':
answer = annuity.CalcPMT()
annuity.PMT = answer
if calculator.SolveFor == 'N':
answer = annuity.CalcN()
annuity.N = answer
if calculator.SolveFor == 'IRR':
answer = annuity.CalcIRR()
annuity.IRR = answer
if calculator.SolveFor == 'Q':
answer = annuity.CalcQ()
annuity.Q = answer
if calculator.SolveFor == 'IntAmt':
answer = annuity.CalcTotalInterest()
#Display the answer to the user
calculator.DisplayAnswer(answer, calculator.SolveFor)

#Waits for user to click again to display the timeline


try:
click2 = calculator.win.getMouse()
except:
break

if 0 < click2.getX() < 500 and 0 < click2.getY() < 500:


calculator.DisplayTimeLine(annuity, Type)
break

#Displays "click to end"


calculator.DisplayEnd()

main()

You might also like