Monte Carlo · Return processes

Filtered Historical Simulation (GJR-GARCH with a variance-regime layer)

fhs_gjr_regime

The default process with one addition: a single slow-moving multiplier that raises and lowers the whole portfolio's variance level over years. It exists because a one-component GARCH fit, however good, produces less variation in realised variance across long windows than markets actually show.

One common multiplier, not per-asset states

Each holding keeps its own variance-targeted GJR fit. On top of all of them sits one shared multiplier following a log AR(1):

is how persistent the level is, how strongly it responds to a market surprise, and the volatility of volatility. There are no hidden states and no regime-conditional residual pools. It is a continuous level that drifts, not a switch that flips.

The multiplier is driven by the equal-weight cross-asset mean of the panel's own residuals. That is worth knowing when you read a result: "the market" here is whatever you happen to hold, so a concentrated portfolio drives the layer with something closer to one name's idiosyncratic noise than with a market factor. Whether to drive it from a benchmark column instead is an open product question, not a settled one.

Why it needs two windows

The variance level is a short-window state: what is volatility doing now. How slowly that level moves, and how much it varies, are slow structural quantities that a short window cannot see at all. One window cannot do both jobs, so this process uses two.

The trailing estimation window sets the level. A second, longer causal window identifies the persistent component and the dispersion target, and it needs at least 2,520 sessions, roughly ten years, with at least eight non-overlapping blocks to measure that target.

If that longer window is not available the layer is simply absent, and the process behaves as the plain variance-targeted GJR default. This is safe by construction rather than by luck: the layer only ever adds dispersion, so omitting it cannot make a run overstate confidence.

What bounds it

An unbounded multiplier on top of a GARCH recursion can produce arbitrarily extreme paths, so four limits apply:

  • The market squared-return surprise feeding is clipped at 4.
  • The log level is clipped at , capping the multiplier at roughly 4.5x volatility either way.
  • Volatility of volatility is capped at 0.5. In the validation study's fits this ceiling bound about 28% of the time, so it is an active constraint rather than a formality.
  • The innovation is standard normal: no fat tail and no jump in the layer itself.

What it measures, and the known gap

In rolling-origin testing this arm tracked realised volatility to +2.0%, +3.4%, +1.6% and +1.8% at one day, one month, three months and one year: the flattest profile across horizons of anything measured, and marginally behind the default on worst-horizon error.

The known gap is speed, not level. The persistence parameter fits high, with a median half-life measured in hundreds of sessions, so the layer walks into a stressed regime over months. Real crises arrive in days. Simulated paths therefore reach elevated volatility, but they cannot enter it abruptly, and a scenario whose danger is the suddenness rather than the depth is not represented. The deficit is the persistence parameter, not the clips above.

Like every generator here, this one is not validated out of sample. It is insufficient_evidence, not accepted, and every probability stays conditional on the selected model, the frozen optimizer-time inputs, the effective configuration, the engine version and the seed.

Not investment advice. Past performance is not indicative of future results.