Monte Carlo · Methodology

Monte Carlo Engine Methodology

mc-v1.25.0

A complete description of what the forward simulator actually does: where its inputs come from, which return-generating process draws each future day, how drift is resolved, what is subtracted along the way, and how every reported number is defined. Nothing here is a forecast claim. This page exists so you can audit the assumptions behind any probability the product shows you.

For the product-level tour of what the feature does and how to run it, start with Forward Monte Carlo Simulation.

The pipeline, in order

Every simulation runs the same seven stages. Each stage is independently configurable and independently disclosed in the result, which is what makes the output auditable rather than merely plausible-looking.

  1. 1

    Frozen calibration inputs

    The simulation loads the exact aligned return panel and CPI series that the parent optimization used, as an immutable content-addressed artifact, and verifies its SHA-256 digest before use. It does not refetch today's market history. Optimized weights and simulated history therefore share one data vintage, and a missing or corrupt artifact fails closed rather than silently substituting fresh data.

  2. 2

    Return-generating process

    One of nine selectable processes draws daily asset returns for every path. This is the single biggest modeling choice in the run, and the spread between processes is real model risk rather than noise to be hidden.

  3. 3

    Drift resolution and estimation risk

    The centre of the simulated distribution is set by the drift layer, and per-path mean perturbation propagates the fact that the historical mean is itself an estimate with a large standard error.

  4. 4

    Portfolio accounting

    Asset returns are aggregated into portfolio wealth under your weights, with rebalancing at the configured frequency and proportional trade costs on every modeled trade.

  5. 5

    Cash flows

    SIP contributions and SWP withdrawals are applied on the monthly grid, tracked against a unitized NAV so a deposit is never mistaken for a return. Withdrawals are grossed up for modeled tax and cost.

  6. 6

    Tax and inflation overlays

    Realized gains at withdrawals, rebalances and terminal liquidation are taxed under a monthly FIFO cohort approximation, and real-wealth outputs are deflated by CPI aligned to the same historical days the return blocks were sampled from.

  7. 7

    Statistics and intervals

    Percentile fans, goal and depletion probabilities with Wilson intervals, drawdown and underwater-spell distributions, and forward VaR/CVaR with separate bootstrap intervals are computed across paths.

The nine return-generating processes

A return-generating process answers one question: given the frozen history, how is tomorrow's cross-section of asset returns drawn? Everything downstream, including the tax and inflation overlays, is identical across processes. Only this stage differs.

None of these has been shown to forecast better than the others. The rolling-origin validation harness ran and published its verdicts (policy mc-validation-india-v1.10.0, engine mc-v1.21.0). It qualified no generator and it approved no generator: both published lists are empty. It recorded block_bootstrap, fhs_ewma, fhs_gjr_garch and gbm_lognormal as rejected out of sample. It recorded the three best-calibrated arms as insufficient_evidence, which is not acceptance. FolioLab therefore does not publish a “best” process. Running two processes and comparing the range is more informative than trusting any single one. Every probability stays conditional on the selected model, the frozen optimizer-time inputs, the effective configuration, the engine version and the seed.

ProcessFamilyReturn supportMinimum aligned history
Filtered historical simulation, variance-targeted GJR-GARCH
fhs_gjr_garch_vt

The default. Models each holding's current volatility with an asymmetric GJR-GARCH fit whose long-run level is pinned to a stated target, then resamples standardised historical shocks on top. By default it runs at two targets on the same paths: the long-run market level as the headline and the recent five-year window beside it. It is not validated out of sample.

Conditional volatilityLog returns, positive gross by constructionAbout 5 years of jointly aligned rows
Filtered historical simulation, GJR-GARCH with a variance-regime layer
fhs_gjr_regime

The default plus one shared slow-moving variance multiplier, identified on a second and longer window. Adds the year-to-year variation in volatility that a single GARCH component understates. Falls back to no layer, safely, when the history is too short to identify it.

Conditional volatilityLog returns, positive gross by constructionAbout 5 years to fit, about 10 for the regime layer to activate
Filtered historical simulation, regime layer with unconditional fallback
fhs_gjr_regime_uv_fallback

The variance-regime process, changing only what a holding falls back to when its volatility fit is rejected: its unconditional variance rather than EWMA, which keeps the recursion stationary at the cost of that holding's responsiveness.

Conditional volatilityLog returns, positive gross by constructionAbout 5 years to fit, about 10 for the regime layer to activate
Stationary block bootstrap
block_bootstrap

Retired, and no longer selectable. Resampled random-length blocks of complete historical days. Rolling-origin testing measured its simulated volatility 27% to 56% above realised at every horizon, so it stopped being the default and no plan grants it.

ResamplingSimple returns, clipped4 rows structurally, 32 for automatic block-length selection
Regime-switching bootstrap
regime_bootstrap

A fitted Markov chain moves each path between calm and stressed states; days are resampled from the active state's labeled history.

ResamplingSimple returns, clipped252 rows for the regime fit, else falls back to the plain bootstrap
Filtered historical simulation, EWMA
fhs_ewma

Divides history by each holding's estimated volatility, resamples the standardized shock rows, then rebuilds volatility forward from today's level.

Conditional volatilityLog returns, positive756 rows, about 3 trading years
Filtered historical simulation, GJR-GARCH
fhs_gjr_garch

The same filter, with a volatility model in which losses raise future risk more than equivalent gains. Failed fits fall back to EWMA per asset.

Conditional volatilityLog returns, positive1,260 rows, about 5 trading years
Lognormal GBM
gbm_lognormal

Independent Gaussian log returns with a Ledoit-Wolf shrinkage covariance. An analytical reference point, not a realism upgrade.

Parametric benchmarkLog returns, positive60 rows
Multivariate Student-t
student_t

Fat-tailed draws with covariance matched to history and one shared mixing variable per day, so the cross-section fattens together.

Parametric comparisonSimple returns, clipped4 rows
Multivariate Gaussian
gaussian

Normal draws on the historical mean and covariance. The thin-tailed reference case: it understates Indian-market tails by design.

Parametric comparisonSimple returns, clipped4 rows

Why return support matters

The six log-domain processes build gross returns as where is a simulated log return. Gross returns are therefore positive by construction: a holding can approach zero but can never go through it, and no corrective clipping exists on those paths.

The four simple-return processes can in principle emit a gross return at or below zero. A disclosed lower gross-return clip prevents negative wealth. The clip is reported with the result, including how often it bound, so you can see whether it was cosmetic or load-bearing. For the bootstrap families it is effectively inert, because resampled historical rows never contain a total loss. For the Gaussian and Student-t processes it is a real part of the model, which is one reason both are labeled comparison processes rather than planning models.

Limitations

Every simulation, its result page, its PDF report and the AI assistant surface show these five statements. They are the part of the method a reader must know before reading any number.

  • Not a forecast. Every probability is conditional on the selected model, the frozen optimizer-time inputs, the effective configuration, the engine version and the seed. No return model has passed out-of-sample validation.
  • The volatility level is an assumption. In a test on held-out stocks and years, neither recent volatility nor long-run volatility predicted the next decade reliably. Read the result at both levels.
  • Bound to past shocks. Paths reuse shocks from the portfolio's own history. An event with no historical analogue cannot appear.
  • The fitted model is held fixed. The ranges show variation across paths under one fitted model. They do not include the estimation error of that model.
  • Planning grade. Weights come from the optimizer as frozen inputs. Tax and costs are modelled for planning, not for filing. The price history holds no delisted stocks.

Two volatility levels, because the level is an assumption

Over a 10 to 40 year horizon, the level that volatility returns to matters more than today's volatility. A test on stocks and years that played no part in choosing the model found that neither a recent window nor the whole history predicted the next decade's level reliably. The recent window understated the long-run level after a calm period; the whole history overstated it during the calm decade from 2016 to 2025. So FolioLab does not pick one and call it a forecast. The default process simulates both, on the same paths:

  • Long-run market level (the headline): each holding's long-run variance is its beta to the NIFTY 50, squared, times the NIFTY 50's variance over all its history since 1990, plus the holding's own idiosyncratic variance. It is the more cautious level for a long plan.
  • Recent five years: each holding's variance over the last 1,260 trading days, the window the model is fitted on.

The result shows both levels side by side. Because the paths are the same, the gap between them comes from the volatility level alone. You can make the recent five years the headline, or run one level only for a faster result, in the simulation form or with long_run_volatility over MCP. If the NIFTY 50 history is not available, a run shows the recent five years only and says so. Neither level is a forecast, and every probability stays conditional on the selected model, the frozen optimizer-time inputs, the effective configuration, the engine version and the seed.

The two-level view applies to fhs_gjr_garch_vt only, and not to a run with the parameter-uncertainty experiment. Such a run reports one level, and unless the request names a level, that level is the recent five years. The long-run market mapping is the single-index split of variance (Sharpe, 1963). It is one of the candidates the volatility-fidelity study screened, and it failed that study's development gates. It is in the engine as a stated assumption, not as a validated model. The study and its literature are listed on the references page.

What every process shares

Whole-row sampling

Both bootstrap families and all five FHS variants resample complete cross-sectional rows rather than each asset independently. A day when everything fell together stays a day when everything falls together, so cross-asset correlation, coskewness and cokurtosis are preserved exactly rather than approximated by a covariance matrix.

Batch-stable state

Paths are simulated in memory-bounded batches, but sampler state carries across chunk boundaries. A bootstrap block that spans a month boundary stays one block, and an FHS conditional-variance recursion is never reset mid-path by an implementation detail.

Common random numbers

Crisis scenarios are paired with the base run through common random numbers, so the difference between a scenario and its base isolates the stress instead of mixing in fresh sampling noise.

Reproducibility

Results reproduce when the seed, configuration, engine version and immutable input digest all match. The seed alone is not a complete reproducibility claim, and the result records all four so the claim can be checked rather than assumed.

Variance reduction

For the plain block bootstrap, the first block start of every path is stratified over the historical index rather than drawn uniformly: path of draws its opening row uniformly inside the stratum

where is the number of historical rows. This is a Latin hypercube-style stratification. It guarantees the set of starting points covers the whole sample evenly instead of clustering by chance, which tightens tail-quantile estimates at no computational cost and without changing the target distribution.

Stratification is disabled when a crisis scenario supplies over-sampling weights, because weighted starts and stratified starts make contradictory assumptions about the start distribution. The regime bootstrap also draws first starts uniformly within the opening regime's rows, so it does not stratify either.

Validation status of each process

Selectability and validation are separate facts, and the engine reports them separately. Every process listed above is fully specified and implemented. All but one are selectable: the table marks the stationary block bootstrap as retired, and no plan grants it. None of them has passed out-of-sample validation.

A generator publishes its validation status per candidate and per estimation window, together with the policy version that produced the verdict. The status is not one flat value for every generator. A candidate the harness scored and rejected publishes a rejection, and a candidate the harness could not score publishes insufficient evidence. That distinction is deliberate: the word “rejected” overstates the evidence for a candidate the harness never scored, as much as “accepted” would understate it. The harness accepted no candidate.

A regime-FHS and MSGARCH core is implemented and unit-tested but is deliberately absent from the selectable process enum, from billing and from every product surface, after failing its rolling-origin activation gates. The all-or-nothing state is the safety property: a partially wired generator that can be reached by one code path and not another is the failure this design prevents. See rolling-origin validation for the measured evidence, and model risk for what an empty accepted cohort does to the reported numbers.

Availability by plan

Process availability is a plan entitlement. Pro includes six of the nine: fhs_ewma, fhs_gjr_garch_vt, gaussian, gbm_lognormal, regime_bootstrap and student_t. Enterprise adds the remaining three: fhs_gjr_garch, fhs_gjr_regime and fhs_gjr_regime_uv_fallback. The configuration form shows processes outside your entitlement as disabled with the reason rather than hiding them, and a request for a process you cannot run is rejected with 403 mc_process_not_allowed rather than being silently downgraded to something else.

Path counts are capped separately: 50,000 per run on Pro and 100,000 on Enterprise, with a workload safety limit that can further restrict presets for long horizons, many assets, or several scenarios at once. More paths reduce sampling noise in the reported probabilities. They do not reduce model uncertainty, which is what the process comparison is for.

The rest of the methodology

References

  • Politis, D. N., & Romano, J. P. (1994). The Stationary Bootstrap. Journal of the American Statistical Association, 89(428), 1303–1313.
  • Politis, D. N., & White, H. (2004). Automatic Block-Length Selection for the Dependent Bootstrap. Econometric Reviews, 23(1), 53–70.
  • Patton, A., Politis, D. N., & White, H. (2009). Correction to Automatic Block-Length Selection for the Dependent Bootstrap. Econometric Reviews, 28(4), 372–375.
  • Hamilton, J. D. (1989). A New Approach to the Economic Analysis of Nonstationary Time Series and the Business Cycle. Econometrica, 57(2), 357–384.
  • Barone-Adesi, G., Giannopoulos, K., & Vosper, L. (1999). VaR without Correlations for Portfolios of Derivative Securities. Journal of Futures Markets, 19(5), 583–602.
  • Glosten, L. R., Jagannathan, R., & Runkle, D. E. (1993). On the Relation between the Expected Value and the Volatility of the Nominal Excess Return on Stocks. Journal of Finance, 48(5), 1779–1801.
  • Sharpe, W. F. (1963). A Simplified Model for Portfolio Analysis. Management Science, 9(2), 277–293. The single-index split of variance behind the long-run market level.
  • Christoffersen, P. F., & Diebold, F. X. (2000). How Relevant Is Volatility Forecasting for Financial Risk Management? Review of Economics and Statistics, 82(1), 12–22. On how little of today's volatility is forecastable at long horizons, which leaves the long-run level to carry the result.
  • Starica, C., Herzel, S., & Nord, T. (2005). Why Does the GARCH(1,1) Model Fail to Provide Sensible Longer-Horizon Volatility Forecasts? Working paper.
  • Pastor, L., & Stambaugh, R. F. (2012). Are Stocks Really Less Volatile in the Long Run? Journal of Finance, 67(2), 431–478.
  • Newey, W. K., & West, K. D. (1987). A Simple, Positive Semi-Definite, Heteroskedasticity and Autocorrelation Consistent Covariance Matrix. Econometrica, 55(3), 703–708.
  • Ledoit, O., & Wolf, M. (2004). A Well-Conditioned Estimator for Large-Dimensional Covariance Matrices. Journal of Multivariate Analysis, 88(2), 365–411.
  • Wilson, E. B. (1927). Probable Inference, the Law of Succession, and Statistical Inference. Journal of the American Statistical Association, 22(158), 209–212.

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