Compounding times a year
A quoted annual rate paid times a year pays a fraction of the balance at the end of each period. The interest then earns interest itself. That is all compounding means.
So each period multiplies the balance by the same factor, . This is percentage growth from lesson 1.4.2, with . Throughout, is a yearly rate written as a decimal (5% is 0.05) and is a time in years.
- 1
- 2
- 3
Here is the present value, the amount you start with, and is the future value after years.
Try it
Watch $100 at 5% as payments get more frequent. Run the cell, then change r and run it again.
Going from annual to monthly adds 11.6 cents. Going from daily to hourly adds less than a twentieth of a cent. The value is settling down.
Continuous compounding
In lesson 1.4.2 you saw settle on as grows. Here , so the yearly factor settles on , and years gives . That is continuous compounding, and running it backwards is continuous discounting:
Try it
math.exp(x) computes . Compare it with compounding a million times a year.
The last line is $100 compounded continuously for one year: just under $105.13.
Try the explorer below. Step through the presets from annual to hourly and watch the growth factor climb towards the dashed line at . Then raise the rate or the years: the gap between the conventions widens.
Explore
Compound more often and the growth climbs, but more and more slowly, towards continuous compounding, the dashed line. The axis for n is spaced by powers of 10.
- Compounded monthly
- (1 + 0.05/12)^(12 × 1) = 1.0511619
- Continuously
- e^(0.05 × 1) = 1.0512711
- Gap on $1,000,000.00
- $109.20
- Gap × n
- $1,310.38: it settles as n grows, so the gap shrinks like 1 ÷ n
Over many years
Over one year the conventions differ by cents. Over ten years at 10%, they differ by dollars.
$100 at 10% a year for ten years. Simple interest grows in a straight line, annual compounding jumps once a year, and continuous compounding grows smoothly ahead of both.
Simple interest (case study 0.3.5) pays interest on the original $100 only, so it adds $10 a year. Compounding pays interest on interest, so each year adds more than the last. The more often it compounds, the faster it pulls away.
Try it
The cell computes all three at a few dates. Each row is one year count: simple, annual, continuous.
Comparing rates fairly
Rates quoted with different compounding cannot be compared directly. Convert each to its effective annual rate (EAR): the plain growth it produces over one year.
Rule: Effective annual rate
The EAR answers "what simple annual rate would grow money by the same amount?"
| Quoted 5% compounded | Growth of $1 in a year | EAR |
|---|---|---|
| Annually | 1.050000 | 5.0000% |
| Monthly | 1.051162 | 5.1162% |
| Daily (365) | 1.051267 | 5.1267% |
| Continuously | 1.051271 | 5.1271% |
One function for every compounding convention
Write future_value(pv, rate, years, n=None).
nis the number of compounding periods per year.- When
nisNone, compound continuously.
Use math.exp for the continuous case. The tests check annual, monthly and continuous compounding, a zero rate, and that very frequent compounding approaches the continuous answer.
See the tests
import math
def test_annual():
"""$100 at 5% compounded annually for 1 year is $105"""
expect_close(future_value(100, 0.05, 1, n=1), 105.0, what="future_value(100, 0.05, 1, n=1)")
def test_monthly():
"""$100 at 5% compounded monthly for 1 year"""
expect_close(future_value(100, 0.05, 1, n=12), 100 * (1 + 0.05 / 12) ** 12, what="future_value(100, 0.05, 1, n=12)")
def test_many_years():
"""Years multiply the number of periods: 10 years quarterly is 40 periods"""
expect_close(future_value(250, 0.04, 10, n=4), 250 * 1.01**40, what="future_value(250, 0.04, 10, n=4)")
def test_continuous():
"""n=None compounds continuously: $100 at 5% for 2 years is 100·e^0.1"""
expect_close(future_value(100, 0.05, 2), 100 * math.exp(0.1), what="future_value(100, 0.05, 2)")
def test_zero_rate():
"""A zero rate leaves the money unchanged, for any convention"""
expect_close(future_value(80, 0.0, 7, n=12), 80.0, what="future_value(80, 0, 7, n=12)")
expect_close(future_value(80, 0.0, 7), 80.0, what="future_value(80, 0, 7)")
def test_frequent_approaches_continuous():
"""Compounding a million times a year is within 1e-6 of continuous"""
frequent = future_value(100, 0.05, 1, n=1_000_000)
expect_close(frequent, 100 * math.exp(0.05), what="n=1,000,000 vs continuous", rel=1e-8, abs_tol=1e-6)
Effective annual rate
Write effective_annual_rate(rate, n=None) returning the EAR as a decimal (so 5% is 0.05). As before, n=None means continuous compounding. The EAR is the growth of $1 over one year, minus 1.
See the tests
import math
def test_annual_is_unchanged():
"""Annual compounding: the EAR equals the quoted rate"""
expect_close(effective_annual_rate(0.05, n=1), 0.05, what="effective_annual_rate(0.05, n=1)")
def test_monthly():
"""5% compounded monthly has an EAR of about 5.1162%"""
expect_close(effective_annual_rate(0.05, n=12), (1 + 0.05 / 12) ** 12 - 1, what="effective_annual_rate(0.05, n=12)")
def test_continuous():
"""5% continuous has an EAR of e^0.05 - 1, about 5.1271%"""
expect_close(effective_annual_rate(0.05), math.exp(0.05) - 1, what="effective_annual_rate(0.05)")
def test_returns_a_decimal():
"""The answer is a decimal, not a percentage: 5% annual gives 0.05, not 5"""
ear = effective_annual_rate(0.05, n=1)
assert ear is not None and abs(ear) < 1, f"expected a decimal like 0.05, got {ear}"
Converting between conventions
Two quotes are equivalent when they grow $1 by the same amount in a year. Write for a rate compounded times a year and for a continuous rate. Equivalence says
Solving for each rate takes one log or one root (lessons 1.4.1 and 1.4.4).
Rule: Converting a quoted rate
To continuous, from continuous, and from one discrete convention to another. Each keeps the growth over a year the same.
Take 6% compounded twice a year. A year multiplies $1 by , so the EAR is 6.09%. The equivalent quarterly rate is 5.9557%, the monthly rate 5.9263% and the continuous rate 5.9118%. The more often a rate compounds, the lower it must be quoted to give the same growth.
Try it
The cell converts 6% semiannual into annual, quarterly and monthly rates, then into a continuous rate.
The last column is the growth of $1 in a year at each converted rate. It is 1.0609 every time, which is the check.
Year fractions and day counts
Every formula here measures in years. A desk measures time in days, so each quote comes with a rule for turning days into a year fraction: its day-count convention.
Dollar and euro money markets divide the actual number of days by 360, written ACT/360. Sterling divides by 365, written ACT/365. Many bonds use 30/360, which counts every month as 30 days.
Deposits shorter than a year usually earn simple interest (case study 0.3.5). Put $1,000,000 on deposit for 91 days at 5%:
- ACT/360: , so the interest is $12,638.89.
- ACT/365: , so the interest is $12,465.75.
The same quoted 5% pays $173.14 more under ACT/360. So 5% on ACT/360 is worth on an ACT/365 basis.
Try it
The cell works out the year fraction and the interest under both conventions, then restates the ACT/360 rate on an ACT/365 basis.
How long until money doubles?
Set the growth factor equal to 2 and solve for , exactly as in lesson 1.4.4:
With continuous compounding, gives the simpler . At 7%, doubling takes 10.24 years with annual compounding and 9.90 years continuously.
The formula breaks where the maths says it should. At a rate of 0 it divides by zero, and at a negative rate it returns a negative time. Both mean the money never doubles: has no positive solution unless , the no-solution case of lesson 1.4.4.
Try it
Traders estimate doubling time with the rule of 72: divide 72 by the rate in percent.
Run the cell to see how close it gets. It is best for rates between about 4% and 12%.
What rate does a price imply?
A bond you hold rose from 92.50 to 100 in 1.5 years. The continuous rate satisfies . Divide by 92.50, take , and divide by 1.5:
A fall in value gives a negative rate. Negative rates are not only for losses: some central banks have set them, so desk code must accept them. The formulas still work, as long as each period's factor stays positive.
Rates over several periods
Continuous rates add over time, because exponents add (lesson 1.4.1). Two years at 4% and then one year at 6%, both continuous, grow $1 to . The average rate over the three years is the time-weighted average, .
With annual compounding the plain average is slightly off: the equivalent steady rate is . This is one reason risk systems store rates in continuous form. Case study 1.4.7 meets the same idea as log returns that add up.
Try it
The cell checks the bond's implied rate and both averages.
Units and bad inputs
Most rate bugs on a desk are unit bugs. Check these before trusting a number.
- Percent or decimal. Formulas take decimals: 5% is 0.05. A basis point (bp) is 0.01%, or 0.0001, so a rise of 25 bp takes 5% to 5.25%.
- Per year. is per year and is in years. A monthly rate of 0.5% is 6% a year compounded monthly, not 0.5% a year.
- Day counts. Turn days into years with the quote's convention, not always 365.
- Factors stay positive. needs , so a rate of or below breaks every formula here.
Try it
Passing 5 instead of 0.05 is the classic slip. math.exp(5) is about 148.41: a "5%" rate that multiplies money 148 times in a year.
Solve for time and for rate
Write two functions.
years_to_grow(multiple, rate, n=None): the number of years for money to grow bymultiple(2 means doubling) at annualratecompoundedntimes a year.n=Nonemeans continuous.implied_continuous_rate(pv, fv, years): the continuous rate that growspvintofvinyears.
Both answers come from taking math.log of a growth factor. Write the formula on paper first.
years_to_grow must raise ValueError when no length of time works. That happens at a zero rate, or when the rate moves money away from the multiple: a negative rate never doubles it. A falling value can still reach 90%, so that case has an answer.
See the tests
import math
def test_doubling_annual():
"""Doubling at 7% annual takes ln 2 / ln 1.07, about 10.24 years"""
expect_close(years_to_grow(2, 0.07, n=1), math.log(2) / math.log(1.07), what="years_to_grow(2, 0.07, n=1)")
def test_doubling_continuous():
"""Doubling at 7% continuous takes ln 2 / 0.07, about 9.90 years"""
expect_close(years_to_grow(2, 0.07), math.log(2) / 0.07, what="years_to_grow(2, 0.07)")
def test_monthly_tripling():
"""Tripling at 6% monthly: (1 + 0.005)^(12t) = 3"""
t = years_to_grow(3, 0.06, n=12)
expect_close((1 + 0.06 / 12) ** (12 * t), 3.0, what="growth after years_to_grow(3, 0.06, n=12) years")
def test_round_trip():
"""Growing for the computed time gives back the multiple"""
t = years_to_grow(1.5, 0.04, n=4)
expect_close(1.01 ** (4 * t), 1.5, what="1.01^(4t) with t = years_to_grow(1.5, 0.04, n=4)")
def test_falling_value():
"""At -5% continuous, a value falls to 90% after ln 0.9 / -0.05, about 2.107 years"""
expect_close(years_to_grow(0.9, -0.05), math.log(0.9) / -0.05, what="years_to_grow(0.9, -0.05)")
def _raises_value_error(*args, **kwargs):
try:
years_to_grow(*args, **kwargs)
except ValueError:
return True
except ZeroDivisionError:
raise AssertionError(f"years_to_grow{args} divided by zero: raise ValueError instead")
return False
def test_zero_rate_never_doubles():
"""At a 0% rate money never doubles, so raise ValueError"""
assert _raises_value_error(2, 0.0), "expected ValueError for years_to_grow(2, 0.0)"
assert _raises_value_error(2, 0.0, n=12), "expected ValueError for years_to_grow(2, 0.0, n=12)"
def test_negative_rate_never_doubles():
"""At -2% a year money never doubles, so raise ValueError (not a negative time)"""
assert _raises_value_error(2, -0.02), "expected ValueError for years_to_grow(2, -0.02)"
assert _raises_value_error(2, -0.02, n=1), "expected ValueError for years_to_grow(2, -0.02, n=1)"
def test_implied_rate_bond():
"""92.50 to 100 in 1.5 years implies about 5.197% continuous"""
expect_close(implied_continuous_rate(92.5, 100, 1.5), math.log(100 / 92.5) / 1.5, what="implied_continuous_rate(92.5, 100, 1.5)")
def test_implied_rate_loss():
"""A fall in value gives a negative rate"""
r = implied_continuous_rate(100, 90, 2)
assert r is not None and r < 0, f"expected a negative rate for a loss, got {r}"
expect_close(100 * math.exp(2 * r), 90.0, what="100·e^(2r)")
Summary card
- Compounding times a year for years: .
- Continuous compounding: ; discounting: .
- Compare rates with the effective annual rate: , or when continuous.
- Convert by matching a year's growth: and .
- Day counts turn days into years: ACT/360 and ACT/365 give different interest on the same quote.
- Time to grow by a multiple : with annual compounding, or continuously. No answer if the rate cannot get there.
- Continuous rate between two values: . Continuous rates add over time.