| Type: | Package |
| Title: | Bounds Testing for Cointegration with Many Persistent Controls |
| Version: | 0.1.0 |
| Description: | An implementation of the DML-Bounds procedure of Villena (2026) <doi:10.2139/ssrn.6472826> for testing cointegration in data-rich time-series settings. The Autoregressive Distributed Lag (ARDL) bounds test of Pesaran, Shin and Smith (2001) <doi:10.1002/jae.616> avoids pretesting the integration order of the regressors but is not designed for a high-dimensional conditioning set. Residualising the lagged levels against persistent controls can absorb stochastic trends and thereby change the finite-sample null distribution, so what governs the null is the effective number of stochastic trends surviving residualisation rather than the integration order of the original regressors. The procedure combines h-block cross-fitting, a balanced nuisance projection in the Double Machine Learning (DML) style of Chernozhukov and others (2018) <doi:10.1111/ectj.12097>, adaptive weighting after Zou (2006) <doi:10.1198/016214506000000735>, and a restricted system wild bootstrap that regenerates the dependent variable and the focal regressor jointly. No critical-value table is shipped: the classical bracket is regenerated by simulation and the operational critical value is bootstrapped. A trend-absorption diagnostic and a penalty-sensitivity sweep report whether a verdict survives a change of conditioning set. Monthly United States macroeconomic series from the 'FRED-MD' database of McCracken and Ng (2016) <doi:10.1080/07350015.2015.1086655> are bundled so every example runs offline. |
| License: | MIT + file LICENSE |
| Encoding: | UTF-8 |
| Language: | en-GB |
| Depends: | R (≥ 4.1.0) |
| Imports: | glmnet, stats, graphics, grDevices, utils |
| Suggests: | knitr, rmarkdown, testthat (≥ 3.0.0), tseries |
| VignetteBuilder: | knitr |
| LazyData: | true |
| RoxygenNote: | 7.3.3 |
| URL: | https://github.com/merwanroudane/ardldml |
| BugReports: | https://github.com/merwanroudane/ardldml/issues |
| Config/testthat/edition: | 3 |
| NeedsCompilation: | no |
| Packaged: | 2026-09-07 18:36:46 UTC; HP |
| Author: | Merwan Roudane [aut, cre, cph] |
| Maintainer: | Merwan Roudane <merwanroudane920@gmail.com> |
| Repository: | CRAN |
| Date/Publication: | 2026-09-15 12:20:09 UTC |
ardldml: Bounds Testing for Cointegration with Many Persistent Controls
Description
An implementation of the DML-Bounds procedure of Villena (2026) for testing a long-run relationship when the conditioning set is high-dimensional and may itself carry stochastic trends.
The problem
The Autoregressive Distributed Lag (ARDL) bounds test of Pesaran, Shin and Smith (2001) brackets one unknown: because the integration order of the regressors is not known, the lower critical value treats them as stationary and the upper as integrated, and the statistic is read against the interval. With many persistent controls a second unknown appears. Projecting the lagged levels onto controls that themselves carry stochastic trends can absorb part of the long-run variation that identifies the error-correction relation, so what governs the null is the number of stochastic trends that survive residualisation rather than the integration order of the original regressors. The null then sits somewhere inside the classical bracket, at a point that is not known.
Stationary controls are harmless: a stationary regressor cannot track the stochastic trend of an integrated one, so it cannot absorb one. Only integrated controls make the bracket live.
Why no bounds table is shipped
Tabulated critical values are not valid in this setting, and the
generated-regressor remainder is not negligible at the sample sizes applied
work actually uses. Every critical value here is computed instead:
dml_bootstrap for inference on real data, and
simulate_pss_bounds to regenerate the classical bracket at any
sample size and any k.
Where to start
dml_bounds fits the test and dml_bootstrap
attaches a critical value. trend_absorption is the diagnostic
you should run before believing a non-rejection, and
penalty_sensitivity reports whether the verdict survives a
change of penalty. passthrough is the bundled data.
Author(s)
Dr Merwan Roudane merwanroudane920@gmail.com
References
Villena, M. J. (2026). Testing cointegration with many persistent controls. SSRN working paper. doi:10.2139/ssrn.6472826
Pesaran, M. H., Shin, Y. and Smith, R. J. (2001). Bounds testing approaches to the analysis of level relationships. Journal of Applied Econometrics, 16(3), 289–326. doi:10.1002/jae.616
Chernozhukov, V., Chetverikov, D., Demirer, M., Duflo, E., Hansen, C., Newey, W. and Robins, J. (2018). Double/debiased machine learning for treatment and structural parameters. The Econometrics Journal, 21(1), C1–C68. doi:10.1111/ectj.12097
Zou, H. (2006). The adaptive lasso and its oracle properties. Journal of the American Statistical Association, 101(476), 1418–1429. doi:10.1198/016214506000000735
McCracken, M. W. and Ng, S. (2016). FRED-MD: A monthly database for macroeconomic research. Journal of Business & Economic Statistics, 34(4), 574–589. doi:10.1080/07350015.2015.1086655
See Also
Useful links:
Report bugs at https://github.com/merwanroudane/ardldml/issues
Deterministic Case Labels
Description
The five deterministic specifications of Pesaran, Shin and Smith (2001).
Cases 2 and 4 restrict the intercept or the trend, which puts it inside the
tested null and adds one restriction; see n_restrictions.
Usage
CASE_LABELS
Format
A named character vector of length 5.
References
Pesaran, M. H., Shin, Y. and Smith, R. J. (2001). Bounds testing approaches to the analysis of level relationships. Journal of Applied Econometrics, 16(3), 289–326. doi:10.1002/jae.616
The Seven Controls of the Full Conditioning Set
Description
The Seven Controls of the Full Conditioning Set
Usage
CONTROLS
Format
A character vector of length 7.
Controls Treated as Integrated of Order One
Description
On economic grounds, not by pretest.
Usage
DEFAULT_INTEGRATED
Format
A character vector of length 6.
Significance Levels Reported by Default
Description
Significance Levels Reported by Default
Usage
DEFAULT_LEVELS
Format
A numeric vector of length 3.
Series Transformed to Logs
Description
The quantity and price series. The four interest and unemployment rates stay in levels: they are already in percentage-point units and can approach zero.
Usage
LOG_SERIES
Format
A character vector of length 5.
The Four Monetary Regimes
Description
Testing within regimes rather than across them avoids conflating a structural break with a long-run relationship.
Usage
PASSTHROUGH_REGIMES
Format
A named list of start and end months, as "YYYY-MM" strings.
Penalty Rules of the Robustness Grid
Description
The three penalty choices the paper's robustness tables label Low, Medium and High, mapped onto the points of the cross-validation profile they take.
Usage
PENALTY_RULES
Format
A named character vector of length 3.
Controls Dropped to Form the Reduced Set
Description
The two most likely to share a stochastic trend with the pass-through relation itself, which is what makes them the interesting ones to drop in the trend-absorption diagnostic.
Usage
REDUCED_DROP
Format
A character vector of length 2.
Adaptive Post-LASSO
Description
LASSO selection followed by an unpenalised refit on the selected support, with optional adaptive weights on a designated block of columns.
Usage
adaptive_post_lasso(
X,
y,
lam = NULL,
adaptive = TRUE,
c = 1.1,
adaptive_mask = NULL,
min_dof = 1L
)
Arguments
X |
Numeric matrix of regressors. |
y |
Numeric target. |
lam |
Penalty. If |
adaptive |
Enable adaptive weighting. |
c |
Constant in the plug-in penalty. |
adaptive_mask |
Logical vector marking the columns that receive adaptive
weights. If |
min_dof |
Minimum residual degrees of freedom required for the refit. |
Details
The adaptive weighting exists because vanilla \ell_1 over-selects
integrated regressors and thereby induces spurious trend absorption. Following
the paper, the weights apply to the integrated block only; stationary
columns stay under the plain penalty. The weight on column j is the
absolute univariate slope of y on that column, applied by rescaling the
column, which is equivalent to penalising |\beta_j| by its reciprocal.
Selection runs on standardised columns so the penalty is scale-free, and the coefficients are mapped back before the refit. Degrees of freedom charge the selected support size, so a cell with non-positive residual degrees of freedom is reported as not estimable rather than silently returning a number.
Value
A list with fitted, coef, support,
intercept, lam and estimable.
References
Zou, H. (2006). The adaptive lasso and its oracle properties. Journal of the American Statistical Association, 101(476), 1418–1429. doi:10.1198/016214506000000735
Examples
set.seed(1)
X <- matrix(rnorm(60 * 5), 60, 5)
y <- X[, 1] + rnorm(60)
fit <- adaptive_post_lasso(X, y, adaptive = FALSE)
which(fit$support)
Colours Used by the Package's Figures
Description
A colourblind-safe palette (Wong 2011) with semantic names, so the same contrast carries the same colour across every figure: the bootstrap against the borrowed bound, the full against the reduced control set.
Usage
ardl_colors
Format
A named character vector of hex colours.
Examples
ardl_colors[["bootstrap"]]
Mark a Statistic Against a Pair of Bounds
Description
Returns "reject" above the upper bound, "inconclusive" between
the two, and "fail to reject" below the lower bound. A single verdict
would hide the inconclusive region, which is the whole point of a bounds test.
Usage
bounds_verdict(stat, lower, upper)
Arguments
stat |
The statistic. |
lower, upper |
The bracket. |
Value
A character string.
Examples
bounds_verdict(6.2, lower = 4.94, upper = 5.73)
bounds_verdict(5.2, lower = 4.94, upper = 5.73)
bounds_verdict(2.0, lower = 4.94, upper = 5.73)
Build the Balanced First-Stage Design
Description
The two nuisance projections have different regressor sets, because their
targets have different integration orders. \Delta Y_t is stationary, so
the integrated controls enter its projection in first differences;
regressing a stationary target on integrated levels would be unbalanced and
spurious. The tested levels Z_{t-1} = (Y_{t-1}, D_{t-1}) are integrated
and are projected on the control levels. That second projection is
the only place trend absorption can happen, and therefore the only place the
effective integrated count is determined.
Usage
build_balanced_design(
y,
d,
W,
lags = 4L,
integrated = NULL,
dlags = FALSE,
adf_alpha = 0.1
)
## S3 method for class 'ardl_design'
print(x, ...)
Arguments
y |
Numeric vector: the outcome in levels. |
d |
Numeric vector: the focal regressor in levels. |
W |
Matrix or data frame of controls in levels, with column names. |
lags |
Short-run lag order |
integrated |
Character vector of controls to treat as integrated of
order one; see |
dlags |
Include lags of |
adf_alpha |
Level for the Augmented Dickey-Fuller fallback. |
x |
An |
... |
Ignored. |
Details
A stationary regressor cannot track the stochastic trend of an integrated one, so partialling out stationary controls leaves the unit root intact no matter how many there are. Only integrated controls can absorb a trend.
Value
An object of class "ardl_design": a list with dY,
X and its column groups, Z, Wlev,
dD_lags, index, stationary and integrated.
Examples
df <- passthrough_regime("1999-2007")
des <- build_balanced_design(df$cpi, df$neer,
as.matrix(df[, CONTROLS]),
lags = 4, integrated = DEFAULT_INTEGRATED)
des
The Classical Bounds Test
Description
All three steps of the Pesaran, Shin and Smith procedure. Rejecting the joint
F null is not on its own evidence of a level relationship, because two
degenerate cases survive it, so the t test on the speed of adjustment
and the Wald test on the long-run coefficients are reported alongside.
Usage
classical_bounds_test(
y,
x,
lags = 1L,
order = 1L,
case = 3L,
fixed = NULL,
nsim = 20000L,
seed = 0,
levels = DEFAULT_LEVELS
)
Arguments
y |
Numeric vector: outcome in levels. |
x |
Matrix or data frame of long-run forcing regressors in levels. |
lags |
ARDL order |
order |
ARDL order |
case |
Deterministic case, an integer in 1:5. Cases 2 and 3 estimate the same model and differ only in whether the intercept is inside the tested null; the same holds for the trend in cases 4 and 5. |
fixed |
Optional matrix of regressors entered contemporaneously and
never lagged – the |
nsim |
Replications for the simulated bracket. |
seed |
Seed for the bracket simulation. |
levels |
Significance levels for the bracket. |
Details
The bracket is generated by simulate_pss_bounds at the model's
own sample size, not read off a table calibrated at T = 1000.
Value
An object of class "classical_bounds" with the F and
t statistics, the step-3 Wald statistic and its p-value, the speed of
adjustment, the long-run coefficients and the simulated bracket.
References
Pesaran, M. H., Shin, Y. and Smith, R. J. (2001). Bounds testing approaches to the analysis of level relationships. Journal of Applied Econometrics, 16(3), 289–326. doi:10.1002/jae.616
Examples
df <- passthrough_regime("1999-2007")
# Small nsim keeps this quick; raise it for anything you report.
cb <- classical_bounds_test(df$cpi, cbind(neer = df$neer), lags = 4,
nsim = 100, seed = 1)
cb
Split the Control Set into Stationary and Integrated Blocks
Description
The balanced design has to know which controls to difference, so the two
blocks must be named. Passing integrated explicitly, on economic
grounds, is the recommended route: the appeal of the bounds framework is that
it avoids pretesting, and a pretest here would put its own error into
everything downstream without any of the inference accounting for it.
Usage
classify_controls(W, integrated = NULL, alpha = 0.1)
Arguments
W |
A matrix or data frame of controls in levels, with column names. |
integrated |
Character vector of control names to treat as integrated of order one. If supplied, the classification is taken as stated. |
alpha |
Significance level for the Augmented Dickey-Fuller fallback. |
Details
If integrated is NULL an Augmented Dickey-Fuller fallback is
used, which requires the tseries package.
Value
A list with character vectors stationary and integrated.
Examples
W <- as.matrix(passthrough[, c("m2", "unrate")])
classify_controls(W, integrated = "m2")
Build the Design, Cross-Fit, Residualise and Test
Description
The whole pipeline in one call, so the bootstrap can invoke it on a regenerated path and obtain a statistic carrying the same generated-regressor error as the observed one.
Usage
compute_statistic(y, d, W, spec, frozen_dY_support = NULL)
Arguments
y |
Numeric vector: outcome in levels. |
d |
Numeric vector: focal regressor in levels. |
W |
Matrix or data frame of controls in levels. |
spec |
A |
frozen_dY_support |
Optional logical vector fixing the support of the stationary projection. The paper freezes the stationary support across bootstrap draws while re-selecting the level supports, so that selection error is reflected in the bootstrap law. |
Value
A list with stat, alpha, theta, theta_se,
design, folds, supports, resid and
estimable.
Examples
df <- passthrough_regime("1999-2007")
out <- compute_statistic(df$cpi, df$neer, as.matrix(df[, CONTROLS]),
dml_spec(lags = 4, n_blocks = 5, buffer = 6,
integrated = DEFAULT_INTEGRATED))
round(out$stat, 4)
Fit the Unrestricted Conditional Error Correction Model
Description
The classical half of the package: the unrestricted conditional error correction model of Pesaran, Shin and Smith (2001),
\Delta y_t = c_0 + c_1 t + \pi_y y_{t-1} + \pi_x' x_{t-1}
+ \sum_{i=1}^{p-1}\psi_{yi}\Delta y_{t-i}
+ \sum_{i=0}^{q-1}\psi_{xi}'\Delta x_{t-i} + \gamma' z_t + u_t,
from which the speed of adjustment is \alpha = -\pi_y and the long-run
coefficients are \theta = \pi_x/\alpha.
Usage
conditional_ecm(y, x, lags = 1L, order = 1L, case = 3L, fixed = NULL)
Arguments
y |
Numeric vector: outcome in levels. |
x |
Matrix or data frame of long-run forcing regressors in levels. |
lags |
ARDL order |
order |
ARDL order |
case |
Deterministic case, an integer in 1:5. Cases 2 and 3 estimate the same model and differ only in whether the intercept is inside the tested null; the same holds for the trend in cases 4 and 5. |
fixed |
Optional matrix of regressors entered contemporaneously and
never lagged – the |
Value
A list with the design matrix X, the response dy,
coefficients, vcov, nobs and the column names of the
level terms.
References
Pesaran, M. H., Shin, Y. and Smith, R. J. (2001). Bounds testing approaches to the analysis of level relationships. Journal of Applied Econometrics, 16(3), 289–326. doi:10.1002/jae.616
Examples
df <- passthrough_regime("1999-2007")
m <- conditional_ecm(df$cpi, cbind(neer = df$neer), lags = 4, case = 3)
m$nobs
Cross-Fitted Projection
Description
Out-of-fold predictions of y on X under an h-block partition.
Each evaluation block is predicted by a model estimated on that block's
buffered training set, so the first-stage error is decoupled from the
evaluation-fold innovations.
Usage
cross_fit_projection(
X,
y,
folds,
lam = NULL,
adaptive = TRUE,
c = 1.1,
penalised = TRUE,
adaptive_mask = NULL
)
Arguments
X |
Numeric matrix of regressors. |
y |
Numeric target. |
folds |
An |
lam |
Penalty passed to |
adaptive |
Adaptive weighting. |
c |
Constant in the plug-in penalty. |
penalised |
If |
adaptive_mask |
Columns receiving adaptive weights. |
Value
A list with fitted, resid, support_union,
lams and estimable.
Examples
set.seed(1)
X <- matrix(rnorm(80 * 3), 80, 3)
y <- X[, 1] + rnorm(80)
fit <- cross_fit_projection(X, y, hblock_folds(80, 4, 2), penalised = FALSE)
stats::sd(fit$resid)
Verdict of a Fitted Test
Description
Verdict of a Fitted Test
Usage
decision(object, level = 0.05)
Arguments
object |
A |
level |
Significance level. |
Value
"reject", "fail to reject", or "no bootstrap run".
Examples
df <- passthrough_regime("1999-2007")
fit <- dml_bounds(df$cpi, df$neer, as.matrix(df[, CONTROLS]),
lags = 4, n_blocks = 5, buffer = 6,
integrated = DEFAULT_INTEGRATED)
decision(fit)
The Restricted System Wild Bootstrap
Description
Attaches a critical value and a p-value to a fitted dml_bounds
object. This is the inference: because the tabulated bounds are not
operationally valid once the conditioning set is large and persistent, the
critical value is generated from the data under the imposed null.
Usage
dml_bootstrap(
object,
B = 999L,
level = 0.05,
seed = NULL,
scheme = c("system", "fixed"),
freeze_stationary_support = TRUE,
progress = FALSE
)
Arguments
object |
A fitted |
B |
Bootstrap replications. The paper uses 999. |
level |
Significance level for the reported critical value. |
seed |
Optional seed. |
scheme |
|
freeze_stationary_support |
Freeze the stationary first-stage support across draws while re-selecting the level supports, as the paper does. |
progress |
Print a line every 10% of draws. |
Details
Two auxiliary models are estimated under H_0: a restricted conditional
model for \Delta Y_t carrying the same deterministic terms and
short-run lag structure as the empirical specification but excluding the
lagged levels and the confounder levels; and a marginal model for
\Delta D_t on an intercept, its own lags and the first-stage-selected
differenced controls. A single Rademacher sequence is then applied to
the stacked residual pair, both paths are regenerated recursively,
and the entire residualised statistic is recomputed with the level supports
re-selected.
Sharing one weight is what keeps the endogeneity channel alive. The
Pesaran-Shin-Smith framework exists because the focal regressor need
not be exogenous, and the conditional model absorbs the contemporaneous
correlation through the \Delta D_t term. A scheme that holds d at
its realised path and reweights the equation error alone simulates a world
with zero correlation between the two innovations whatever the data say; it is
available as scheme = "fixed" and is valid only under strong
exogeneity.
The controls are held at their realised path, which conditions the bootstrap
on the realised trend content. Because the marginal model conditions on the
differenced controls, any stochastic trend that d shares with W
is inherited rather than broken.
Value
The object with a boot component attached, holding
crit, pvalue, draws, B, level,
scheme, n_failed and corr_eps_v.
References
Villena, M. J. (2026). Testing cointegration with many persistent controls. SSRN working paper. doi:10.2139/ssrn.6472826
Examples
df <- passthrough_regime("1999-2007")
fit <- dml_bounds(df$cpi, df$neer, as.matrix(df[, CONTROLS]),
lags = 4, n_blocks = 5, buffer = 6,
integrated = DEFAULT_INTEGRATED)
# Tiny B so the example is quick; use B = 999 for anything you report.
fit <- dml_bootstrap(fit, B = 5, seed = 1)
fit$boot$pvalue
Test for a Long-Run Relationship with Many Persistent Controls
Description
Fits the DML-Bounds test of Villena (2026): the lagged levels
Z_{t-1} = (Y_{t-1}, D_{t-1}) are orthogonalised against a
high-dimensional control set with a balanced, cross-fitted first stage, and
the null of no level relationship is tested on the residualised variables.
Usage
dml_bounds(y, d, W, B = 0L, level = 0.05, seed = NULL, ...)
## S3 method for class 'dml_bounds'
print(x, ...)
## S3 method for class 'dml_bounds'
summary(object, ...)
Arguments
y |
Numeric vector: outcome in levels. |
d |
Numeric vector: focal regressor in levels. |
W |
Matrix or data frame of controls in levels, with column names. |
B |
Bootstrap replications. |
level |
Significance level for the reported critical value. |
seed |
Optional seed for the bootstrap. |
... |
Passed to |
x, object |
A |
Details
Do not compare the statistic with a tabulated bound. The asymptotic
reference sits somewhere inside the Pesaran-Shin-Smith bracket, at a position
governed by the number of stochastic trends that survive residualisation, and
the finite-sample law is further perturbed by generated-regressor error. Call
dml_bootstrap, or pass B, to attach a critical value.
The estimand is conditional. This does not ask whether y and d
cointegrate unconditionally; it asks whether they cointegrate given
W. If a control is itself part of the equilibrium system, partialling
it out removes the relation rather than the confounding, and a non-rejection
then reflects over-absorption. That failure mode is not visible in a single
fit – use trend_absorption.
Value
An object of class "dml_bounds".
References
Villena, M. J. (2026). Testing cointegration with many persistent controls. SSRN working paper. doi:10.2139/ssrn.6472826
See Also
dml_bootstrap, trend_absorption,
classical_bounds_test
Examples
df <- passthrough_regime("1999-2007")
fit <- dml_bounds(df$cpi, df$neer, as.matrix(df[, CONTROLS]),
lags = 4, n_blocks = 5, buffer = 6,
integrated = DEFAULT_INTEGRATED)
fit
# With inference. The paper uses B = 999; this is smaller so it stays quick.
fit <- dml_bootstrap(fit, B = 99, seed = 20260625)
summary(fit)
Specification of a DML-Bounds Fit
Description
Everything that defines a fit, held separately from the data so the bootstrap can rebuild the identical statistic on a regenerated path.
Usage
dml_spec(
lags = 4L,
case = 3L,
n_blocks = 5L,
buffer = 0L,
adaptive = TRUE,
penalised = TRUE,
penalty = "plugin",
c = 1.1,
integrated = NULL,
include_constant = FALSE,
adaptive_integrated_only = TRUE,
dlags = FALSE
)
## S3 method for class 'dml_spec'
print(x, ...)
Arguments
lags |
Short-run lag order |
case |
Deterministic case, 1 or 3. This does not enter the statistic, which is a no-intercept projection on the orthogonalised levels regardless. It sets the deterministic terms of the restricted conditional model the bootstrap resamples from. |
n_blocks |
Cross-fitting blocks |
buffer |
Cross-fitting buffer |
adaptive |
Adaptive weights on the level projection. |
penalised |
If |
penalty |
How the |
c |
Constant in the plug-in penalty. |
integrated |
Character vector of controls to treat as integrated of order one. |
include_constant |
Add an intercept to the final regression. Defaults to
|
adaptive_integrated_only |
Restrict the adaptive weights to the integrated block, which is what the paper specifies. |
dlags |
Include lags of |
x |
A |
... |
Ignored. |
Value
An object of class "dml_spec".
Examples
dml_spec(lags = 4, n_blocks = 5, buffer = 6)
h-Block Cross-Fitting Folds
Description
Cuts the sample into n_blocks contiguous chronological blocks and
builds, for each, a training set that excludes the block itself and the
buffer observations either side of it.
Usage
hblock_folds(n, n_blocks = 5L, buffer = 0L)
## S3 method for class 'ardl_folds'
as.data.frame(x, ...)
## S3 method for class 'ardl_folds'
print(x, ...)
Arguments
n |
Number of observations. |
n_blocks |
Number of chronological blocks |
buffer |
Buffer width |
x |
An |
... |
Ignored. |
Details
In cross-sectional double machine learning the folds are random. In a time
series they cannot be: randomly held-out observations are adjacent in time to
their own training data, so the first-stage error stays correlated with the
evaluation innovations. The buffer is what buys the decoupling, and it costs
sample – at most h(K-1) observations across the training sets. Use
sample_use_table to see the cost before choosing a
configuration.
Value
An object of class "ardl_folds": a list with n,
n_blocks, buffer, and lists eval and train of
integer index vectors.
See Also
Examples
f <- hblock_folds(100, n_blocks = 4, buffer = 5)
sapply(f$train, length)
The k-Convention Offset
Description
Two conventions for k sit next to each other in this literature, and
mixing them shifts every bound by one row. Pesaran, Shin and Smith count the
long-run forcing regressors only; some implementations pass the number of
level terms, which is one larger, into a table indexed the first way. Every
bound then comes out one row too far down, hence too small, and the test
over-rejects.
Usage
k_convention_offset(k = 1L, case = 3L, level = 0.05)
Arguments
k |
Number of long-run forcing regressors; see |
case |
Deterministic case, an integer in 1:5. |
level |
Significance level to report. |
Details
This function puts the correct row next to the shifted one so the size of the
distortion can be seen. It is a demonstration, not something the package uses
internally: classical_bounds_test indexes on k.
Value
A data frame with one row for the correct bounds and one for the shifted bounds.
Examples
k_convention_offset(k = 1, case = 3)
Number of Restrictions in the Bounds Test
Description
The level terms are y_{t-1} and the k lagged forcing regressors.
Cases 2 and 4 add one more because the intercept or the trend is restricted
and therefore tested jointly with them.
Usage
n_restrictions(k, case = 3L)
Arguments
k |
Number of long-run forcing regressors. This does not count the dependent variable; mixing that convention up shifts every bound by one row. |
case |
Deterministic case, an integer in 1:5. |
Value
An integer.
Examples
n_restrictions(k = 1, case = 3)
n_restrictions(k = 1, case = 4)
MATLAB Parula Color Palette
Description
The MATLAB R2014b Parula colormap as hexadecimal colors, interpolated from its 64 stops. Parula is perceptually uniform and runs from dark blue-purple through teal and green to yellow.
Usage
parula_colors(n = 256)
Arguments
n |
Number of colors to interpolate. |
Value
A character vector of n hex colors.
Examples
parula_colors(6)
Exchange-Rate Pass-Through Data
Description
Nine monthly United States macroeconomic series, 1973-01 to 2020-12, in raw levels. These are the series behind the paper's main application, bundled so every example runs offline.
Usage
passthrough
Format
A data frame with 576 rows and 10 columns:
- date
First day of the month, class
Date.- cpi
Consumer price index, all urban consumers (
CPIAUCSL).- neer
Trade-weighted United States dollar index (
TWEXAFEGSMTHx).- m2
M2 money stock (
M2SL).- ffr
Effective federal funds rate (
FEDFUNDS).- ip
Industrial production index (
INDPRO).- unrate
Civilian unemployment rate (
UNRATE).- oil
Crude oil spot price, West Texas Intermediate (
OILPRICEx).- gs10
Ten-year Treasury constant maturity yield (
GS10).- baa
Moody's Baa corporate bond yield (
BAA).
Details
The sample starts in 1973-01 because that is the first observation of the trade-weighted dollar index, which is also why the first monetary regime starts there. The four regimes give complete samples of 156, 156, 108 and 156 observations.
Source
FRED-MD, the monthly macroeconomic database of McCracken and Ng (2016), vintage 2025-06. doi:10.1080/07350015.2015.1086655
References
McCracken, M. W. and Ng, S. (2016). FRED-MD: A monthly database for macroeconomic research. Journal of Business & Economic Statistics, 34(4), 574–589. doi:10.1080/07350015.2015.1086655
Examples
str(passthrough)
range(passthrough$date)
Load a Regime of the Pass-Through Data
Description
Load a Regime of the Pass-Through Data
Usage
passthrough_regime(regime = NULL, log = TRUE, start = NULL, end = NULL)
Arguments
regime |
One of the names of |
log |
Take logs of |
start, end |
Optional |
Value
A data frame with a date column and the nine series.
Examples
df <- passthrough_regime("1999-2007")
nrow(df)
head(df[, c("date", "cpi", "neer")])
Sweep the Penalty and the Projection
Description
The paper's robustness tables vary the penalty, the lag order and the level projection, and a verdict can turn on that choice. Reporting a single cell from this grid is specification search; reporting the grid is the method.
Usage
penalty_sensitivity(
y,
d,
W,
rules = c("low", "medium", "high"),
lags_grid = 4L,
projections = c("adaptive", "plain", "ols"),
B = 0L,
seed = NULL,
...
)
Arguments
y |
Numeric vector: outcome in levels. |
d |
Numeric vector: focal regressor in levels. |
W |
Matrix or data frame of controls in levels. |
rules |
Penalty rules to sweep: |
lags_grid |
Short-run lag orders to try. |
projections |
Estimators to compare: |
B |
Bootstrap draws per cell, or |
seed |
Base seed; each fit is offset so the four bootstraps are independent. |
... |
Passed to |
Details
The column to watch is n_selected_Z, the number of control
levels the level projection retained. It is the empirical
counterpart of the effective integrated count: zero means nothing was
absorbed, so the test sits at the classical corner and orthogonalisation did
nothing, while a large value means heavy absorption and the long-run
coefficient should be checked for instability. A long-run coefficient that
changes sign across the grid is a warning that the conditioning set, not the
data, is driving the answer.
Value
A data frame with one row per cell, carrying a
"theta_sign_flips" attribute.
Examples
df <- passthrough_regime("1999-2007")
penalty_sensitivity(df$cpi, df$neer, as.matrix(df[, CONTROLS]),
lags_grid = 4, n_blocks = 5, buffer = 6,
integrated = DEFAULT_INTEGRATED)
Plot the Simulated Null of a Fitted Test
Description
The bootstrap null with the observed statistic and, optionally, a borrowed classical bound marked. The gap between the bootstrap critical value and the borrowed bound is the whole argument for not reading the statistic against a table.
Usage
## S3 method for class 'dml_bounds'
plot(x, borrowed = 5.73, breaks = 30, main = "Bootstrap null", xlab = "F", ...)
Arguments
x |
A |
borrowed |
Optional classical bound to mark, for contrast. |
breaks |
Passed to |
main, xlab |
Plot labels. |
... |
Passed to |
Value
Invisibly, the object.
Examples
df <- passthrough_regime("1999-2007")
fit <- dml_bounds(df$cpi, df$neer, as.matrix(df[, CONTROLS]),
lags = 4, n_blocks = 5, buffer = 6,
integrated = DEFAULT_INTEGRATED, B = 5, seed = 1)
plot(fit)
Plot the Trend-Absorption Bracket
Description
Where the limiting null sits as a function of the number of stochastic trends
that survive residualisation. As that count falls from k to zero, the
null slides from the integrated endpoint to the stationary one; classical
bounds testing is the right-hand end of this picture.
Usage
plot_bracket(
k = 10L,
lower = 4.94,
upper = 5.73,
k_tilde = NULL,
main = "Trend-absorption bracket",
xlab = "effective integrated count",
ylab = "limiting null, 95th percentile"
)
Arguments
k |
Number of level terms. |
lower, upper |
The classical bracket endpoints. |
k_tilde |
Optional effective count to mark. |
main, xlab, ylab |
Plot labels. |
Value
Invisibly, a data frame of the plotted curve.
Examples
plot_bracket(k = 10, k_tilde = 6)
The Plug-In Penalty
Description
\lambda = c \sqrt{\log(d)/n} \hat\sigma, the standard high-dimensional
choice: large enough to dominate the noise, small enough to leave the signal.
The paper uses c = 1.1.
Usage
plugin_penalty(n, d, sigma, c = 1.1)
Arguments
n |
Sample size. |
d |
Number of regressors. |
sigma |
Noise scale. |
c |
Constant. |
Value
A single number.
Examples
plugin_penalty(n = 100, d = 40, sigma = 1)
Print a Classical Bounds Test
Description
Print a Classical Bounds Test
Usage
## S3 method for class 'classical_bounds'
print(x, ...)
Arguments
x |
A |
... |
Ignored. |
Value
Invisibly, x.
Examples
df <- passthrough_regime("1999-2007")
print(classical_bounds_test(df$cpi, cbind(neer = df$neer), lags = 4,
nsim = 100, seed = 1))
Published Bounds for a Validated Cell
Description
A small set of cells from the printed Pesaran, Shin and Smith (2001) tables,
stored so simulate_pss_bounds can be checked against print and
so the borrowed-bound comparison can quote the exact published number. Only a
subset of cells is stored; the simulator has no such gaps.
Usage
pss_reference(k, case = 3L)
Arguments
k |
Number of long-run forcing regressors; see |
case |
Deterministic case, an integer in 1:5. |
Value
A data frame with columns level, I0 and I1.
Examples
pss_reference(k = 1, case = 3)
Rademacher Weights
Description
A vector of independent \pm 1 draws.
Usage
rademacher(n)
Arguments
n |
Length. |
Value
A numeric vector of -1 and 1.
Examples
set.seed(1)
rademacher(8)
Share of the Sample Available for Training
Description
Average, across blocks, of the training-set size as a fraction of n.
With no buffer this is 1 - 1/K; each unit of h removes roughly
2h/n more. This is the quantity that makes the cost of buffering
concrete.
Usage
sample_use(n, n_blocks = 5L, buffer = 0L)
Arguments
n |
Number of observations. |
n_blocks |
Number of chronological blocks |
buffer |
Buffer width |
Value
A single number between 0 and 1.
Examples
sample_use(108, n_blocks = 5, buffer = 6)
Training Share Across a Grid of K and h
Description
Tabulates sample_use over a grid, so the point at which the
buffer costs more than the cross-fitting buys is visible before a
configuration is committed to. Infeasible cells are NA.
Usage
sample_use_table(n, n_blocks = c(4L, 5L, 6L, 8L), buffers = c(0L, 2L, 5L, 10L))
Arguments
n |
Number of observations. |
n_blocks |
Integer vector of block counts |
buffers |
Integer vector of buffer widths |
Value
A numeric matrix, rows named by K and columns by h.
Examples
sample_use_table(108)
Generate the Classical Bounds by Simulation
Description
Regenerates the Pesaran, Shin and Smith (2001) bracket from the data-generating process printed in the notes to their Table CI, rather than reading a stored table.
Usage
simulate_pss_bounds(
k,
case = 3L,
TT = 1000L,
nsim = 40000L,
levels = DEFAULT_LEVELS,
seed = NULL
)
Arguments
k |
Number of long-run forcing regressors; see |
case |
Deterministic case, an integer in 1:5. |
TT |
Sample size. |
nsim |
Replications. The published tables use 40000. Tail quantiles settle slowest, so reduce this only for exploratory work. |
levels |
Significance levels. |
seed |
Optional seed. Passed to |
Details
Under the null, with y_0 = x_0 = 0 and k+1 independent standard
normal innovations, y_t = y_{t-1} + \epsilon_{1t} and
x_t = P x_{t-1} + e_{2t}, with P = I_k for the upper bound
(regressors purely integrated of order one) and P = 0 for the lower
bound (purely stationary). The statistic is the F form of the Wald test
on the level terms.
Generating rather than tabulating has three advantages: it is checkable
against print via pss_reference; it extends to any sample size,
so small-sample bounds come from passing TT = n rather than shipping a
second table; and it extends past k = 10, where the published tables
stop.
Value
A data frame with columns level, I0 (the lower,
stationary bound) and I1 (the upper, integrated bound).
References
Pesaran, M. H., Shin, Y. and Smith, R. J. (2001). Bounds testing approaches to the analysis of level relationships. Journal of Applied Econometrics, 16(3), 289–326. doi:10.1002/jae.616
Narayan, P. K. (2004). Reformulating critical values for the bounds F-statistics approach to cointegration. Monash University Discussion Paper 02/04.
Examples
# Small and fast; raise nsim for anything you intend to quote.
simulate_pss_bounds(k = 1, case = 3, TT = 120, nsim = 100, seed = 1)
# Closer to the published asymptotic table.
simulate_pss_bounds(k = 1, case = 3, TT = 1000, nsim = 4000, seed = 1)
The Trend-Absorption Diagnostic
Description
The most important thing in the package to actually run, and the easiest to skip.
Usage
trend_absorption(y, d, W, drop, B = 999L, seed = NULL, progress = FALSE, ...)
## S3 method for class 'trend_absorption'
as.data.frame(x, ...)
absorption_verdict(x, level = 0.05)
## S3 method for class 'trend_absorption'
print(x, ...)
Arguments
y |
Numeric vector: outcome in levels. |
d |
Numeric vector: focal regressor in levels. |
W |
Matrix or data frame of controls in levels. |
drop |
Character vector of controls to omit from the reduced set. Choose these on economic grounds: the ones most likely to be cointegrated with the tested relation itself. |
B |
Bootstrap replications per fit. Four bootstraps are run, so the cost
is roughly |
seed |
Base seed; each fit is offset so the four bootstraps are independent. |
progress |
Report which fit is running. |
... |
Passed to |
x |
A |
level |
Significance level. |
Details
Residualisation is valid only when the controls remove nuisance stochastic trends, not the equilibrium relation being tested. If a control is itself part of the equilibrium system, partialling it out absorbs the very trend the test is meant to detect, and a non-rejection then reflects over-absorption rather than the absence of a long-run relationship. That requirement cannot be verified directly; this contrast is the practical substitute.
Four fits are run: the full and reduced control sets crossed with the
adaptive and the unpenalised level projection. Writing p_{ad} and
p_{ols} for the bootstrap p-values under the two projections and
p_{full} and p_{red} for those under the two control sets, the
diagnostic is the pair of gaps \Delta_m = p_{ols} - p_{ad} and
\Delta_W = p_{full} - p_{red}, read together with the stability of the
long-run coefficient.
A large positive \Delta_W – a verdict that rejects under the reduced
set but not under the full set – together with a long-run coefficient that is
more sharply estimated under the reduced fit, is evidence that the nuisance
space is absorbing part of the tested relation, and the reduced-set verdict is
then the more credible one. Concordant verdicts across the four fits indicate
the conclusion is not an artefact of over-absorption.
This is a hypothesis-generating device, not a formal test. It has no size and no power; it tells you where to look.
Value
An object of class "trend_absorption".
References
Villena, M. J. (2026). Testing cointegration with many persistent controls. SSRN working paper. doi:10.2139/ssrn.6472826
Examples
df <- passthrough_regime("1999-2007")
ta <- trend_absorption(df$cpi, df$neer, as.matrix(df[, CONTROLS]),
drop = REDUCED_DROP, B = 19, seed = 1,
lags = 4, n_blocks = 5, buffer = 6,
integrated = DEFAULT_INTEGRATED)
ta
Rolling-Origin Cross-Validation for the Penalty
Description
For origins t = T_0, \ldots, T-1 the first stage is fitted on
\{1, \ldots, t\} and evaluated one step ahead, and \lambda
minimises the average out-of-sample squared error. This respects temporal
ordering, unlike ordinary k-fold cross-validation, which would train on the
future.
Usage
tscv_penalty(
X,
y,
grid = NULL,
min_train = NULL,
adaptive = FALSE,
n_grid = 20L,
rule = "min",
return_profile = FALSE
)
Arguments
X |
Numeric matrix of regressors. |
y |
Numeric target. |
grid |
Penalties to search. Defaults to a log grid spanning two decades around the plug-in value. |
min_train |
|
adaptive |
Adaptive weights during the search. |
n_grid |
Grid size when |
rule |
One of |
return_profile |
Also return the grid and its error profile. |
Details
The verdict can depend on which point of the profile is taken, which is why
penalty_sensitivity sweeps all three rather than reporting one.
Value
A single penalty, or a list with the profile if
return_profile = TRUE.
Examples
set.seed(1)
X <- matrix(rnorm(60 * 4), 60, 4)
y <- X[, 1] + rnorm(60)
tscv_penalty(X, y, n_grid = 4, min_train = 45)