October 2026 upgrade: real market history
The simulator used to draw every year's return from a bell curve. It now runs on 152 years of US stock and bond returns built from Robert Shiller's data, and can replay a plan through every retirement year in the record, resample that record in blocks, or keep the bell curve for comparison. The default plan (age 32, $150k saved, $40k a year saved, $60k a year spent from 50 to 95, 75% stocks) lasts in 82% of normal draws, 85% of bootstrap paths and 84 of 90 historical retirement years.
engine.js agree on all 18 test cases.Success by retirement age under each engine
Age 32, $150k saved, $40k a year saved until retirement, $60k a year spent after, plan until 95, 75% stocks and 25% 10-year Treasuries rebalanced yearly. All in today's dollars.
Computing 52 runs of 5,000 paths and 26 historical replays in your browser.
engine.js) on data/returns.json, with the app's fixed seed, so each point matches what the app shows for that age. The historical line moves in steps of about one window in ninety.Test log
The old app's default assumed a 5.0% real return with 13.0% volatility for the whole portfolio and gave the same plan 56% in a run recorded in the README (54% with the seed used here). Most of the jump to the new figures comes from the return assumption, not the model: US stocks returned 6.9% a year after inflation from 1871 to 2022 (8.4% arithmetic mean, 17.9% standard deviation), and a 75/25 mix with 10-year Treasuries comes out well above 5%. Holding the mean and spread fixed and changing only how years are drawn moves the result from 82% to 93%.
| Engine | Success | Runs | Median at 95 |
|---|---|---|---|
| Loading data/validation.json | |||
From python3 scripts/check_engine.py --js, which runs the Python port and engine.js on the same cases and checks that they agree to the dollar. The first row is the old app's assumption, 100% in one asset, kept for comparison. Superseded on 2026-10-28: the old headline of 3% at 40, 56% at 50 and 90% first at 59 came from that assumption.
Historical start years
The historical engine lines the whole life up with history: "retire in 1929" means saving from 1911 to 1928 and drawing down from 1929. For a 30-year retiree with a fixed pot, 1929 and 1966 are the classic failures. For this plan they are not, because the decades before them built large balances: the 1966 retiree arrives with $2.77 million after saving through 1948 to 1965, when the 75/25 mix returned 10.7% a year after inflation. The 1973 to 1977 retirees saved through weaker years, reach retirement with $0.85 to $1.5 million, and then meet 6.6 to 8.7% a year inflation in their first decade. They run out between 83 and 95. The 1917 retiree runs out at 92.
Every retirement year from 1889
Each cell is the year the plan retires. Retirement years after 1978 have not finished 45 years of drawdown by 2022 and are shown with their balance in 2022.
Why history is kinder here
The bootstrap draws runs of consecutive real years, stock and bond together. With one-year blocks it is close to the normal engine (82.6% against 82.2%), so fat tails alone do little to this plan. Longer blocks raise the success rate, and full historical replay raises it most. Over a 63-year plan, bad decades in US history have tended to be followed by better ones, and independent draws cannot reproduce that.
Holdout
To see whether the random engines are calibrated, I fitted them on 1871 to 1945 only and asked them about a 30-year retirement on a 75/25 mix, then compared their answers with what actually happened to the 48 retirements that started from 1946 to 1993. At 4% withdrawals the bootstrap said 96% and the normal engine 93%; 92% of those retirements lasted. At 5% they said 88% and 81%, and 73% lasted. From 4% up, both engines overstated success in the post-war sample, and the bootstrap missed by more.
Benchmark
Before trusting the engine I ran the textbook case: $40,000 a year from $1 million, adjusted for inflation, for 30 years, replayed through every start year. The Trinity study (Cooley, Hubbard and Walz, 1998) reported 95% success at 50/50 and 98% at 75/25 for start years 1926 to 1966. Bengen (1994) found 4.15% the highest rate that survived every 30-year period with 50 to 75% stocks.
| Stocks | Trinity 1998 | This data, 1926 to 1966 | Failed start years | All 123 windows | Safe maximum |
|---|---|---|---|---|---|
| Loading data/validation.json | |||||
The safe maximum is the highest starting rate, to 0.01%, that lasted 30 years from every start year. Damodaran's calendar-year series (Dec 31 to Dec 31) gives the same failing years at 50/50 and 75/25, so January-to-January timing is not the cause.
This data fails about one more start year than Trinity at each mix: 1965 and 1966 at 75/25, 1964 to 1966 at 50/50. They run out late, in their 25th to 29th year (the 1965 retiree at 94, a year short), and the safe maximum comes out at 3.75% for 75/25 against Bengen's 4.15%. Both studies used different bonds from mine: Trinity long-term corporate bonds and Bengen intermediate Treasuries, where I use 10-year Treasuries derived from yields. I have not tested how much of the gap that explains.
Spending rules
The app has three spending rules. Constant spending withdraws $60k every year. The percentage rule spends 4% of the portfolio, held between a floor of 75% and a ceiling of 150% of the target. Guardrails, after Guyton and Klinger, start at $60k and cut 10% when the withdrawal rate rises 20% above where it started (except in the last 15 years) or raise 10% when it falls 20% below, within the same floor and ceiling. A pension or Social Security payment reduces what the portfolio has to cover from the age it starts.
| Rule | Historical | Bootstrap | Lowest year, median path | Lowest year, 10th pct | Average spending, median |
|---|---|---|---|---|---|
| Computing | |||||
Spending columns are from the bootstrap engine, in today's dollars. The percentage rule rarely runs out, by construction; its cost shows in the spending columns.
Under the bootstrap, guardrails lift the default plan from 84.8% to 90.0%. The price is that about a quarter of the paths spend at least one year at the $45,000 floor. In history neither flexible rule ran out in any of the 90 windows, but the percentage rule's median path dropped to the floor at some point.
Solver
For the plan on screen, the solver searches each lever on its own: the first later retirement age that reaches the target, the highest spending (to $500), the lowest yearly saving (to $500), and the fixed stock share closest to the current one. Under the bootstrap the default plan needs one of: retire at 52, spend $51,500, or save $48,500 a year. No fixed mix gets it there; 100% stocks reaches 88%. Under historical replay it already clears 90%, and the solver reports the slack instead: spending can rise to $62,500 or saving fall to $38,000. Normal and bootstrap searches reuse the same 5,000 seeded paths for every candidate, so the comparison is between plans, not between random draws, and the search runs in a Web Worker in under a second.
Method
scripts/build_returns.py reads Shiller's monthly
ie_data.xls. Stock returns chain his monthly total return,
(Pm+1 + Dm/12) / Pm, from January to
January. Bond returns are a constant-maturity approximation: buy a
10-year bond at par at January's yield, collect a year's coupon, and
sell it as a 9-year bond at the next January's yield. Before 1953 the
yield column is Shiller's long-term government rate. Each series is
deflated by the change in January CPI. Shiller's file runs to
September 2023, with estimated CPI for its last two months, so the last
full year is 2022.
Against Damodaran's histretSP.xls for 1928 to 2022, the
series agree on average (real stocks 8.33% against 8.26% a year, real
bonds 1.88% against 1.87%) and correlate at 0.97 year by year. Single
years differ because Damodaran uses December 31 closes and Shiller
monthly averages: 1987 is -6.2% here and +1.4% there, because a sharp
rally in January 1987 put that month's average 6% above December's, so
this window starts higher. The bond approximation matches Shiller's own
monthly bond-return column to 0.34 points a year on average.
scripts/engine.py ports the engine to Python; scripts/check_engine.py --js runs both and the benchmarksLimitations
The data is one country's history, and the US had one of the best stock markets of the period. Every engine here inherits that, and the holdout shows the random engines were too optimistic about the post-war years even with US data.
There are no taxes, fees, one-off costs, or changes in spending with age. Bonds are a single 10-year Treasury series with no TIPS, cash or international assets. Contributions stay fixed in real terms through the working years. The historical engine for a long plan has few complete windows: 90 for the default plan, heavily overlapping.
Use it to compare plans against each other. It is an educational tool and does not give financial advice.
Related
The EPUB reading-level analyzer is built the same way: static files, everything client-side. The full narrative and the October 2026 changes are in writeup.md.