VEEMANDocs
DocumentationSupportOpen Dashboard

Getting started

  • Introduction
  • Quick start
  • Core concepts

Importing data

  • Supported formats
  • How import works
  • Splitting a file
  • Troubleshooting

Building your portfolio

  • Workspace & equity
  • Weights & capital
  • Weekday filter
  • Library
  • Variants & comparison
  • History (undo/redo)
  • Shared spaces

Analytics

  • Metrics reference
  • Charts (deep-dive)
  • Monthly P&L
  • Correlations
  • Strategy comparison
  • Monte Carlo
  • Equity Control
  • Optimization
  • Veeman AI

Reference

  • Keyboard & command palette
  • Exporting data
  • Settings

Technical

  • Architecture
  • API reference
  • Security
  • Your data & privacy
  • Risk disclosure
← PreviousStrategy comparisonNext →Equity Control

Monte Carlo

Your track record is one sample of what could have happened. Monte Carlo resamples or models your daily returns to generate thousands of alternative paths, so you can see the range of outcomes instead of a single line — and estimate the odds of deep drawdowns or ruin.

You can run it on the whole portfolio or a single strategy. Active weekday filters are respected.

The series fed to the simulation is built from trading days only — the days you actually had a position. Idle calendar days (weekends, holidays, gaps between trades) are not part of the resampling pool, for the portfolio exactly as for a single strategy. This keeps both runs on the same footing: the horizon below is counted in trading days, and the portfolio simulation is directly comparable to the single-strategy one (no diluted volatility, no drift collapsing toward zero from padded idle days).

With dynamic sizing: the engine changes

When at least one strategy uses dynamic sizing (and the source is the portfolio, or that sized strategy), the simulator switches engine automatically: instead of resampling a produced P/L series, it resamples the raw trades in calendar blocks and re-runs the allocation inside every path — capital, compounding and the daily risk budget react to each simulated history, so the capital→size feedback loop is genuinely stressed. In a hybrid portfolio each block carries the sized trades and the fixed legs' daily P/L of the same period, preserving the correlation between legs; fixed-leg P/L enters the compounding capital from the next day and never consumes the max daily risk. Manual costs on sized legs are subtracted per path, so the bands are net and match the historical reference line.

One thing the per-path allocator doesn't model

The margin cap (safe-80% / limit-100%) is applied by the historical reference curve but not inside the simulated paths. When you run a dynamic Monte Carlo with a margin cap active and margin data present, the results carry a warning: with the cap biting, the simulated position sizes — and therefore returns and ruin probability — can read slightly optimistic versus reality. Everything else (sizing, daily risk budget, compounding, costs) is modelled per path.

What changes in the controls, and why:

  • Model becomes the trade block size (weekly ≈ 5, monthly ≈ 21, quarterly ≈ 63 trading days). Parametric models (Student-t, GARCH…) generate returns, not trades — they cannot feed a per-trade allocator, so they are unavailable; asking for one returns an explicit error.
  • Horizon disappears: every path replays as many blocks as the history contains, so simulated paths have the history's length.
  • Sizing is not asked: it is governed by the workspace's own Compounding switch and max daily risk, echoed in the results header.
  • Winsorize here trims the premium/margin reference used for the fixed-sizing comparison — not the P/L tail.

The verdict compares the dynamic path (the real engine re-run per path) against a fixed sizing benchmark (contracts computed once on the initial capital), with ruin probability, final distribution and drawdown read off the dynamic paths. Selecting a single fixed strategy as the source keeps the classic series engine below, untouched.

The dynamic run exposes the same result tabs as the fixed-contract one — Statistics, Distributions (final-value and max-drawdown histograms) and Horizons — all read off the dynamic paths. One reading caveat: with Compounding on, position size follows the account, so the final-value distribution is naturally right-skewed (a long right tail by construction) — read the median and percentiles, not just the maximum. In the Horizons ladder the "days" are the path's close-day steps (the dynamic fan has no fixed trading-day horizon), so treat them as sequence checkpoints rather than calendar days.

Fixed-contract sizing

The classic Monte Carlo models a fixed-contract book. It resamples the daily P/L in dollars and rebuilds equity additively (equity = capital + Σ P/L): a +$400 day is +$400 wherever the path sits — the dollar P/L does not grow with the account. This is the honest model for a strategy that trades a fixed number of contracts (e.g. always 1 lot), and it is the only mode here — there is no "compounding %" toggle.

An absorbing ruin barrier is active: a path that touches the ruin level stops there (a real account that draws down that far can no longer post margin to keep trading), so the ruin probability is meaningful and responds to your starting capital.

Want to study scaling with capital?

Compounding — position size that grows with the account — is not a Monte Carlo toggle, because on a fixed-lot series it is a fiction: a constant ±$400/day is a big % on a small early account and a tiny % on a large late one, so compounding those percentages manufactures exponential growth the strategy can never realise (inflating the median, driving ruin to ~0, making drawdown vanish). To model scaling faithfully, switch the strategy to dynamic sizing in the workspace (risk- or margin-based): it recomputes the contract count each day from your current capital and risk rules, honouring integer contracts and margin. The Monte Carlo then automatically runs in its dynamic form — resampling trades in blocks and re-running that real allocation in every path, governed by the workspace's own Compounding switch and max daily risk (see With dynamic sizing above) — instead of naively compounding percentage returns off a fixed-lot series.

Choosing a model

VEEMAN offers six models, from production-grade to didactic. Block bootstrap is the default and a safe general choice.

ModelWhat it doesUse it when
FHS + GARCH(1,1)Filtered Historical Simulation: fits a GARCH conditional-volatility model and bootstraps its standardised residuals. The market standard for VaR.Returns show volatility clustering (calm and stormy regimes).
Block bootstrapStationary bootstrap (Politis–Romano): resamples blocks of consecutive days, preserving autocorrelation and clustering.A robust default — structure without a parametric model.
Skewed Student-tFernández–Steel distribution: captures skew + fat tails, with degrees of freedom and skew fitted by MLE.Returns are asymmetric (e.g. short-vol, crash risk).
Student-tSymmetric fat-tailed distribution fitted by MLE, re-centred on the empirical mean.Symmetric returns with tail risk.
Bootstrap (IID)Resamples single days independently. Destroys serial dependence and underestimates drawdowns.Teaching / baseline only.
Gaussian GBMLog-normal random walk. Ignores fat tails.Teaching / baseline only.

IID and Gaussian are didactic

The last two models deliberately ignore real-world structure (clustering, fat tails). They're useful to show why the others matter — not to size real risk. Prefer Block bootstrap or FHS+GARCH for decisions.

Why the Student-t model is re-centred

The Student-t is symmetric, so the mean of the values it generates is exactly its fitted location. On a skewed sample that location settles on the bulk of the data and absorbs the few large losses into the tail — it behaves like a median, not a mean.

For a premium-selling strategy the gap can invert the conclusion: 99 days at +$100 and one at −$15,000 average −$51 a day, while the fitted location sits near +$100. Left as-is, the simulation would show near-certain growth for a strategy that loses money.

VEEMAN therefore centres the simulated drift on the empirical mean, so that E[equity at horizon] = capital + horizon × mean(P/L) — the property block bootstrap, IID bootstrap and Gaussian already have. The fitted location is still reported in the diagnostics, and when it diverges from the mean by more than sampling noise the run carries a warning suggesting block bootstrap or skewed-t instead. This is a deliberate modelling choice: it trades the model's status as a strict MLE fit for a drift that matches your actual track record.

Parameters

Model
One of the six above. Default: Block bootstrap.
Simulations
Number of paths, 100–1,000,000 (default 1,000). More paths = more precise risk numbers (VaR, CVaR, ruin, drawdown), at a slower run. The equity fan converges after a few thousand paths and won't visibly change beyond that — raise the count to sharpen the tail statistics, not the picture.
Horizon
Length of each path in trading days, 10–1,260 (default 252 ≈ one year). Going much beyond your historical sample length just resamples the same history repeatedly.
Ruin threshold
Equity level, as a % of starting capital, that counts as "ruin" if touched (0–100, default 50).
Block length
For bootstrap / FHS: days per block. Auto-estimated (Politis–White) or set manually. Presets: 5 (week), 21 (month), 63 (quarter).
Winsorize
Clips the sampled days to the chosen low/high percentiles before resampling, so a few extreme days don't dominate. Off by default (0–100). Caution: it also clips the loss tail, so drawdown, VaR and ruin probability come out more optimistic — use it to tame anomalous outliers, not to measure tail risk.
Starting capital
Defaults to the session's initial capital; you can override it for the run.

Reading the results

A run returns several views:

  • Percentile fan — the equity envelope over time at the 5th, 25th, 50th (median), 75th and 95th percentiles. The wider the fan, the more uncertain the outcome.
  • Sample paths — a handful of individual simulated curves, to feel the texture of the variation.
  • Final value distribution — a histogram of where equity ends up at the horizon (worst-case → typical → best-case).
  • Max drawdown distribution — a histogram of the deepest drawdown reached on each path. This is the one most traders underestimate.
  • Horizons — a ladder of horizons (a week out to a year, plus your full period) read off the fan: the expected P/L and the 50% / 90% bands at each, so you can see what to expect over the next N days without a separate run.

Horizons (short-term references)

The Horizons tab answers a specific question: what should I expect over the next 10, 15, 30… trading days? It reads the same percentile fan at intermediate days — no extra simulation — and lays out, for a ladder of horizons (5, 10, 15, 21, 30, 63, 126 ≈ six months, 252 ≈ one year trading days, plus your full horizon), the P/L relative to the initial capital:

  • Expected P/L — the median path at that day (with its % of capital).
  • Half the cases (50%) — the P25–P75 band: the typical spread.
  • 9 out of 10 (90%) — the P05–P95 band: the realistic best/worst envelope.

A per-row bar shows the band widening as the horizon grows — the visual signature that short-term outcomes are dominated by chance: over a handful of days the drift (days × mean) is tiny next to the spread (√days × volatility), so the honest reference is the range, not a point. The 10-day horizon is the classic regulatory VaR window, so these near-term bands are a standard, solid reference. The last row is your full horizon, so it ties back to the numbers in Statistics.

Equity band only — drawdown and tails stay terminal

The Horizons tab reads the equity percentile bands at each day. The max drawdown and the fine tails (VaR / CVaR) are cumulative to the path endpoint, so they remain in the Statistics tab, referred to the full horizon. If you need those risk metrics at one specific short horizon, run the simulation with that horizon set directly.

Summary statistics

StatMeaning
Probability of ruinshare of paths that touch the ruin threshold at any point. In fixed-contract mode the barrier is absorbing, so this responds to your starting capital.
Prob. below capitalshare of scenarios that end below the starting capital — the plain "chance of losing money".
Median / P05 / P95 finaltypical and tail outcomes for final equity.
Max DD median / P95typical and bad-case worst drawdowns, shown in % of peak and in dollars.
Tail P/L 5% & 1% (VaR / CVaR)the final P/L in dollars at the 5th / 1st percentile (VaR) and the average P/L of that worst tail (CVaR). Negative = a loss; positive = even the tail is a gain (a profitable strategy at a long horizon). Computed on terminal dollars, so there is no "−1,129%" artefact from compounded returns.

Diagnostics

Each run also reports the fitted model internals — for FHS+GARCH that includes the persistence (α + β), the volatility half-life in days and the long-run volatility; for the t-models, the fitted degrees of freedom and skew; for bootstraps, the block length used. These let you sanity-check that the model captured your data.

For the Student-t the degrees of freedom are reported twice: the raw MLE estimate and the value actually used. VEEMAN floors ν at 2.1 so the simulated distribution has a finite variance — without it, drawdown and ruin statistics would not converge. When the raw estimate falls below that floor the run says so, because a reported 2.10 on an estimate of 1.04 describes the floor, not your data.

It's still a model

Monte Carlo assumes the future resembles the resampled past. It widens your view of risk, but it cannot foresee regime changes the history never contained. Treat the ruin probability as a relative gauge across configurations, not a guarantee.

Performance

A million paths over a 1,260-day horizon is heavy but feasible; 1,000–10,000 simulations are plenty for interactive work, with 100,000+ reserved for final risk reports. Runs are offloaded to a worker thread so the UI stays responsive.

You can leave the tab while it runs

The simulation runs on the server as a background job: launching it returns immediately and the app polls for the result. You can switch tabs, open another app, or lock your phone — the run keeps going server-side and the result appears when you come back. The job is self-contained and saved, so it survives a page reload — and even a server restart: if the backend recycles mid-run, the next poll transparently re-runs it from the stored recipe.