Methodology & Math Reference

How the Calculator Actually Works

No black boxes. No hand-waving. Every assumption this calculator makes is documented here — with the exact formulas and worked examples so you can verify it yourself.

You don't have to read any of this to use the calculator.
This page exists for the obsessive types — like me — who need to know exactly how the math works before they trust a number.

Contents
  1. Accumulation Phase
  2. Growth & Mid-Year Contributions
  3. Tax Framework Overview
  4. Federal Income Tax
  5. Per-Account Tax Treatment
  6. State Tax
  7. Lump Sum & One-Time Events
  8. Inheritance & the 10-Year Rule
  9. Required Minimum Distributions
  10. Roth Conversion Ladder
  11. Monte Carlo Simulation
  12. Market Crash Stress Test
01

The Accumulation Phase

The accumulation phase covers every year from today until the first year you start withdrawing from any account. The calculator runs a year-by-year loop — one iteration per year — tracking five account types for both you and a spouse: Roth IRA/401(k), Traditional IRA/401(k), Taxable Brokerage, High-Yield Savings (HYS), and Crypto/Alternative.

Each year in the loop represents the age at the end of that year. If you are currently age 45 and retire at 65, the loop runs 20 iterations, ending at age 65.

Cranky Earl says

Every calculator has to make a call about when contributions happen. Some put them at the start of the year — optimistic. Some split them in half — a wash. I put them at the end of the year, which means your contributions earn zero return in the year you make them. It's the most conservative choice available. Your projected balance will always be slightly lower than a calculator that assumes you front-load everything on January 1st. I'd rather you retire with more money than expected than run short because a calculator was too generous with the math.

The Order of Operations Each Year

Within each accumulation year, the calculator performs operations in this exact sequence:

Accumulation Year — Order of Operations Step 1: Grow income and contributions by the income growth rate (year 2+) income = prior_income × (1 + income_growth_rate) contribution = prior_contrib × (1 + income_growth_rate) Step 2: Calculate earnings on starting balance only (contributions not yet added) earnings = start_balance × return_rate Step 3: Process any lump sum, inheritance, or one-time expense events Step 4: Calculate and deduct pre-retirement investment taxes Step 5: Add contributions + earnings to balance (end-of-year) end_balance = start_balance + contribution + earnings

Notice that contributions are added in Step 5 — after growth is already calculated. Growth is applied to the starting balance only. This is the deliberate end-of-year pessimistic choice detailed in the next section.

02

Growth & the Mid-Year Contribution Approximation

The calculator uses an annual time step. That means it can't simulate money trickling in each paycheck. Instead, it has to make an assumption about when during the year contributions arrive. There are three common choices:

AssumptionEarnings formulaBias
Beginning of year(balance + contribution) × rOptimistic
End of year (this calculator)balance × rPessimistic ✓
Mid-year(balance + contribution/2) × rNeutral approximation

The calculator uses the end-of-year method: growth is applied to the starting balance only, and contributions are added afterward. This means new money earns zero return in the year it's contributed — as if every dollar you save lands on December 31st.

Annual Earnings — End-of-Year Method For Roth IRA: roth_earnings = roth_balance × roth_pre_return For Traditional (employer match included in end-of-year balance update): trad_earnings = trad_balance × trad_pre_return For Taxable Brokerage: tax_earnings = tax_balance × taxable_pre_return Same pattern for HYS and Crypto. Then the end-of-year balance update (contributions land after growth): end_balance = start_balance + earnings + contribution
Why this is pessimistic

In the real world, your January 401(k) contribution has been compounding since January. This calculator pretends every dollar arrived on December 31st. The result: your projected ending balance is lower than it would be if contributions earned any return at all during the year. Over a long accumulation horizon that difference adds up — but it always works in your favor. You'll likely end up ahead of the projection, not behind it.

Worked Example

Example — Single Accumulation Year

Inputs: Roth balance = $100,000 | Annual contribution = $7,000 | Return = 7%

MethodEarnings CalculationEnd Balance
Beginning of year (optimistic)($100,000 + $7,000) × 0.07 = $7,490$114,490
Mid-year (neutral)($100,000 + $3,500) × 0.07 = $7,245$114,245
End of year (this calculator)$100,000 × 0.07 = $7,000$114,000

The end-of-year method produces the lowest result — $490 less than the optimistic method on a single $100K balance in a single year. Over 20 years of contributions to a growing portfolio, that gap compounds into real money. It all stays in your pocket when you retire ahead of the projection.

Catch-Up Contributions

Once either person reaches age 50, the calculator automatically adds the catch-up contribution to the annual contribution total before applying the end-of-year earnings formula. There is no phase-in — it's a binary switch at exactly age 50.

Catch-Up Contribution Logic effective_roth_contrib = roth_contrib + (age >= 50 ? roth_catchup : 0) effective_trad_contrib = trad_contrib + (age >= 50 ? trad_catchup : 0)

Income Growth

If you specify an income growth rate, your salary, employer match, and all contributions grow by that rate each year starting in year 2. This compounds — a 3% growth rate applied to $7,000 in year 1 means $7,210 in year 2, $7,426 in year 3, and so on.

Income and Contribution Growth (Year 2+) income_year_n = income_year_1 × (1 + growth_rate)^(n-1) contribution_year_n = contrib_year_1 × (1 + growth_rate)^(n-1) employer_match_n = income_year_n × match_percent
03

Tax Framework Overview

The calculator models taxes in two separate phases: pre-retirement (on investment earnings and inherited IRA withdrawals) and retirement (on all income sources). Both phases use the same core tax engine — calculateTotalIncomeTax() — which handles federal, state, and local taxes in sequence.

The total tax bill is built by stacking income sources in a specific order because some types (like long-term capital gains) are taxed at preferential rates that depend on how much ordinary income you already have. The order matters.

Income Stack — Tax Calculation Order 1. Earned income (wages, minus Traditional contributions) 2. Traditional IRA/401(k) withdrawals and pension income 3. Interest / ordinary income (HYS earnings, brokerage if set to "interest") 4. Taxable Social Security (calculated separately, see Federal Tax section) 5. Capital gains (brokerage if set to "capital gains", crypto — always capital gains) total_ordinary_income = earned + trad_withdrawals + interest + taxable_SS taxable_income = max(0, total_ordinary_income - standard_deduction) total_income = taxable_income + capital_gains

Key assumption: The calculator does not model itemized deductions. It uses only the standard deduction — inflated each year by the user's inflation rate — for federal taxes. Most retirees will take the standard deduction, so this is a reasonable simplification. If you itemize significantly, your actual tax bill will differ.

Standard Deduction — Inflation Adjustment

Tax brackets and the standard deduction are indexed to inflation in the real world. The calculator replicates this by multiplying 2026 base values by a cumulative inflation multiplier each year.

Inflation-Adjusted Federal Tax Parameters inflation_multiplier = (1 + inflation_rate)^year_index 2026 base values (IRS Rev. Proc. 2025-32): std_deduction_mfj = $32,200 × inflation_multiplier std_deduction_single = $16,100 × inflation_multiplier All bracket limits scale the same way.
04

Federal Income Tax Calculation

Standard Deduction

Before any bracket math happens, the standard deduction is subtracted from ordinary income. This is the single most important number in the whole tax calculation — it's the amount of income you pay zero federal tax on. The calculator uses 2026 IRS values as its base year, inflated forward each year by the user's assumed inflation rate.

2026 Standard Deduction (IRS Rev. Proc. 2025-32) Married Filing Jointly: $32,200 Single Filer: $16,100 Inflated forward each year: std_deduction_year_n = base_std_deduction × (1 + inflation_rate)^year_index Example: At 3% inflation, year 10 MFJ standard deduction ≈ $32,200 × 1.03^10 ≈ $43,260
Why the standard deduction matters so much in retirement

In retirement, you control when income lands. A married couple with a $32,200 standard deduction pays zero federal income tax on the first $32,200 of ordinary income. That means the first ~$32K of Traditional IRA withdrawals can be completely tax-free at the federal level. Combine that with the 0% capital gains bracket and you can engineer years with very low tax bills. The calculator shows you exactly how much room you have — use it.

Ordinary Income Tax — Progressive Brackets

The calculator uses 2026 federal brackets as its base year, inflated forward annually. Each dollar of ordinary taxable income above the standard deduction is taxed at the marginal rate of the bracket it falls into.

2026 Federal Brackets — Married Filing Jointly (IRS Rev. Proc. 2025-32) 10% on the first $24,800 12% on $24,801 – $100,800 22% on $100,801 – $211,400 24% on $211,401 – $403,550 32% on $403,551 – $512,450 35% on $512,451 – $768,700 37% on above $768,700 2026 Federal Brackets — Single Filer (IRS Rev. Proc. 2025-32) 10% on the first $12,400 12% on $12,401 – $50,400 22% on $50,401 – $105,700 24% on $105,701 – $201,775 32% on $201,776 – $256,225 35% on $256,226 – $640,600 37% on above $640,600

Social Security Taxation

Social Security is not automatically fully taxable — it depends on your "provisional income." Provisional income is calculated as the sum of all other income plus half of your Social Security benefit. The IRS thresholds determine what percentage of SS becomes taxable ordinary income.

Social Security Taxation provisional_income = earned_income + trad_withdrawals + interest_income + (SS_benefit × 0.5) Thresholds (2026, inflated each year): MFJ: Tier 1 = $32,000 | Tier 2 = $44,000 Single: Tier 1 = $25,000 | Tier 2 = $34,000 if provisional_income > Tier 2: taxable_SS = SS_benefit × 0.85 if provisional_income > Tier 1: taxable_SS = SS_benefit × 0.50 else: taxable_SS = 0 taxable_SS is then added to ordinary income above.
Cranky Earl says

Up to 85% of your Social Security can be taxable federal income. Nobody tells you this when you're paying in for 40 years. Most online calculators pretend it doesn't exist. This one models it. Your projected tax bill in retirement may be higher than you expected. You're welcome.

Capital Gains Tax

Long-term capital gains are not taxed using the ordinary income brackets. They sit on top of your ordinary taxable income and are taxed at 0%, 15%, or 20% depending on how much "room" is left in the preferential brackets.

Capital Gains — Stacked on Top of Ordinary Income 2026 thresholds (IRS Rev. Proc. 2025-32, inflated annually): MFJ 0% limit: $98,900 | 15% limit: $613,700 Single 0% limit: $48,350 | 15% limit: $533,400 The key: gains are "stacked" on top of ordinary taxable income. gains_stack_start = taxable_ordinary_income (after standard deduction) room_at_0pct = max(0, CG_0pct_limit - gains_stack_start) gains_at_0pct = min(capital_gains, room_at_0pct) remaining_gains = capital_gains - gains_at_0pct room_at_15pct = max(0, CG_15pct_limit - gains_stack_start - gains_at_0pct) gains_at_15pct = min(remaining_gains, room_at_15pct) remaining_gains -= gains_at_15pct gains_at_20pct = remaining_gains CG_tax = (gains_at_15pct × 0.15) + (gains_at_20pct × 0.20)
Example — Capital Gains Stack (MFJ, Year 1 of Retirement)

Inputs: Trad withdrawal = $40,000 | SS = $30,000 (85% taxable = $25,500) | Standard deduction = $29,200 | Capital gains = $15,000

ItemAmount
Ordinary income$40,000 + $25,500 = $65,500
Taxable ordinary income$65,500 − $29,200 = $36,300
CG 0% room ($94,050 − $36,300)$57,750
Gains at 0% (all of $15,000 fits)$15,000 × 0% = $0

All $15,000 in capital gains lands in the 0% bracket. Federal CG tax = $0.

05

Per-Account Tax Treatment

Each account type is treated differently for tax purposes. This is fundamental to retirement planning and the calculator models each bucket accurately.

Account Pre-Retirement Earnings Tax Withdrawal Tax Treatment
Roth IRA/401(k)NoneTax-free (qualified distributions)
Traditional IRA/401(k)None (deferred)100% ordinary income
Taxable BrokerageYes — interest or long-term CG (user choice)Gains above cost basis = CG or interest
HYS / Private LendingYes — ordinary income (interest)Withdrawals are return of principal (no additional tax on basis)
Crypto / AlternativeYes — long-term capital gainsGains above cost basis = long-term CG

Taxable Brokerage — Interest vs. Capital Gains

The taxable brokerage account has a user-controlled setting: are the earnings modeled as interest income (ordinary rates) or long-term capital gains? This is your choice because it depends on what you hold. Bond funds generate interest. Index funds generate qualified dividends and capital gains. The calculator doesn't guess — you tell it.

HYS / High-Yield Savings (and Private Lending)

HYS earnings are always treated as ordinary interest income. This reflects how the IRS actually treats savings account interest, money market funds, and private lending income (notes receivable). There is no preferential rate for this income — it stacks directly into your ordinary income tax calculation at your marginal rate.

Why private lending belongs here

Private money lending generates interest income. Not dividends. Not capital gains. Interest. Which means it gets taxed at ordinary income rates — same as your Traditional IRA withdrawal. The HYS bucket in this calculator is the right home for private lending income because it reflects that tax reality. A brokerage account set to "interest" would also work, but HYS is the more natural fit.

Cost Basis Tracking — Brokerage and Crypto

For taxable accounts, the calculator tracks cost basis separately from balance. When you withdraw, only the gain portion of the withdrawal is treated as taxable income — the return of principal (basis) is not taxed again.

Realized Gain on Withdrawal — Pro-Rata Method What fraction of the account's value is unrealized gain? gain_ratio = (balance - cost_basis) / balance (only if balance > cost_basis) Apply that ratio to the withdrawal amount: basis_portion = withdrawal × (1 - gain_ratio) realized_gain = withdrawal × gain_ratio After withdrawal, reduce cost basis by the basis portion returned: new_cost_basis = old_cost_basis - basis_portion
Example — Cost Basis and Realized Gain

Account: Brokerage balance = $200,000 | Cost basis = $120,000 | Withdrawal = $20,000

StepCalculationResult
Gain ratio($200,000 − $120,000) / $200,00040%
Basis portion returned$20,000 × 60%$12,000
Realized taxable gain$20,000 × 40%$8,000
New cost basis$120,000 − $12,000$108,000

Only $8,000 enters the capital gains calculation. The $12,000 return of principal is not taxed.

Pre-Retirement Investment Tax

During the accumulation phase, your accounts are earning money each year. Roth and Traditional accounts grow tax-deferred, so no annual tax is applied. But taxable brokerage, HYS, and crypto earnings are taxed annually during accumulation — because the IRS doesn't care that you're still working. Those 1099s show up every year.

The calculator computes the marginal tax cost of these investment earnings — by calculating tax with and without them, and taking the difference. This correctly captures the fact that investment income is taxed at your top marginal rate given your wage income that year.

Marginal Pre-Retirement Investment Tax tax_on_wages_only = calculateTax(wages, 0, 0, 0, ...) tax_on_wages_earnings = calculateTax(wages, 0, interest, cap_gains, ...) investment_tax = tax_on_wages_earnings - tax_on_wages_only This tax is charged against the account balance — reducing the effective net return each year.
06

State Income Tax

State tax rules vary dramatically — from states with no income tax at all (Florida, Texas, Nevada, and 7 others) to states with complex bracket systems and varying treatment of retirement income. The calculator models all 50 states plus D.C.

Three major state-level variations are modeled:

State Tax Variations 1. States with no income tax at all: AK, FL, NV, NH, SD, TN, TX, WA, WY → state_tax = 0 2. States that exempt retirement income: (e.g. IL, PA, MS, IA) → trad_withdrawals excluded from state taxable income 3. States with retirement income exemptions (partial): (e.g. NY exempts first $20,000; NJ exempts first $100,000) taxable_trad = max(0, trad_withdrawal - retirement_exemption) 4. Social Security treatment: "none" → SS not taxed at state level "partial" → SS above any exemption may be taxed (implied) → SS fully taxable 5. Massachusetts special case: Capital gains taxed at 12% (separate from ordinary income)

Note on state standard deductions: Most state standard deductions are not modeled — state taxable income is computed from gross income directly. The primary reductions are the state-specific retirement exemptions listed above. For states with complex deduction rules, this may slightly overstate your state tax.

07

Lump Sum & One-Time Events

The calculator supports three distinct types of one-time money events: a generic lump sum (sale of business, real estate, etc.), a pension lump sum rollover, and a one-time large expense. Each works differently.

Generic Lump Sum

You specify an amount, the age at which you receive it, the account it goes into (taxable brokerage, HYS, or crypto), and whether it's pre-tax income or an after-tax amount. The calculator processes it in the year corresponding to that age.

If the lump sum is pre-tax (for example, an early retirement buyout, severance package, or RIF payment), the calculator computes the tax due in that specific year — adding the lump sum to all other income sources and computing the marginal tax cost using the same stacked approach described in Section 3. Only the after-tax amount is deposited. This income type is ordinary income — taxed at your marginal rate, not at capital gains rates.

Pre-Tax Lump Sum Processing Tax is computed as the marginal cost of adding the lump sum to that year's income: tax_without_lump = calculateTax(all_other_income_this_year, ...) tax_with_lump = calculateTax(all_other_income_this_year + lump_sum, ...) lump_sum_tax = tax_with_lump - tax_without_lump net_deposit = lump_sum - lump_sum_tax Net deposit is added to the target account balance and cost basis. account_balance += net_deposit account_cost_basis += net_deposit
Example — Lump Sum Tax

Scenario: Age 55, MFJ, $80,000 ordinary income for the year, receive a $200,000 early retirement buyout (ordinary income), depositing to taxable brokerage.

The calculator adds $200,000 to taxable income for that year, computes the marginal federal + state + local tax on just that increment, subtracts it, and deposits the remainder into the brokerage. Because $280,000 in combined income likely straddles the 22% and 24% brackets, the buyout is taxed at those marginal rates — not a flat rate, and not at capital gains rates. A $200,000 buyout does not net you $200,000.

Pension Lump Sum Rollover

If you choose a pension lump sum instead of monthly payments, the calculator adds the full pre-tax amount directly to your Traditional IRA/401(k) balance in the year of receipt. No immediate tax is due — this models a direct rollover, which is the standard approach and avoids triggering ordinary income tax upfront. The money then grows tax-deferred and is taxed upon withdrawal like any Traditional account.

Pension Lump Sum Rollover In the year ageP1 equals lump_sum_pension_age: trad_balance += pension_lump_sum_amount No immediate tax. Taxed on withdrawal like regular Traditional IRA.

One-Time Large Expense

A single large expense (new roof, boat, helping a kid with a down payment) is subtracted directly from the balance of whichever account you specify. The expense is not tax-adjusted — it's treated as already after-tax spending. For Roth, Traditional, and HYS accounts, cost basis is reduced proportionally. For Traditional accounts, no basis adjustment is made because Traditional balances have no tracked basis (the whole balance is pre-tax).

If both you and a spouse hold the account, the expense is split in proportion to each person's share of that account type.

08

Inheritance & the 10-Year Rule

The calculator models a single inheritance event — you specify the age at which you expect to receive it and the amounts by account type: cash (goes directly to brokerage), inherited brokerage, inherited Traditional IRA, inherited Roth IRA, and inherited crypto.

The SECURE 2.0 10-Year Rule

Since the SECURE Act of 2019 (updated by SECURE 2.0), most non-spouse beneficiaries who inherit a Traditional or Roth IRA must fully distribute the account within 10 years of the original owner's death. There are no annual RMD requirements during those 10 years — you just have to empty the account by the end of year 10.

The calculator implements a simple equal-distribution approach: each year within the 10-year window, the inherited balance is divided by the number of remaining years and that amount is withdrawn.

Inherited IRA — 10-Year Rule Distribution years_since_inheritance = current_age - inheritance_age if years_since_inheritance >= 1 AND <= 10: remaining_years = max(1, 11 - years_since_inheritance) annual_withdrawal = inherited_balance / remaining_years if years_since_inheritance > 10: annual_withdrawal = 0 (account should be empty)
Example — Inherited Traditional IRA Distribution

Inputs: Inherited $300,000 Traditional IRA at age 55. Account grows at 5% annually.

YearAgeBalance (start)Years LeftWithdrawalBalance (end)
156$315,00010$31,500$283,500
257$297,6759$33,075$264,600
1065~$X1full balance$0

Each year's withdrawal is gross ordinary income — taxed at your marginal federal and state rates for that year.

Tax Treatment of Inherited Account Withdrawals

Inherited Traditional IRA: Every dollar withdrawn is ordinary income, added on top of all other income that year. The calculator computes the marginal tax cost using the same stacking approach — adding the inherited withdrawal to the income pile and computing the tax increment.

Inherited Roth IRA: Withdrawals are tax-free, since the original contributions were made with after-tax dollars. The inherited Roth balance is distributed over 10 years and the net amounts flow directly to your taxable brokerage (as cash with full cost basis).

Inherited Brokerage: Gets a step-up in basis at the time of inheritance — meaning the full inherited amount is treated as cost basis. You only owe capital gains tax on appreciation after you inherit it, not on prior gains.

During accumulation vs. retirement: The 10-year clock runs regardless of whether you're still working. If you inherit at age 52 and retire at 60, the calculator will be distributing the inherited Traditional IRA during accumulation years (52–61) and taxing it against your wages. Plan accordingly — those distributions could push you into a higher bracket while you're still earning income.

09

Required Minimum Distributions (RMDs)

The SECURE 2.0 RMD Age Change

The SECURE 2.0 Act of 2022 changed RMD start ages based on birth year. The calculator assumes the user was born in 1960 or later, which means the RMD start age is 75.

Birth YearRMD Start AgeApplicable Law
Before 195170½Pre-SECURE Act
1951–195973SECURE Act 2019
1960 and later75SECURE 2.0 Act 2022
Why the 1960 assumption?

Someone 8 years from retirement today is roughly in their mid-to-late 50s — born in the late 1960s or early 1970s. The 75 start age applies cleanly. If you were born before 1960, your RMDs kick in at 73 instead. The math below is identical; just substitute 73 for 75 in the formulas. I may add a birth-year input in a future version.

The Uniform Lifetime Table

RMDs are calculated by dividing the prior year-end Traditional IRA balance by a life expectancy factor from the IRS Uniform Lifetime Table. The table assigns a different factor to each age — essentially, how many years of distributions are expected to remain.

RMD Formula RMD = prior_year_end_balance / uniform_lifetime_factor[age] Sample factors (IRS Uniform Lifetime Table, 2022 update): age 75 → factor 24.6 age 76 → factor 23.7 age 80 → factor 20.2 age 85 → factor 16.0 age 90 → factor 12.2 age 95 → factor 8.9
Example — First RMD at Age 75

Inputs: Traditional IRA balance at end of age 74 = $800,000

StepCalculationResult
Prior year-end balance$800,000$800,000
Uniform lifetime factor (age 75)24.6
RMD$800,000 / 24.6$32,520

That $32,520 is ordinary taxable income regardless of whether you need or want it. If you were planning to take less from your Traditional account that year, RMD overrides your plan.

RMD vs. Planned Withdrawal

Your planned withdrawal rate may produce a withdrawal amount larger or smaller than the RMD. The calculator always takes the greater of the two. If the RMD exceeds your planned withdrawal, the excess is a forced distribution.

Effective Traditional Withdrawal planned_withdrawal = trad_balance × your_withdrawal_rate rmd_required = prior_year_trad_balance / lifetime_factor[age] Inherited Traditional IRA distributions count toward satisfying the RMD: rmd_remaining = max(0, rmd_required - inherited_trad_withdrawal) effective_withdrawal = max(planned_withdrawal, rmd_remaining) Final withdrawal is capped at actual available balance: actual_withdrawal = min(trad_balance, effective_withdrawal)

The RMD Balance: Prior Year-End vs. Current Balance

The IRS bases each year's RMD on the prior December 31st balance — not the current balance. The calculator honors this by using the previous year's end-of-year Traditional balance stored in the withdrawal data array. In the first RMD year, if no prior data exists, it uses the current balance as an approximation.

Optional RMD Reinvestment

If you don't need the full RMD amount for living expenses, you can tell the calculator to reinvest the excess into another account (taxable brokerage, HYS, or crypto). The reinvestment amount is the post-tax value of the excess RMD, computed using the effective tax rate for that year.

Excess RMD Reinvestment (Optional) excess_rmd = actual_withdrawal - planned_withdrawal effective_tax_rate = total_taxes / total_pre_tax_income post_tax_excess = excess_rmd × (1 - effective_tax_rate) If "pace expenses" option is enabled, only reinvest if there is a cash surplus: cash_surplus = after_tax_income - current_year_expenses reinvest_amount = max(0, min(post_tax_excess, cash_surplus))
10

Roth Conversion Ladder

The Roth conversion ladder is a pre-RMD tax planning tool. It runs automatically whenever your retirement age is earlier than age 75 — the age at which Required Minimum Distributions begin under SECURE 2.0. The window it operates in is every year from your first retirement withdrawal age up through age 74.

The core idea is straightforward: every dollar sitting in a Traditional IRA right now will eventually be taxed as ordinary income — either when you choose to withdraw it, or when the IRS forces you to withdraw it via RMDs. The ladder identifies years where you can convert some of that Traditional balance to Roth, pay tax at today's rate, and avoid paying a higher rate later.

The Conversion Window

The calculator only suggests conversions during the gap between retirement and RMD age. Before retirement, you're still earning income and your marginal rate is likely already elevated — converting on top of a salary usually doesn't help. After 75, RMDs are mandatory and the point is moot. The sweet spot is the years in between, when your income may be lower than it will be once Social Security, pensions, and RMDs all stack up simultaneously.

Conversion Window window_start = retirement_age window_end = 74 RMDs begin at 75 under SECURE 2.0 (born 1960+) if retirement_age >= 75: no window — ladder does not run

How Much to Convert Each Year

Each year in the window, the calculator identifies the largest conversion amount that keeps your total taxable income within the top of the 22% federal bracket. This is the "Room to 22% Top" figure in the table. Converting beyond the 22% bracket pushes dollars into the 24% bracket — at which point the trade-off becomes less favorable and the suggested conversion stops.

Bracket Room Calculation — Each Year in Window Inflation-adjust the 22% bracket ceiling and standard deduction: bracket_top = (211,400 if MFJ else 105,700) × (1 + inflation_rate)^year_index std_ded = (32,200 if MFJ else 16,100) × (1 + inflation_rate)^year_index Estimate income already in place this year (before any conversion): taxable_SS = SS_income × 0.85 conservative: max 85% of SS is taxable base_ordinary = pension_income + interest_income taxable_ordinary = max(0, base_ordinary + taxable_SS − std_ded) Room remaining in the 22% bracket: room_in_22 = max(0, bracket_top − taxable_ordinary) Suggested conversion = bracket room, capped by Traditional balance: suggested_conversion = min(room_in_22, trad_balance)

IRMAA Guardrail

Starting at age 63, Roth conversions begin to affect Medicare IRMAA surcharges — because IRMAA uses a two-year income lookback. A conversion at age 63 shows up in your MAGI for Medicare purposes at age 65. The calculator tracks this and computes an IRMAA-safe conversion amount: the largest conversion that keeps your MAGI just below the next IRMAA threshold (with a $500 buffer). If the full suggested conversion would cross a tier, the table flags it with a warning and shows the IRMAA-safe alternative.

IRMAA Lookback Logic irmaa_applies = age >= 63 2-year lookback: age 63 → affects IRMAA at 65 irmaa_lookback_age = age + 2 if irmaa_applies: magi_with_full = base_ordinary + SS_income + suggested_conversion + cap_gains magi_with_safe = base_ordinary + SS_income + irmaa_safe_conversion + cap_gains surcharge_full = irmaa_surcharge(magi_with_full) surcharge_safe = irmaa_surcharge(magi_with_safe) if surcharge_full > surcharge_safe: flag warning — full conversion crosses an IRMAA tier show irmaa_safe_conversion as the recommended amount

Balance Simulation

The ladder simulates balances forward year by year. At the start of each year, all account balances grow at their respective post-retirement return rates. After the conversion is applied, the Traditional balance shrinks by the conversion amount and the Roth balance grows by the same amount. Each year's calculation uses the balance after all prior years' conversions have already been applied — so early conversions reduce both the Traditional balance and the projected RMD in later years.

Annual Balance Update — Ladder Simulation Step 1: Grow balances trad_balance *= (1 + trad_post_return) roth_balance *= (1 + roth_post_return) taxable *= (1 + taxable_post_return) hys *= (1 + hys_post_return) Step 2: Apply conversion trad_balance -= suggested_conversion roth_balance += suggested_conversion

Net Benefit Calculation

The Net Benefit column answers the key question: is it worth paying tax on this conversion today, given what I'd pay at age 75 if I left it alone?

The logic is a marginal tax rate comparison. The calculator projects what the Traditional balance will be at age 75 (assuming no further conversions after this year), computes the estimated first RMD, and calculates what tax rate that RMD would face — including the income already in place at that future age (Social Security, pension, taxable account earnings). That effective RMD tax rate is then applied to this year's conversion amount to estimate the future tax the conversion avoids. The net benefit is that avoided tax minus the tax paid on the conversion today.

Net Benefit — Rate Comparison Method Project Traditional balance to age 75 (assuming no further conversions): years_to_rmd = 75 − current_age projected_trad_at_75 = trad_balance × (1 + trad_post_return)^years_to_rmd Estimate first RMD using IRS Uniform Lifetime Table divisor at age 75: estimated_first_rmd = projected_trad_at_75 / 24.6 Compute the marginal tax cost of that RMD at age 75 income levels: tax_with_rmd = total_income_tax(rmd_ordinary_income + estimated_first_rmd, age_75_SS, ...) tax_without_rmd = total_income_tax(rmd_ordinary_income, age_75_SS, ...) rmd_tax_cost = max(0, tax_with_rmd − tax_without_rmd) Effective marginal rate the RMD would face: effective_rmd_rate = rmd_tax_cost / estimated_first_rmd Tax this year's conversion would have paid at that future rate: attributable_rmd_tax = suggested_conversion × effective_rmd_rate Tax paid on the conversion today: conversion_tax_cost = total_tax_with_conversion − total_tax_without_conversion Net benefit — positive means converting now is advantageous: net_benefit = attributable_rmd_tax − conversion_tax_cost
Cranky Earl says

The net benefit number is not a guarantee. It's a comparison of two tax rates under projected future assumptions — your actual future income, bracket boundaries, and tax law may all differ. What it tells you is directional: if your effective RMD rate at 75 is projected to be meaningfully higher than your conversion rate today, converting now looks favorable. If the rates are close or inverted, it may not be worth the cash-flow hit of paying the tax early. Use this as a planning signal, not a precise forecast. And for anything beyond rough guidance, talk to a CPA who specializes in Roth conversion strategy.

Worked Example — Net Benefit at Age 65

Inputs: Married filing jointly, retiring at 65. Traditional balance: $800,000. Post-retirement return: 6%. Social Security (both): $48,000/year combined starting at 67. No pension. Inflation: 3%.

Step 1 — Bracket room at 65:

SS not started yet → taxable_ordinary = $0 (below standard deduction)
bracket_top ≈ $211,400 × 1.03^0 = $211,400
room_in_22 = $211,400 − $0 = $211,400
suggested_conversion = min($211,400, $800,000) = $211,400

Step 2 — Project to age 75:

projected_trad_at_75 = $800,000 × 1.06^10 ≈ $1,432,684
estimated_first_rmd = $1,432,684 / 24.6 ≈ $58,239

Step 3 — RMD tax rate at age 75:

Age-75 income: SS ($48k × 0.85 taxable) + RMD ($58,239) vs. just SS
rmd_tax_cost ≈ $12,813 (marginal federal + state on the RMD increment)
effective_rmd_rate = $12,813 / $58,239 ≈ 22.0%

Step 4 — Net benefit:

attributable_rmd_tax = $211,400 × 22.0% ≈ $46,508
conversion_tax_cost ≈ $36,600 (marginal federal on $211,400 in 22% bracket)
net_benefit = $46,508 − $36,600 = +$9,908

Interpretation: by converting $211,400 at age 65, you avoid approximately $9,908 in lifetime taxes compared to leaving that money to be forced out as an RMD at a higher effective rate. Every year you convert during the window further reduces the projected RMD and stacks additional net benefit.

The "Proj. RMD at 75" column shows what your first RMD would be if you stopped converting right now — it is recalculated each row based on that year's remaining Traditional balance. As conversions reduce the balance in earlier years, this number will decrease in later rows. It is not the same projected RMD shown in every row — it is a per-row "what if you stopped here" estimate.

11

Monte Carlo Simulation

The main calculator uses fixed average return rates — every year grows at exactly the rate you specify. That's fine for getting a baseline number, but it hides a critical risk: sequence of returns. A market that drops 40% in the first year of your retirement and then recovers is far more damaging than one that drops in year 15, even if the average annual return ends up identical. Monte Carlo simulation exposes this risk.

How It Works: Historical Bootstrapping

Rather than generating random numbers from a statistical distribution, this simulation uses historical bootstrapping for stock accounts. It holds a dataset of actual monthly S&P 500 returns going back to 1928. For each simulation run, it picks a random starting month in history and plays out your entire retirement — accumulation and withdrawal — using that actual historical sequence of returns.

Monte Carlo — Core Approach NUM_SIMULATIONS = 10,000 total_plan_months = months_accumulating + months_in_retirement for each simulation: start_index = random integer from [0, len(sp500_data) − total_plan_months] Run full plan using sp500_data[start_index ... start_index + total_plan_months] record final portfolio balance Results are sorted and analyzed: success_rate = % of simulations ending with balance > 0 median_balance = outcome at 50th percentile 10th_percentile = outcome at 10th percentile (bad-case scenario)
Why historical data instead of random returns?

Because markets don't behave like a bell curve. Standard Monte Carlo tools assume returns are normally distributed — they generate synthetic random numbers with a mean and standard deviation. Real markets have fat tails, momentum, crashes, and bubbles that don't show up in a normal distribution. Historical bootstrapping keeps all of that messiness intact. If 1929 is in the dataset, a simulation can start there. If 2008 is in the dataset, a simulation can retire right into it. That's the point.

Per-Account Return Models

Each account type has its own return model, which you select in the simulation settings panel. This matters because a Roth IRA holding index funds and a crypto wallet should not be growing at the same rate with the same volatility profile.

Return Models by Asset Class Stocks (S&P 500 historical — default for Roth, Trad, Taxable): monthly_return = sp500_data[start_index + month] actual historical return Bonds (parametric normal distribution): monthly_return ~ Normal(mean=0.0033, std=0.015) ≈ 4% annual, low volatility Crypto (parametric normal distribution, fat tails): monthly_return ~ Normal(mean=0.010, std=0.18) ≈ 12% annual, extreme volatility Cash / HYS (fixed rate): monthly_return = hys_annual_rate / 12 no volatility — uses your input rate

Bond and crypto returns use Box-Muller transformation to generate normally distributed random numbers. They are not correlated with the S&P 500 historical slice — each month draws independently. This is a simplification; real asset classes have correlation. The practical effect is that crypto in the simulation has higher variance than reality might produce in a diversified portfolio, and bonds won't reliably zig when stocks zag.

Accumulation Phase — Monthly Compounding

The Monte Carlo simulation runs at monthly time steps — twelve per year — giving it higher resolution than the main calculator's annual loop. During accumulation, contributions are added at the start of each month and then the balance grows by that month's return.

Monte Carlo Accumulation — Monthly Step (per account) For each account, contributions land at the start of the month: balance_t+1 = (balance_t + monthly_contrib) × (1 + return_for_asset_class_t) Employer match is added to the Traditional balance each month: trad_t+1 = (trad_t + trad_contrib + employer_match) × (1 + trad_return_t) Inherited balances grow alongside main balances and merge at retirement: inherited_trad_t+1 = inherited_trad_t × (1 + trad_return_t)

Withdrawal Phase — Separate Buckets & Withdrawal Order

At retirement, all five account types remain separate throughout the withdrawal phase. The simulation does not collapse everything into a single stock bucket. This matters because different accounts have different tax treatment, different return profiles, and different mandatory distribution rules. Each account grows at its own rate and is drawn down according to a fixed withdrawal order designed to minimize lifetime taxes.

Withdrawal Waterfall — Each Month Step 1: Calculate income and withdrawal need non_portfolio_income = SS_this_month + pension_this_month needed = max(0, monthly_expense − non_portfolio_income) monthly_expense *= (1 + monthly_inflation_rate) inflate for next month Step 2: RMD (December only, age 75+) — see below Step 3: Draw from accounts in tax-efficient order (BEFORE growth) draw from hys cash first — no market risk, no tax drag from gains draw from taxable LTCG rates — still better than ordinary income draw from trad ordinary income — defer as long as possible draw from roth tax-free — preserve for last draw from crypto last resort — highest volatility Step 4: Grow remaining balances by their respective asset class returns roth *= (1 + roth_return_t) trad *= (1 + trad_return_t) taxable *= (1 + taxable_return_t) hys *= (1 + hys_monthly_post_return) crypto *= (1 + crypto_return_t)
Why this withdrawal order?

Taxable first, Roth last is the standard tax-efficient playbook. You want to let the Roth compound tax-free for as long as possible, and you want to draw down the taxable account (which is already generating annual tax drag) before you touch the tax-sheltered accounts. Traditional sits in the middle because it's ordinary income — expensive tax-wise, but unavoidable once RMDs kick in. Crypto goes last not because it's special, but because it's the most volatile bucket and you don't want to be forced to sell it during a crash if any other account still has money.

Why withdraw before growing? The simulation takes withdrawals at the start of each month, then grows whatever is left. This is the conservative assumption — you spend first, your remaining balance compounds. The alternative (grow then spend) is slightly more optimistic because every dollar you're about to spend gets one extra month of market return before it leaves the portfolio. CrankyEarl errs conservative. If the math is going to be wrong, it should be wrong in a direction that doesn't get you into trouble.

Required Minimum Distributions (RMDs)

RMDs apply to the Traditional IRA/401(k) balance starting at age 75 (SECURE 2.0 — for those born 1960 or later). They are calculated and applied once per year, in December of each simulation year, using the IRS Uniform Lifetime Table.

RMD in the Monte Carlo — Annual, Applied in December When age_p1 >= 75 and month == December: dist_period = IRS_uniform_lifetime_table[age] e.g., 24.6 at age 75 rmd_required = trad_balance / dist_period actual_rmd = min(rmd_required, trad_balance) trad_balance -= actual_rmd RMD vs. expense need for this month: rmd_excess = max(0, actual_rmd − monthly_withdrawal_needed) if rmd_excess > 0: taxable_balance += rmd_excess reinvest surplus into taxable brokerage monthly_withdrawal_needed = 0 else: monthly_withdrawal_needed -= actual_rmd RMD partially covers the month's need

The excess RMD reinvestment into taxable is intentional — the IRS forces you to take the money, so if you don't need it for expenses, it goes somewhere. Taxable brokerage is the realistic destination. This also means that heavy RMD years can increase your taxable account balance while shrinking your Traditional account — which has long-term tax implications captured in the annual tax settlement below.

Annual Tax Settlement

The simulation calculates and deducts taxes once per year in December, using 2026 federal brackets and your selected state rate. Taxes are not estimated or approximated as a flat haircut — they are computed using the actual bracket structure on each income type.

Annual Tax Calculation (December) Ordinary income sources: ordinary_income = annual_trad_withdrawals + (annual_SS_income × 0.85) max 85% of SS is taxable + annual_pension_income Long-term capital gains: ltcg_income = annual_taxable_account_withdrawals Federal ordinary tax — 2026 brackets, standard deduction applied first: taxable_ordinary = max(0, ordinary_income − standard_deduction) federal_ordinary_tax = bracket_calc(taxable_ordinary, federal_brackets) LTCG tax — stacked on top of ordinary income in the brackets: ltcg_tax = bracket_calc(ltcg_income, ltcg_brackets, stacked_above=taxable_ordinary) State tax — flat effective rate on combined taxable income: state_tax = (taxable_ordinary + ltcg_income) × state_rate total_tax = federal_ordinary_tax + ltcg_tax + state_tax Tax bill paid from accounts in order: Trad → Taxable → Roth → Crypto
2026 Federal Brackets — Married Filing Jointly $0 – $24,800 → 10% $24,801 – $100,800 → 12% $100,801 – $211,400 → 22% $211,401 – $403,550 → 24% $403,551 – $512,450 → 32% $512,451 – $768,700 → 35% $768,701+ → 37% Standard deduction (MFJ): $32,200 LTCG rates (MFJ): 0% up to $98,900 / 15% up to $613,700 / 20% above

Roth withdrawals are tax-free and do not appear in ordinary income or LTCG calculations. HYS withdrawals are treated similarly — the interest income is not separately modeled as ordinary income in the Monte Carlo (a simplification). The primary tax levers are Traditional withdrawals and RMDs (ordinary income) and Taxable account withdrawals (LTCG).

Market Crash Stress Test

The simulation includes an optional crash stress test that lets you layer a "lost decade" scenario on top of the historical bootstrapping. You control three inputs: the retirement year the crash hits, how severe the initial drop is, and how many years the market stagnates afterward.

The crash is modeled as a return shock rather than an instantaneous balance cut. On the first month of the crash year, the market return for all market-exposed accounts is set to −crash_severity (e.g., −0.40 for a 40% drop). Because withdrawals are taken before growth each month, the sequence on the crash month is: spend first, then the crash return hits the already-drawn-down balance. This is how sequence-of-returns risk actually works — you're forced to sell into a falling market to cover living expenses. HYS/cash is never affected — it has no market exposure.

After the crash month, the simulation enters a stagnation window. During that window, normal historical returns are replaced with a low-volatility ~2%/year random return (mean ≈ 0.165%/month, std ≈ 3%/month), simulating a prolonged sideways market with dividend income but no price appreciation. The recovery window is capped at the months remaining in the plan — a crash late in retirement can't produce a recovery window that extends past the end of the simulation. Once the window expires, historical returns resume.

Market Crash — First Month of Crash Year Withdrawals happen first (spend before growth — see above) Then the crash return fires as a return shock: crash_return = −crash_severity e.g., −0.40 for a 40% drop roth *= (1 + crash_return) trad *= (1 + crash_return) taxable *= (1 + crash_return) crypto *= (1 + crash_return) HYS is not market-exposed — always earns hys_monthly_post_return months_remaining = total_retirement_months − current_month − 1 recoveryMonthsLeft = min(crash_recovery_years × 12, months_remaining) During Stagnation Window — Each Month RECOVERY_MEAN = (1.02)^(1/12) − 1 ≈ 0.165%/month RECOVERY_STD = 0.03 low vol sideways grind if recoveryMonthsLeft > 0: monthly_return = randNormal(RECOVERY_MEAN, RECOVERY_STD) roth *= (1 + monthly_return) trad *= (1 + monthly_return) taxable *= (1 + monthly_return) crypto *= (1 + monthly_return) recoveryMonthsLeft−− else: resume normal historical returns from the S&P 500 data sequence
Why 2% mean and not zero?

Zero return would mean the market is completely dead — no dividends, no activity. That's not what a lost decade looks like. The 2000s S&P 500 lost roughly 10% in price over the decade, but dividend reinvestment kept total return from being catastrophic. The ~2% mean models the dividend-yield floor: the market goes nowhere in price, but you still collect a small trickle. The added volatility (std ≈ 3%/month) means each simulation gets a slightly different grind — some recoveries are a little better, some a little worse — rather than a perfectly smooth guaranteed crawl. It's pessimistic without being apocalyptic. If you want apocalyptic, set the severity to 60% and the recovery window to 10 years. That'll do it.

Why does a later crash sometimes show a lower success rate? It depends on your account withdrawal ages. If your stock accounts (Roth, Traditional) aren't accessible until age 63 or 65, a crash in year 1 of retirement hits those accounts — but they have years to recover before you ever touch them. Your HYS/cash is funding expenses in the meantime and is immune to market crashes. The real danger zone is a crash right around the age you start drawing from your stock accounts. At that point there's no recovery runway — you're forced to sell depressed shares immediately to cover expenses month after month. That's true sequence-of-returns risk. If your results look counterintuitive, check your account withdrawal ages — the simulation is probably telling you something true about your specific plan.

An earlier version of this simulation used a different crash model — it applied an instantaneous balance haircut (multiplying all balances by 1 − severity) at the January boundary of the crash year, then used a flat deterministic ~2%/year recovery return. That approach had two problems: the instantaneous cut happened before withdrawals, so the portfolio shrank first and then you spent from the depleted balance (rather than spending first and then taking the crash hit); and the flat recovery return was deterministic, meaning every simulation got the exact same recovery path. The current model corrects both: the crash fires as a return shock after withdrawals, and the recovery uses a random draw each month so simulations diverge realistically during the stagnation window.

Sequence-of-Returns Fan Chart

In addition to the final-balance histogram, the simulation produces a fan chart that shows how portfolio value evolves year-by-year across retirement — not just at the end. It samples 1,000 of the 10,000 simulations and plots five percentile bands: 10th, 25th, 50th (median), 75th, and 90th.

The fan chart is where sequence-of-returns risk becomes visible. A wide fan early in retirement means the outcome is highly sensitive to which historical window you happen to land in. A narrow fan means the return sequence matters less — usually because large guaranteed income (SS, pension) covers most of the expenses regardless of what the market does.

Fan Chart — Percentile Extraction For each year y in retirement (sampled from 1,000 paths): values_at_year_y = [path[y] for path in sampled_paths] sort(values_at_year_y) p10 = values_at_year_y[floor(0.10 × n)] p25 = values_at_year_y[floor(0.25 × n)] p50 = values_at_year_y[floor(0.50 × n)] p75 = values_at_year_y[floor(0.75 × n)] p90 = values_at_year_y[floor(0.90 × n)]
↑ Back to Top