Python: The Hodrick-Prescott filter or HP filter

This post shows how to extract trend and cyclical components from a univariate time series using the Hodrick-Prescott (HP) filter. A more realistic version is the one-sided HP filter since it uses only the information available at time t, not the full sample.


The Hodrick-Prescott Filter



The Hodrick-Prescott filter models a time series (\(y_t\)) as \[\begin{align} y_t = \tau_t + c_t \end{align}\] where \(\tau_t\) and \(c_t\) are trend and cyclical components respectively.

To decompose in this way, we solve the following minimization problem. \[\begin{align} \min_{\tau} \left( \sum_{t=1}^{T} (y_t - \tau_t)^2 + \lambda \sum_{t=2}^{T-1} (\Delta \tau_{t+1} - \Delta \tau_t)^2 \right) \end{align}\] \(\lambda\) is typically recommended as 100, 1600, and 14400 for annual, quarterly, and monthly frequency, respectively.


HP filter using statsmodels


statsmodels library provides a function for the HP filter. I make the one-sided HP filter which uses only the information available at time t, not the full sample. The following Python Jupyter notebook code shows the difference between the conventional and one-sided HP filter.

import statsmodels.api as sm
import yfinance as yf
import matplotlib.pyplot as plt
plt.rcParams.update({'font.size'14})
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"
 
#------------------------------------
# monthly stock data: AAPL
#------------------------------------
yts = yf.download('AAPL','2011-01-01','2022-12-31'
                  interval='1mo')['Adj Close']
date = yts.index.to_numpy()
y    = yts.to_numpy()
 
#====================================
# HP filter using statsmodels
#====================================
lamb = 14400
tr = sm.tsa.filters.hpfilter(y, lamb)[1]
cy = y - tr
 
#====================================
# One-sided HP filter
#====================================
def one_sided_HP_filter(y, lamb=1600):
    sn = 2; cy_os = y*0
    tr_os =  y*0; tr_os[:sn] = y[:sn]; 
    for i in range(sn,len(y)):
        tr_os[i] = sm.tsa.filters.hpfilter(y[:i+1], lamb)[1][-1]
        cy_os[i] = y[i] - tr_os[i]
    return tr_os
 
tr_os = one_sided_HP_filter(y, lamb)
cy_os = y-tr_os
 
#------------------------------------
# Graph
#------------------------------------
plt.figure(figsize=(9,5))
plt.plot(date, y,linewidth=2, color='black', label='Data')
plt.plot(date, tr,linewidth=4
         color='dodgerblue', label='Trend')
plt.plot(date, tr_os, linewidth=4
         color='tomato', label='Trend-one-sided')
plt.plot(date, cy, linewidth=4
         color='dodgerblue', label='Cycle')
plt.plot(date, cy_os, linewidth=4
         color='tomato', label='Cycle-one-sided')
plt.axhline(y=0, color='gray', linestyle='-')
plt.legend()
plt.show()
 
cs


The outputs of two HP filters coincide at the end of the full sample, as shown in the following figure.

Python: The Hodrick-Prescott Filter



No comments:

Post a Comment

Tentative Topics (Keeping Track to Avoid Forgetting)

Segmented Nelson-Siegel model
Shifting Endpoints Nelson-Siegel model
Nadaraya-Watson estimator
Locally weighted scatterplot smoothing (LOWESS)
Time-Varying Parameter Vector Autoregressions (TVP-VAR)
Time-varying or Dynamic Copula
Bayesian VAR
Adrian-Crump-Moench (ACM) term premium model
GARCH-EVT-Copula approach