cultivars.multivariate.large_dim.factor_augmented#
The factor-augmented VAR: a few shocks, answered by hundreds of series.
A small VAR cannot say what a policy shock does to the hundreds of series a policymaker actually watches, and a VAR on hundreds of series cannot be estimated. The factor-augmented VAR threads the needle (Bernanke, Boivin and Eliasz, 2005): a wide informational panel is summarized by a handful of principal-component factors, a VAR runs on those factors augmented with the observed variables of interest, and an observation equation maps everything the small system produces back onto every series,
so a shock identified in the \(r + m\) dimensional system has a response on all \(N\) panel series through \(\Lambda\).
The design is composition, on the pattern the functional VAR set: the
factor dynamics are a
VAR
on the augmented block, riding on the result as factors with its
entire reduced-form surface intact. Because that result is a closed
system, the entire structural layer applies to it unchanged –
recursive, sign, narrative, proxy, heteroskedasticity, any of them – and
FactorAugmentedSVAR
translates whatever was identified into responses of all \(N\) panel
series. That translation is rotation-invariant: principal components
recover the factor space only up to rotation, but loadings and factors
rotate together, so panel responses to an identified shock do not depend
on the arbitrary factor basis.
Two commitments shape the surface. First, the estimation subtlety that
is load-bearing for policy analysis is handled the way the original
paper handles it, and its absence is disclosed. Principal components of
the full panel absorb the observed variables’ contemporaneous
influence, which contaminates a recursive identification that orders
those variables last. Declaring the slow series – those that do not
react within the period – triggers the Bernanke-Boivin-Eliasz cleaning,
\(\hat F_t = C_t - \hat b_Y' Y_t\) with \(\hat b_Y\) from the
regression of the full-panel components on the slow-panel components and
\(Y_t\); skipping the declaration puts a warning on the summary
rather than papering over it. Second, nothing structural happens here.
The result is reduced-form – a VAR, loadings and a fit – and every
identifying assumption is made explicitly, afterwards, in
cultivars.multivariate.structural, where it is named and
auditable.
Layout. FAVAR validates the panel through
_validate_wide_panel and the observed block through
validate_exog_matrix from _core, standardizes, extracts with
principal_components, cleans by least squares, and fits the scores
with VAR.
FAVARResult carries the loadings, the scores and the
standardization, and takes the summary from _SummaryMixin. The
factor model without an observed block is
dynamic_factor; the
identification step is
factor_augmented.
References
Bernanke, B. S., Boivin, J., & Eliasz, P. (2005). Measuring the effects of monetary policy: A factor-augmented vector autoregressive (FAVAR) approach. Quarterly Journal of Economics, 120(1), 387-422.
Stock, J. H., & Watson, M. W. (2002). Forecasting using principal components from a large number of predictors. Journal of the American Statistical Association, 97(460), 1167-1179.
Example
A panel whose fast half reacts to the policy shock within the period and whose slow half does not. With the slow series declared, a recursive identification ordering the rate last recovers that structure in the impact responses of the panel:
>>> import numpy as np
>>> from cultivars.multivariate.structural.factor_augmented import FactorAugmentedSVAR
>>> rng = np.random.default_rng(0)
>>> n = 400
>>> factor = np.zeros(n)
>>> for t in range(1, n):
... factor[t] = 0.7 * factor[t - 1] + rng.standard_normal()
>>> shock = rng.standard_normal(n)
>>> policy = 0.5 * factor + shock
>>> lam = rng.uniform(0.5, 1.5, 30)
>>> slow_half = np.outer(factor, lam[:15]) + 0.5 * rng.standard_normal((n, 15))
>>> fast_half = np.outer(factor, lam[15:]) + 0.5 * rng.standard_normal((n, 15))
>>> fast_half += 0.8 * np.outer(shock, rng.uniform(0.5, 1.5, 15))
>>> panel = np.column_stack([slow_half, fast_half])
>>> names = tuple(f"s{i}" for i in range(15)) + tuple(f"q{i}" for i in range(15))
>>> res = FAVAR(
... panel, policy, n_factors=1, order=1, panel_names=names,
... slow=names[:15], observed_names=("rate",),
... ).fit()
>>> res.cleaned, res.factors.names
(True, ('f1', 'rate'))
>>> svar = FactorAugmentedSVAR(res).identify()
>>> svar.shock_names, svar.irf(8).shape
(('f1', 'rate'), (9, 30, 2))
>>> impact = np.abs(svar.impact[:, 1])
>>> bool(impact[15:].mean() > 10 * impact[:15].mean())
True
Classes#
Factor-augmented VAR over a wide informational panel, BBE (2005). |
|
A fitted factor-augmented vector autoregression. |