Managing Debt
1 Book Topics
- Debt hierarchy: toxic (15–29% APR: credit cards, payday loans) → moderate (5–10%: car loans, private student loans) → strategic (3–7%: mortgages, federal student loans)
- Credit cards: eliminate before any other goal; minimum payments are designed to keep you in debt indefinitely; once paid off, use as a payment tool not a financing tool
- Student loans: evaluate the return — debt ≤ 1× first-year salary on a 10-year plan; for federal loans, compare IBR/SAVE/RAP based on income
- Car loans: depreciating assets financed at 7–9% are wealth killers; 20/4/10 rule (20% down, ≤4-year term, ≤10% of gross income on transportation)
- Pay off vs. invest: if loan rate > expected return → pay off; if loan rate < expected return → invest; add a peace-of-mind premium for guaranteed vs. probable returns
2 APR vs. APY
Lenders quote APR; you actually pay APY (which is higher due to compounding).
Formula: APY = (1 + APR ÷ n)^n − 1
show/hide
apr_to_apy <- function(apr, periods_per_year = 365) {
(1 + apr / periods_per_year)^periods_per_year - 1
}
apr_to_apy(apr = 0.24)
#> [1] 0.2711489show/hide
def apr_to_apy(apr, periods_per_year=365):
return (1 + apr / periods_per_year) ** periods_per_year - 1
apr_to_apy(apr=0.24)
#> 0.27114889144128895With APR in A2 and compounding periods in B2:
=(1+A2/B2)^B2-13 Minimum Payment Trap
Minimum payments barely reduce the balance — most goes to interest.
Approximation: Months to pay off ≈ Balance ÷ (Payment − Monthly Interest)
show/hide
months_to_payoff <- function(balance, monthly_payment, apr) {
monthly_interest <- balance * (apr / 12)
principal_reduction <- monthly_payment - monthly_interest
balance / principal_reduction
}
# $5,000 balance, $100/month, 22% APR
months_to_payoff(balance = 5000, monthly_payment = 100, apr = 0.22)
#> [1] 600show/hide
def months_to_payoff(balance, monthly_payment, apr):
monthly_interest = balance * (apr / 12)
principal_reduction = monthly_payment - monthly_interest
return balance / principal_reduction
months_to_payoff(balance=5000, monthly_payment=100, apr=0.22)
#> 600.0000000000003With balance in A2, payment in B2, APR in C2:
=A2/(B2-(A2*(C2/12)))4 Total Interest on a Loan
The real cost of any installment loan is principal plus all the interest paid over the life of the loan.
Formula: Total Interest = (Monthly Payment × Number of Payments) − Principal
show/hide
total_interest <- function(monthly_payment, n_months, principal) {
(monthly_payment * n_months) - principal
}
total_interest(monthly_payment = 512, n_months = 72, principal = 30000)
#> [1] 6864
total_interest(monthly_payment = 719, n_months = 48, principal = 30000)
#> [1] 4512show/hide
def total_interest(monthly_payment, n_months, principal):
return (monthly_payment * n_months) - principal
print(total_interest(monthly_payment=512, n_months=72, principal=30000))
#> 6864
print(total_interest(monthly_payment=719, n_months=48, principal=30000))
#> 4512With payment in A2, months in B2, principal in C2:
=(A2*B2)-C25 Debt-to-Income Ratio (DTI)
Lenders calculate this about you. Calculate it yourself first.
Formula: DTI = (Total Monthly Debt Payments ÷ Gross Monthly Income) × 100
Benchmarks: <20% healthy; 20–35% manageable; 36–43% stretched; >43% danger zone.
show/hide
dti_ratio <- function(monthly_debt_payments, gross_monthly_income) {
(monthly_debt_payments / gross_monthly_income) * 100
}
dti_ratio(monthly_debt_payments = 2500, gross_monthly_income = 7000)
#> [1] 35.71429show/hide
def dti_ratio(monthly_debt_payments, gross_monthly_income):
return (monthly_debt_payments / gross_monthly_income) * 100
dti_ratio(monthly_debt_payments=2500, gross_monthly_income=7000)
#> 35.714285714285715With debt payments in A2 and income in B2:
=(A2/B2)*1006 Avalanche vs. Snowball Order
Two methods for ordering debt payoff; both work, each has trade-offs.
- Avalanche: highest APR first — mathematically optimal, saves the most interest
- Snowball: smallest balance first — psychologically motivating, builds momentum
show/hide
order_avalanche <- function(debts) debts[order(-debts$apr), ]
order_snowball <- function(debts) debts[order(debts$balance), ]
debts <- data.frame(
name = c("Card A", "Card B", "Card C"),
balance = c(1000, 5000, 2000),
apr = c(0.18, 0.24, 0.15)
)
order_avalanche(debts)
#> # A tibble: 3 × 3
#> name balance apr
#> <chr> <dbl> <dbl>
#> 1 Card B 5000 0.24
#> 2 Card A 1000 0.18
#> 3 Card C 2000 0.15
order_snowball(debts)
#> # A tibble: 3 × 3
#> name balance apr
#> <chr> <dbl> <dbl>
#> 1 Card A 1000 0.18
#> 2 Card C 2000 0.15
#> 3 Card B 5000 0.24show/hide
def order_avalanche(debts):
return sorted(debts, key=lambda d: d["apr"], reverse=True)
def order_snowball(debts):
return sorted(debts, key=lambda d: d["balance"])
debts = [
{"name": "Card A", "balance": 1000, "apr": 0.18},
{"name": "Card B", "balance": 5000, "apr": 0.24},
{"name": "Card C", "balance": 2000, "apr": 0.15},
]
print(order_avalanche(debts))
#> [{'name': 'Card B', 'balance': 5000, 'apr': 0.24}, {'name': 'Card A', 'balance': 1000, 'apr': 0.18}, {'name': 'Card C', 'balance': 2000, 'apr': 0.15}]
print(order_snowball(debts))
#> [{'name': 'Card A', 'balance': 1000, 'apr': 0.18}, {'name': 'Card C', 'balance': 2000, 'apr': 0.15}, {'name': 'Card B', 'balance': 5000, 'apr': 0.24}]Sort the debt table by APR descending for avalanche, or by Balance ascending for snowball — use Data → Sort on the appropriate column.



