Skip to main content

Methodology · Open Math

FIRE & Coast FIRE

The FIRE number is annual spending divided by a safe withdrawal rate (the 4% rule from the Trinity Study). Coast FIRE discounts that target back to today at an expected return.

Authoritative reference

Our implementation is validated against Trinity Study — Cooley, Hubbard & Walz (1998), sustainable withdrawal rates.

Reference test cases

These exact cases are asserted in our unit test suite, so a regression would fail the build.

InputExpected
$40,000 annual spending at a 4% withdrawal rate $1,000,000 FIRE number
$1,000,000 target, 7% return, 20 years out $258,419 Coast FIRE number

Source code

This is the exact, unmodified source that powers the FIRE Calculator.

FIRE & Coast FIRE engine (fire.ts)

// src/lib/finance/fire.ts
//
// FIRE (Financial Independence, Retire Early) calculations.
//   FIRE Number = annual spending / safe withdrawal rate
//   Coast FIRE   = FIRE Number / (1 + return)^years_to_retirement
//
// Once you hit Coast FIRE you can stop contributing and your portfolio still
// grows to the FIRE number by retirement.

/** Lump-sum portfolio size that supports `annualSpending` at `swr`. */
export function fireNumber(annualSpending: number, swr: number): number {
  if (swr <= 0) throw new Error(`fireNumber: swr must be > 0, got ${swr}`);
  return annualSpending / swr;
}

/**
 * Coast FIRE number: how much you'd need invested today, with no further
 * contributions, to grow to {@link target} in {@link yearsToRetirement} years
 * at the given return rate.
 */
export function coastFireNumber(target: number, returnRate: number, yearsToRetirement: number): number {
  if (yearsToRetirement <= 0) return target;
  return target / Math.pow(1 + returnRate, yearsToRetirement);
}

/**
 * Iterate year by year and find the first year where balance ≥ target.
 * Returns Infinity if the target isn't reached within maxYears (default 100).
 */
export function yearsToReach(
  startBalance: number,
  annualContribution: number,
  returnRate: number,
  target: number,
  maxYears = 100
): number {
  let bal = startBalance;
  for (let y = 1; y <= maxYears; y++) {
    bal = bal * (1 + returnRate) + annualContribution;
    if (bal >= target) return y;
    if (!Number.isFinite(bal)) return Infinity;
  }
  return Infinity;
}