SigmaField
Mathematical Foundations for Quant Finance
0 of 256 lessons

Level 0, Orientation and fluency 0.3 Algebra fluency Lesson 5 of 5

Case studyLesson 0.3.5 in Level 0, Orientation and fluency. About 20 minutes.

Case study: simple interest and break-even

Simple interest

Simple interest pays the same amount every year, worked out on the original sum only. Interest does not earn interest; that is compounding, which module 1.4 covers. Money markets use simple interest for loans and deposits shorter than a year.

Rule: Simple interest

I=P r tA=P+I=P(1+rt)\begin{gathered} I = P\,r\,t \\ A = P + I = P(1 + rt) \end{gathered}

PP is the principal, the sum borrowed or deposited. rr is the yearly rate as a decimal and tt the time in years. II is the interest and AA the amount repaid at the end.

For the loan in the opening: I=5000×0.06×3=900I = 5000 \times 0.06 \times 3 = 900, so A=5900A = 5900.

One formula, four questions

A=P(1+rt)A = P(1 + rt) links four quantities. Know any three and you can find the fourth by making it the subject. Here is the rate, using the gather-and-divide method from lesson 0.3.2:

  1. 1
    A=P+PrtA = P + Prt
  2. 2
    A−P=PrtA - P = Prt
  3. 3
    r=A−PPtr = \frac{A - P}{Pt}

The same moves give the other two. Dividing A=P(1+rt)A = P(1 + rt) by 1+rt1 + rt gives the present value, what a future sum is worth today. Dividing A−P=PrtA - P = Prt by PrPr gives the time.

P=A1+rtr=A−PPtt=A−PPr\begin{gathered} P = \frac{A}{1 + rt} \qquad r = \frac{A - P}{Pt} \\[6pt] t = \frac{A - P}{Pr} \end{gathered}

Try it

SymPy rearranges the formula for each letter in turn, which checks all three results at once. It writes 1+rt1 + rt as r*t + 1.

The second half puts in numbers. $5,900 due in 3 years at 6% is worth $5,000 today. $5,000 that grows to $5,600 in 2 years earned 6% a year.

⌘/Ctrl + Enter runs the cell
Check yourself
At 4% simple interest, how many years does it take $5,000 to grow to $7,000?
Exercise

Simple interest, rearranged

Write three functions, one for each rearrangement of A=P(1+rt)A = P(1 + rt):

  1. present_value(amount, rate, years) returns PP.
  2. implied_rate(principal, amount, years) returns rr as a decimal.
  3. years_needed(principal, amount, rate) returns tt.

Work out each formula on paper first, or check them with SymPy in the cell above.

See the tests
def test_present_value():
    """$5,900 due in 3 years at 6% is worth $5,000 today"""
    expect_close(present_value(5900, 0.06, 3), 5000.0, what="present_value(5900, 0.06, 3)")


def test_present_value_part_year():
    """$10,125 due in a quarter of a year at 5% is worth $10,000 today"""
    expect_close(present_value(10125, 0.05, 0.25), 10000.0, what="present_value(10125, 0.05, 0.25)")


def test_implied_rate():
    """$5,000 that grows to $5,600 in 2 years earned 6% a year"""
    expect_close(implied_rate(5000, 5600, 2), 0.06, what="implied_rate(5000, 5600, 2)")


def test_years_needed():
    """$5,000 takes 10 years to reach $7,000 at 4%"""
    expect_close(years_needed(5000, 7000, 0.04), 10.0, what="years_needed(5000, 7000, 0.04)")


def test_round_trip():
    """Each answer leads back to the amount: P(1 + rt) = A"""
    p, a, t = 2500, 2800, 1.5
    r = implied_rate(p, a, t)
    expect_close(p * (1 + r * t), a, what="P(1 + rt) with your implied_rate")
    expect_close(present_value(a, r, t), p, what="present_value with that rate")
    expect_close(years_needed(p, a, r), t, what="years_needed with that rate")

Break-even

Now the coffee cart. Each cup sells for a price pp and costs vv to make: coffee, milk and the cup. This is the variable cost, because it grows with sales. The cart also has a fixed cost FF that must be paid whatever it sells: the licence, the cart itself and insurance.

Selling qq cups brings in pqpq and costs F+vqF + vq, so the profit is

profit(q)=pq−(F+vq)=(p−v) q−F\begin{aligned} \text{profit}(q) &= pq - (F + vq) \\ &= (p - v)\,q - F \end{aligned}

Each cup adds p−vp - v to profit. That amount, the contribution margin, chips away at the fixed cost. For the cart, p=3.50p = 3.50 and v=1.70v = 1.70, so each cup contributes $1.80.

The cart breaks even when profit is no longer negative. That is an inequality, solved as in lesson 0.3.3:

(p−v) q−F≥0(p−v) q≥Fq≥Fp−v\begin{aligned} (p - v)\,q - F &\ge 0 \\ (p - v)\,q &\ge F \\ q &\ge \frac{F}{p - v} \end{aligned}

The last step divides by p−vp - v. That is positive when the price is above the variable cost, so the sign does not flip. If p≤vp \le v, every cup loses money and the cart never breaks even.

Try it

For the cart, F/(p−v)=12000/1.80≈6666.67F / (p - v) = 12000 / 1.80 \approx 6666.67. You cannot sell part of a cup, so round up: the cart needs 6,667 cups.

The cell checks the two whole numbers either side. At 6,666 cups the cart is still $1.20 short; at 6,667 it is 60 cents ahead.

⌘/Ctrl + Enter runs the cell

Try it

On a graph, break-even is where the revenue line pqpq crosses the cost line F+vqF + vq. Left of the crossing the cart loses money; right of it, it makes money.

To aim for a profit target TT instead of zero, solve (p−v)q−F≥T(p - v)q - F \ge T, which gives q≥F+Tp−vq \ge \frac{F + T}{p - v}. A $6,000 profit needs 18000/1.80=10,00018000 / 1.80 = 10{,}000 cups.

⌘/Ctrl + Enter runs the cell
Check yourself
The cart raises its price to $3.90 a cup. Costs stay the same. How many cups does it need to break even now?
Exercise

Break-even units

Write break_even_units(fixed, price, variable) that returns the smallest whole number of units whose profit is zero or more.

  • Use F/(p−v)F / (p - v) and round up with math.ceil.
  • If price <= variable, the business can never break even. Raise ValueError.

The tests check that profit is non-negative at your answer and negative one unit below it.

See the tests
def _profit(q, fixed, price, variable):
    return (price - variable) * q - fixed


def _raises_value_error(*args):
    try:
        break_even_units(*args)
    except ValueError:
        return True
    return False


def test_coffee_cart():
    """The coffee cart (fixed $12,000, price $3.50, cost $1.70) needs 6,667 cups"""
    expect_equal(break_even_units(12_000, 3.50, 1.70), 6667, what="break_even_units(12000, 3.50, 1.70)")


def test_exact_division():
    """When the margin divides the fixed cost exactly, no rounding: 1000 / (15 − 5) = 100"""
    expect_equal(break_even_units(1000, 15, 5), 100, what="break_even_units(1000, 15, 5)")


def test_profit_either_side():
    """Profit is at least 0 at the answer and below 0 one unit earlier"""
    args = (7_500, 24.0, 13.0)
    q = break_even_units(*args)
    assert _profit(q, *args) >= 0, f"profit at your answer {q} is {_profit(q, *args):.2f}, which is negative"
    assert _profit(q - 1, *args) < 0, f"profit at {q - 1} units is already {_profit(q - 1, *args):.2f}, so {q} is not the smallest"


def test_whole_number():
    """The answer is a whole number of units"""
    q = break_even_units(12_000, 3.50, 1.70)
    assert isinstance(q, int), f"expected a whole number (int), got {q!r}"


def test_no_fixed_cost():
    """With no fixed cost, zero units already break even"""
    expect_equal(break_even_units(0, 5, 2), 0, what="break_even_units(0, 5, 2)")


def test_never_breaks_even():
    """A price at or below the variable cost never breaks even, so raise ValueError"""
    assert _raises_value_error(1000, 2.0, 2.5), "expected ValueError when price < variable"
    assert _raises_value_error(1000, 3.0, 3.0), "expected ValueError when price == variable"

Summary card

  • Simple interest: I=PrtI = Prt and A=P(1+rt)A = P(1 + rt). Rearranged: P=A1+rtP = \frac{A}{1 + rt}, r=A−PPtr = \frac{A - P}{Pt}, t=A−PPrt = \frac{A - P}{Pr}.
  • sp.solve(sp.Eq(left, right), letter) rearranges a formula for any letter, and checks your algebra.
  • Profit on qq units is (p−v)q−F(p - v)q - F. Break-even is q≥Fp−vq \ge \frac{F}{p - v}, rounded up; it exists only if p>vp > v.
  • For a profit target TT: q≥F+Tp−vq \ge \frac{F + T}{p - v}.

Free further reading

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