Retirement Accounts

TipHow these exercises work

Each topic starts with a real scenario and a bar model, then three levels of exercises. Level 1 (Guided) fills in the blanks of a finished function. Level 2 (Practice) applies it to new numbers. Level 3 (Challenge) asks a new question with little scaffolding. The R and Python exercises run in your browser: click Run Code to try, Submit Answer to check, and use the hint or solution if you get stuck. Excel exercises have a solution you can reveal.

1 Book Topics

  • What a retirement account is: a container, not an investment; it decides how the investments inside get taxed; early withdrawals before 59 and a half usually cost a 10% penalty plus income tax
  • 2026 limits: 401(k)/403(b)/TSP $24,500; Traditional and Roth IRA $7,500 (one shared limit); HSA $4,400 (self); taxable brokerage has no limit
  • The plan at work: contributions come out of each paycheck; a common match is “50% of the first 6%”; watch vesting and don’t settle for the automatic enrollment rate
  • Traditional vs. Roth: pay tax later or pay it now; the only thing that matters is whether your tax rate is lower now or later, so Roth is the usual default early in a career
  • The IRA: opened at a brokerage, yours no matter where you work; Roth contributions (not earnings) can come out any time; Roth income limit is $153,000 to $168,000 (single)
  • The HSA: needs an HSA eligible (high deductible) plan; tax free going in, growing, and coming out for medical costs
  • Which account first: deductible cash, full match, high interest debt, emergency fund, HSA and Roth IRA, more 401(k), then taxable; aim for 15% of income including the match
  • Changing jobs: roll the old plan over directly; cashing out $5,000 at 22 leaves about $3,900 instead of about $100,000 by 67

2 The Employer Match

Your employer adds money on top of what you put in, up to a limit.

Formula: Match = Salary × min(Your %, Match limit %) × Match rate

Example: $45,000 × min(6%, 6%) × 50% = $1,350 a year in free money. At 3%, you’d only get $675.

6% of a $45,000 salary, plus a 50% employer match

6% of a $45,000 salary, plus a 50% employer match

6% of a $45,000 salary, plus a 50% employer match

6% of a $45,000 salary, plus a 50% employer match

The second example runs the same job across several contribution rates. Past 6%, your own deposits keep growing but the match stops. The last example shows what one year of the full match becomes if it stays invested for 40 years at 7%.

Read the match as a 50% return the day you deposit. Contributing 3% instead of 6% leaves $675 a year on the table, and one year of the full match grows to about $20,200 over 40 years at 7%.

2.1 Level 1: Guided

Fill in the blanks to finish employer_match(), then find the yearly match on a $45,000 salary when you contribute 6% and the employer matches 50% of the first 6%.

Put the salary in A2 (45000), your contribution in B2 (0.06), the match limit in C2 (0.06), and the match rate in D2 (0.5). Find the match in E2.

=A2 * MIN(B2, C2) * D2

This returns $1,350.

The employer only matches up to the limit, so use the smaller of your % and the limit.

def employer_match(salary, contribution_pct, match_limit_pct, match_rate):
    return salary * min(contribution_pct, match_limit_pct) * match_rate

employer_match(45000, 0.06, 0.06, 0.50)

The employer only matches up to the limit, so use the smaller of your % and the limit. pmin() does it element by element.

employer_match <- function(salary, contribution_pct, match_limit_pct, match_rate) {
  salary * pmin(contribution_pct, match_limit_pct) * match_rate
}
employer_match(45000, 0.06, 0.06, 0.50)

2.2 Level 2: Practice

Same job, but you only contribute 3%. What is the match now?

Change B2 to 0.03.

The same formula returns $675. Half the match is left on the table.

Only your contribution changes: 0.03.

employer_match(45000, 0.03, 0.06, 0.50)

Only your contribution changes: 0.03.

employer_match(45000, 0.03, 0.06, 0.50)

2.3 Level 3: Challenge

Five coworkers each earn $45,000 with the same 50%-of-6% match, contributing 0%, 3%, 6%, 10%, and 15%. The full match is $1,350. How much match do they miss in total?

Put the rates in A2:A6, the match in B, and the missed match in C, then total it.

In B2, filled down: =45000*MIN(A2, 6%)*50%. In C2: =1350 - B2. Then =SUM(C2:C6) returns $2,025. Contributing more than 6% doesn’t earn more match.

min() can’t compare a whole column to 0.06. Use coworkers["pct"].clip(upper=0.06) to cap each row.

coworkers["match"] = 45000 * coworkers["pct"].clip(upper=0.06) * 0.50
(1350 - coworkers["match"]).sum()

employer_match() uses pmin(), so it works on the whole pct column. Then summarise(missed = sum(1350 - match)).

coworkers |>
  mutate(match = employer_match(45000, pct, 0.06, 0.50)) |>
  summarise(missed = sum(1350 - match)) |>
  pull(missed)

3 Traditional vs. Roth

Roth takes the tax out first, then the rest grows. Traditional lets the whole amount grow, then takes the tax out at the end.

Formula: Amount you keep = Contribution × (1 − tax rate) × (1 + r)^n

For Roth, use today’s tax rate. For Traditional, use your tax rate in retirement.

Example: $1,000 for 40 years at 7%. With 12% now and 12% later, both accounts leave you about $13,178. If your rate in retirement is 22% instead, the Traditional account leaves you about $11,680.

$1,000 after 40 years at 7%, with a 12% tax rate now and later

$1,000 after 40 years at 7%, with a 12% tax rate now and later

$1,000 after 40 years at 7%, with a 12% tax rate now and later

$1,000 after 40 years at 7%, with a 12% tax rate now and later

Multiplying by (1 − tax rate) gives the same answer whether you do it first or last. So the second example holds today’s rate at 12% and tries several retirement rates. The edge case is a retirement rate lower than today’s, where Traditional wins.

Read the result as a contest between two tax rates. Growth and years don’t pick the winner. Roth wins when today’s rate is lower than your rate in retirement, and Traditional wins when it’s higher.

3.1 Level 1: Guided

Fill in the blanks to finish after_tax_value(), then find what $1,000 in a Roth leaves you after 40 years at 7%, taxed at today’s 12% rate.

Put the contribution in A2 (1000), the tax rate in B2 (0.12), the return in C2 (0.07), and years in D2 (40). Find the after-tax value in E2.

=A2 * (1 - B2) * (1 + C2)^D2

This returns $13,177.52.

Taxes take a share of the money; growth multiplies it once per year.

def after_tax_value(contribution, tax_rate, rate, years):
    return contribution * (1 - tax_rate) * (1 + rate) ** years

after_tax_value(1000, 0.12, 0.07, 40)

Taxes take a share of the money; growth multiplies it once per year.

after_tax_value <- function(contribution, tax_rate, rate, years) {
  contribution * (1 - tax_rate) * (1 + rate)^years
}
after_tax_value(1000, 0.12, 0.07, 40)

3.2 Level 2: Practice

In a Traditional account, the same $1,000 is taxed when you take it out. If your retirement tax rate is 22%, what do you keep?

Change B2 to 0.22.

The same formula returns $11,680.08, about $1,500 less than the Roth.

Only the tax rate changes: 0.22.

after_tax_value(1000, 0.22, 0.07, 40)

Only the tax rate changes: 0.22.

after_tax_value(1000, 0.22, 0.07, 40)

3.3 Level 3: Challenge

Today’s rate is 12% (the Roth). Compare it with a Traditional account at retirement rates of 10%, 12%, 22%, and 24%. Label each “Roth”, “Traditional”, or “tie” (within $1).

Put the retirement rates in A2:A5. Find the Traditional value in B, then the winner in C against the Roth’s $13,177.52.

In B2, filled down: =1000*(1-A2)*1.07^40. In C2, filled down:

=IF(ABS(13177.52-B2)<1, "tie", IF(13177.52>B2, "Roth", "Traditional"))

The winners are Traditional, tie, Roth, Roth.

Write a small function that compares roth with after_tax_value(1000, r, 0.07, 40), checking the tie first, and use a list comprehension.

roth = after_tax_value(1000, 0.12, 0.07, 40)
later = [0.10, 0.12, 0.22, 0.24]
def winner(r):
    trad = after_tax_value(1000, r, 0.07, 40)
    if abs(roth - trad) < 1:
        return "tie"
    return "Roth" if roth > trad else "Traditional"

[winner(r) for r in later]

trad <- after_tax_value(1000, later, 0.07, 40) works on all four rates. Then case_when() with the tie test first.

roth <- after_tax_value(1000, 0.12, 0.07, 40)
later <- c(0.10, 0.12, 0.22, 0.24)
trad <- after_tax_value(1000, later, 0.07, 40)
case_when(
  abs(roth - trad) < 1 ~ "tie",
  roth > trad ~ "Roth",
  .default = "Traditional"
)

4 Cashing Out vs. Rolling Over

The book’s job change example: cash out $5,000 at 22, or leave it invested until 67.

Formula: Cash in hand = Balance × (1 − tax rate − penalty); Left invested = Balance × (1 + r)^n

Example: $5,000 × (1 − 0.12 − 0.10) = $3,900 today, vs. $5,000 × 1.07^45 ≈ $105,000 at 67 (the book rounds to about $100,000).

Read it as a trade: $3,900 now costs you about $100,000 later.

4.1 Level 1: Guided

Fill in the blanks to finish cash_out(), then find what you’d actually receive cashing out a $5,000 401(k) at a 12% tax rate with the 10% penalty.

Put the balance in A2 (5000), tax rate in B2 (0.12), and penalty in C2 (0.10). Find the cash in D2.

=A2 * (1 - B2 - C2)

This returns $3,900.

Both the tax and the penalty come off the top.

def cash_out(balance, tax_rate, penalty=0.10):
    return balance * (1 - tax_rate - penalty)

cash_out(5000, 0.12)

Both the tax and the penalty come off the top.

cash_out <- function(balance, tax_rate, penalty = 0.10) {
  balance * (1 - tax_rate - penalty)
}
cash_out(5000, 0.12)

4.2 Level 2: Practice

Roll it over instead. If the $5,000 stays invested at 7% from age 22 to 67, what is it worth?

Find the value in one formula.

=5000 * 1.07^(67-22)

This returns $105,012.26.

The money grows from 22 to 67: that’s 45 years.

5000 * (1 + 0.07) ** (67 - 22)

The money grows from 22 to 67: that’s 45 years.

5000 * (1 + 0.07)^(67 - 22)

4.3 Level 3: Challenge

Three job changers at 22 cash out $2,000, $5,000, and $20,000 at a 12% tax rate with the 10% penalty. Combined, how much more would they have at 67 by rolling over (7% a year) than the cash they got?

Put balances in A2:A4, cash now in B, value at 67 in C, and find the total difference.

In B2: =A2*(1-12%-10%). In C2: =A2*1.07^45. Fill down, then =SUM(C2:C4)-SUM(B2:B4) returns $546,006.20.

cash_out() works on a whole column. at_67 is balance * 1.07 ** 45.

changers["cash_now"] = cash_out(changers["balance"], 0.12)
changers["at_67"] = changers["balance"] * 1.07 ** 45
changers["at_67"].sum() - changers["cash_now"].sum()

mutate(cash_now = cash_out(balance, 0.12), at_67 = balance * 1.07^45), then summarise(gap = sum(at_67) - sum(cash_now)).

changers |>
  mutate(
    cash_now = cash_out(balance, 0.12),
    at_67    = balance * 1.07^45
  ) |>
  summarise(gap = sum(at_67) - sum(cash_now)) |>
  pull(gap)