SigmaField
Mathematical Foundations for Quant Finance
0 of 256 lessons
      1. 1.4.1 Exponents and their rulesConcept · 25 min
      2. 1.4.2 Exponential functions and the number eConcept · 25 min
      3. 1.4.3 Logarithms and their rulesConcept · 25 min
      4. 1.4.4 Solving exponential and logarithmic equationsConcept · 25 min
      5. 1.4.5 Practice set: exponents and logarithmsPractice set · 25 min
      6. 1.4.6 Case study: compounding and continuous ratesCase study · 35 min
      7. 1.4.7 Case study: simple returns and log returnsCase study · 30 min

Level 1, Precalculus 1.4 Exponents and logarithms Lesson 6 of 7

Case studyLesson 1.4.6 in Level 1, Precalculus. About 35 minutes.

Case study: compounding and continuous rates

Compounding nn times a year

A quoted annual rate rr paid nn times a year pays a fraction r/nr/n 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, 1+r/n1 + r/n. This is percentage growth from lesson 1.4.2, with g=r/ng = r/n. Throughout, rr is a yearly rate written as a decimal (5% is 0.05) and tt is a time in years.

  1. 1
    after 1 period:PV(1+rn)\begin{gathered} \text{after 1 period:} \\ PV\left(1+\frac{r}{n}\right) \end{gathered}
  2. 2
    after 1 year:PV(1+rn)n\begin{gathered} \text{after 1 year:} \\ PV\left(1+\frac{r}{n}\right)^n \end{gathered}
  3. 3
    FV=PV(1+rn)ntFV = PV\left(1+\frac{r}{n}\right)^{nt}

Here PVPV is the present value, the amount you start with, and FVFV is the future value after tt 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.

⌘/Ctrl + Enter runs the cell
Check yourself
A deposit earns 6% a year compounded quarterly. Which expression gives the value of $1 after 3 years?

Continuous compounding

In lesson 1.4.2 you saw (1+x/n)n(1 + x/n)^n settle on exe^x as nn grows. Here x=rx = r, so the yearly factor settles on ere^r, and tt years gives erte^{rt}. That is continuous compounding, and running it backwards is continuous discounting:

FV=PV ertPV=FV e−rtFV = PV\,e^{rt} \qquad\qquad PV = FV\,e^{-rt}

Try it

math.exp(x) computes exe^x. Compare it with compounding a million times a year.

The last line is $100 compounded continuously for one year: just under $105.13.

⌘/Ctrl + Enter runs the cell

Try the explorer below. Step through the presets from annual to hourly and watch the growth factor climb towards the dashed line at erte^{rt}. 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.

1101001,00010,0001.051.05051.051
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.

⌘/Ctrl + Enter runs the cell

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

EAR=(1+rn)n−1EARcontinuous=er−1\begin{gathered} \text{EAR} = \left(1+\frac{r}{n}\right)^n - 1 \\ \text{EAR}_{\text{continuous}} = e^{r} - 1 \end{gathered}

The EAR answers "what simple annual rate would grow money by the same amount?"

Quoted 5% compoundedGrowth of $1 in a yearEAR
Annually1.0500005.0000%
Monthly1.0511625.1162%
Daily (365)1.0512675.1267%
Continuously1.0512715.1271%
Check yourself
Which pays more over one year: 10% compounded monthly, or 9.6% compounded continuously?
Exercise

One function for every compounding convention

Write future_value(pv, rate, years, n=None).

  • n is the number of compounding periods per year.
  • When n is None, 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)
Exercise

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 rnr_n for a rate compounded nn times a year and rcr_c for a continuous rate. Equivalence says

(1+rnn)n=(1+rmm)m=erc\begin{gathered} \left(1 + \frac{r_n}{n}\right)^n \\ = \left(1 + \frac{r_m}{m}\right)^m = e^{r_c} \end{gathered}

Solving for each rate takes one log or one root (lessons 1.4.1 and 1.4.4).

Rule: Converting a quoted rate

rc=nln⁡(1+rnn)rn=n(erc/n−1)rm=m[(1+rnn)n/m−1]\begin{gathered} r_c = n \ln\left(1 + \frac{r_n}{n}\right) \\ r_n = n\left(e^{r_c / n} - 1\right) \\ r_m = m\left[\left(1 + \frac{r_n}{n}\right)^{n/m} - 1\right] \end{gathered}

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 1.032=1.06091.03^2 = 1.0609, 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.

⌘/Ctrl + Enter runs the cell
Check yourself
Which continuous rate is equivalent to 6% compounded annually?

Year fractions and day counts

Every formula here measures tt 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: t=91360≈0.252778t = \frac{91}{360} \approx 0.252778, so the interest is $12,638.89.
  • ACT/365: t=91365≈0.249315t = \frac{91}{365} \approx 0.249315, 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 5%×365360≈5.0694%5\% \times \frac{365}{360} \approx 5.0694\% 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.

⌘/Ctrl + Enter runs the cell

How long until money doubles?

Set the growth factor equal to 2 and solve for tt, exactly as in lesson 1.4.4:

(1+r)t=2tln⁡(1+r)=ln⁡2t=ln⁡2ln⁡(1+r)\begin{aligned} (1 + r)^t &= 2 \\ t\ln(1 + r) &= \ln 2 \\ t &= \frac{\ln 2}{\ln(1 + r)} \end{aligned}

With continuous compounding, ert=2e^{rt} = 2 gives the simpler t=ln⁡2/rt = \ln 2 / r. 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: ert=2e^{rt} = 2 has no positive solution unless r>0r > 0, 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%.

⌘/Ctrl + Enter runs the cell

What rate does a price imply?

A bond you hold rose from 92.50 to 100 in 1.5 years. The continuous rate rr satisfies 100=92.50 e1.5r100 = 92.50\,e^{1.5r}. Divide by 92.50, take ln⁡\ln, and divide by 1.5:

r=11.5ln⁡10092.50≈0.05197=5.20%r = \frac{1}{1.5}\ln\frac{100}{92.50} \approx 0.05197 = 5.20\%

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 1+r/n1 + r/n 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 e0.08e0.06=e0.14≈1.150274e^{0.08} e^{0.06} = e^{0.14} \approx 1.150274. The average rate over the three years is the time-weighted average, 0.14/3≈4.6667%0.14 / 3 \approx 4.6667\%.

With annual compounding the plain average is slightly off: the equivalent steady rate is (1.042×1.06)1/3−1≈4.6624%(1.04^2 \times 1.06)^{1/3} - 1 \approx 4.6624\%. 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.

⌘/Ctrl + Enter runs the cell

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. rr is per year and tt 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. ln⁡(1+r/n)\ln(1 + r/n) needs 1+r/n>01 + r/n > 0, so a rate of −100%-100\% 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.

⌘/Ctrl + Enter runs the cell
Exercise

Solve for time and for rate

Write two functions.

  1. years_to_grow(multiple, rate, n=None): the number of years for money to grow by multiple (2 means doubling) at annual rate compounded n times a year. n=None means continuous.
  2. implied_continuous_rate(pv, fv, years): the continuous rate that grows pv into fv in years.

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 nn times a year for tt years: FV=PV(1+rn)ntFV = PV\left(1+\frac{r}{n}\right)^{nt}.
  • Continuous compounding: FV=PVertFV = PV e^{rt}; discounting: PV=FVe−rtPV = FV e^{-rt}.
  • Compare rates with the effective annual rate: (1+r/n)n−1(1+r/n)^n - 1, or er−1e^r - 1 when continuous.
  • Convert by matching a year's growth: rc=nln⁡(1+rn/n)r_c = n\ln(1 + r_n/n) and rn=n(erc/n−1)r_n = n(e^{r_c/n} - 1).
  • Day counts turn days into years: ACT/360 and ACT/365 give different interest on the same quote.
  • Time to grow by a multiple MM: ln⁡M/ln⁡(1+r)\ln M / \ln(1+r) with annual compounding, or ln⁡M/r\ln M / r continuously. No answer if the rate cannot get there.
  • Continuous rate between two values: r=1tln⁡FVPVr = \frac{1}{t}\ln\frac{FV}{PV}. Continuous rates add over time.

Free further reading

Optional. This lesson covers what you need; these add more examples and practice. All are free to read.