The package holds a dozen estimators and tests. Each vignette
documents one of them; this one is the map. It runs a predictive
regression from the plain ivx() fit through the diagnostics
that decide which extension applies, and follows every branch with the
kms (monthly S&P 500 excess returns, Kostakis et
al. 2015) and ylpc (quarterly US housing returns, Yang et
al. 2020) data.
ivx(y ~ x, data) KMS Wald test, the baseline
│
├─ residuals serially correlated? ac_test() → ivx_ar()
├─ residuals heteroskedastic? ac_test(res^2) → ivx(robust = TRUE)
├─ |delta| near 1 and x near a unit root, short sample, one-sided test?
│ delta(), hlt_test → ivx_boot(), ivx_ra()
├─ weak signal, power a concern? → ivx(lag_y = TRUE)
├─ K-period cumulative return? → ivx(horizon = K), ivx_ra(horizon = K)
├─ several responses, cross-equation restrictions? → ivx_sys()
├─ tail or median predictability? → ivx_qr(), ivx_qr_boot()
├─ predictability confined to episodes? → ivx_episodic(), rolling ivx()
└─ single predictor, want a non-IVX cross-check? → hlt_test(), cy_test(), arm(), el_test(), ivx_iv()
The branches are not exclusive: the worked example below ends up combining three of them.
m <- ivx(Ret ~ DP + TBL, data = kms)
summary(m)
#>
#> Call:
#> ivx(formula = Ret ~ DP + TBL, data = kms, horizon = 1)
#>
#> Coefficients:
#> Estimate Std. Error t value Wald Ind Pr(> chi)
#> DP 0.006145 0.004557 1.349 1.819 0.177
#> TBL -0.080717 0.057701 -1.399 1.957 0.162
#>
#> Joint Wald statistic: 3.644 on 2 DF, p-value 0.1617
#> Multiple R-squared: 0.004968, Adjusted R-squared: 0.003036The IVX Wald test is valid for any degree of persistence in \(x_t\), so the first question is not “is \(x_t\) persistent?” but “is anything else wrong with the errors, and is the sample short enough for finite-sample distortions to matter?”. Three checks answer it.
Persistence and endogeneity. The lag-1 autocorrelation of each predictor and the correlation \(\delta\) between the return innovation and the predictor innovation:
sapply(kms[c("DP", "TBL")], function(x) cor(head(x, -1), tail(x, -1)))
#> DP TBL
#> 0.9923205 0.9926221
delta(m)
#> [1] -0.9755750 -0.0610432Both predictors are near unit roots. DP is also strongly
endogenous, \(\delta \approx -0.98\),
the combination under which the asymptotic test over-rejects in samples
of a few hundred observations (Hosseinkouchack & Demetrescu 2021;
Demetrescu et al. 2023). TBL is persistent but nearly
exogenous, so its asymptotic p-value can be taken at face value.
hlt_test() classifies persistence formally through the ADF
normalised-bias statistic (see its adf and
test components) if a rule is preferred to eyeballing.
Serial correlation in the errors.
ac_test() on the fit reports four tests at lags 1 to
lag_max. The Wald column is the
regression-based test of Yang et al. (2020), the one
ivx_ar() reports as Wald_AR; the Box tests are
portmanteau tests on the residual autocorrelations.
ac_test(m, 4)
#> Lag Wald LjungBox BoxPierce BreuschGodfrey
#> 1 0.8348 8.63*** 8.605*** 9.497***
#> 2 2.871 8.764** 8.739** 9.836***
#> 3 4.638 17.23*** 17.17*** 17.49***
#> 4 7.181 18.66*** 18.59*** 20.48***The Box and Breusch–Godfrey statistics are significant, the Wald test
is not. The first-order autocorrelation of the residuals is 0.092: the
mild low-order dependence typical of monthly returns, which is not what
ivx_ar() is for. Compare the housing data:
mh <- ivx(hpi ~ log(res) + cpi, data = ylpc)
ac_test(mh, 4)
#> Lag Wald LjungBox BoxPierce BreuschGodfrey
#> 1 31.15*** 65.89*** 64.76*** 65.28***
#> 2 38.57*** 100.4*** 98.51*** 66.64***
#> 3 66.62*** 160.5*** 156.8*** 88.84***
#> 4 133.6*** 226*** 220.1*** 93.28***Every statistic is an order of magnitude larger and the Wald test
rejects at lag 1. That is the ivx_ar() case (Step 2).
Heteroskedasticity. The same portmanteau tests on the squared residuals are the McLeod–Li test for ARCH effects:
ac_test(residuals(m)^2, 4)
#> Lag Wald LjungBox BoxPierce
#> 1 1.407 58.23*** 58.06***
#> 2 13.22*** 110.5*** 110.1***
#> 3 23.1*** 205.5*** 204.7***
#> 4 23.23*** 240*** 238.9***Strong volatility clustering, as expected for returns. The Eicker–White standard errors of Step 3 handle it.
robust = TRUEDemetrescu, Georgiev, Rodrigues & Taylor (2023) show the IVX statistics keep their limits under conditional and unconditional heteroskedasticity if the homoskedastic \(\hat\sigma^2_u \sum z z'\) is replaced by the Eicker–White \(\sum z_{t-1} z_{t-1}' \hat u_t^2\). There is no cost when the errors are homoskedastic, so for return data this is the sensible default.
mr <- ivx(Ret ~ DP + TBL, data = kms, robust = TRUE)
coef(summary(mr))
#> Estimate Std. Error t value Wald Ind Pr(> chi)
#> DP 0.006145163 0.004792361 1.282283 1.644250 0.1997434
#> TBL -0.080716672 0.057249850 -1.409902 1.987823 0.1585687robust = TRUE is defined for horizon = 1;
ivx_ra() uses heteroskedasticity-robust standard errors at
every horizon.
ivx_boot() and
ivx_ra()For a strongly endogenous, near-unit-root predictor such as
DP, the asymptotic test is oversized in finite samples,
more so one-sided. Two remedies, in order of preference:
vignette("robust-inference")).mb <- ivx_boot(m, B = 499, type = "rwb", seed = 1)
mb
#>
#> Call:
#> ivx(formula = Ret ~ DP + TBL, data = kms, horizon = 1)
#>
#> Residual wild bootstrap, B = 499
#>
#> Coefficients (bootstrap p-values):
#> Estimate t value Wald Ind Pr(> chi) Pr(t < 0) Pr(t > 0)
#> DP 0.006145 1.349 1.819 0.3768 0.6693 0.331
#> TBL -0.080717 -1.399 1.957 0.2084 0.1002 0.900
#>
#> Joint Wald statistic: 3.644, bootstrap p-value 0.3186ivx_ra()
(Demetrescu & Rodrigues 2022) augments the regression with the
residuals of the predictors’ autoregression, which removes the
endogeneity-driven bias before instrumenting. Its standard errors are
robust by construction.mra <- ivx_ra(Ret ~ DP + TBL, data = kms)
coef(summary(mra))
#> Estimate Std. Error t value Wald Ind Pr(> chi)
#> DP -0.001934204 0.005010361 -0.3860409 0.1490275 0.6994664
#> TBL -0.053983764 0.055313355 -0.9759626 0.9525030 0.3290830The four p-values for the two-sided test of DP line up
as the theory predicts, with the asymptotic homoskedastic one the most
optimistic:
p_dp <- c(
asymptotic = coef(summary(m))["DP", "Pr(> chi)"],
eicker_white = coef(summary(mr))["DP", "Pr(> chi)"],
rwb_bootstrap = mb$p.value$Wald_Ind[["DP"]],
residual_augmented = coef(summary(mra))["DP", "Pr(> chi)"]
)
round(p_dp, 3)
#> asymptotic eicker_white rwb_bootstrap residual_augmented
#> 0.177 0.200 0.377 0.699Use cores > 1 for the bootstrap with \(B\) in the thousands; use
ivx_boot(type = "frwb") if the predictor’s autoregression
is unstable (very short samples, structural breaks in the
predictors).
lag_y = TRUEThe IVX instrument is less persistent than \(x_t\), so the test gives up local power against OLS exactly when \(x_t\) is near-integrated and endogenous. Demetrescu (2014) recovers part of it by adding \(y_{t-1}\) to the regression, instrumented by itself. This is a modelling choice rather than a diagnostic outcome: it costs nothing asymptotically when the extra term is irrelevant and can raise power substantially when the signal is weak.
coef(summary(ivx(Ret ~ DP + TBL, data = kms, lag_y = TRUE)))
#> Estimate Std. Error t value Wald Ind Pr(> chi)
#> DP 0.007247938 0.004665519 1.553512 2.413398 0.120300968
#> TBL -0.073288383 0.057667741 -1.270873 1.615119 0.203773782
#> y_lag 0.091950133 0.031084801 2.958042 8.750010 0.003096004The joint Wald statistic still tests only the predictors.
The steps above fix the error structure. The remaining branches depend on what is being tested.
Long horizons. For a \(K\)-period cumulative return,
horizon = K gives the Kostakis et al. (2023) statistic,
which handles the overlap of the sums through the instrument.
ivx_ra(horizon = K) is the transformed-regression
alternative of Demetrescu, Rodrigues & Taylor (2023), which avoids
HAC estimation altogether. Report both when the horizon is long relative
to the sample: they use different information and the overlap (\(K - 1\) observations per statistic) is
where long-horizon tests misbehave.
c(kms = ivx(Ret ~ DP + TBL, data = kms, horizon = 12)$Wald_Joint,
drt = ivx_ra(Ret ~ DP + TBL, data = kms, horizon = 12)$Wald_Joint)
#> kms drt
#> 3.998219 1.200306Several responses. ivx_sys()
(Magdalinos 2022) estimates the system jointly and returns the full
covariance of \(\mathrm{vec}(A)\), so
restrictions across equations, e.g. “does DP predict the
return and the payout ratio equally?”, are Wald tests on
vcov() (vignette("ivx-sys")).
Quantiles. If the question is about the tails (“does
DP predict large losses?”) rather than the mean,
ivx_qr() (Lee 2016) runs the IVX test in a quantile
regression; ivx_qr_boot() gives block-bootstrap intervals
that are robust to the conditional heteroskedasticity found in Step 1
(vignette("ivx-qr")).
q <- ivx_qr(Ret ~ DP + TBL, data = kms, tau = c(0.1, 0.5, 0.9))
sapply(q, function(fit) c(Wald_Joint = fit$Wald_Joint, p = 1 - pchisq(fit$Wald_Joint, 2)))
#> 0.1 0.5 0.9
#> Wald_Joint 6.66613413 7.54143943 3.458798e+01
#> p 0.03568349 0.02303548 3.085432e-08Episodes. A full-sample rejection can be driven by a
short pocket of predictability, and a full-sample non-rejection can hide
one. ivx_episodic() computes the IVX statistic over
rolling, forward or backward windows with the full-sample instrument and
bootstraps the supremum of the sequence
(vignette("ivx-episodic")); a plain rolling
ivx() with Bonferroni critical values is the
bubble-detection variant of vignette("rolling-ivx").
ivx_episodic(ivx(Ret ~ DP, data = kms), scheme = "rolling", window = 0.2, B = 199, seed = 1)
#>
#> Call:
#> ivx(formula = Ret ~ DP, data = kms, horizon = 1)
#>
#> Subsample IVX tests, rolling scheme (window = 0.2), 827 windows
#> Fixed regressor wild bootstrap, B = 199
#>
#> statistic bootstrap p
#> sup t (H1: beta > 0) 2.921 0.1508
#> inf t (H1: beta < 0) -0.4166 1.0000
#> sup t^2 (H1: beta != 0) 8.533 0.2714With a single predictor, the benchmarks in
vignette("benchmarks") answer the same question from a
different direction and are cheap to run. The hybrid test of Harvey,
Leybourne & Taylor (2021) is the most useful companion: it
classifies the predictor’s persistence, picks the test accordingly and
reports the intermediate quantities.
hlt_test(Ret ~ DP, data = kms)
#>
#> Call:
#> hlt_test(formula = Ret ~ DP, data = kms)
#>
#> Hybrid predictability test (Harvey, Leybourne & Taylor, 2021)
#>
#> Selected test: T~_con (quasi-GLS-demeaned t, conservative critical value)
#> statistic = 1.298, 5% critical value = 1.944 (alternative: beta > 0): do not reject
#> slope = 0.006172, t = 1.63, t (quasi-GLS) = 1.298, ADF = -5.97 (p = 21, cutoff -128.6), rho_xy = -0.9485
cy_test(Ret ~ DP, data = kms)
#>
#> Call:
#> cy_test(formula = Ret ~ DP, data = kms)
#>
#> Bonferroni Q-test (Campbell & Yogo, 2006)
#>
#> delta = -0.972, DF-GLS = -1.468 (p = 2), CI for c at levels (0.055, 0.082): [-9.319, 1.045], rho: [0.991, 1.001]
#> OLS slope = 0.006193; Q-estimates at the ends of the rho interval: 0.009076, 0.0004903
#> 90% Bonferroni confidence interval for beta: [-0.000973, 0.01054]
#> 5% one-sided Q-tests: H1 beta > 0 do not reject H0; H1 beta < 0 do not reject H0cy_test() (Campbell & Yogo 2006) gives a Bonferroni
confidence interval for \(\beta\) that
is exact for a near-unit-root predictor but conservative otherwise;
arm() and el_test() are the reduced-bias OLS
and empirical likelihood alternatives; ivx_iv() (Breitung
& Demetrescu 2015) replaces the IVX filter by sine or
fractional-difference instruments, a check that the result does not
hinge on the choice of \(\rho_n\).
For Ret ~ DP + TBL the diagnostics said: near-unit-root
predictors, DP strongly endogenous, mild residual
autocorrelation, strong ARCH. The decisions that follow are Eicker–White
standard errors, a bootstrap p-value for the endogenous predictor, and
ivx_ra() as the bias-reduced cross-check; no
ivx_ar(). The p-values collected above, plus the joint
test:
rbind(
DP = round(p_dp, 3),
joint = round(c(
asymptotic = 1 - pchisq(m$Wald_Joint, 2),
eicker_white = 1 - pchisq(mr$Wald_Joint, 2),
rwb_bootstrap = mb$p.value$Wald_Joint,
residual_augmented = 1 - pchisq(mra$Wald_Joint, 2)
), 3)
)
#> asymptotic eicker_white rwb_bootstrap residual_augmented
#> DP 0.177 0.200 0.377 0.699
#> joint 0.162 0.235 0.319 0.491Every robust version moves the p-value up, and the more the method does about endogeneity the further it moves: the pattern DGRT (2023) document for the dividend–price ratio. Here nothing rejects at any conventional level, but with a borderline asymptotic p-value the ordering is what decides the conclusion.
For hpi ~ log(res) + cpi the diagnostics said: strong
residual autocorrelation. The decision is ivx_ar(), whose
Wald_AR confirms the AR terms and whose IVX statistics
replace the plain ones.
| Symptom | Diagnostic | Use |
|---|---|---|
| Serially correlated errors | ac_test(fit), Wald column |
ivx_ar() |
| Conditional heteroskedasticity | ac_test(residuals(fit)^2) |
ivx(robust = TRUE) |
| \(\lvert\delta vert\) near 1, \(x\) near unit root, short sample, one-sided test | delta(), hlt_test()$adf |
ivx_boot(type = "rwb"), ivx_ra() |
| Weak signal | — | ivx(lag_y = TRUE) |
| Multi-period return | — | ivx(horizon = K), ivx_ra(horizon = K) |
| Several responses | — | ivx_sys() |
| Tail predictability | — | ivx_qr(), ivx_qr_boot() |
| Pockets of predictability | plot of rolling ivx() t-ratios |
ivx_episodic() |
| Single predictor, second opinion | — | hlt_test(), cy_test(), arm(),
el_test(), ivx_iv() |
ivx_ar()robust = TRUEivx_boot() and
ivx_ra()lag_y = TRUE