Decomposes a time series into trend and cycle components using the regression-based filter proposed by Hamilton (2018). The trend is the fitted value from an OLS regression of \(y_{t+h}\) on \((1, y_t, y_{t-1}, \ldots, y_{t-p+1})\), and the cycle is the residual.
Arguments
- x
Numeric vector,
ts,xts, orzooobject.- h
Integer horizon (number of periods ahead). If
NULL(default), auto-detected from the series frequency using Hamilton's rule: annual = 2, quarterly = 8, monthly = 24.- p
Integer number of lags in the regression (default 4).
- boot_iter
Non-negative integer. Number of block-bootstrap iterations for uncertainty quantification (default
0, bootstrap disabled). When> 0, the function adds$trend_lowerand$trend_upper: a 95% normal-approximation band,trend +/- 1.96 * sd(bootstrap trends), centred on the estimated trend. The bootstrap sd is used instead of empirical percentiles because it is smooth and stable at practicalboot_iter. Each bootstrap refit uses the samemstopas the base fit, so largerboot_iterraises cost linearly. See alsoblock_size.- block_size
Positive integer or
"auto". Block length for the moving-block bootstrap (used only whenboot_iter > 0). If"auto"(default), it is set to2 * stats::frequency(x)(two full cycles), bounded above byfloor(length(x) / 3)to keep at least three blocks.
Value
A list of class c("macrofilter", "list") with trend, cycle,
data, and meta (h, p, coefficients, compute_time). When
boot_iter > 0 it also carries trend_lower and trend_upper (95%
normal-approximation band). The bootstrap is a residual bootstrap
conditional on the initial h + p - 1 observations (the regression
lead-in is held fixed); band entries for those lead-in positions are NA.
Details
Hamilton (2018) proposes replacing the HP filter with a simple regression: $$y_{t+h} = \beta_0 + \beta_1 y_t + \beta_2 y_{t-1} + \cdots + \beta_p y_{t-p+1} + v_{t+h}$$ The fitted values \(\hat{y}_{t+h}\) define the trend and the residuals \(\hat{v}_{t+h}\) define the cycle.
The first \(h + p - 1\) observations have no computable trend or cycle
and are filled with NA.
The lag matrix is constructed vectorized via embed() and the
regression is solved with stats::lm.fit() for speed.
When boot_iter > 0, the confidence band comes from a residual bootstrap
that holds the observed lead-in fixed (conditional on initial values) and
resamples residuals only over the valid window. A direct consequence is
that the band is narrow at the start of the valid window – where the
predictors are entirely the (frozen) lead-in, so only the regression
coefficients vary across replicates – and widens forward as the predictors
themselves become resampled quantities. This is the correct behaviour of a
conditional bootstrap, not an artefact.
