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

APR cost vs. APY cost on a $10,000 balance (24% APR, daily compounding)

APR cost vs. APY cost on a $10,000 balance (24% APR, daily compounding)
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.2711489
show/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.27114889144128895

With APR in A2 and compounding periods in B2:

=(1+A2/B2)^B2-1

3 Minimum Payment Trap

Minimum payments barely reduce the balance — most goes to interest.

Approximation: Months to pay off ≈ Balance ÷ (Payment − Monthly Interest)

Where a $100 minimum payment actually goes

Where a $100 minimum payment actually goes
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] 600
show/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.0000000000003

With 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

$30,000 car loan — principal vs. interest for 6-year and 4-year terms

$30,000 car loan — principal vs. interest for 6-year and 4-year terms
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] 4512
show/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))
#> 4512

With payment in A2, months in B2, principal in C2:

=(A2*B2)-C2

5 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.

$7,000 monthly income split into debt payments and remaining income

$7,000 monthly income split into debt payments and remaining income
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.71429
show/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.714285714285715

With debt payments in A2 and income in B2:

=(A2/B2)*100

6 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.24
show/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.