--- title: "Replicating Clark & McCracken (2001)" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Replicating Clark & McCracken (2001)} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 4 ) ``` This article reproduces the Phillips-curve forecasting illustration of Clark and McCracken (2001, Section 5) on the bundled `cm2001` dataset. The benchmark forecasts US unemployment from its own lags (univariate AR). The alternative adds lagged inflation (ARX). `enc_new()` tests whether the AR forecasts *encompass* those of the larger ARX model, i.e. whether lagged inflation adds any predictive content beyond what lagged unemployment already contains. ```{r setup, message = FALSE} library(forecastdom) data(cm2001) # CM (2001) sample: 1957:01-1997:08. cm <- subset(cm2001, date >= as.Date("1957-01-01") & date <= as.Date("1997-08-01")) nrow(cm) ``` ## Recursive forecasts Both models are estimated by OLS on an expanding window with `p` lags of each variable. At each origin we form a one-step-ahead forecast, then roll forward by one month. ```{r helper} make_lags <- function(z, p) { n <- length(z) sapply(seq_len(p), function(k) c(rep(NA, k), z[seq_len(n - k)])) } recursive_arx <- function(y, x, p, R) { n <- length(y) YL <- make_lags(y, p) XL <- make_lags(x, p) target <- y[(p + 1):n] YL <- YL[(p + 1):n, , drop = FALSE] XL <- XL[(p + 1):n, , drop = FALSE] P <- length(target) - R e_ar <- e_arx <- numeric(P) for (j in seq_len(P)) { idx <- seq_len(R + j - 1) fit_ar <- lm.fit(cbind(1, YL[idx, ]), target[idx]) fit_arx <- lm.fit(cbind(1, YL[idx, ], XL[idx, ]), target[idx]) pred_ar <- sum(coef(fit_ar) * c(1, YL[R + j, ])) pred_arx <- sum(coef(fit_arx) * c(1, YL[R + j, ], XL[R + j, ])) e_ar[j] <- target[R + j] - pred_ar e_arx[j] <- target[R + j] - pred_arx } list(e_ar = e_ar, e_arx = e_arx) } ``` ## ENC-NEW across lag orders CM (2001) consider lag orders `p = 1, 3, 6, 12`. The initial estimation window is `R = 120` months (10 years); the remaining observations form the out-of-sample period. ```{r encnew-table} R <- 120L rows <- lapply(c(1L, 3L, 6L, 12L), function(p) { fc <- recursive_arx(cm$unrate, cm$infl, p = p, R = R) enc <- enc_new(fc$e_ar, fc$e_arx) msfe1 <- mean(fc$e_ar ^ 2) msfe2 <- mean(fc$e_arx ^ 2) data.frame(p = p, n_oos = length(fc$e_ar), pi_ratio = round(length(fc$e_ar) / R, 2), MSFE_AR = msfe1, MSFE_ARX = msfe2, R2OS_pct = 100 * (1 - msfe2 / msfe1), ENC_NEW = unname(enc$statistic)) }) tab <- do.call(rbind, rows) knitr::kable( tab, digits = 3, row.names = FALSE, format = "html", table.attr = "style='width:auto;'", escape = FALSE, col.names = c("\\(p\\)", "\\(T_{OOS}\\)", "\\(\\pi = P/R\\)", "\\(\\mathrm{MSFE}_{AR}\\)", "\\(\\mathrm{MSFE}_{ARX}\\)", "\\(R^2_{OS}\\) (%)", "ENC-NEW")) ``` At every lag order the ARX model achieves a lower MSFE than the pure AR. That is, knowing past inflation reduces out-of-sample unemployment-forecast error. The ENC-NEW values are large relative to the Clark-McCracken (2001) Table 2 asymptotic 5% critical values, which are tabulated in $k_2$ (number of extra regressors, equal to $p$) and $\pi = P/R$. For $k_2 = 6$ and $\pi \approx 2$ the 5% critical value is about 8.6; for $k_2 = 12$ and $\pi \approx 2$ it is about 12.0. Our statistics exceed these benchmarks decisively at every specification, so the AR forecasts do **not** encompass the ARX: lagged inflation carries real predictive content for unemployment. ## Comparison with Clark-West MSFE-adjusted `enc_new()` answers the encompassing question. `cw_test()` answers the closely related "equal MSFE" question using a $t$-statistic that is approximately $N(0, 1)$ under the null. The two tests typically point the same way, and reporting both is informative. ```{r cw-comparison} rows2 <- lapply(c(1L, 3L, 6L, 12L), function(p) { fc <- recursive_arx(cm$unrate, cm$infl, p = p, R = R) f1 <- cm$unrate[(p + 1 + R):nrow(cm)] - fc$e_ar f2 <- cm$unrate[(p + 1 + R):nrow(cm)] - fc$e_arx cw <- cw_test(fc$e_ar, fc$e_arx, f1, f2) data.frame(p = p, CW_stat = unname(cw$statistic), CW_pvalue = unname(cw$pvalue)) }) knitr::kable( do.call(rbind, rows2), digits = 3, row.names = FALSE, col.names = c("$p$", "CW stat", "CW $p$-value")) ``` CW p-values are far below 5% for every lag order, agreeing with the ENC-NEW decision: the ARX model has significantly lower out-of-sample MSFE than the pure AR. ## References - Clark, T. E. and McCracken, M. W. (2001). Tests of equal forecast accuracy and encompassing for nested models. *Journal of Econometrics*, 105(1), 85-110. - Clark, T. E. and West, K. D. (2007). Approximately normal tests for equal predictive accuracy in nested models. *Journal of Econometrics*, 138(1), 291-311.