Index Funds
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 an index fund is: an index is a list of companies (like the S&P 500); an index fund buys everything on the list, weighted by company size (market cap), with no manager picking winners (passive vs. active)
- Why index funds win: SPIVA shows 79% of large US active funds trailed the S&P 500 in 2025 and about 9 in 10 over 15 years; Bogle’s math says the average investor earns the market return minus costs
- Expense ratios: a yearly fee taken out quietly; 0.04% vs. 1% is $4 vs. $100 on $10,000; look for funds under 0.10%
- Total market vs. S&P 500 vs. international: the first two move almost together; experts disagree on how much international to hold (Bogle and Collins little or none, Felix anywhere from 10% to 60% home country)
- ETFs vs. mutual funds: same holdings, different packaging; ETFs trade all day and are a little more tax efficient, mutual funds make automatic investing easier
- How to pick one: go broad, check the expense ratio, look for “index” in the 401(k) list (or a target date fund), automate, and leave it alone
2 The Fee This Year
Every year, the fund takes a slice of your balance as its fee.
Formula: Yearly fee = Balance × Expense ratio
Example: $10,000 × 0.0004 = $4. $10,000 × 0.01 = $100, or 25 times as much.
The second example uses a table of typical expense ratios across a few balances. The fee grows with your balance, so a fee that costs pocket change at $10,000 costs real money at $250,000.
Read the fee as dollars you never see. The active fund costs $2,500 a year on $250,000, and the index fund costs $100.
2.1 Level 1: Guided
Fill in the blanks to finish yearly_fee(), then find this year’s fee on $10,000 in an index fund with a 0.04% expense ratio.
Put the balance in A2 (10000) and the expense ratio in B2 (0.0004). Find the fee in C2.
=A2 * B2This returns $4.
The fee is a percentage of your balance. 0.04% is 0.0004.
def yearly_fee(balance, expense_ratio):
return balance * expense_ratio
yearly_fee(10000, 0.0004)The fee is a percentage of your balance. 0.04% is 0.0004.
yearly_fee <- function(balance, expense_ratio) {
balance * expense_ratio
}
yearly_fee(10000, 0.0004)2.2 Level 2: Practice
An active fund charges 1%. What is this year’s fee on a $250,000 balance?
Change A2 to 250000 and B2 to 0.01.
The same formula returns $2,500, every year, whether the fund does well or not.
Write 1% as 0.01.
yearly_fee(250000, 0.01)Write 1% as 0.01.
yearly_fee(250000, 0.01)2.3 Level 3: Challenge
Your portfolio holds $40,000 in an index fund (0.04%), $25,000 in a target-date fund (0.10%), and $10,000 in an active fund (1%). What is the blended expense ratio of the whole portfolio? Give a decimal.
Put balances in B2:B4 and ratios in C2:C4. Find the total fee and divide by the total balance.
=SUMPRODUCT(B2:B4, C2:C4) / SUM(B2:B4)This returns 0.00188, or 0.188%. The $10,000 active fund causes most of it ($100 of the $141 in fees).
Add a fee column with yearly_fee(), then portfolio["fee"].sum() / portfolio["balance"].sum().
portfolio["fee"] = yearly_fee(portfolio["balance"], portfolio["expense_ratio"])
portfolio["fee"].sum() / portfolio["balance"].sum()mutate(fee = yearly_fee(balance, expense_ratio)), then summarise(blended = sum(fee) / sum(balance)).
portfolio |>
mutate(fee = yearly_fee(balance, expense_ratio)) |>
summarise(blended = sum(fee) / sum(balance)) |>
pull(blended)3 What Fees Cost Over a Lifetime
The fee comes out of the return, so subtract it from the return before you compound.
Formula: FV = PMT × [((1 + r)^n − 1) ÷ r], where r = (return − expense ratio) ÷ 12
Example: $300/month for 40 years at 7%. The index fund (0.04%) grows to about $778,655 and the active fund (1%) to about $597,447. Both people put in the same $144,000. The 1% fee cost $181,207, about 23% of what the index fund investor ended up with.
The second example tries several expense ratios over 40 years and reports the share of the low cost result each one loses. The edge case is a 0% fee, which is just the plain annuity from Investing Basics.
Read the lifetime cost as a share, not a dollar amount. A 1% fee takes about 23% of the ending balance, which is why the book says close to a quarter. A negative “lost” number means that fund beat the 0.04% index fund (only possible here with a lower fee).
3.1 Level 1: Guided
Fill in the blanks to finish future_value_after_fees(), then find what $300 a month grows to over 40 years at 7% in an index fund charging 0.04%.
Find the value in one formula with the fee taken off the return.
=FV((7%-0.04%)/12, 480, -300)This returns $778,654.71.
The fee comes straight off the return. 0.04% is 0.0004.
def future_value_after_fees(contribution, annual_return, expense_ratio, years):
rate = (annual_return - expense_ratio) / 12
periods = years * 12
return contribution * (((1 + rate) ** periods - 1) / rate)
future_value_after_fees(300, 0.07, 0.0004, 40)The fee comes straight off the return. 0.04% is 0.0004.
future_value_after_fees <- function(contribution, annual_return, expense_ratio, years) {
rate <- (annual_return - expense_ratio) / 12
periods <- years * 12
contribution * (((1 + rate)^periods - 1) / rate)
}
future_value_after_fees(300, 0.07, 0.0004, 40)3.2 Level 2: Practice
Same $300 a month for 40 years at 7%, but in an active fund charging 1%. How much does the fee cost compared with the index fund?
Find both values and the difference.
=FV((7%-0.04%)/12, 480, -300) - FV((7%-1%)/12, 480, -300)This returns $181,207.49.
Only the expense ratio changes: 0.01.
index_fund = future_value_after_fees(300, 0.07, 0.0004, 40)
active_fund = future_value_after_fees(300, 0.07, 0.01, 40)
index_fund - active_fundOnly the expense ratio changes: 0.01.
index_fund <- future_value_after_fees(300, 0.07, 0.0004, 40)
active_fund <- future_value_after_fees(300, 0.07, 0.01, 40)
index_fund - active_fund3.3 Level 3: Challenge
Compare expense ratios of 0%, 0.04%, 0.10%, 0.50%, 1%, and 1.5% over the same 40 years. Which is the lowest ratio that loses more than 10% of the index fund’s (0.04%) ending balance?
Put the ratios in A2:A7, the ending balance in B, and the share lost in C. Find the first ratio above 10%.
In B2: =FV((7%-A2)/12, 480, -300). In C2: =1 - B2/$B$3. Fill down, then:
=INDEX(A2:A7, MATCH(TRUE, C2:C7>0.1, 0))The answer is 0.50%, which loses 12% of the ending balance.
Share lost is 1 - fv / index_fund. Use min(er for er in ratios if ...).
index_fund = future_value_after_fees(300, 0.07, 0.0004, 40)
ratios = [0, 0.0004, 0.001, 0.005, 0.01, 0.015]
min(er for er in ratios if 1 - future_value_after_fees(300, 0.07, er, 40) / index_fund > 0.10)The function is vectorized over expense_ratio. Share lost is 1 - fv / index_fund; then min(ratios[...]).
index_fund <- future_value_after_fees(300, 0.07, 0.0004, 40)
ratios <- c(0, 0.0004, 0.001, 0.005, 0.01, 0.015)
fv <- future_value_after_fees(300, 0.07, ratios, 40)
min(ratios[1 - fv / index_fund > 0.10])


