This article replicates the
out-of-sample Diebold-Mariano panel of Table 1 in Rossi
(2006), “Are exchange rates really random walks? Some evidence
robust to parameter instability” (Macroeconomic Dynamics,
10(1), 20-38), using dm_test() from
forecastdom and the bundled rossi2006
dataset.
The exercise is the classical Meese-Rogoff (1983) question: at the monthly horizon, can a small linear AR model of exchange-rate returns beat a driftless random walk out of sample?
Five bilateral nominal exchange rates against the U.S. dollar (Canada, France, Germany, Italy, Japan), monthly from March 1973 to December 1998, 310 observations per country.
ggplot(rossi2006, aes(date, log(fx))) +
geom_line(colour = "#47A5C5") +
facet_wrap(~ country, scales = "free_y") +
labs(x = NULL, y = "log(FX)",
title = "Log nominal exchange rates vs. USD") +
theme_minimal()Let \(e_t\) denote the log exchange rate and \(\Delta e_t = e_t - e_{t-1}\) its monthly return. We compare two one-step-ahead forecasts of \(\Delta e_{t+1}\):
Following Rossi, AR(1) and AR(2) share the same usable sample of \(n_{obs} = 307\) returns. The first \(R = \lceil n_{obs}/2 \rceil = 154\) observations fit the initial model, and the remaining \(P = 153\) are evaluated out of sample. Three estimation schemes are considered:
forecast_oos <- function(log_fx, p, scheme = c("split", "recursive", "rolling")) {
scheme <- match.arg(scheme)
T_full <- length(log_fx)
dy <- diff(log_fx)
Y <- dy[3:(T_full - 1)]
L1 <- dy[2:(T_full - 2)]
L2 <- dy[1:(T_full - 3)]
Xm <- if (p == 1) matrix(L1, ncol = 1) else cbind(L1, L2)
n_obs <- length(Y)
R <- as.integer(ceiling(n_obs / 2))
P_oos <- n_obs - R
e_alt <- numeric(P_oos)
e_bench <- numeric(P_oos)
for (j in seq_len(P_oos)) {
idx <- switch(scheme,
split = seq_len(R),
recursive = seq_len(R + j - 1),
rolling = j:(R + j - 1)
)
Z <- cbind(1, Xm[idx, , drop = FALSE])
b <- as.numeric(solve(crossprod(Z), crossprod(Z, Y[idx])))
pred <- as.numeric(c(1, Xm[R + j, ]) %*% b)
e_alt[j] <- Y[R + j] - pred
e_bench[j] <- Y[R + j]
}
list(e_bench = e_bench, e_alt = e_alt, P = P_oos)
}We call dm_test() with correction = FALSE
to match Rossi’s asymptotic \(\chi^2(1)\) reference distribution. Rossi
defines the loss differential as \(f_t =
u^{AR}_t - u^{RW}_t\), so a positive DM statistic means
the random walk has the lower MSFE. To reproduce her sign convention we
pass the AR errors as e1 and the random-walk errors as
e2.
countries <- levels(rossi2006$country)
schemes <- c("split", "recursive", "rolling")
run_panel <- function(p) {
out <- expand.grid(country = countries, scheme = schemes,
stringsAsFactors = FALSE)
out$DM <- NA_real_
out$DM_p <- NA_real_
for (i in seq_len(nrow(out))) {
log_fx <- log(subset(rossi2006, country == out$country[i])$fx)
fc <- forecast_oos(log_fx, p = p, scheme = out$scheme[i])
res <- dm_test(fc$e_alt, fc$e_bench,
alternative = "two.sided", correction = FALSE)
out$DM[i] <- res$statistic
out$DM_p[i] <- res$pvalue
}
out$cell <- sprintf("%.2f (%.2f)", out$DM, out$DM_p)
wide <- reshape(out[, c("country", "scheme", "cell")],
idvar = "scheme", timevar = "country", direction = "wide")
names(wide) <- gsub("^cell\\.", "", names(wide))
wide
}knitr::kable(run_panel(1), row.names = FALSE,
caption = "$DM_T$ statistic ($p$-value), AR(1) vs. RW")| scheme | Canada | France | Germany | Italy | Japan |
|---|---|---|---|---|---|
| split | 0.87 (0.38) | 1.38 (0.17) | -0.89 (0.38) | 0.80 (0.42) | -0.53 (0.60) |
| recursive | 1.16 (0.25) | 2.24 (0.03) | 0.14 (0.89) | 0.77 (0.44) | 0.00 (1.00) |
| rolling | 2.15 (0.03) | 1.77 (0.08) | 0.93 (0.35) | 0.60 (0.55) | 0.39 (0.69) |
knitr::kable(run_panel(2), row.names = FALSE,
caption = "$DM_T$ statistic ($p$-value), AR(2) vs. RW")| scheme | Canada | France | Germany | Italy | Japan |
|---|---|---|---|---|---|
| split | 1.97 (0.05) | 1.91 (0.06) | -0.03 (0.98) | 0.72 (0.47) | -0.64 (0.52) |
| recursive | 1.94 (0.05) | 1.71 (0.09) | 0.37 (0.71) | 0.73 (0.47) | 0.04 (0.97) |
| rolling | 2.29 (0.02) | 1.74 (0.08) | 0.79 (0.43) | 0.75 (0.45) | 0.41 (0.68) |
These cells reproduce the OOS DM panel of Rossi (2006) Table 1 exactly. The statistics are typically positive (the random walk has the lower MSFE), and the two-sided \(p\)-values rarely fall below 5%. This is the classical Meese-Rogoff finding.
log_fx_jp <- log(subset(rossi2006, country == "Japan")$fx)
fc_jp <- forecast_oos(log_fx_jp, p = 1, scheme = "recursive")
dm_test(fc_jp$e_alt, fc_jp$e_bench, alternative = "two.sided", correction = FALSE)
#>
#> ╭────────────────────────────────────────────────────╮
#> │ Diebold-Mariano Test (1995) │
#> ├────────────────────────────────────────────────────┤
#> │ H0: Equal predictive ability │
#> │ H1: Methods have different predictive ability │
#> ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┤
#> │ Test Results: │
#> │ DM statistic: 0.0001 │
#> │ P-value: 0.9999 │
#> ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┤
#> │ Details: │
#> │ Observations (n): 153 │
#> │ Forecast horizon (h): 1 │
#> │ Loss function: SE │
#> │ Reference distribution: N(0,1) │
#> ╰────────────────────────────────────────────────────╯The cumulative squared-error differential, using Rossi’s sign convention so that positive values mean the random walk is winning, shows that the AR’s edge is concentrated in narrow sub-samples rather than spread uniformly across the OOS period.
loss_diff <- fc_jp$e_alt^2 - fc_jp$e_bench^2
oos_dates <- tail(unique(rossi2006$date), fc_jp$P)
ggplot(data.frame(date = oos_dates, cum = cumsum(loss_diff)),
aes(date, cum)) +
geom_hline(yintercept = 0, linetype = "dashed") +
geom_line(colour = "#47A5C5", linewidth = 0.8) +
labs(x = NULL,
y = "Cumulative SE loss (AR(1) - RW)",
title = "Cumulative squared-error loss differential, Japan",
subtitle = "Above zero = RW doing better; below = AR(1) doing better") +
theme_minimal()