Skip to main content

What Is Forecast Simulation?

Forecast simulation produces many possible future trajectories instead of one prediction per timestamp. Each trajectory is a coherent path through the forecast horizon, making the output useful when a decision depends on an entire future sequence. Use NixtlaClient.simulate() to generate these paths.
Simulation runs as an asynchronous job on the server. NixtlaClient.simulate() submits the job and polls its status until the paths are ready, so the call blocks like any other client method. By default the client waits up to 10 minutes per job; adjust async_job_wait_timeout and async_job_poll_interval when creating the NixtlaClient if you need a different behavior. A job that fails on the server raises nixtla.AsyncJobError with the server’s message.

Why Use Forecast Simulation

Simulated paths let you estimate quantities that no single forecast, and no per-timestamp quantile, can answer:
  • The probability that demand exceeds capacity at least once next week
  • The distribution of total sales over the next month
  • Inventory or staffing requirements under different futures
  • Best-case, typical, and worst-case cumulative outcomes
Use simulation when you want to explore many ways the future could unfold. If you only need one forecast, use a regular forecast. If you only need a likely range for each future time, use quantiles.

How to Simulate Future Paths

Step 1: Import Packages

Import the required packages and initialize a Nixtla client to connect with TimeGPT.

Step 2: Load Data

This tutorial uses 90 days of daily demand for a single product: a level around 100 units with a weekly rhythm and some noise.

Step 3: Generate Sample Paths

Ask for 100 possible 14-day futures. The seed makes the result repeatable.
The result contains 100 * 14 = 1,400 rows: one row per path and future day.

Step 4: Plot the Paths

Reshape the paths to one column per sample_id and draw them behind the recent history:
Recent demand history followed by a fan of 100 simulated 14-day paths continuing the weekly pattern

Faint lines are the 100 individual simulated futures; the bold line is their median at each day.

Each faint line is one complete, internally consistent future: the paths carry the weekly rhythm forward and disagree about exactly how high the peaks and how low the troughs will be. That disagreement is the uncertainty the next step turns into numbers.

Step 5: Understand the Output

The output is long-form and ordered by sample_id, then series, then forecast timestamp. Select one complete trajectory with:
The pivot from Step 4 gives the complementary wide view: one column per path, one row per future day.

Step 6: Answer Risk Questions

Because each sample_id is a complete future, questions about the whole horizon become simple group-by operations. What is the chance that demand drops below 80 units on at least one of the 14 days?
In 11% of the simulated futures, at least one day falls below 80 units. And how much total demand should you plan for?
Eighty percent of the simulated futures put the 14-day total between roughly 1,386 and 1,410 units. Neither number can be read off a per-timestamp quantile forecast: both depend on how the days within one future relate to each other.
Congratulations! You have generated simulated future paths with TimeGPT and turned them into probability and risk estimates.

Going Further

Scenario Analysis with Simulation

Compare simulated futures under different input assumptions, using real electricity-price data and future exogenous variables.

Plan Retail Promotions with Coupled Simulation

Simulate several related products together so each future describes the whole group at once.

Choose the Number of Paths

n_paths controls the number of trajectories generated per series.
  • Use tens of paths for exploration and plotting.
  • Use hundreds or thousands when estimating probabilities or tail outcomes.
  • Increase the count until the decision statistic you care about becomes stable.
  • Response size and local dataframe memory grow linearly with n_paths.
The accepted range is 1 to 10,000 paths. The overall size of a request is also bounded, counting the marginal grid the service builds alongside the paths:
When you do not pass a quantile grid, the quantile count is the model’s native one. For example, 500 series, a horizon of 20, and 500 paths is well inside the limit; raising the horizon to 200 would exceed it.

Reproduce a Simulation

Provide an integer seed when you need a repeatable result: two calls with the same data, arguments, and seed return the same paths. Repeated calls without seed can return different paths. Use a fixed seed in tests, audited analyses, and reproducible reports. The seed must be between -2**63 and 2**64 - 1.

Advanced Usage

Features known across the forecast horizon belong in X_df, using the same layout as forecasting with exogenous variables:
Each future feature must also appear in the historical df, and X_df must contain exactly h rows per series. Changing the future feature values is how you simulate different scenarios — see Scenario Analysis with Simulation for a complete walkthrough.
Declare features unavailable in the future with hist_exog_list:
List categorical columns explicitly with categorical_exog_list. Future-known categorical features also need their future values in X_df:
See categorical features for the complete data requirements.
Date-derived features use the same interface as forecasting:
Learn more in the date features guide.
Pass the ID returned by finetune():
The requested base model must match the base model used for fine-tuning. See reusing fine-tuned models for model lifecycle guidance.
simulate() preserves the input dataframe library:
Distributed dataframes are not currently supported.
You can provide a strictly increasing grid of between 2 and 200 values inside the open interval (0, 1):
A denser grid can represent the marginal forecast distribution in greater detail, but it also increases computation and memory use.
quantiles does not add columns to the returned dataframe. It refines the marginal distribution the paths are drawn from, so the output stays sample_id, TimeGPT, and coupled. Compute any quantiles you need from the paths themselves, or use quantile forecasts when quantile columns are what you are after.
A wide grid counts towards the request size limit: number of series * h * (n_paths + len(quantiles)) may not exceed 10,000,000.
If the payload exceeds the request size limit, pass num_partitions to split the series across several concurrent requests:
The results are stitched back together, so the returned dataframe is the same shape as an unpartitioned call.
num_partitions cannot be combined with multivariate=True, because coupling is computed across the series within a single request. Partitioning would return uncoupled paths, so the call is rejected instead.Each partition is also sent a distinct seed derived from seed, so partitions never share their random draws and the call stays reproducible. A partitioned call still returns different paths than an unpartitioned one for the same seed. Every path is a valid draw; only the specific values differ.

Troubleshooting

The series is too short

Simulation requires enough valid history to estimate forecast uncertainty and to construct complete trajectories. The required length depends on the model, horizon, and exogenous configuration. Add more history or reduce h.

Future exogenous data has the wrong shape

For every future-known feature, provide exactly h values per series in X_df. The ID and timestamps must match the requested future grid.

The response is too large

Reduce n_paths, h, or the number of series. Remember that a long dataframe has one row for every series, path, and future timestamp. If the request is too large instead, see partitioning.

A repeated call changed

Provide the same integer seed and keep all data and arguments identical.
Simulated paths represent plausible model-based futures. They are not guaranteed outcomes, and their quality depends on the data, model, horizon, and assumptions. Validate decision rules with historical backtesting before using them in production.