hegy#

cultivars.diagnostics.seasonality.hegy(endog, *, period, trend='c', seasonal=True, lags=None, max_lags=None, method='aic', replications=_HEGY_REPLICATIONS, seed=None)[source]#

HEGY test of a unit root at each seasonal frequency, and at the zero frequency.

Hylleberg, Engle, Granger and Yoo’s regression tests every factor of \(1 - L^s\) in one equation,

\[(1 - L^s)\, y_t = d_t + \pi_0\, x_{0,t-1} + \pi_{s/2}\, x_{s/2,t-1} + \sum_{k = 1}^{s/2 - 1} \bigl(\pi_k^a\, x^a_{k,t-1} + \pi_k^b\, x^b_{k,t-1}\bigr) + \sum_{j = 1}^{p} \phi_j\, (1 - L^s)\, y_{t-j} + e_t,\]

where each \(x\) is the series with every root of \(1 - L^s\) but one filtered out, so that \(\pi_0 = 0\) is a unit root at frequency zero, \(\pi_{s/2} = 0\) one at the Nyquist frequency \(\pi\), and \(\pi_k^a = \pi_k^b = 0\) the complex pair at \(2\pi k / s\). The zero and Nyquist roots are tested by \(t\) in the lower tail, each harmonic pair by \(F\), and two joint \(F\) statistics, every seasonal root and every root, close the family. Under the null of a seasonal random walk none of these has a tabulated law that fits every period and deterministic set, so replications seasonal random walks of the same length are run through the same regression and the p-values and critical values read off them; at \(s = 4\), \(T = 100\) with intercept and dummies the simulated 5% points reproduce Hylleberg et al.’s Table 1 to within Monte Carlo error. The augmentation absorbs serial correlation in the seasonal difference and is chosen by method on a common sample when lags is not given.

Parameters:
  • endog (ArrayLike) – The series, (T,), at least a few full cycles long.

  • period (int) – Seasonal period \(s\), even and at least 2.

  • trend (str) – "n", "c" or "ct".

  • seasonal (bool) – Whether seasonal dummies enter the regression, which is what makes the test robust to a deterministic seasonal pattern under the alternative; True by default, and the record’s trend reads "c+s" when they do.

  • lags (int | None) – Augmentation lags of the seasonal difference, or None to select by method.

  • max_lags (int | None) – Largest augmentation under selection; default Schwert’s \(\lfloor 12 (T / 100)^{1/4} \rfloor\).

  • method (str) – "aic", "bic" or "t-stat" for the selection. BIC under-augments against moving-average errors, with an MA(1) coefficient of 0.5 it picks one or two lags where four are needed and the tests over-reject at 10-15%, so AIC is the default, as for adf().

  • replications (int) – Seasonal random walks behind the p-values, at least 500.

  • seed (int | Generator | None) – Seed or generator for the simulation.

Return type:

SeasonalUnitRootTest

Returns:

The zero-frequency SeasonalUnitRootTest, with the Nyquist \(t\), each harmonic \(F\), the joint seasonal \(F\) and the joint \(F\) as companions, in that order.

Raises:
  • SpecificationError – If the period is odd or below 2, the trend, method, lags, or replication count is unusable, or the series is too short.

  • NumericalError – If a value is not finite or the regression is degenerate.

Note

The simulated null is the pure seasonal random walk with i.i.d. Gaussian innovations and the same deterministic set; the augmentation is fixed at the selected lags in the draws, so the p-values are exact for the regression as run rather than for the selection procedure. Each row rejects in its own tail, which the record’s lower_tail carries, and the table’s notes say how to read the pattern of rejections.

See also

References

Hylleberg, S., Engle, R. F., Granger, C. W. J., & Yoo, B. S. (1990). Seasonal integration and cointegration. Journal of Econometrics, 44(1-2), 215-238.

Beaulieu, J. J., & Miron, J. A. (1993). Seasonal unit roots in aggregate U.S. data. Journal of Econometrics, 55(1-2), 305-328.

Ghysels, E., Lee, H. S., & Noh, J. (1994). Testing for unit roots in seasonal time series: Some theoretical extensions and a Monte Carlo investigation. Journal of Econometrics, 62(2), 415-442.

Example

A seasonal random walk, \(y_t = y_{t-4} + e_t\), has a unit root at every frequency, and no row rejects:

>>> import numpy as np
>>> rng = np.random.default_rng(0)
>>> y = rng.standard_normal(200)
>>> for t in range(4, 200):
...     y[t] = y[t - 4] + rng.standard_normal()
>>> verdict = hegy(y, period=4, replications=500, seed=0)
>>> verdict.reject(), all(not c.reject() for c in verdict.companions)
(False, True)
>>> verdict.lags, verdict.method
(2, 'aic over 0..14')

A random walk with a fixed quarterly pattern keeps only the zero-frequency root:

>>> z = np.cumsum(rng.standard_normal(200)) + np.tile([1.0, -0.5, 2.0, -2.5], 50)
>>> verdict = hegy(z, period=4, replications=500, seed=0)
>>> verdict.reject(), [c.reject() for c in verdict.companions]
(False, [True, True, True, True])