Most retirement calculators give you one number. Monte Carlo gives you 10,000 — each a different possible future based on market volatility, sequence of returns, and withdrawal timing. This guide walks you through building a working simulator in Python that outputs probability of success, not false certainty.
Key Takeaways
- You'll build a simulator that runs 10,000+ retirement scenarios using return distributions, not single averages
- The calculator outputs a probability-of-success percentage and a visual distribution of final portfolio balances
- You'll model withdrawals, contributions, market volatility, and inflation adjustments across all simulation runs
Before You Start
You need basic Python syntax, the ability to run scripts from a terminal or Jupyter notebook, and conceptual understanding of Monte Carlo simulation. The code handles return distributions, compounding, and probability calculations. You need to understand the inputs: starting balance, annual contributions, expected returns, volatility, withdrawal rate, time horizon.
What you won't get from this: tax modeling, Social Security integration, healthcare cost projections. What you will get: a clear probability estimate that accounts for the single biggest risk most retirement calculators ignore — sequence of returns.
What You Need
- Python 3.8 or later (python.org)
- NumPy for array operations and random number generation (
pip install numpy) - Matplotlib for charting results (
pip install matplotlib) - Pandas (optional but helpful for organizing results) (
pip install pandas) - A text editor or IDE — VS Code, PyCharm, or Jupyter Notebook
- Understanding of portfolio basics: balance, withdrawal rate, expected return, standard deviation
Step 1: Set Up Your Python Environment and Import Libraries
Create a new file called monte_carlo_retirement.py. Import the required libraries at the top. This step ensures your simulation can generate random returns, perform array calculations efficiently, and visualize results. Without these libraries, you'd need hundreds of lines of manual calculation code.
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd # optional, for organizing results
Run python monte_carlo_retirement.py to confirm no import errors. If you see ModuleNotFoundError, install the missing library with pip install [library-name].
Step 2: Define Your Retirement Scenario Inputs
Create variables for the key parameters. These inputs drive every simulation run. Use realistic values based on your situation or standard planning assumptions. Separating assumptions from simulation logic makes it easy to test different scenarios later.
starting_balance = 500000 # current portfolio value in dollars
annual_contribution = 20000 # dollars added per year until retirement
years_until_retirement = 10 # years from now until you stop working
retirement_duration = 30 # years you expect to live in retirement
annual_withdrawal = 40000 # dollars withdrawn per year in retirement
expected_return = 0.07 # 7% average annual return
return_std_dev = 0.15 # 15% standard deviation (volatility)
inflation_rate = 0.03 # 3% annual inflation adjustment
num_simulations = 10000 # number of Monte Carlo scenarios to run
The expected return and standard deviation should reflect your actual asset allocation. A 60/40 stock/bond portfolio historically shows around 7% average return with 12-15% volatility. More aggressive: 8-9% return with 18% volatility. Check historical return data for your specific allocation before committing to these assumptions. Markets can shift dramatically — conservative assumptions protect against overconfidence.
Step 3: Build the Core Simulation Function
Write a function that simulates one retirement scenario from start to finish. It models portfolio growth during working years, then withdrawals during retirement. Each year, the portfolio experiences a random return drawn from a normal distribution, then contributions or withdrawals adjust the balance. This is the engine.
def simulate_retirement():
balance = starting_balance
# Accumulation phase: add contributions and grow portfolio
for year in range(years_until_retirement):
annual_return = np.random.normal(expected_return, return_std_dev)
balance = balance * (1 + annual_return) + annual_contribution
# Retirement phase: withdraw funds and adjust for inflation
for year in range(retirement_duration):
annual_return = np.random.normal(expected_return, return_std_dev)
balance = balance * (1 + annual_return)
withdrawal = annual_withdrawal * ((1 + inflation_rate) ** year)
balance = balance - withdrawal
if balance <= 0:
return 0 # portfolio depleted
return balance # final balance if money lasted
The function returns 0 if the portfolio runs out of money before the retirement period ends. Otherwise it returns the final balance. This binary success/failure metric is what Monte Carlo simulations count to calculate probability of success. The interesting part: two portfolios with the same average return can have wildly different success rates depending on the order of those returns.
Step 4: Run the Monte Carlo Simulation
Execute the simulation function thousands of times and store the results. Each run uses different random returns, producing a distribution of possible outcomes. This transforms a single deterministic projection into a probabilistic forecast.
results = []
for i in range(num_simulations):
final_balance = simulate_retirement()
results.append(final_balance)
results = np.array(results)
With 10,000 simulations, this loop runs in under 5 seconds on a modern laptop. Need faster execution? Increase NumPy vectorization or reduce to 5,000 simulations — still statistically valid.
Step 5: Calculate Probability of Success
Count how many simulations ended with a positive balance. Divide by total simulations. This is the single most important output.
success_count = np.sum(results > 0)
success_rate = (success_count / num_simulations) * 100
print(f"Probability of success: {success_rate:.1f}%")
print(f"Median final balance: ${np.median(results):,.0f}")
print(f"10th percentile final balance: ${np.percentile(results, 10):,.0f}")
A success rate above 85% is generally considered safe. Below 70% suggests you need to save more, work longer, or reduce withdrawal rates. The 10th percentile shows what happens in bad-luck scenarios — useful for stress-testing your plan. That bottom decile is where sequence-of-returns risk lives.
Step 6: Visualize the Distribution of Outcomes
Create a histogram showing the distribution of final portfolio balances. This chart reveals the full range of possible outcomes, not just the average. Some scenarios end with zero. Others with millions. Visualization makes the uncertainty concrete.
plt.figure(figsize=(10, 6))
plt.hist(results, bins=50, edgecolor='black', alpha=0.7)
plt.axvline(x=0, color='red', linestyle='--', linewidth=2, label='Failure threshold')
plt.xlabel('Final Portfolio Balance ($)')
plt.ylabel('Number of Simulations')
plt.title(f'Monte Carlo Retirement Simulation ({num_simulations:,} runs)')
plt.legend()
plt.grid(axis='y', alpha=0.3)
plt.show()
The red dashed line at zero separates successful scenarios from failures. If a large portion of the distribution sits left of that line, your retirement plan needs adjustment. Even institutional investors adjust strategy when risk environments change — and Monte Carlo tells you when yours has.
Step 7: Export Results for Further Analysis
Save simulation results to a CSV file so you can analyze them in a spreadsheet or compare multiple scenarios. This makes your calculator reusable for testing different withdrawal rates, contribution levels, or retirement ages.
df = pd.DataFrame({'Final_Balance': results})
df.to_csv('retirement_simulation_results.csv', index=False)
print("Results saved to retirement_simulation_results.csv")
Import this file into Excel, Google Sheets, or another Python notebook to run sensitivity analysis. Change one variable at a time and compare success rates to find the most impactful adjustments. The question isn't whether to adjust — it's which lever moves the probability most.
Common Problems
Simulation returns unrealistic results (all successes or all failures). Check that your expected return and standard deviation match your actual asset allocation. A 12% expected return with 5% volatility is unrealistic for any diversified portfolio. Use historical data for your specific mix.
Code runs slowly with 10,000 simulations. Reduce to 5,000 simulations or vectorize the calculation using NumPy array operations instead of Python loops. Statistical validity remains strong above 5,000 runs.
Inflation adjustment causes balance to deplete too quickly. Make sure you're applying inflation only to withdrawals, not to the annual return. The formula withdrawal * ((1 + inflation_rate) ** year) should appear only in the withdrawal calculation.
Best Practices
- Run the simulation with multiple sets of assumptions — optimistic, realistic, pessimistic — to understand the range of planning outcomes. Single-scenario projections hide tail risk.
- Use historical standard deviation from your actual portfolio holdings rather than generic market averages. A 100% bond portfolio has far lower volatility than the 15% used in this example.
- Test sequence-of-returns risk by front-loading negative returns in the first five years of retirement. Early losses deplete portfolios faster than average returns predict.
- Adjust withdrawal rates dynamically in your model — reduce withdrawals by 10% if portfolio drops below certain thresholds. This models real retirement behavior better than fixed withdrawals.
- Rerun the simulation annually with updated balances and market conditions. Monte Carlo is not a one-time calculation. It's a planning tool you revisit.
When Not to Use This
This basic calculator does not model Social Security income, pension payments, required minimum distributions, tax implications, or healthcare costs. If those factors represent a large portion of your retirement plan, you need a more complex model or professional financial planning software.
Monte Carlo simulations assume returns follow a normal distribution, which underestimates the frequency of extreme market crashes. For highly concentrated portfolios or alternative assets with non-normal return patterns, this approach may underestimate risk.
Finally: this calculator does not account for behavioral factors. Panic selling during downturns. Overspending in good years. Those often matter more than statistical projections.
FAQ
How many simulations do I need for accurate results?
Between 5,000 and 10,000 simulations provide stable probability estimates. Below 1,000, results fluctuate significantly between runs. Above 10,000, you gain minimal additional accuracy for the computational cost. If two runs with 10,000 simulations each produce success rates within 2-3 percentage points, your model is stable enough for planning decisions.
Can I use historical market returns instead of random distributions?
Yes, but historical sampling (bootstrapping) has tradeoffs. It captures real sequence-of-returns patterns but assumes the future resembles the past. Random normal distributions allow you to model scenarios outside historical ranges. Consider running both methods and comparing results — if they differ significantly, your plan may be sensitive to assumptions about future return patterns.
How do I adjust this calculator for early retirement or variable income?
Modify the accumulation phase loop to accept an array of annual contributions instead of a fixed amount. For early retirement, increase retirement_duration and reduce expected_return slightly to account for longer exposure to sequence risk. Test withdrawal rates between 3-4% instead of the traditional 4% rule when retirement spans 40+ years.
What's the difference between this and a simple compound interest calculator?
Compound interest calculators use a single average return and produce one deterministic outcome. Monte Carlo simulations model volatility — the year-to-year variation in returns — which dramatically affects retirement success when you're withdrawing funds. A portfolio that averages 7% annually but experiences -20%, +15%, -10%, +25% in sequence behaves very differently from one that earns exactly 7% every year. Monte Carlo captures that real-world uncertainty. The difference between the two approaches is the difference between hope and planning.
```