5. Forecasting and simulation

Once a model is solved, the two main entry points for forward-looking analysis are simulate and forecast. Both return structs of ts time-series objects keyed by endogenous-variable name and respect the model’s regime structure when there is one. For deterministic scenarios, perfect_foresight is the third entry point.

All three consume the same simulation plan – the universal mechanism for choosing initial conditions, pinning endogenous variables, activating shocks, announcing anticipated shocks, or imposing algebraic constraints. This chapter is the reference for the three consumers. It assumes you have read Simulation plans; the constructions used below are all from that chapter.

5.1. simulate

simulate propagates the perturbation solution forward through a plan. With no plan it draws an unconditional path:

sim = simulate(m, simul_periods = 200);
plot(sim.PAI, sim.Y);

With a plan, every choice on that path is the plan’s: pinned shocks stay pinned, NaN shocks are backed out, pinned endogenous variables hit their conditioning values, anticipated shocks act through the perturbation solution’s anticipation horizon:

sp  = simplan(m, [rq(2020,1), rq(2030,4)], 1, 'shockInit', 'zero');
sp  = append(sp, 'EETA', rq(2020,2), 2.0);
sim = simulate(m, simul_historical_data = sp);

For a single-regime constant-parameter model, sim is a struct of ts keyed by endogenous-variable name. For a regime-switching model the output structure depends on simul_to_time_series (see below).

5.1.1. Common options

Option

Effect

simul_periods

Number of periods to simulate. Default 100.

simul_burn

Burn-in periods discarded from the head of the returned series. Default 100.

simul_seed

Seed forwarded to MATLAB’s RNG before drawing. [] keeps the current state.

simul_shock_uncertainty

When true (default), draws structural shocks from their declared distributions. When false, sets every shock to its mean and returns the deterministic continuation.

simul_historical_data

Initial conditions and / or a conditioning path. Accepts a struct of ts or a simulation plan.

simul_history_end_date

Last date of the history segment (the point from which the simulation continues forward). Inferred from the plan when one is passed.

simul_regime

Pin the regime path. Pass an integer for a constant regime, a vector for a fixed path, or a function handle for a state-dependent switch rule.

simul_anticipate_zero

When true, agents see only the contemporaneous shock realization – future draws stay private. Default false.

simul_to_time_series

Output shape – see below. Default true.

5.1.2. Output shape

By default (simul_to_time_series = true) the output is a struct of ts keyed by variable name. For switching models with multiple parameterizations or solution branches, the underlying numeric representation has extra dimensions for parameterization and regime path; the ts object carries them in its pages.

When you need the raw numeric arrays – for instance to feed into a hand-rolled diagnostic – set simul_to_time_series = false:

[db, info] = simulate(m, ...
    simul_periods        = 200, ...
    simul_to_time_series = false);

db is then a numeric matrix and info carries the layout information needed to reconstruct the ts if you ever want it back.

Note

Even with simul_to_time_series = true, regime-switching simulations can in some configurations return a numeric matrix rather than a ts struct (an artefact of the underlying solution shape). Confirm with isstruct(sim) before reaching for sim.varname; if you got a matrix, the columns are the variables in the order returned by get(m, 'endo_list').

5.1.3. Returning the regime path

simulate returns the realised composite-regime sequence as its second output:

[sim, states] = simulate(m, simul_periods = 200);

states is a vector of integer regime IDs over the simulation horizon. Plot it to see when each regime was active.

5.2. Theoretical and sample moments

For linear solved models the unconditional first and second moments are available in closed form via the Lyapunov equation, returned by theoretical_autocovariances and the theoretical_autocorrelations wrapper:

[Acov, info, retcode] = theoretical_autocovariances(m, 10);
Acorr                 = theoretical_autocorrelations(m, autocorr_ar = 10);

For higher-order or regime-switching models, simulate a long path and compute sample moments from the ts (mean, var, cov, corrcoef are overloaded on ts). The two should agree at order 1 up to Monte Carlo error.

The variance-decomposition routine is documented in the legacy chapter Master stoch simul and remains canonical for the modern toolbox.

5.3. Pruned simulations (higher order)

RISE solves and simulates regime-switching DSGE models up to fifth-order perturbation. At order \(\geq 2\) a straight perturbation simulation is not guaranteed to be bounded: the policy is a polynomial in the state, and iterating it forward lets the higher-order terms feed on themselves, so an unlucky shock sequence can send the state off to inf. Pruning (Andreasen, Fernández-Villaverde and Rubio-Ramírez, 2018, generalized here to regime switching) removes those spurious higher-order terms at simulation time without changing the policy function, keeping the path bounded and the implied moments well defined.

Pruning is controlled by the simul_pruned option, honored by simulate, irf, forecast and any path that iterates the solution forward:

sim = simulate(m, simul_periods = 200, simul_pruned = true);

simul_pruned accepts:

  • false (default) — no pruning;

  • true — prune with the built-in automatic scheme (one_step_pruning_automatic);

  • the name of a pruning routine ('one_step_pruning' or 'one_step_pruning_automatic'), or a function handle, to select a specific scheme.

Pruning applies only at order \(\geq 2\) (at order 1 the recursion is already linear and bounded) and is implemented up to order 5.

Note

Pruning changes the object you simulate: the pruned recursion is a stabilized companion of the policy, not the policy itself. It is the right tool for moments and long simulations at high order, but it is a modeling choice — an order-\(\geq 2\) path that explodes without pruning is often telling you that perturbation is being pushed outside its region of validity, where a global method (Taylor projection, Extending RISE through paradigms) may be the better answer. See Solution accuracy for how pruning interacts with the accuracy measures.

5.4. forecast

forecast produces forward paths from the end of history, with or without conditions and with or without shock uncertainty. The unconditional case is the trivial one:

fkst = forecast(m, ...
    forecast_nsteps            = 12, ...
    forecast_shock_uncertainty = true);

The conditional case is where forecast earns its keep. The conditioning is supplied through a simulation plan passed in simul_historical_data:

sp = simplan(m, [rq(2020,1), rq(2030,4)], 1, 'shockInit', 'zero');
sp = append(sp, 'ER',  cond_dates, NaN);            % activate
sp = append(sp, 'PAI', cond_dates, PAI_target);     % condition

fkst = forecast(m, ...
    simul_historical_data      = sp, ...
    forecast_nsteps            = numel(cond_dates) + 4, ...
    forecast_shock_uncertainty = true);

The plan alone carries the conditioning – the conditioned variables and the freed shocks are read from it, so no separate list of conditioned variables is needed.

Two knobs matter at the consumer side:

  • Anticipated vs unanticipated – per-condition, encoded in the plan’s pages. Page 1 is contemporaneous (unanticipated); page k is announced \(k-1\) periods ahead. The perturbation solution honours the announcement horizon as long as anticipationHorizon covered it at plan construction.

  • Surprises vs no surprises past the conditioning windowforecast_shock_uncertainty. true draws structural shocks past the window; false zeroes them and returns the deterministic continuation.

5.4.1. Hard and soft conditions

A hard condition pins a variable to a specific value at a specific date. The identification rule (see Simulation plans) determines whether a plan is solvable.

A soft condition restricts a variable to a band [lo hi] appended to the plan (see Simulation plans for the band syntax and free_shocks). Because the conditioned variable is moved through the freed shock(s), the model assigns it a predictive distribution at the conditioning date – a normal \(N(a,\sigma^2)\) whose mean \(a\) is the deterministic forecast and whose standard deviation \(\sigma\) is the freed shock’s impulse response. The band truncates that distribution. The simul_soft_conditioning option chooses what is returned:

  • 'density' (the default once a band is present) runs simul_nsim plans – each a draw from the truncated distribution, pinned and solved as a hard condition – and returns the propagated ensemble. fanchart then collapses it (below).

  • 'mean' returns a single path, the conditional mean (the mean of that ensemble).

Where the band sits relative to the forecast \(a\) is what matters. A band that straddles \(a\) is mildly truncated and roughly symmetric. An off-center band clips one tail, so the distribution is asymmetric and its mean, its median and the hard pin at the midpoint are three different numbers. A one-sided band – a floor [lo NaN] – gives a strongly skewed, zero-lower-bound-style pile-up at the bound. A hard pin discards this; the soft condition keeps the model’s view of where in the band the variable is likely to sit:

sp = simplan(m, [rq(2020,1), rq(2023,1)], 1, 'shockInit', 'zero');
sp = free_shocks(sp, 'EPS_D', rq(2020,2));        % free the channel
sp = append(sp, {'Y', rq(2020,2), [0.10 0.70]});  % an off-center band

fkst = forecast(m, simul_historical_data = sp, ...
                   simul_soft_conditioning = 'density', ...
                   simul_nsim = 2000);

Setting simul_soft_conditioning = 'mean' instead returns the single conditional-mean path for the same plan.

5.4.2. The option surface

Option

Effect

forecast_nsteps

Number of forecasting steps (default 12).

forecast_start_date

Date when the forecasts start (end of history + 1).

simul_soft_conditioning

How a band [lo hi] condition is consumed: '' (the default – the plan decides, and with no band the problem is hard), 'density' (a propagated distribution / fan), or 'mean' (the conditional-mean path). See Hard and soft conditions.

simul_nsim

Number of draws/plans in 'density' soft conditioning (default 100; ignored otherwise).

forecast_shock_uncertainty

Draw structural shocks over the forecast horizon. Default false.

forecast_to_time_series

When true (default), output is a struct of ts; otherwise a numeric matrix with reconstruction info.

simul_historical_data

The history-plus-conditioning database. May be a ts struct, a struct of ts, or a simulation plan.

simul_history_end_date

Last date of the history segment.

5.5. perfect_foresight

perfect_foresight solves the full nonlinear model as a boundary-value problem over the plan’s horizon. The plan supplies the boundary data; the solver returns a deterministic path consistent with every condition in the plan and with the model equations at every period, without the perturbation approximation:

pfs = perfect_foresight(m, simul_historical_data = sp);

Because perfect_foresight and simulate consume the same plan, a side-by-side comparison is the canonical check on the approximation error of the perturbation solution: when the two outputs agree, the deviation is small enough that the nonlinearity does not matter for the scenario; when they diverge, the nonlinear path is the accurate one and the perturbation path is the local approximation.

Occasionally-binding constraints are honoured exactly by perfect_foresight; see Occasionally-binding constraints for the deterministic-OBC patterns.

5.6. Fan charts

A forecast that carries multiple draws – the 'density' soft conditioning above, or a stochastic forecast with forecast_shock_uncertainty = true – is collapsed into central tendency and probability bands by fanchart and plot_fanchart:

fkst = forecast(m, simul_historical_data = sp, ...
                   simul_soft_conditioning = 'density', ...
                   simul_nsim = 1000);

out = fanchart(fkst.PAI, [30 50 68 90]);
plot_fanchart(out, [244 122 66]/255);

The percentages are the central probability mass enclosed by each band. The plotting tools live in the legacy chapter Plotting tools and apply unchanged to the modern toolbox.

5.7. Relative-entropy tilting

Relative-entropy tilting is a fourth mechanism, distinct from the plan-based routes above: it does not produce a new forecast path; it reweights an existing forecast ensemble (e.g. the output of forecast(..., forecast_shock_uncertainty = true)) so that its moments satisfy a user-specified constraint.

The algorithm and its option surface are documented in the legacy chapter Conditional forecasting Using Relative Entropy. Use it when you already have an unconditional ensemble and want to constrain its moments (e.g. “the mean inflation forecast over the next 8 quarters equals 2%”) rather than its paths.

5.8. Smoother-based conditional forecasts

For linear models with conditions on observed variables, the Kalman smoother imputes the conditioning path directly from the data, without a simulation plan – treat the conditioning values as observations on the forecast horizon, mark every other observable as NaN for those dates, and run filter. The output is the smoothed path for every endogenous variable.

This route is based on least squares and inherits its tradeoffs. It is documented in Filtering; use it when the conditions are observed-side and the model is linear. For anything else – hard endogenous constraints on unobserved states, nonlinear solutions, regime switching, anticipated shocks, soft conditions – use a simulation plan.

5.9. See also

  • Simulation plans – the construction reference (this chapter is its consumer side).

  • Filtering – the smoother-based alternative for linear observed-side conditioning.

  • Deterministic and quasi-deterministic solutions – the deterministic context in full.

  • Occasionally-binding constraints – scenarios with OBC.

  • Solution accuracy – how pruning interacts with the Euler-error accuracy measures.

  • Legacy Master stoch simul – variance decomposition and the impulse-response surface (irf_type, irf_periods, irf_shock_sign, generalized IRFs, anticipation horizons).

  • Legacy Resimulation and counterfactuals – the +resim_bridge surface for Viterbi / FFBS regime histories, counterfactual shock kills, Shapley shock decomposition.

  • Legacy Plotting toolsquick_irfs, fanchart, plot_fanchart, and the multi-panel layouts.