Occasionally-binding constraints by anticipated shocks
The principle
Keep the model’s standard (perturbation) solution, but whenever the constraint would be violated, add a sequence of anticipated shocks – “fudge” shocks announced over a horizon \(H\) – whose values are chosen so that the constrained variable stays on the admissible side over that horizon. Agents see the announced shocks and react to them, which is what bends the path of the constrained variable; once the constraint is expected to hold of its own accord, no further fudge shocks are needed. This is the approach that underlies modern treatments of the zero lower bound.
RISE finds the fudge shocks on a moving window: at each date it solves for the
shock values that satisfy the constraint over the next \(H\) periods (taking
into account the shocks it announced earlier), then rolls forward. The shocks
can be found by direct inversion or by an optimisation (the routines live in
the m/+obcas/ package).
The shock horizon
How far ahead the anticipated shocks may reach is the shock horizon. The fudge
shock that enforces the constraint is generated automatically for the kink
equation and named FUDGE_FACTOR_<equationIndex>; set the horizon on that
shock specifically rather than on every shock in the model:
exo = get(m, 'exo_list');
fudge_name = exo{startsWith(exo, 'FUDGE_FACTOR_')};
m = set(m, 'solve_shock_horizon', {fudge_name, 8});
A bare scalar applies the horizon to all shocks:
m = set(m, 'solve_shock_horizon', 8); % every shock anticipated 8 periods
which is usually not what you want – it makes the genuine structural shocks anticipated too, changing the experiment.
The constraint is enforced period by period on a rolling window, so a short horizon holds the bound across a much longer binding episode: at each date the algorithm fixes the next \(H\) periods, then rolls forward. You therefore do not need a horizon that spans the whole episode. In practice expectations cease to matter beyond a modest horizon unless the system is hit very hard; an unnecessarily long horizon only enlarges the per-step problem (and can make it harder to solve), so prefer a small \(H\) (a handful of periods) and increase it only if a deep, persistent episode demands it.
Using it
Build the constrained model (see Occasionally-binding constraints), then call solve and the usual
analysis methods – simulate, irf, forecast, filter – which
will inject the fudge shocks as needed:
exo = get(m, 'exo_list');
fudge_name = exo{startsWith(exo, 'FUDGE_FACTOR_')};
m = set(m, 'solve_shock_horizon', {fudge_name, 8});
m = solve(m);
There are two ways to drive the simulation, and the choice is about what question you are asking, not about the constraint machinery.
A controlled scenario. Prescribe the shocks with a simulation plan and turn shock uncertainty off. The path is deterministic and reproducible – the canonical “adverse shock, the bound binds, then liftoff” experiment:
sp = simplan(m, [0, 40], 1, 'shockInit', 'zero');
sp = append(sp, 'E_D', 1, -3); % one adverse demand shock
sim = simulate(m, 'simul_historical_data', sp, ...
'simul_shock_uncertainty', false);
Random draws. Let RISE draw the shocks (the default) to study how the model behaves – for instance how often the bound binds – under its own shock distribution. Seed the generator for reproducibility:
rng(7);
sim = simulate(m, 'simul_periods', 200);
Generalized impulse responses are the meaningful ones here, because the response to a shock depends on whether (and how hard) the constraint binds; a simple IRF from a fixed point can misrepresent the dynamics:
gir = irf(m, 'irf_type', 'girf', 'irf_shock_list', {'E_D'});
The constrained step is solved by RISE’s OBC solver. The default solver
selection works out of the box; for a strongly nonlinear constraint you may set
simul_optimize explicitly to 'kkt', 'jacobian' or 'fmincon'.
Tip
Always ensure there is a feasible path by adjusting potentially-extreme probabilities, e.g.
Estimation
Models with anticipated-shock constraints can be estimated – filter runs
the constrained one-step simulator, so the likelihood is consistent with the
constraint. For dates at which the constraint is known to bind (or not), see
the conditional-forecasting / filtering options that let you flag them and
avoid solving an unnecessary inversion.
A worked example
A complete, runnable example – a small New Keynesian model with a zero lower
bound written as the kink R = max(0, RSTAR) – ships with the test models at
models/dsge/obc/worked_examples/anticipated_shocks_zlb/. Its driver
howto_zlb_anticipated.m solves the model, runs both the controlled-scenario
and random-draw simulations described above, and plots the generalized impulse
responses. A 3-sigma adverse demand shock holds the policy rate at zero for
about four periods before liftoff.