Visualizing Risk

Featuring a pension savings calculation, Monte Carlo simulation, and ‘scrollytelling’

July 1, 2024

For an interactive pension savings calculator, I’m aiming to illustrate some value of compound interest, employer matching, tax relief, and to visually highlight the impact of changes to calculation inputs like starting age or investment growth rate.

But every illustration it produces suggests a smooth path to a retirement fund.

Reality isn’t so smooth, however. In a few parts I’ll focus on this, but I’ll zoom into one uncertain feature among others first: risky investments mean risky outcomes.

Here we’ll work towards a visualization to interpret uncertain outcomes - in other words to visualize risk.

To go there: just scroll!

My pension savings calculator suggests a smooth path to a retirement fund 🌈💰

But pension savings usually involve risky investments

Risky investments do not have smooth paths! 📈

So, a savings path might rather look like this:

or this:

By assuming a distribution (or range of values) for investment growth: rather than a single value

then we can simulate many paths

and again:

In a barchart 📊 we can track where simulated paths land

And simulate again… keep scrolling! showing simulations

Now we can visualize risk with help from this barchart 📊

The barchart shows us a distribution of outcomes 📊

The average simulated result is close (ish!) to the original - ‘smooth’ result

but many of our paths landed far below that! 😱

For example 🕵️

We just scanned ↕️ the downside tail of our outcome distribution

For risk analysis, it can be useful to test results here against our risk appetite


To visualize risk - to do anything with risk, it’s important to consider many scenarios: not just one (and not even just one exercise).

What we visualized above is a very simple form of Monte Carlo simulation.

Risk is not necessarily bad - and it can be mitigated or managed: talk to your financial adviser. This blog post is not financial advice, and it shouldn’t be used for financial advice.

As far as pension savings go: thankfully, on a long savings journey it’s normal to have lots of ways to control risk.

Notably - choosing investments that suit your risk appetite.

Almost certainly this should change as you approach retirement. Above: it doesn’t.

But maybe in another blog post it will 💭

Here is a plot of some samples from the investment growth rate per annum distribution:

I didn’t do a lot of science to determine these assumptions, or other notable ones that are fixed:

  • Person (‘employee’) starts saving aged 25, to retire at 65
  • Salary at age 25 is 30k, which grows at 2% per annum until retirement
  • Person saves 10% of salary per annum
  • Employer matches 50% of contributions: so that effectively 15% of persons salary is contributed per annum


  • Contribution charge of 4% is taken from all contributions (one-off)
  • Management charge of 1% is taken from accumulated fund per annum (annual)

You can follow this link (better on a desktop) for the calculang formulas and for a visualization of cashflows.

  • In the visualization: employee contributions are split into tax relief (based on an Irish income tax calculation) and cost.
    It’s the sum and not this split that’s used for the retirement fund calculations (so Irish income tax calculations are effectively not relevant here)

versus pension savings calculator 🤼‍♂️

  • We expect this: here we did monte carlo simulation using the same calculang model 🔗 (not ‘Copy of..’ etc.)

You can use this link to validate that (better on a desktop: same link as above) , and for a visualization of the cashflows behind 731k - which you can also download.

⚠️ caveats relating to that model also apply here

The assumptions, methodology and limitations of a model and model outputs should be carefully considered for any purpose you apply them to.

I haven’t completely listed these - or otherwise properly documented models and outputs.

🙏 Making this post was a chance to finally experiment with certain interaction and visualization techniques that are firsts for me - and I hope useful for explanation and communication about numbers, models, and modelling techniques.

Please report issues you encounter, and get in touch if you’d like to provide some feedback!

ignore this (will delete)
cursor = ({
    age_0_in: 25,
    retirement_age_in: 65,
    fund_value_0_in: 0,
    empee_contribution_rate_in: 0.1,
    emper_matching_rate_in: 0.5,
    salary_0_in: 30000,
    salary_age_0_in: 25,
    salary_inflation_rate_in: 0.02,
    contribution_charge_rate_in: 0.04,
    management_charge_rate_in: 0.01,
    missed_contribution_age_in: 0,
    random_seed_in: 'na',
    actual_unit_growth_rates_co_in: 70,
    lifestyling_in: false,
    unit_growth_rate_mean_in: 0.06,
    unit_growth_rate_std_dev_in: 0.1

limit = 3e6

model init stuff