2.12. Generic simulations with plans

2.13. Forecasting

RISE extends the algorithms in [Maih, 2010] for conditional forecasting in linear constant-parameter models to nonlinear regime-switching DSGE models. A conditional forecast is a forecast in which one or more model variables are required to follow a user-specified path over some segment of the forecast horizon. RISE offers three complementary ways to build such a forecast, each suited to a different question.

2.13.1. The three routes

Route

Best for

Anticipation / surprises

Kalman filter with conditions as observed data

Imposing observed-side conditions in a linear model

The filter treats conditions as data; the future is unanticipated by construction.

Perfect foresight via perfect_foresight + simplan

Deterministic scenarios with hard endogenous and exogenous conditions

Everything is fully anticipated by definition; no shock uncertainty.

Stochastic forecast via forecast + simplan

Nonlinear / regime-switching models, soft conditions, mixed anticipated / unanticipated paths

User controls whether each condition is announced ahead of time and whether future shocks are drawn.

The first route is the simplest case (linear, no shock uncertainty beyond what the filter draws). The second is the deterministic limit (no shocks at all on the conditioning horizon). The third is the general nonlinear case and the one most users will pick.

2.13.2. Kalman filter with conditions as observed data

For a linear constant-parameter model, the simplest conditional forecast is “treat the conditions as additional observations and let the Kalman filter / smoother return the conditional distribution of everything else.” Feed the conditioning path on the observed variables to filter exactly as you would historical data:

db = struct();

db.Y   = [Y_history; Y_future_conditions];    % conditioned path
db.PAI = [PAI_history; nan(H, 1)];            % free over forecast horizon

m = set(m, 'data', db);

[filtration, ll, ~, rc] = filter(m);

The smoothed-variables / smoothed-shocks structs returned by filter are the conditional forecast for every endogenous variable and every shock. NaN entries in the conditioning path mean “free at this date” and the filter fills them in. This route is exact for linear models, but it can only condition on observed variables and the forecast is unanticipated by construction (the filter sees the conditions one period at a time).

2.13.3. Conditional forecasting via perfect foresight

When the conditioning information includes exogenous variables, or when the underlying problem is deterministic (no shock uncertainty over the conditioning horizon), the cleanest route is perfect foresight. A simulation plan (simplan) carries the conditioning paths in a single object and is then passed to perfect_foresight:

% horizon 0..H, period-1 conditioning is the anchor; build the plan,
% append one endogenous path (say a level path for output Y) and one
% exogenous path (a sequence of demand shocks E_D):
sp = simplan(m, [0, H]);

sp = append(sp, 'Y',   1:H, Y_target_path);     % endogenous condition
sp = append(sp, 'E_D', 1:H, E_D_path);          % exogenous condition

[sims, fval, retcode] = perfect_foresight(m, ...
    'simul_historical_data', sp);

Because perfect foresight solves the full nonlinear system simultaneously across the horizon, both endogenous and exogenous conditions are respected exactly (modulo solver tolerance) and the path is fully anticipated by every agent in the model. There is no concept of “surprise” along the path. The OBC chapters (Occasionally-binding constraints by anticipated shocks, Occasionally-binding constraints by regime switching) and the Deterministic and quasi-deterministic solutions chapter use this route as the workhorse for scenario analysis with hard bounds.

2.13.4. Conditional forecasting in a stochastic context

For a stochastic forecast that still respects user-supplied conditions, the forecast entry point combines the solved model’s law of motion with a simulation plan:

sp = simplan(m, [0, H]);

sp = append(sp, 'Y', 1:k, Y_conditions);

fkst = forecast(m, ...
    'simul_historical_data', sp, ...
    'forecast_nsteps',       H, ...
    'forecast_cond_endo_vars', {'Y'},  ...   % which conditions are hard
    'forecast_shock_uncertainty', true);     % draw shocks past period k

This route is the most general – it can condition on endogenous or exogenous variables, can mix hard and soft conditions (soft conditions are entered with the prefix variables lower_<name> / upper_<name>), can mix announced and unannounced paths, and generates draws of the shocks past the conditioning horizon. Two knobs matter most:

  • Anticipated vs unanticipated. When the conditioning path is set up on the simulation plan with simplan.anticipate(true) (or the per-condition equivalent), agents in the model see the future conditions and re-optimise against them; with simplan.anticipate(false) the future condition is enforced only when the model gets there (and the prior history is built without knowledge of it).

  • Surprises vs no surprises. forecast_shock_uncertainty=true draws structural shocks past the conditioning window, so the expected forecast is the model’s stochastic response to the imposed conditions; forecast_shock_uncertainty=false zeroes future shocks and returns the deterministic continuation.

For nonlinear / regime-switching models this is the right route by default. The fan-chart machinery (fanchart, plot_fanchart; see Plotting tools) consumes the multi-page output of forecast directly.

2.13.5. Choosing among the three

A useful decision shortcut:

  • Conditions on observed variables only, linear model, no shock uncertainty beyond data noise – use the Kalman route.

  • Hard deterministic scenario, possibly with OBC, possibly with exogenous conditions – use perfect_foresight + simplan.

  • Anything else (nonlinear, regime-switching, mix of anticipated and unanticipated conditions, soft conditions, fan charts) – use forecast + simplan.

Relative-entropy tilting (see Conditional forecasting using relative entropy) is a fourth, distinct mechanism that reweights an existing forecast ensemble to satisfy a moment condition. It does not produce a new forecast path on its own; it modifies the weights on the paths you already have. Use it when you have an unconditional ensemble (e.g. from forecast(... 'forecast_shock_uncertainty', true)) and want to constrain its moments rather than its paths.