BeveridgeNelsonDecomposition#

class cultivars.spectral.filters.BeveridgeNelsonDecomposition(result)[source]#

The Beveridge-Nelson decomposition of a fitted ARIMA(p, 1, q).

With \(\Delta y_t = \mu + \psi(L) e_t\) the permanent component is the level the long-horizon forecast settles on once the drift is netted out,

\[\tau_t = \lim_{H \to \infty}\bigl(E_t\, y_{t+H} - H\mu\bigr) = y_t + \sum_{k \ge 1}\bigl(E_t \Delta y_{t+k} - \mu\bigr),\]

and the cycle \(c_t = y_t - \tau_t\) is minus the sum of forecastable future changes. The trend is a random walk with drift \(\mu\) and innovation \(\psi(1) e_t\), so the decomposition is the unique one in which the permanent component carries all the long-run variance. The sum is evaluated exactly in companion form (Morley 2002): with the state \(x_t\) of the demeaned differences and their innovations and companion matrix \(F\),

\[\sum_{k \ge 1} E_t \Delta y_{t+k} - \mu = e_1' F (I - F)^{-1} x_t,\]

which is a linear function of the current state and needs no truncation. In closed form for the simplest cases: an ARIMA(1, 1, 0) with coefficient \(\phi\) has cycle \(-\phi (\Delta y_t - \mu) / (1 - \phi)\), an ARIMA(0, 1, 1) with coefficient \(\theta\) has cycle \(-\theta e_t\), and a pure random walk has none.

Not an estimator: takes the fitted model and reads the decomposition off its parameters and residuals, so every number is conditional on that ARIMA being right.

Variables:

_result – The fitted ARIMA(p, 1, q) result.

Parameters:

result (ARMAResult) – A fitted ARIMA with d = 1, no seasonal part, no exogenous regressors and trend "n" or "c".

Raises:

SpecificationError – If the result is not fitted, or not an ARIMA(p, 1, q) of that form.

Note

The BN cycle is typically small, volatile, and – for an ARIMA(1, 1, 0) with \(\phi > 0\) – perfectly negatively correlated with growth, because it is a fixed multiple of the demeaned current change. Readers used to the smooth HP hump find this counterintuitive; it is the correct answer to “how far is the level from where it is heading”, which is a different question from “what is the smooth part of the level”. The decomposition is one-sided and exact, so no row is provisional, but it inherits the ARIMA’s estimation error in full and the cycle changes with the model order chosen.

See also

  • DecompositionFilterResult – the container returned.

  • ARIMA – the model whose fitted result is the input.

  • HamiltonFilter – the other forecast-based definition of the cycle, model-free.

  • UnobservedComponents – the structural alternative, in which trend and cycle are separate stochastic components with their own variances.

References

Beveridge, S., & Nelson, C. R. (1981). A new approach to decomposition of economic time series into permanent and transitory components. Journal of Monetary Economics, 7(2), 151-174.

Morley, J. C. (2002). A state-space approach to calculating the Beveridge-Nelson decomposition. Economics Letters, 75(1), 123-127.

Example

>>> from cultivars.univariate.box_jenkins import ARIMA
>>> rng = np.random.default_rng(0)
>>> y = np.cumsum(0.2 + rng.standard_normal(300))
>>> res = BeveridgeNelsonDecomposition(ARIMA(y, order=(1, 1, 0)).fit()).compute()
>>> res.trend.shape, res.offset
((299, 1), 1)

With genuine momentum in the differences the cycle is the closed form \(-\phi(\Delta y_t - \mu)/(1 - \phi)\), and much smaller than the HP cycle of the same series:

>>> e = rng.standard_normal(400)
>>> z = np.zeros(400)
>>> for t in range(1, 400):
...     z[t] = 0.5 * z[t - 1] + e[t]
>>> y = np.cumsum(0.2 + z)
>>> fit = ARIMA(y, order=(1, 1, 0)).fit()
>>> bn = BeveridgeNelsonDecomposition(fit).compute()
>>> phi, mu = float(fit.ar_params[0]), float(fit.beta[0])
>>> bool(np.allclose(bn.cycle[:, 0], -phi * (np.diff(y) - mu) / (1 - phi)))
True
>>> hp = HodrickPrescottFilter().filter(y)
>>> bool(bn.cycle.std() < 0.6 * hp.cycle.std())
True

Methods#

compute()

Decompose the levels.