Financial literacy is something we all need. Without it, our chances of making a good living and securing a happy future are lower than they’d otherwise be. It’s a huge topic, but we can learn bits at a time. One area worth getting a handle on is the power of compound interest. If you are able to invest at a good rate for long enough, your wealth can grow substantially.
Let’s use programming to help us experiment with compound interest. Programming speeds up time so we can think faster and try out lots of ideas.
In this lab, we’ll come up with ways to compute the amount of money we’ll have at the end of an investment cycle, and determine how many months it will take to reach a desired goal. We’ll look at the effects of making deposits along the way, and what happens if taxes are taken out every year versus only at the end. We’ll also see whether the number of times interest is compounded per year makes much of a difference or not.
Because exponents—which humans are usually pretty bad at—are involved, some of the results may surprise you. Or not. The point is that programming gives you ways to figure stuff out in the way you want them figured out. It even gives your an avenue to do crazy things such as asking what happens if you compound interest $n$ times per year, where $n$ is a negative number (which you can try in one of the challenges).
Formatting strings
Arithmetic
+=
-=
**
Algorithm design
Locales
Start by making a new folder ~/cmsi1010/lab07. Create the file interest.py with the contents:
print("This will be a program to compute compound interest.")
Run it!
We will start simply with a program that computes the amount of money earned each year starting with 1000 monetary units (see how we’re not biased toward any particular culture’s currency?) with 5% interest over 10 years. Let’s write this code together. Edit the file to read (we’ll explain it, especially that += operator, in class):
balance = 1000 interest_rate = 0.05 years = 10 print("At the start you have", balance) for year in range(1, years + 1): interest_earned = balance * interest_rate balance += interest_earned print("After year", year, "you have", balance)
Run it and see:
At the start you have 1000 After year 1 you have 1050.0 After year 2 you have 1102.5 After year 3 you have 1157.625 After year 4 you have 1215.50625 After year 5 you have 1276.2815624999998 After year 6 you have 1340.0956406249998 After year 7 you have 1407.1004226562497 After year 8 you have 1477.4554437890622 After year 9 you have 1551.3282159785153 After year 10 you have 1628.894626777441
What is going on with Year 5?Here’s the deal: computer arithmetic is not always exact. It is subject to roundoff error. The details of why this happens will come in a later course, but you can ask an AI chatbot about it.
We can make improvements. The first one is to format the output nicely. Python formats with something it calls an f-string. Let’s print to hundredths of a monetary unit:
balance = 1000 interest_rate = 0.05 years = 10 print(f"At the start you have {balance:.2f}") for year in range(1, years + 1): interest_earned = balance * interest_rate balance += interest_earned print(f"After year {year} you have {balance:.2f}")
Run it. Much better, right!
At the start you have 1000.00 After year 1 you have 1050.00 After year 2 you have 1102.50 After year 3 you have 1157.62 After year 4 you have 1215.51 After year 5 you have 1276.28 After year 6 you have 1340.10 After year 7 you have 1407.10 After year 8 you have 1477.46 After year 9 you have 1551.33 After year 10 you have 1628.89
Negative interest?Why not? Programming gives you the power to explore. We are not necessarily bound by the physical world. You have played video games before, right?
Now let’s see what happens if at the end of every year, we make a deposit of 100 monetary units. How long does it take to double your money now?
balance = 1000 interest_rate = 0.05 years = 10 deposit = 100 print(f"At the start you have {balance:.2f}") for year in range(1, years + 1): interest_earned = balance * interest_rate balance += interest_earned balance += deposit print(f"After year {year} you have {balance:.2f}")
At the start you have 1000.00 After year 1 you have 1150.00 After year 2 you have 1307.50 After year 3 you have 1472.88 After year 4 you have 1646.52 After year 5 you have 1828.84 After year 6 you have 2020.29 After year 7 you have 2221.30 After year 8 you have 2432.37 After year 9 you have 2653.98 After year 10 you have 2886.68
Note that you got in 4 years what took 10 years earlier. And you ended up with quite a bit more at the end. Keep making those contributions!
Now, let’s look at...taxes! Let’s keep the numbers simple for now. We’ll assume we have some great investment that make 13% per year. But each year, you are taxed 25% of the interested earned. Let’s start with 10,000 units and make a 1,000-unit contribution at the end of the year (after taxes are taken out) and see where we are after 30 years:
balance = 10000 interest_rate = 0.13 years = 30 tax_rate = 0.25 deposit = 1000 print(f"At the start you have {balance:.2f}") for year in range(1, years + 1): interest_earned = balance * interest_rate taxes = interest_earned * tax_rate balance += interest_earned balance -= taxes balance += deposit print(f"After year {year} you have {balance:.2f}")
You should get 319,883.75 if you run the program. Now here’s a twist: what would happen if the taxes were not taken out every year, but only at the end?
balance = 10000 interest_rate = 0.13 years = 30 tax_rate = 0.25 deposit = 1000 total_interest_earned = 0 print(f"At the start you have {balance:.2f}") for year in range(1, years + 1): interest_earned = balance * interest_rate total_interest_earned += interest_earned balance += interest_earned balance += deposit taxes = total_interest_earned * tax_rate balance -= taxes print(f"After {year} years and after taxes, you have {balance:.2f}")
Woah. Were you surprised at the difference?
We’ve been experimenting and mucking around in a single program. It’s now time to think about designing something useful for people. What if we packaged all that work we did into a nice function? That way other people could import this function and use it in the apps that they build themselves? ❤️❤️❤️ We’ll build it step-by-step in class, but here’s the result:
def investment_value(start, interest_rate, tax_rate, deposit, years): balance = start for _ in range(1, years + 1): interest_earned = balance * interest_rate taxes = interest_earned * tax_rate balance += (interest_earned - taxes + deposit) return balance
Go ahead and replace all the code you wrote so far with this simple function above. We can now make some calls to see if we got things right. Add these lines of code underneath your function:
print(investment_value(1000, 0.05, 0, 0, 10)) # should be 1628.89 print(investment_value(1000, 0.05, 0, 100, 10)) # should be 2886.68 print(investment_value(10000, 0.13, 0.25, 1000, 30)) # should be 319883.75 print(investment_value(1, 1, 0, 0, 20)) # should be 1048576.0
Hey, don’t the calls look hard to read? Let’s remember from Lab 1 how we named the arguments? Is this better?
print(investment_value(start=1000, interest_rate=0.05, tax_rate=0, deposit=0, years=10)) # should be 1628.89 print(investment_value(start=1000, interest_rate=0.05, tax_rate=0, deposit=100, years=10)) # should be 2886.68 print(investment_value(start=10000, interest_rate=0.13, tax_rate=0.25, deposit=1000, years=30)) # should be 319883.75 print(investment_value(start=1, interest_rate=1, tax_rate=0, deposit=0, years=20)) # should be 1048576.0
Now, a big change. Instead of computing how much money we have after a given number of years, let’s compute how many years it takes to reach a desired sum of money. Remember from the previous lab that we don’t know beforehand how many times we need to loop, so we need a while loop! Let’s make a new function
def years_to_reach_goal(start, interest_rate, tax_rate, deposit, goal): years = 0 balance = start while balance < goal: interest_earned = balance * interest_rate taxes = interest_earned * tax_rate balance += (interest_earned - taxes + deposit) years += 1 return years
You know what the next exercise is: write a handful of calls, playing around with the numbers.
So far we’ve avoided using currency values like dollars, euros, or rubles. That’s because the computation doesn’t require that information. That information is only for display. The particular way that currency, dates, and similar things are displayed is part of what’s called a locale. Most Python environments have very, very few locales that they support, but there are some. Try this code in the Python REPL:
import locale
for loc in ['en_US', 'ru_RU', 'fr_FR', 'de_DE', 'es_ES', 'it_IT', 'fr_CA']:
locale.setlocale(locale.LC_ALL, loc)
print(locale.currency(35888382152.22815, grouping=True))
What do you think happened?
locale and the use of locale.setlocale and locale.currency into your current lab work.
Now it’s your turn. Here are some ideas for you to extend the activities above:
locale.currency when printing.)investment_value function is written with a loop, that for every year, updates the balance. Assuming there are no taxes and no deposits, the code inside the loop was b += b * r where $b$ is the balance and $r$ is the interest rate. This can also be written as b *= (1 + r). Now to get the value after $t$ years, we do this multiplication $t$ times, and repeated multiplication is just exponentiation, so the value after $t$ years is just $b(1+r)^t$. Write a new function called fast_investment_value accepting parameters start, interest_rate, and years, and returning start * (1 + interest_rate) ** years. Compare the results of calling this function with those of calling your existing investment_value function with the same arguments.fast_investment_value function from the previous challenge to support this new parameter $n$. See if you get the same results for similar arguments passed to your existing investment_value function from the second challenge above.fast_investment_value function with start=1000, years=10, rate=0.5 and these values for n:
Try to explain WHAT EVEN IS GOING ON WITH THESE NEGATIVE VALUES. It is perfectly okay if you don’t know...why would you? But take the opportunity to learn. Ask a friend or chatbot.
This was our longest lab yet. If you are still getting your programming legs, you might want to review some of the previously assigned readings from Think Python. Then continue with the following:
You might also have started exploring programming with AI chatbots. Read about how this is changing (and not changing) programming.
We’ve covered:
+= operatorHere are some questions useful for your spaced repetition learning. Many of the answers are not found on this page. Some will have popped up in lecture. Others will require you to do your own research.
x is 5, what is the value of x after x += 3? x after x -= 2 if x was 5 before? x is 5, what are the values of the two strings "x is {x}" and f"x is {x}"? "x is {x}"
"x is 5"x is 3.141592653589793238, what is the string f"{x:.5f}"? "3.14159"
x is 3.141592653589793238, what is the string f"{x:10.5f}"? " 3.14159"
for-loop is used to calculate the ending balance of an investment, but a while-loop is used to for computing the time needed to reach a goal. for-loop is used when we know how many times we want to repeat the operation (e.g., for a fixed number of years), while a while-loop is used when we do not know how many iterations are needed until a condition is met (e.g., until the balance reaches a goal).deposit parameter in the investment functions? deposit parameter allows for additional contributions to be made to the investment at the end of each period, which can significantly increase the final balance over time.locale.setlocale(locale.LC_ALL, 'en_US') and locale.setlocale(locale.LC_ALL, 'ru_RU')? locale.currency function?