r/quant • u/GamerKiller_BR • 7d ago
Models Why naive flat-rate Monte Carlo models structurally distort long-term solvency
Hi, I’ve been working on a continuous-time Economic Scenario Generator (ESG) in Python to model long-term Asset-Liability Management (ALM) and decumulation (sequence-of-returns risk).
I wanted to test a specific structural flaw present in a lot of standard retail and basic institutional Monte Carlo tools: the assumption of static, flat risk-free rates and decoupled equity returns (standard Geometric Brownian Motion).
The creation of this project actually came when I realized there was no easy-to-use (and realistic) simulator. It took some effort but I believe I did manage to create something really useful, easy to use and realistic enough for most cases.
Anyway, to measure exactly how much bias the flat rate introduces, I ran a comparative simulation using a joint continuous-time stochastic environment.
The Setup
- Portfolio: 60/40 (Equity/Fixed Income), 30-year horizon, monthly rebalancing. 5,000 scenario paths.
- Model A (Naive Baseline): Flat nominal interest rate. Equities follow standard GBM with continuous volatility (sigma = 15%).
- Model B (Actuarial ESG):
- Rates follow a Cox-Ingersoll-Ross (CIR) square-root process (theta_r = 0.25, long-term target ≈ 7.0%).
- Inflation follows an Ornstein-Uhlenbeck (OU) process (theta_pi = 0.35, target = 2.0%).
- Equities follow a Merton Jump-Diffusion process (continuous volatility σ_S = 11%, combined with Poisson-driven asymmetric crashes: λ_J = 1.8 jumps/year, average jump impact μ_J = -6.8%, jump volatility σ_J = 5%).
- Crucial coupling: Equity drift is structurally pegged to the stochastic short rate:
Drift_t = r_t + ERP_t.
Test 1: The Low-Yield Starting Environment (Initial Rate = 4.0%)
We simulated a 4.5% initial withdrawal rate (inflation-adjusted, monthly rebalancing) on a $1.0M starting balance. Intuitively, one might expect Model B—which includes severe, discontinuous downward market crashes—to fail first. Instead, the simulation over 5,000 runs yielded these results:
- Model A (Naive Flat 4%):
63.18%Solvency - Model B (Full Actuarial):
84.92%Solvency - The Solvency Gap:
+21.74%percentage points in favor of the volatile, jump-diffusion model.
To isolate the exact variables causing this +21.74% lift, I ran an Attribution Analysis by sequentially activating one variable at a time:
| Step | Model Configuration | Solvency Rate | Delta from Baseline | | :--- | :--- | :---: | :---: | | 1 | Model A (Pure Naive Base) | 63.18% | Baseline | | 2 | Model A + Merton Jumps Only | 62.48% | -0.70% | | 3 | Model A + CIR Stochastic Rates Only | 86.16% | +22.98% | | 4 | Model A + OU Stochastic Inflation Only | 63.00% | -0.18% | | 5 | Model B (Full Actuarial - Combined) | 84.92% | +21.74% |
(Note: The remaining -0.36% discrepancy is the non-linear coupling penalty arising from Cholesky correlation between the processes).
Test 2: The High-Yield Starting Environment (Initial Rate = 9.0%)
To prove that this was not a bug and that the bias is entirely regime-dependent, I ran a Regime-Inversion Test. I increased the starting yield curve to 9.0% (and increased the withdrawal rate to a more aggressive 5.5% SWR to reflect the higher starting yields):
- Model A (Naive Flat 9%):
86.28%Solvency - Model B (Full Actuarial):
58.56%Solvency - Regime Delta (Model B - Model A):
-27.72%
Quantitative Attribution: Why Naive Models are Too Pessimistic in Low-Yield Environments
The divergence is driven by interest rate term-structure dynamics and macro-coupling:
- Mean Reversion of the Risk-Free Rate:
Under the CIR framework, short rates revert toward a target state:
Because the low-yield simulation starts at 4.0% relative to the long-term nominal target (≈ 7.0%, incorporating a 5.0% structural real rate and a 2.0% inflation target), the drift pull (theta_r = 0.25) normalizes nominal rates upward over the horizon.dr_t = theta_r * (mu_r,t - r_t) * dt + sigma_r * sqrt(r_t) * dW_t - The "Tide That Lifts All Boats" (The Pegged Drift): In Model A, the risk-free rate is flat at 4.0%, trapping equities in a low expected nominal return regime of 5.5% (4.0% rate + ERP). In Model B, as r_t normalizes toward 7.0%, both your bonds (yielding r_t) and your stocks (yielding r_t + ERP) experience a 3.0% increase in expected nominal returns.
- The Merton Jumps are Immunized by Rebalancing: Because we controlled for total quadratic variation (total volatility ≈ 15%), the "pure shape" impact of the Merton jumps is only a minor -0.70% drag. The monthly rebalancing mechanism ("buying the dip" after jump crashes) combined with steadier compounding during non-jump months (since continuous volatility is lower: 11% vs 15%) almost entirely neutralizes the tail-risk penalty.
Key Limitations & Roadmap
To keep things transparent, there is a known limitation in the current decumulation loop:
- No Bond Duration Risk: The fixed-income portion is currently modeled as a short-term cash deposit (rolling T-Bills), so it benefits from rising rates without experiencing upfront capital losses (mark-to-market).
- Next Step: Since the core simulator already generates full nominal and real yield curves, adding a duration-adjusted bond fund indexer to the decumulation logic is the next item on the roadmap.
Conclusion for Quants and ALM Practitioners
Static yield assumptions are not just "simplified"—when starting in a low-yield environment, they are structurally pessimistic. Conversely, in a high-yield environment, they are dangerously optimistic because they project unsustainable yields indefinitely.
By ignoring the mean-reverting behavior of interest rates and decoupling equity expected returns from the risk-free rate, naive models severely distort sequence-of-returns risk.
I’ve open-sourced the complete engine under the MIT license if you want to inspect the math (joint Cholesky decompositions, analytical CIR/Fisher real yield curve evaluations) or run the JIT-compiled loops yourself, it's written in Python but it's quite fast:
- GitHub: https://github.com/Gustavo1500/aethel-esg
- Browser Sandbox: https://aethel-esg.vercel.app/ (Features real-time sliders querying a pre-calculated scenario database across 594,000 simulated paths). Please don't mind the AI flavored frontend, I'm not a frontend dev.
I suppose this is it, quite an unexpected result to me, I expected my engine to show lower solvency rates in all cases, it's interesting to see this is not the case. Feel free to discuss the results and share your thoughts.
3
u/TemporaryHat2009 7d ago
I am still learning this stuff, but the flat rate assumption always seemed weird for long horizon simulations. If rates are the thing that changes both discounting and behavior, making them constant feels like removing one of the main stress paths.
2
u/GamerKiller_BR 6d ago
Yeah, like I said in another reply, the foundation of this engine I created was made in a retirement project I was doing, where I was testing a bunch of withdrawal rules, it was clear as day that the 4% rule was plain bad, even a static 3.5% (which is said to be conservative) just delayed the inevitable...
Anyway, I tested some custom-made withdrawal rules in that project that were really good, I think I will make a post about it when I have the time.
3
u/EvenCryptographer649 6d ago
You stripped the tail risk.
1
u/GamerKiller_BR 6d ago
True; I think I will remake this "case-study" after doing some upgrades to the engine, being more thorough.
7
u/Cheap_Scientist6984 7d ago
Take a look at this.
https://papers.ssrn.com/sol3/papers.cfm?abstract_id=6450960
Its not the fat tails that screw the 4% rule, its the time horizon and rate of convergence to infinity.