Impulse responses and decompositions

Practicum · Session 03

A worked MATLAB replication with explicit data, timing, and numerical checks.

Estimate the responses

This walkthrough uses the monthly input from Sessions 01 and 02. The replication includes 500 residual-bootstrap draws and saves all of them; it may take longer than a point-estimate-only run.

% Start in the repository root, which contains the shared dispatcher.
projectRoot = pwd;
% Preserve the original search path before adding the package folder.
originalPath = path;
% Restore that path when the walkthrough's cleanup object is cleared.
pathCleanup = onCleanup(@()path(originalPath));
% Keep namespaced checks accessible after run_session restores its session path.
addpath(fullfile(projectRoot,'matlab'));
% Fit the VAR, construct decompositions, and run the specified residual bootstrap.
result = run_session(3);

Keep the array dimensions visible

The IRF array is response variable by shock by horizon. Its first slice is impact. The historical contribution array is date by response variable by shock. Those different conventions are intentional and documented; use their names rather than assuming every three-dimensional object is ordered the same way.

% Verify all five response variables, five shocks, and forty-nine response horizons.
assert(isequal(size(result.unitResponses),[5,5,49]));
% Impact responses must equal the unscaled structural impact matrix.
assert(max(abs(result.unitResponses(:,:,1)-result.B),[],'all')<1e-12);
% The normalized policy-rate impact must equal the stated fifty-basis-point experiment.
assert(abs(result.policy(4,1)-0.5)<1e-12);
% Month twelve is the thirteenth response slice because impact occupies slice one.
disp(result.policy(:,13));

Audit the bootstrap

The replication uses a private random stream, resamples complete residual vectors, re-estimates each pseudo-sample, and re-identifies the policy shock. Its pointwise interval is not the classroom bias-corrected interval.

% There are five response variables, forty-nine horizons, and five hundred draws.
assert(isequal(size(result.bootstrap.responses),[5,49,500]));
% Every draw must implement the same own-rate impact normalization.
assert(max(abs(result.bootstrap.responses(4,1,:)-0.5),[],'all')<1e-12);
% Inspect how many estimated bootstrap systems had roots outside the stable region.
disp(result.bootstrap.unstable);
% Check the exact lower empirical percentile, with no hidden interpolation.
ordered = sort(result.bootstrap.responses,3);
% The central 68 percent lower endpoint is order statistic ceil(.16*500)=80.
assert(max(abs(ordered(:,:,80)-result.bootstrap.lower),[],'all')<1e-12);

A collapsed funds-rate impact band is imposed by normalization. It is not a statistical result saying there was no uncertainty about the original Cholesky impact coefficient.

Reconstruct the observations

The FEVD’s first slice is a one-step forecast variance, while the IRF’s first slice is a zero-horizon response. The last FEVD slice here is horizon 49.

% Every outcome-horizon row must allocate its forecast-error variance across all shocks.
assert(max(abs(sum(result.fevd,2)-1),[],'all')<1e-12);
% Inspect the monetary share of each variable's forty-nine-step forecast error variance.
disp(result.fevd(:,4,49));
% Add the initial-condition/intercept path to every dated shock contribution.
reconstructed = result.historical.baseline+sum(result.historical.contributions,3);
% Compare the reconstruction with modeled observations, not raw index levels.
assert(max(abs(reconstructed-result.model.Y),[],'all')<1e-9);
% Export the checked pointwise-band figure and inference diagnostics for the notes.
tsma.var.publish_session(result,projectRoot);
% Restore the original package path after the walkthrough.
clear pathCleanup;

A failed reconstruction is usually an orientation, timing, or omitted-baseline problem. A successful reconstruction does not validate the economic shock label: it checks an accounting identity under the chosen model.

Worked exercises

Impact and one-step uncertainty

Impact is \Theta_0=B. One period later,

\Theta_1=AB= \begin{pmatrix}.5&0\\.2&.4\end{pmatrix}.

For variable two, shock one contributes 1^2+.2^2=1.04 to the two-step forecast error variance. Shock two contributes 2^2+.4^2=4.16. The total is 5.2, so the shares are 1.04/5.2=.2 and 4.16/5.2=.8.

At forecast date t, the error for y_{t+2} contains B\varepsilon_{t+2}+AB\varepsilon_{t+1}. An innovation dated t is already in the conditioning information and is not part of the forecast error. This is why \Theta_2=A^2B does not enter. The code’s second FEVD slice therefore uses the first two IRF slices, representing economic response horizons zero and one.

Reconstructing a history

The realized path is

y_1=1+.5(2)+1=3,\quad y_2=1+.5(3)-2=.5,\quad y_3=1+.5(.5)+0=1.25.

With the same initial condition and intercept but zero later innovations, the baseline remains a_1=a_2=a_3=2. The summed shock contribution is

d_1=1,\quad d_2=.5(1)-2=-1.5,\quad d_3=.25(1)+.5(-2)+0=-.75.

Adding baseline and shocks gives 3, .5, and 1.25, exactly the observed path. At date three, the initial positive shock still contributes .25 and the negative second shock contributes -1. The zero third innovation has no direct contribution but does not erase the effects of previous shocks.

If only d_t were plotted and labeled as the level of y_t, every observation would be understated by two. Demeaning may simplify a decomposition in some settings, but it cannot justify silently discarding the initial-state path.

Reading a bootstrap interval

The lower endpoint uses index \lceil500(.16)\rceil=80 and the upper endpoint uses index \lceil500(.84)\rceil=420. The algorithm uses these observed order statistics; it does not interpolate between adjacent draws. Another percentile convention would differ slightly in a finite bootstrap sample and should be stated explicitly.

Under the exercise’s artificial independence assumption, the probability both intervals cover is .68^2=.4624, not .68. For an actual VAR, responses at nearby horizons share coefficients and bootstrap innovations, so their coverage events are dependent. Multiplying marginal coverage probabilities is therefore not the correct simultaneous-coverage calculation.

A simultaneous band needs a joint calibration, for example to a suitable maximum statistic over the selected horizon set. Merely changing the plotted label from pointwise to simultaneous cannot provide that calibration. Also distinguish two uncertainties: more bootstrap draws reduce numerical variation in the estimated quantiles, whereas more observed data may reduce statistical uncertainty about the underlying model.

Growth and levels

For growth, the shock-specific two-step variances are

V^g_1=1^2+(-1)^2=2,\qquad V^g_2=1^2+1^2=2.

Each shock therefore has share one half. For the level, cumulate each shock’s growth responses first. The response sequences become (1,0) for shock one and (1,2) for shock two. Hence

V^\ell_1=1^2+0^2=1,\qquad V^\ell_2=1^2+2^2=5.

The level shares are 1/6 and 5/6. Shock one’s second growth response reverses its first level effect, while shock two’s growth responses reinforce one another. The difference is lost if the growth responses are squared before cumulation. Both sets of shares add to one, so an adding-up check alone would not reveal that the wrong outcome had been decomposed.