Monte Carlo API

Submit a forward wealth simulation against a succeeded optimization run, poll its lifecycle, and fetch its artifact and exports. Simulations are child runs: they inherit the parent's frozen optimizer-time inputs and never refetch market data.

Read this before publishing results

Every probability returned here is conditional on the selected model, the frozen optimizer-time inputs, the effective configuration, the engine version and the seed. It is not a calibrated forecast, and no generator has passed out-of-sample validation. See rolling-origin validation.

Any surface presenting these numbers must present the assumptions block alongside them.

POST
/runs/{run_id}/monte_carlo

Enqueue a simulation on a succeeded parent run. Returns 202 with the child run id.

bash
curl -X POST https://api.foliolab.ai/runs/$RUN_ID/monte_carlo \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "method": "HRP",
    "horizon_years": 20,
    "path_count": 25000,
    "starting_wealth": 2500000,
    "goal_amount": 12000000,
    "goal_basis": "real_pre_tax",
    "contributions": { "amount": 40000, "frequency": "monthly", "escalation_pct": 0.08 },
    "rebalance_frequency": "annual",
    "rgp": "fhs_gjr_garch_vt",
    "scenarios": ["correlation_spike"]
  }'
json
{
  "mc_run_id": "6f2c0b6e-6b6a-4a1e-9d2f-6c9b0f6a2b41",
  "parent_run_id": "1a5b8e2c-77d1-4f3a-b0c2-9e1d3f4a5b6c",
  "status": "queued"
}

Request body

The body is a strict schema. Unknown fields are rejected rather than ignored, so a typo cannot silently run a different configuration.

Core

FieldTypeDescription
methodstring, requiredThe parent run's optimization method whose weights are simulated.
horizon_yearsinteger 1 to 40, default 15Simulation horizon.
path_countinteger 1,000 to 100,000, default 10,000Paths to simulate. Capped by your plan.
starting_wealthnumber, default 1,000,000Initial portfolio value in rupees.
seedinteger or decimal string, optionalOmit to draw and record a fresh seed. Results are byte-reproducible from the echoed config.
batch_sizeinteger 128 to 5,000, default 2,000Path batch size. Part of the reproducibility contract.

Return process

FieldTypeDescription
rgpenum, default fhs_gjr_garch_vtReturn-generating process. See the table below.
expected_block_length_daysnumber, optionalStationary bootstrap expected block length. Omit for automatic Politis-White selection.
student_t_dofnumber > 2, default 5Degrees of freedom for the student_t process.
regimeobjectRegime-bootstrap fit settings.
fhsobjectFiltered historical simulation settings.

Planning and accounting

FieldTypeDescription
goal_amountnumber, optionalTarget terminal wealth for the goal probability.
goal_basisenumnominal_pre_tax, nominal_post_tax, real_pre_tax or real_post_tax.
wealth_floornumber, optionalSolvency floor. A breach is recorded at any month or discontinuous cash-flow event, including t=0.
wealth_floor_basisenum, default nominal_pre_taxSeries the floor is evaluated against.
contributionsobject, optionalSIP: amount, frequency, annual escalation_pct.
withdrawalsobject, optionalSWP: amount, frequency, start_year, escalation_mode, escalation_pct.
inflationobjectenabled and annual_rate_override. Deflates real-basis series.
taxobjectIndian listed-equity capital gains on a monthly FIFO basis.
slippage_bpsnumber 0 to 500, default 0Proportional cost on traded value at every modeled trade, not only rebalances.

Policy and evidence

FieldTypeDescription
rebalance_frequencynone, annual, quarterly, monthlyDefault annual.
rebalance_modestatic or reoptimizeDefault static.
walk_forwardobjectreoptimize_frequency, estimation_window, estimation_window_years, weight_observation_grid.
paired_static_baselineboolean, default falseSimulate the frozen-weight arm on identical shock coordinates.
driftobjectmode plus anchor fields. See drift modes below.
risk_freeobjectsource: parent_run, explicit or disabled. Metric benchmark only.
scenariosarray, max 8Named scenario arms run beside the base arm under common random numbers.
precisionobject, optionaltier and optional max_paths. Enables adaptive sequential stopping.
parameter_uncertaintyobject, optionalouter_draws and paired_fixed_control. Mutually exclusive with precision.

Return processes

Which values are accepted also depends on your plan's mc_allowed_processes entitlement.

FieldTypeDescription
fhs_gjr_garch_vtDefaultFiltered historical simulation with a variance-targeted GJR-GARCH volatility model. Needs about 5 years of jointly aligned history; a shorter panel falls back and the result reports generator_fallback.
fhs_gjr_regimeSelectableThe same filter under a slow-moving variance-regime layer.
fhs_gjr_regime_uv_fallbackSelectableThe regime layer, falling back to unconditional variance when regime identification fails.
block_bootstrapRetiredPolitis-Romano stationary block bootstrap. Retired: rolling-origin testing measured simulated volatility 27% to 56% above realized at every horizon. No plan grants it.
regime_bootstrapSelectableMarkov regime chain over portfolio returns; blocks drawn from the active state's labelled history.
fhs_ewmaSelectableFiltered historical simulation with EWMA conditional volatility.
fhs_gjr_garchSelectableThe same filter with a GJR-GARCH volatility model. Failed fits fall back to EWMA per asset.
gbm_lognormalSelectableLognormal geometric Brownian motion with Ledoit-Wolf shrinkage covariance.
gaussianComparisonMultivariate normal on simple returns. Understates Indian-market tails.
student_tComparisonMultivariate Student-t on simple returns.

Each process has a minimum history requirement and its own admission checks. The full mathematics for each one is documented under engine methodology.

Drift modes

drift.mode accepts historical (default), override, shrunk and forward_shrunk. A fifth mode, market_forward_shrunk, resolves a versioned market-level Indian equity assumption and accepts no user-supplied anchor values.

Drift modes are plan-gated by mc_allowed_drift_modes. The gate has one deliberate asymmetry: a "*" wildcard expands to every mode except the house-view modes, so the wildcard cannot switch one on by analogy. A missing or malformed key degrades to the four caller-supplied modes, which means the fail-closed direction is “no house view” rather than “no Monte Carlo”. See drift and estimation risk.

GET
/runs/{run_id}/monte_carlo

Every simulation submitted against one parent optimization run.

bash
curl https://api.foliolab.ai/runs/$RUN_ID/monte_carlo \
  -H "Authorization: Bearer $ACCESS_TOKEN"
GET
/mc

All of the caller's simulations across every parent run, newest first. Cursor paginated.

bash
curl "https://api.foliolab.ai/mc?limit=20&cursor=$NEXT_CURSOR" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

limit defaults to 20. Pass the next_cursor from the previous response to page forward.

GET
/mc/{mc_run_id}

Lifecycle, inline summary, timing, and signed artifact and export links once the run has succeeded.

json
{
  "mc_run_id": "6f2c0b6e-6b6a-4a1e-9d2f-6c9b0f6a2b41",
  "parent_run_id": "1a5b8e2c-77d1-4f3a-b0c2-9e1d3f4a5b6c",
  "status": "succeeded",
  "method": "HRP",
  "engine_version": "mc-v1.21.0",
  "seed": "8127364512873645",
  "attempt_count": 1,
  "created_at": "2026-08-11T09:14:02Z",
  "started_at": "2026-08-11T09:14:19Z",
  "finished_at": "2026-08-11T09:17:41Z",
  "timing": {
    "queue_wait_seconds": 17.4,
    "execution_seconds": 202.1
  },
  "summary": {
    "run_information": { "...": "..." },
    "metric_suite": { "...": "..." },
    "model_risk": { "...": "..." },
    "assumptions": { "...": "..." }
  },
  "config": { "...": "the echoed immutable configuration" },
  "artifact": {
    "signed_url": "https://...",
    "bytes": 4821904,
    "content_type": "application/json"
  },
  "exports": {
    "pdf": { "signed_url": "https://...", "bytes": 1284512, "filename": "..." },
    "xlsx": { "signed_url": "https://...", "bytes": 284512, "filename": "..." }
  }
}

Timing is two numbers, not one. A run that sat in the queue for four minutes and simulated for forty seconds is not a slow simulation. Both are observations rather than a service level, and a retried run reports the last attempt's execution, which is why the attempt count sits beside them.

Signed links are short lived and are regenerated on each detail request. Do not cache them.

Exports are best effort. A failed export never turns a completed simulation into a failed run, so the exports object may be empty on a succeeded run.

Errors

StatusDetailCause
403mc_api_key_not_supportedSimulations require a user session or an MCP OAuth grant. Programmatic API keys are not accepted.
403mc_not_allowedThe plan has no Monte Carlo access.
403mc_path_limit_exceeded:<cap>path_count exceeds the plan's path cap. The cap is in the detail.
403mc_process_not_allowed:<process>The plan does not include the requested return process.
403mc_drift_mode_not_allowed:<mode>The plan does not include the requested drift mode.
409parent_not_readyThe parent optimization run has not succeeded.
422MC_METHOD_REQUIREDmethod was not pinned on the request.
429mc_monthly_limit_exceededThe plan's monthly simulation meter is exhausted.
429mc_inflight_limit_exceededAt most two simulations may be queued or running at once.
422mc_insufficient_history_for_processThe chosen process needs more jointly aligned daily history than the portfolio has.
503MC_GOVERNANCE_UNAVAILABLEThe service could not load or apply its model-risk governance record. Retry unchanged.

Admission and the worker apply the same workload and optimizer-attestation checks, so a row queued under an older policy cannot bypass a newer safety gate when it finally executes. A request the engine cannot honour exactly is refused rather than downgraded.

Plan limits

PlanSimulations / monthPaths / runGenerators
FreeNoneNoneNone
Pro2050,000fhs_gjr_garch_vt, regime_bootstrap, fhs_ewma, gbm_lognormal, gaussian, student_t
EnterpriseUnlimited100,000All

Monte Carlo has its own monthly meter, separate from optimizations and backtests. GET /billing/me reports all three.

Related

Backtests API for the other child-run workflow. Jobs for the parent optimization lifecycle.

Walk-forward re-optimization explains the rebalance_mode and walk_forward fields. Adaptive precision explains precision, and parameter uncertainty explains why the two cannot be combined.

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