library(ggplot2)
theme_set(theme_bw())
repo_dir <- if (dir.exists("data")) "." else ".."
data_file <- file.path(repo_dir, "data", "module_10_consumption_income.csv")
stopifnot(file.exists(data_file))Module 10: Cointegration and Error Correction
Econ 6376 — Applied Time Series Econometrics
How to Use These Notes
The main text is Core: the concepts, interpretations, and applied skills expected of everyone. Deeper Dive develops an idea further; Technical Note supplies formal qualifications; Looking Ahead prepares a later module. Those three optional layers are not required for credit. Read the Core, predict the results before running the R examples, and work through the Core Practice questions. The optional material can wait until you have a question it helps answer.
Our organizing question is simple: How can two series keep wandering while a relationship between them remains stable? We will answer it with a story, a mathematical definition, an experiment we can break, and an economic application. Sections 10.1–10.5 establish the concept and its economic meaning; 10.6 builds the experiment; 10.7–10.9 develop testing and error correction and carry them to real data. This shared sequence lets us reuse the same objects instead of introducing a separate simulation for every definition.
Read Michael Murray’s short “A Drunk and Her Dog: An Illustration of Cointegration and Error Correction” (1994) alongside these notes. On pp. 37–38, watch for four things: what keeps wandering, what stays statistically stable, who adjusts, and what a regression containing only changes would leave out. We will also use his less familiar teeter-totter example to challenge the idea that cointegrated variables must move in the same direction.
Before running the examples
Run the code in chapter order in a fresh R session. You need ggplot2, knitr, and urca. The real-data example reads the shipped CSV; it does not download data or require a FRED key. The HTML can be read offline. Keep the accompanying PDF beside the course folders if you want its local link to work offline as well.
course folder/
data/module_10_consumption_income.csv
data/module_10_consumption_income_README.md
Literature of Note/Murray-DrunkDogIllustration-1994.pdf
notes/module_10_notes.qmd
Set R’s working directory to the course folder containing data and notes. The setup also works from its notes subfolder, as when Quarto renders this chapter. The functions we build below also live in helpers/cointegration_utils.R for reuse in later modules; here we write them inline so that each calculation is visible.
Core Learning Objectives
By the end of this module, you should be able to:
- Define cointegration in the I(1)/I(0) case and identify the stationary object.
- Distinguish cointegration from correlation, similar deterministic trends, and a causal or structural claim.
- Explain how economists use long-run relationships in consumption, interest rates, purchasing-power parity, and money demand.
- Conduct an Engle–Granger residual test using the appropriate cointegration critical value and interpret both rejection and nonrejection.
- Read an error-correction equation as short-run dynamics plus a response to a previous disequilibrium.
- Report evidence with its sample, deterministic specification, lag choices, diagnostics, and limitations.
10.1 The Puzzle: Meaningful Levels or Spurious Regression? — Core
Module 9 asked whether one history adds information for predicting another series. Its baseline inference used stationary variables. The final preview raised a problem: what if the economically interesting variables have unit roots, yet their levels seem connected?
Recall the two lessons from Modules 1 and 2. First, unrelated random walks can produce a high and apparently impressive regression coefficients. Shared persistence can fool the usual stationary-regression reasoning. Second, if a series is I(1), its first difference is I(0). Differencing can therefore help us model changes with stationary tools.
It is tempting to turn those lessons into a universal instruction: “Never model nonstationary levels; always difference first.” But consider consumption and income. Households may react to recent income growth, and the level of consumption relative to income may also matter. A model containing only growth rates has no explicit variable telling it that consumption has moved unusually far from a long-run relationship.
We need to distinguish two possibilities:
- Two levels drift independently. Their impressive sample regression is misleading.
- Two levels share persistent movements, leaving a stationary deviation from a relationship between them. That deviation may help predict subsequent changes.
Cointegration is the property in the second case. It does not grant a blanket exemption from the problems of nonstationary regression. It identifies a particular stable combination and calls for testing and inference designed for that setting.
Where are we on the mixing board?
Module 8 added and its lags to the master equation:
We are keeping that dynamic-regression framework. The new feature is a restriction connecting I(1) levels: some combination may be stationary even when neither level is. Later, we will rearrange the same ARDL into changes and a lagged deviation. The algebra will be familiar; the statistical justification is what this module supplies.
Pause and predict. If and both wander, what plot would help you decide whether a relationship connects them? Looking only at their separate levels cannot answer the question. We need to inspect an economically meaningful combination.
10.2 A Drunk, Her Dog, and a Stable Separation — Core
Murray begins with a woman and a puppy wandering separately. A random walk captures the simplest version of their movement: the next location equals the previous location plus a new step. As the time since we last saw either one grows, uncertainty about that location grows too.
Now suppose the dog belongs to the woman. She calls; the dog responds. Each notices the separation and partly closes it. Their individual locations can still wander far from where they began, but locating one gives information about where the other is likely to be.
The stable object is their separation, not a particular point on the street. Murray explicitly emphasizes that the dog is not on a leash. This corrects the shorthand used in the Module 9 preview: a literal leash suggests a hard bound, whereas cointegration is a probabilistic property. A stationary gap can occasionally be large. It does not have to be zero, and a reunion is not guaranteed on a schedule.
Harry, Mouse, and Karen
Our course adaptation uses Harry, his puppy Mouse, and an independently wandering Karen. Let , , and denote their locations. Give Harry and Mouse a response to yesterday’s signed gap:
For our experiment, the three innovations are independent standard-normal draws across people and time. The coefficients and measure responses to the gap. If Mouse is ahead, the gap is positive: Harry’s correction moves him forward, and Mouse’s correction moves him back. New shocks can still push either one away.
These are data-generating equations, so their coefficients are unhatted and their innovations are . They describe how our artificial world works. The equations do not say that we know the dynamics of an actual person or dog.
Two distinctions will matter throughout the chapter. First, after correction is introduced, Harry and Mouse are generally no longer literal random walks with independent increments; yesterday’s separation helps predict their next steps. Second, both need not adjust. Harry could wander without responding while Mouse does the correcting. The existence of a stable relationship and the allocation of adjustment are different questions.
Think before plotting. If Harry and Mouse remain connected, which should look more stable: Mouse’s position, Harry’s position, or ? What should happen to ?
10.3 What Cointegration Means — Core
In this module, we study the common case in which two individual series are I(1), but a fixed, nontrivial linear combination is I(0):
The cointegrating relationship is . The cointegrating deviation, or equilibrium error, is . The word “equilibrium” here labels the reference relationship; it does not by itself prove that a market clears or that a structural economic model is correct.
The long-run intercept allows the relationship to have a nonzero average separation. The slope need not be one. For example, if with I(0) and I(1) , then is stationary. The unweighted difference is not.
The weights form a cointegrating vector. Multiplying both weights by any nonzero constant describes the same stationary relationship. Setting the coefficient on to one is a normalization: a way to write the relation, not a discovery that is caused by .
A stationary deviation is not necessarily white noise
Suppose . Deviations persist, but the stable AR coefficient prevents their uncertainty from accumulating without limit as it would in a random walk. Serial dependence in the levels-regression residuals is therefore not, by itself, evidence against cointegration. We will need to distinguish the persistence of the deviation from serial dependence left in a fitted test or dynamic equation’s innovations.
Population quantities and fitted quantities also differ. If we estimate the relationship by OLS,
The fitted residual estimates a deviation; it is not the unobserved innovation . Whether the population deviation is stationary is the question the cointegration test will address.
Let be a random walk, and let be jointly stationary processes. Construct
with nonzero . Both series inherit the random walk. Yet
is stationary. The combination removes the common stochastic trend. Two independent random walks do not generally provide a common trend that a fixed nonzero pair of weights can cancel. This is why two series being I(1) is necessary for our baseline setup but is not sufficient for cointegration.
10.4 What Cointegration Is Not — Core
| Observation or claim | What is missing? |
|---|---|
| “The levels have a correlation of 0.99.” | A large correlation does not establish a stationary deviation. |
| “Both series trend upward.” | Similar deterministic trends are not shared stochastic trends. |
| “Both series are I(1).” | A fixed combination must remove their nonstationarity. |
| “Two stationary series have a stationary difference.” | That is not the I(1)-to-I(0) reduction at issue here. |
| “The estimated slope is significant.” | Ordinary levels-regression significance is not a cointegration test. |
| “The estimated adjustment coefficient is negative.” | Its sign alone does not establish cointegration or stability of all the dynamics. |
Two trend-stationary series, such as and , can have a stationary combination. Their nonstationarity comes from deterministic time trends, however, rather than the I(1) stochastic trends in our baseline definition. First identify the source of nonstationarity; do not label every cancellation of visible trends as the same problem.
They need not move in the same direction
Murray’s teeter-totter variation makes this vivid. If a woman’s position and a dog’s position must offset one another to maintain balance, the stable object can be a sum, rather than a difference. Unequal weights require an appropriately weighted sum. In our notation, this corresponds to a negative . Cointegration does not require a positive long-run slope or side-by-side movement.
A statistical relationship is not a causal effect
A stationary combination can arise from a common driving process, a behavioral relationship, an accounting connection, or a reduced-form relationship among several mechanisms. The cointegration property alone does not distinguish those explanations. Placing on the right-hand side does not make it exogenous, and estimating does not automatically identify what would happen if a policymaker changed .
Nor does cointegration imply perfect tracking. A stationary gap may be persistent or economically large. A historical relationship may change after a regime shift, and a model containing it need not win an out-of-sample forecasting comparison. Those are further questions to investigate.
10.5 Why Economists Use Cointegration — Core
Economic reasoning often says more about relationships that should persist than about every short-run movement. Cointegration lets us ask whether the data support a stable long-run combination while allowing the individual levels to be nonstationary. It is useful for assessing economic propositions, estimating long-run relationships, and constructing dynamic models that retain those relationships.
| Application | Candidate stable object | Economic motivation |
|---|---|---|
| Consumption and income | Consumption and resources may share long-run movements despite temporary saving or borrowing. | |
| Interest rates of different maturities | Long and short yields may share persistent movements while their spread fluctuates. | |
| Purchasing-power parity (PPP) | The log real exchange rate may return toward a stable distribution. | |
| Money demand | A combination of real balances, income, and interest rates | Transactions demand and opportunity cost may support a stable long-run money-demand relationship. |
Here is the log domestic-currency price of foreign currency, is the log domestic price level, and is the log foreign price level. Under that exchange-rate convention, is the log real exchange rate. Always define the exchange-rate quote before interpreting the signs.
How it has been used
Engle and Granger (1987, pp. 271–274) examine U.S. consumption and income, short- and long-term interest rates, and monetary aggregates with nominal income. Their consumption application uses quarterly real per-capita nondurable consumption and disposable income from 1947Q1 to 1981Q2; they report evidence of cointegration and estimate adjustment equations. Their interest-rate example also reports evidence of a long-run relationship. Results for money depend on the aggregate being examined. These are findings for particular definitions, periods, and procedures, not permanent properties guaranteed by the variables’ names.
PPP provides a useful counterweight. Hamilton’s U.S.–Italy example (pp. 598–600) finds little evidence of cointegration in its 1973–1989 sample, despite a plausible economic motivation and seemingly precise levels coefficients. Enders’ PPP discussion (pp. 370–373) likewise makes country and exchange-rate regime part of the empirical question. Economic plausibility tells us what to investigate; it does not determine what a test must find.
Existence versus the coefficients a theory requires
Suppose consumption and income are cointegrated with some slope . That is weaker than establishing the particular restriction , which would make stationary. In logs, a long-run slope is an elasticity-like association, not a dollar marginal propensity to consume and not automatically a causal elasticity.
Similarly, cointegration of two yields with an unrestricted slope does not establish that the simple yield spread is stationary. PPP specifies particular weights on exchange rates and price levels; finding some cointegrating vector is weaker than validating those weights. Testing economic coefficient restrictions requires appropriate additional inference. An ordinary OLS slope t-test is not a shortcut to that conclusion.
Murray makes this point through the story: an estimated relationship with an implausible relative weight needs an economic explanation, not merely a statistical label. The useful workflow begins with a proposed relationship and returns to that interpretation after testing.
10.6 Simulate the Relationship, Then Break It — Core
We know the truth in a simulation, so it lets us distinguish a population property from the evidence one sample happens to provide. We will build the three-walker model, reuse the same shocks across adjustment settings, and switch the connection off. This is a new course illustration of Murray’s mechanism, not a reproduction of his Figure 1.
Build the mechanism as a loop
The function takes the shocks as an input. Keeping them fixed means differences between scenarios come from the adjustment coefficients rather than a new set of random draws. The three columns still contain independent innovations within each scenario.
walkers <- function(shocks, adjust_h = 0.1, adjust_m = 0.1, burn = 500L) {
stopifnot(is.matrix(shocks), ncol(shocks) == 3L,
all(is.finite(shocks)), burn >= 0L, burn == as.integer(burn),
nrow(shocks) > burn + 2L,
adjust_h >= 0, adjust_m >= 0, adjust_h + adjust_m < 2)
n <- nrow(shocks)
h <- m <- k <- numeric(n + 1L)
for (t in seq_len(n)) {
gap <- m[t] - h[t]
h[t + 1L] <- h[t] + adjust_h * gap + shocks[t, 1L]
m[t + 1L] <- m[t] - adjust_m * gap + shocks[t, 2L]
k[t + 1L] <- k[t] + shocks[t, 3L]
}
keep <- (burn + 2L):(n + 1L)
data.frame(t = seq_along(keep), Harry = h[keep], Mouse = m[keep],
Karen = k[keep], gap = m[keep] - h[keep])
}
set.seed(637610)
shocks <- matrix(rnorm(3 * 1240), ncol = 3)
connected <- walkers(shocks, adjust_h = 0.1, adjust_m = 0.1, burn = 1000)
slow <- walkers(shocks, adjust_h = 0.005, adjust_m = 0.005, burn = 1000)
unconnected <- walkers(shocks, adjust_h = 0, adjust_m = 0, burn = 1000)We keep 240 observations after a 1,000-step initialization period. For the stable gaps this reduces the effect of starting both locations at zero. It cannot turn the disconnected gap into a stationary process. The baseline combined correction is ; the slow case uses .
locations <- data.frame(
t = rep(connected$t, 3),
location = c(connected$Harry, connected$Mouse, connected$Karen),
walker = rep(c("Harry", "Mouse", "Karen"), each = nrow(connected)))
ggplot(locations, aes(t, location, colour = walker)) +
geom_line(linewidth = 0.65) +
labs(x = "Retained observation", y = "Location", colour = NULL)The levels alone can suggest a relationship. Now examine the candidate deviations. We show the same units on all panels so that the scale of the independent separation remains visible.
gaps <- data.frame(
t = rep(connected$t, 4),
gap = c(connected$gap, slow$gap, unconnected$gap,
connected$Karen - connected$Harry),
case = rep(c("Mouse - Harry: correction 0.20",
"Mouse - Harry: correction 0.01",
"Mouse - Harry: correction off",
"Karen - Harry: separate wanderer"), each = nrow(connected)))
ggplot(gaps, aes(t, gap)) + geom_hline(yintercept = 0, colour = "grey65") +
geom_line(colour = "#176B87", linewidth = 0.6) +
facet_wrap(~ case, ncol = 1) +
labs(x = "Retained observation", y = "Signed separation")The Mouse–Harry cases with correction 0.20 (connected) and correction 0.01 (slow) have stable population gaps under the stated DGP. Mouse–Harry with correction off is the difference between two independent random walks. Karen–Harry compares a separately wandering Karen with Harry. A short path can still mislead us: slow correction produces a persistent gap that may look like a unit root.
What do the changes show?
changes <- data.frame(t = rep(connected$t[-1], 2),
change = c(diff(connected$Harry), diff(connected$Mouse)),
walker = rep(c("Harry", "Mouse"), each = nrow(connected) - 1))
ggplot(changes, aes(t, change, colour = walker)) + geom_line(linewidth = 0.55) +
facet_wrap(~walker, ncol = 1) +
labs(x = "Retained observation", y = "One-period change", colour = NULL) +
guides(colour = "none")The growth or change plots do not display the accumulated separation. To ask whether a large separation predicts the next step, we must retain a measure of that separation in the model. This is the reason for error correction, not a claim that differencing the observations is inherently wrong.
Try a deliberate change. Set Harry’s response to zero and keep Mouse’s response at 0.2. Predict the behavior of the gap before rerunning the plots. Then compare the first 60 retained observations with all 240. Changing the window changes the evidence available, not the DGP’s integration property.
Subtract Harry’s change from Mouse’s and define :
The AR coefficient is . With independent white-noise innovations, the gap has a stable stationary solution when , or . Starting at zero creates a transient; the initialization period reduces its influence in the displayed stable examples. Positive responses alone are not sufficient if combined correction overshoots too strongly. With , the gap is a random walk.
For , the weighted location has changes driven only by a weighted sum of the new shocks. This is the remaining common stochastic trend. The stable gap and the wandering common location can coexist.
This derivation is our explanatory development of Murray’s equations. It also translates the course’s root convention: the characteristic coefficient lies inside the unit circle, while the root of lies outside it.
10.7 Testing for Cointegration: Engle–Granger — Core
In real data, the population relationship and its integration properties are unknown. Engle–Granger turns the question into a sequence: assess the individual series, estimate a candidate levels relationship, and test whether its fitted deviation still has a unit root.
Step 1: Specify the question and assess integration
Start with definitions and plots. Are the variables in compatible units and frequencies? Do they represent the economic relationship being proposed? Are there missing dates or major regime changes? Our baseline concerns two plausibly I(1) variables. Use Module 2’s tools on levels and first differences, with deterministic terms suited to each hypothesis.
A unit-root nonrejection is not proof of I(1). A trend-stationary series, a break, or a short sample can complicate the evidence. If one series is clearly I(0), or a series appears I(2), the simple bivariate I(1) procedure taught here is not justified by applying it mechanically.
Step 2: Estimate the candidate levels relationship
Fit
and save . We use an intercept but no time trend in this baseline. The intercept allows a nonzero mean relationship; excluding a trend makes the proposed deviation fluctuate around that relationship rather than around a separately drifting one.
OLS is a way to estimate the candidate combination. Its , conventional slope standard error, and usual significance stars do not determine whether the residual is stationary. The first stage supplies an object to test; it does not settle the question.
Step 3: Test the estimated deviation
Fit the residual ADF regression, with lagged differences:
This is a test-regression specification; fitted coefficients carry hats and its fitted disturbance is an ADF residual, distinct from the first-stage residual . The coefficient’s t-ratio is . We retain the ratio but use a different reference distribution from an ordinary regression t-test.
The hypotheses are:
Under the maintained I(1) setup, the null leaves a unit root in the deviation. Sufficiently negative values favor stationary reversion. Lagged differences accommodate short-run dependence; without enough of them, the test’s disturbance can retain serial correlation. With too many, we spend observations and degrees of freedom.
The residual equation has no additional intercept or trend in our implementation: the first-stage intercept has already been projected out. This does not mean its critical values are the ordinary no-intercept ADF values. The null distribution still reflects estimation of the first-stage relationship, including its deterministic terms and number of integrated variables.
Do not apply the ordinary ADF p-value or the critical values printed by urca::ur.df() to these estimated residuals. The fitted relationship was chosen from the same data, so residual-based cointegration inference requires its own calibration. ur.df() can help verify the regression statistic; its default reference values do not become Engle–Granger values just because the input is a residual vector.
Build a transparent, deliberately narrow test
Our helper covers two variables, an intercept without a trend in the levels regression, and a 5% decision. It is not a general-purpose cointegration package. For a different deterministic specification or more variables, use a matching procedure and critical-value family.
MacKinnon’s (2010) response surface gives the bivariate constant-case 5% critical value as
We use , where is the number of levels observations, following the statsmodels Engle–Granger implementation convention. This is a sample-size approximation to the reference distribution, not a promise of exact 5% rejection in every finite sample. The ADF regression’s actual row count is reported separately. We do not substitute its residual degrees of freedom into this formula.
eg_cv5 <- function(n_levels) {
# MacKinnon (2010), Table 2: N=2, tau_c (intercept, no trend).
# n_levels-1 follows the statsmodels residual-cointegration convention.
stopifnot(length(n_levels) == 1L, is.finite(n_levels),
n_levels == as.integer(n_levels), n_levels >= 30L)
n_cv <- n_levels - 1L
-3.33613 - 6.1101 / n_cv - 6.823 / n_cv^2
}
eg_test <- function(y, x, p_adf = 4L) {
# Inputs must already be complete, aligned, equally spaced observations.
stopifnot(is.numeric(y), is.numeric(x), length(y) == length(x),
all(is.finite(y)), all(is.finite(x)), sd(x) > 0,
length(p_adf) == 1L, is.finite(p_adf), p_adf >= 0L,
p_adf == as.integer(p_adf), length(y) > 2L * p_adf + 5L)
n <- length(y)
levels_fit <- lm(y ~ x)
e <- residuals(levels_fit)
block <- embed(diff(e), p_adf + 1L)
adf_data <- data.frame(de = block[, 1L], e_lag = e[(p_adf + 1L):(n - 1L)])
if (p_adf > 0L) {
lag_names <- paste0("de_lag", seq_len(p_adf))
adf_data[lag_names] <- as.data.frame(block[, -1L, drop = FALSE])
}
adf_fit <- lm(de ~ . - 1, data = adf_data)
tau <- unname(coef(summary(adf_fit))["e_lag", "t value"])
cv <- eg_cv5(n)
list(levels = levels_fit, residuals = e, adf = adf_fit,
statistic = tau, cv5 = cv, reject5 = tau < cv,
p_adf = p_adf, n_levels = n, n_adf = nrow(adf_data))
}The indexing is worth reading. One difference removes the first level observation. Including lagged differences removes another observations. With levels, the test regression uses rows. The lagged level must refer to the period immediately preceding each dependent difference; it cannot be aligned by position after unrelated missing-value deletions.
Apply the same test to our known worlds
We use one lagged difference for all three simulation settings, without changing it after seeing their results.
sim_pairs <- list(Connected = connected, Slow = slow, Disconnected = unconnected)
sim_tests <- lapply(sim_pairs, function(d) eg_test(d$Mouse, d$Harry, p_adf = 1))
sim_table <- data.frame(
Case = names(sim_tests),
Statistic = vapply(sim_tests, `[[`, numeric(1), "statistic"),
Critical_5pct = vapply(sim_tests, `[[`, numeric(1), "cv5"),
Reject_no_cointegration = vapply(sim_tests, `[[`, logical(1), "reject5"))
knitr::kable(sim_table, digits = 3, row.names = FALSE)| Case | Statistic | Critical_5pct | Reject_no_cointegration |
|---|---|---|---|
| Connected | -4.767 | -3.362 | TRUE |
| Slow | -2.731 | -3.362 | FALSE |
| Disconnected | -2.357 | -3.362 | FALSE |
The connected and slow worlds are cointegrated by construction; the disconnected world is not. A test outcome can disagree with those truths. A failure to reject in the slow case is a power problem in this sample, not a change in the DGP. A rejection in a disconnected world would be a false positive. We do not change the seed to obtain more convenient teaching results.
Step 4: Interpret the evidence and inspect the specification
Rejection supports cointegration under the maintained specification. Nonrejection means the test has not supplied sufficient evidence of cointegration; it does not establish that the variables lack an economic relationship. Check residual dependence in the test regression and report sensible lag/specification sensitivity. Running many versions and presenting only the one that rejects disguises exploratory choices as confirmatory evidence.
The stationary deviation itself may have autocorrelation. The relevant diagnostic question for the augmented test regression is whether its remaining disturbance still has substantial serial dependence. These are different residual plots answering different questions.
If an economic theory specifies the weights in advance, we can construct that observed spread and test it using an appropriate ordinary unit-root procedure. For example, testing a pre-specified yield difference differs from first estimating a slope and then testing the fitted residual. An ordinary ADF intercept may account for an unknown mean of the observed spread. The key distinction is whether the linear combination was fixed independently of these data or estimated to fit them. Hamilton emphasizes this distinction in §19.2.
An unrestricted cointegration finding also does not establish a particular slope restriction. Inference on an estimated cointegrating vector needs its own appropriate method.
10.8 Error Correction: Why Yesterday’s Gap Matters — Core
Cointegration tells us about a stable relationship among levels. An error-correction model (ECM) describes how changes respond to departures from that relationship while allowing additional short-run dynamics.
Start with the Module 8 ARDL(1,1):
Subtract and write :
For , define
Then
The first term responds to the previous gap; the second describes the conditional association with the current change in . More general models add lagged changes in and . The long-run relationship and the short-run dynamics play different roles.
Read the sign and magnitude in the equation you actually wrote
With , a positive gap means was high relative to the relationship. A negative gives a downward contribution to its next change. If and the gap is one unit, that contribution is units, holding the other terms fixed. A gap of contributes units.
This does not promise that the observed gap will shrink by exactly 20% next period. New shocks, changes in , and additional lagged terms also matter. Reversing the sign used to define the gap reverses the sign of its coefficient without changing the fitted contribution. The stability of the entire dynamic specification cannot be inferred from a single negative coefficient.
Estimate adjustment in the world whose truth we know
In our connected simulation, take and . The true relationship is , and Mouse’s structural response in the generating equation is . Because the contemporaneous innovations were explicitly independent, Harry’s current innovation does not create an omitted contemporaneous shock in Mouse’s equation. That condition belongs to this constructed example; it is not inherited automatically by economic data.
sim_eg <- sim_tests$Connected
n_sim <- nrow(connected)
ecm_data <- data.frame(
dm = diff(connected$Mouse),
dh = diff(connected$Harry),
e_lag = sim_eg$residuals[-n_sim])
ecm_fit <- lm(dm ~ e_lag + dh, data = ecm_data)
ecm_coefficients <- data.frame(
Term = c("Intercept", "Previous estimated gap", "Current change in Harry"),
Estimate = unname(coef(ecm_fit)))
knitr::kable(ecm_coefficients, digits = 3, row.names = FALSE)| Term | Estimate |
|---|---|
| Intercept | 0.014 |
| Previous estimated gap | -0.151 |
| Current change in Harry | -0.060 |
The estimated equation uses in place of the unknown population gap. Its adjustment estimate is -0.151. It need not equal the generating value exactly: we observe a finite path and estimate both the relationship and its dynamics. The coefficient on the current change in Harry should be interpreted conditional on the gap, not as a general claim that Harry does or does not affect Mouse.
correction <- data.frame(gap = ecm_data$e_lag,
contribution = coef(ecm_fit)["e_lag"] * ecm_data$e_lag)
ggplot(correction, aes(gap, contribution)) +
geom_hline(yintercept = 0, colour = "grey70") +
geom_vline(xintercept = 0, colour = "grey70") +
geom_point(colour = "#176B87", alpha = 0.6) +
labs(x = "Previous estimated gap", y = "Fitted contribution to change in Mouse")What this adds to Module 9
Predictive information can enter through lagged changes and through a lagged disequilibrium. Omitting the latter can leave a differences-only model missing a useful predictor. Conversely, one equation may have no adjustment even though another variable does the adjusting. A weak estimated response in the chosen outcome equation does not by itself refute cointegration.
The equation above includes current . That is a different information set from Module 9’s past-only Granger comparison. Interpreting or estimating a conditional equation requires suitable assumptions about contemporaneous dependence and exogeneity. Error correction does not remove those questions.
An ARDL can be rearranged into ECM-shaped algebra whether or not a cointegrating relationship exists. For a cointegration interpretation, the proposed deviation must actually be stationary and the dynamic model must be appropriate. Neither a neat rearrangement nor a significant adjustment coefficient substitutes for that analysis.
10.9 Consumption and Income: A Complete Economic Workflow — Core
Our question is whether U.S. real consumption and real disposable income support a stable long-run log relationship over a particular historical sample. We will follow the workflow even if the result is less decisive than the story suggests.
Define the data before fitting the relationship
The supplied archive contains 203 quarterly observations from 1959Q1 to 2009Q3. Consumption is total real personal consumption expenditures, and income is real disposable income; both are billions of chained 2005 dollars at seasonally adjusted annual rates. These are aggregate series, not per-capita measures. The archive was compiled from FRED in December 2009 and is distributed with the public-domain statsmodels macroeconomic dataset. Reading the supplied CSV requires only R.
This is a teaching application, not a replication of Engle–Granger’s earlier per-capita nondurables sample. It is also not a fresh pull of today’s revised data. The accompanying data note documents the archive and export.
ci <- read.csv(data_file)
stopifnot(nrow(ci) == 203, all(complete.cases(ci)),
all(ci$realcons > 0), all(ci$realdpi > 0),
all(diff(4 * ci$year + ci$quarter) == 1))
ci$date <- as.Date(sprintf("%d-%02d-01", ci$year, 3 * ci$quarter - 2))
ci$lc <- log(ci$realcons)
ci$ly <- log(ci$realdpi)
ci_plot <- data.frame(date = rep(ci$date, 2),
log_level = c(ci$lc, ci$ly),
series = rep(c("Consumption", "Disposable income"), each = nrow(ci)))
ggplot(ci_plot, aes(date, log_level, colour = series)) + geom_line(linewidth = 0.7) +
labs(x = NULL, y = "Natural log of real level", colour = NULL)The variables rise together over much of the sample. That is a reason to investigate, not a test result. Taking logs makes the slope describe proportional long-run association; it does not remove a stochastic trend.
Assess levels and changes
For the individual log levels, include an intercept and time trend in the ADF regression, allowing a trend-stationary alternative. For first differences, include an intercept. Use four lagged differences in each as a fixed quarterly teaching specification; this is not a claim that four is universally correct.
unit_tests <- list(
"Log consumption: level" = urca::ur.df(ci$lc, type = "trend", lags = 4),
"Log income: level" = urca::ur.df(ci$ly, type = "trend", lags = 4),
"Log consumption: change" = urca::ur.df(diff(ci$lc), type = "drift", lags = 4),
"Log income: change" = urca::ur.df(diff(ci$ly), type = "drift", lags = 4))
unit_table <- data.frame(
Series = names(unit_tests),
ADF_statistic = vapply(unit_tests, function(u) as.numeric(u@teststat[1]), numeric(1)),
Critical_5pct = vapply(unit_tests, function(u) as.numeric(u@cval[1, "5pct"]), numeric(1)))
unit_table$Reject_unit_root <- unit_table$ADF_statistic < unit_table$Critical_5pct
knitr::kable(unit_table, digits = 3, row.names = FALSE)| Series | ADF_statistic | Critical_5pct | Reject_unit_root |
|---|---|---|---|
| Log consumption: level | -2.435 | -3.43 | FALSE |
| Log income: level | -1.908 | -3.43 | FALSE |
| Log consumption: change | -5.082 | -2.88 | TRUE |
| Log income: change | -6.351 | -2.88 | TRUE |
These results are consistent with treating both log levels as I(1): the level tests do not reject, while the differences do. That is a working assessment rather than proof. Deterministic specification, breaks, and power still matter. The ordinary ADF critical values are appropriate to these individual-series tests; they are not the values we will use for estimated cointegrating residuals.
ci_changes <- data.frame(date = rep(ci$date[-1], 2),
growth = c(100 * diff(ci$lc), 100 * diff(ci$ly)),
series = rep(c("Consumption", "Disposable income"), each = nrow(ci) - 1))
ggplot(ci_changes, aes(date, growth, colour = series)) + geom_line(linewidth = 0.55) +
facet_wrap(~series, ncol = 1) +
labs(x = NULL, y = "100 x quarterly log change", colour = NULL) +
guides(colour = "none")Estimate the levels relationship, then test its residual
The proposed long-run relation includes an intercept but no separate time trend: a stable log relationship is the economic proposition being examined. The trend in the individual-series ADF regressions served a different question and does not force a trend into this relationship.
ci_eg <- eg_test(y = ci$lc, x = ci$ly, p_adf = 4)
level_summary <- data.frame(
Quantity = c("Estimated intercept", "Estimated long-run slope", "Levels R-squared",
"Levels observations", "Residual-ADF observations"),
Value = c(coef(ci_eg$levels), summary(ci_eg$levels)$r.squared,
ci_eg$n_levels, ci_eg$n_adf))
knitr::kable(level_summary, digits = 4, row.names = FALSE)| Quantity | Value |
|---|---|
| Estimated intercept | -0.3758 |
| Estimated long-run slope | 1.0320 |
| Levels R-squared | 0.9984 |
| Levels observations | 203.0000 |
| Residual-ADF observations | 198.0000 |
ci$eg_residual <- ci_eg$residuals
ggplot(ci, aes(date, eg_residual)) +
geom_hline(yintercept = 0, colour = "grey65") +
geom_line(colour = "#176B87", linewidth = 0.65) +
labs(x = NULL, y = "Estimated log consumption-income deviation")The fitted slope is 1.032, and is 0.9984. Neither settles whether the deviation is stationary. The residual ADF statistic is -2.589, compared with the matching 5% cointegration critical value -3.367. Since the statistic is not sufficiently negative, this baseline does not reject no cointegration.
The distinction is the point of the exercise: visually close levels and an excellent in-sample fit coexist with insufficient evidence from the specified cointegration test. We have not shown that consumption and income are unrelated.
Inspect the test regression and report sensitivity
The following ACF is for the remaining residuals of the augmented test regression, not the deviation plotted above. A spike does not by itself diagnose the entire model, but persistent remaining dependence would question whether the augmentation was adequate.
adf_residuals <- residuals(ci_eg$adf)
acf_obj <- acf(adf_residuals, lag.max = 12, plot = FALSE)
acf_data <- data.frame(lag = 1:12, acf = as.numeric(acf_obj$acf)[-1])
ggplot(acf_data, aes(lag, acf)) +
geom_hline(yintercept = 0, colour = "grey65") +
geom_hline(yintercept = c(-1, 1) * 1.96 / sqrt(length(adf_residuals)),
linetype = "dashed", colour = "grey50") +
geom_segment(aes(xend = lag, yend = 0), colour = "#176B87") +
scale_x_continuous(breaks = 1:12) + labs(x = "Lag", y = "Residual ACF")We examine a small declared set of lag lengths on the same 203 levels observations. The residual-ADF row counts differ because each augmentation uses its own lag history. These are sensitivity specifications, not a nested-model RSS comparison requiring an identical dependent sample.
lag_set <- c(0, 2, 4, 8)
sensitivity_fits <- lapply(lag_set, function(p) eg_test(ci$lc, ci$ly, p_adf = p))
sensitivity <- data.frame(
Lagged_differences = lag_set,
ADF_rows = vapply(sensitivity_fits, `[[`, integer(1), "n_adf"),
Statistic = vapply(sensitivity_fits, `[[`, numeric(1), "statistic"),
Critical_5pct = vapply(sensitivity_fits, `[[`, numeric(1), "cv5"),
Reject_no_cointegration = vapply(sensitivity_fits, `[[`, logical(1), "reject5"))
knitr::kable(sensitivity, digits = 3, row.names = FALSE)| Lagged_differences | ADF_rows | Statistic | Critical_5pct | Reject_no_cointegration |
|---|---|---|---|---|
| 0 | 202 | -3.397 | -3.367 | TRUE |
| 2 | 200 | -2.898 | -3.367 | FALSE |
| 4 | 198 | -2.589 | -3.367 | FALSE |
| 8 | 194 | -2.032 | -3.367 | FALSE |
reverse_eg <- eg_test(y = ci$ly, x = ci$lc, p_adf = 4)The zero-lag version rejects narrowly, whereas the two-, four-, and eight-lag versions do not. We report that variation rather than promoting the favorable result to the headline. The reverse normalization at four lags gives -2.626 against the same critical value. Engle–Granger can be sensitive to which variable is normalized on in a finite sample; reversing the equation is a check on that sensitivity, not a test of causal direction.
The zero-lag test regression leaves residual autocorrelations of and at lags 1 and 2, both outside its approximate descriptive reference bands; the four-lag regression’s corresponding correlations are and . The unaugmented rejection therefore has weaker diagnostic support, although these descriptive checks do not establish that the four-lag residuals are white noise.
Decide what model the evidence warrants
The known simulation provided a setting in which an ECM was justified by construction. The baseline economic application has not supplied enough evidence to adopt a stable cointegrating relation as an established feature of this sample. We therefore stop short of presenting a fitted economic ECM as a validated long-run model. A changes-based model is a candidate for further analysis, while the possibility of a long-run relation remains unresolved. Nonrejection does not prove that its adjustment term is zero.
Further work could examine the economic specification, breaks, alternative defensible deterministic assumptions, or additional variables. Those changes answer additional questions and should be documented as such. A break may invalidate a fixed relationship over the whole sample; the present exercise has not conducted a formal break-robust cointegration test.
A defensible conclusion: “For the archived 1959Q1–2009Q3 U.S. consumption and income data, the individual-series evidence is consistent with an I(1) working specification. The intercept-only Engle–Granger test with four lagged residual differences does not reject no cointegration at 5%. The result varies with lag specification, so the high levels is insufficient to support a stable long-run relation. This does not establish that consumption and income lack an economic relationship.”
10.10 Limits and Defensible Reporting — Core
Keep three questions separate. Existence asks whether there is evidence of a stationary combination. Estimation asks what relationship and dynamics the chosen procedure estimates. Interpretation asks which economic claims follow and which require additional assumptions. Solving one does not automatically solve the others.
Short samples and slow correction make persistent stationary gaps difficult to distinguish from unit roots. Breaks can make a historically useful relation fail over a longer interval. A bivariate model can omit wealth, prices, or another variable needed for the proposed relationship. Measurement changes, seasonal patterns, and the choice of deterministic terms can alter the question being tested.
A useful report states:
- Question and data: variables, units, transformations, dates, frequency, and data vintage.
- Integration assessment: what plots and tests support the I(1) working setup, and what remains uncertain.
- Cointegration specification: estimated or pre-specified weights, deterministic terms, normalization, augmentation, and correct reference values.
- Evidence: statistic, decision, diagnostics, and declared sensitivity checks, including inconvenient results.
- Model and interpretation: whether an ECM is supported, how adjustment is defined, and the limits of any forecasting or causal claim.
Under standard cointegration conditions, OLS can estimate the long-run slope at a faster rate than the usual square-root-sample-size rate; this is called superconsistency. The claim concerns convergence of the estimator. It does not generally give ordinary OLS t-statistics their familiar reference distribution. Serial dependence and endogeneity can affect inference on the cointegrating vector, and finite-sample error can still be consequential.
Similarly, treating a regressor as weakly exogenous for inference about long-run parameters is an additional condition, not a consequence of placing it on the right-hand side. Appropriate long-run inference and system modeling are developed in the references and later modules. For now, do not turn a precise-looking levels regression into an unqualified parameter test or structural claim.
Phillips–Ouliaris is another approach to residual-based cointegration testing. urca::ca.po() implements its and statistics; it is not an Engle–Granger residual-ADF test. Its statistic, correction for dependence, critical values, and rejection rule must be read as that procedure’s own. It can provide an additional specification check, but combining methods does not mean choosing whichever gives the preferred answer. The version is invariant to normalization. See the package documentation and Phillips and Ouliaris (1990).
10.11 Core Practice and the Next Step
Core Practice
- Both and are I(1), and with stationary . Identify the stationary combination. Must be stationary? Must the levels move in the same direction?
- Two unrelated random walks have a sample correlation of 0.97 and a highly significant conventional OLS slope. Explain why neither is a cointegration finding.
- A student says the dog must always remain within five meters of the woman. Explain how that changes Murray’s probabilistic story.
- Write the null of an Engle–Granger test. Explain why an ordinary ADF critical value is inappropriate when the tested residual was constructed from an estimated levels regression.
- A residual test statistic is and its matching 5% cointegration critical value is . State the decision and write a conclusion that does not confuse nonrejection with proof.
- In , let . What is the correction’s contribution when the previous gap is ? Why need the observed change not equal that contribution?
- A pair is cointegrated, but the adjustment coefficient in one equation is zero. Explain how this can happen and what else you would need to know.
- An estimated cointegrating slope between two yields differs from one. Explain the difference between evidence of cointegration and evidence of a stationary simple spread. Why is neither automatically a complete validation of the expectations hypothesis?
- Explain the two distinct mistakes in “the levels regression has high , so it is valid” and “the series have unit roots, so all levels information should be discarded.”
- Use the consumption-income results to write a five-sentence report containing the data definition, working integration assessment, baseline test, sensitivity, and limitation. Do not select the zero-lag result simply because it rejects.
With more variables, there can be several independent stationary combinations. The cointegrating rank is the number of such independent relationships. With two I(1) variables, there is at most one; multiplying a vector by two does not create a second independent relationship.
Johansen’s approach asks about rank in a system of dynamic equations. It handles a different scope from estimating one normalized relation and testing its residual. We will first build VAR mechanics in Module 11, then return to VECMs in Module 13. The scalar ECM here is the entry point: a system allows several variables to respond to one or more previous disequilibria. Matrix estimation and trace/max-eigenvalue testing can wait until that machinery has a purpose.
Common Pitfalls — Core
- Looking for a stationary raw difference when the appropriate weights are not .
- Treating a stationary deviation as if it must be white noise, small, or always zero.
- Comparing an estimated residual’s ADF statistic with ordinary univariate critical values.
- Reading ordinary levels-regression significance as a test of cointegration or a theory’s coefficient restrictions.
- Calling nonrejection proof of no economic relationship.
- Choosing a lag, sample, or normalization after seeing which one rejects and presenting that choice as predetermined.
- Treating an ECM rearrangement or a negative coefficient as proof that the relationship is stationary.
- Confusing normalization, predictive adjustment, exogeneity, and an intervention effect.
Connection to the Textbooks and Sources
- Murray, M. P. (1994), “A Drunk and Her Dog: An Illustration of Cointegration and Error Correction,” The American Statistician, 48(1), 37–39. Accompanying local reading; published record. Pages 37–38 provide the story, mutual correction, no-leash qualification, weighted-combination extension, and the lesson about omitting the lagged relationship. Our simulation and gap-stability derivation develop those ideas in course notation.
- Enders, Applied Econometric Time Series, 4th ed., Chapter 6 §§1–6, pp. 344–373. Definitions and economics (§1), common trends (§2), error correction (§3), Engle–Granger (§4), simulation (§5), and PPP (§6). Sections 7–9 preview system methods. Translate the textbook’s cointegration/adjustment symbols to here.
- Hamilton, Time Series Analysis (1994), §§19.1–19.2, pp. 571–601. Common trends, error correction, known versus estimated vectors, and residual-based testing. Section 19.3 supplies optional depth on inference for long-run coefficients. His PPP and consumption examples are historical applications with their own samples.
- Engle, R. F., and Granger, C. W. J. (1987), “Co-integration and Error Correction: Representation, Estimation, and Testing,” Econometrica, 55(2), 251–276. Local source; published record. The discussion of economic applications above uses pp. 271–274.
- MacKinnon, J. G. (2010), “Critical Values for Cointegration Tests,” Queen’s Economics Department Working Paper 1227, reissuing the 1990 paper with an expanded appendix. Paper. The narrow helper uses the Table 2, , , 5% response-surface coefficients, cross-checked against the statsmodels implementation. Its finite-sample convention is stated explicitly above.
- Phillips, P. C. B., and Ouliaris, S. (1990), “Asymptotic Properties of Residual Based Tests for Cointegration,” Econometrica, 58(1), 165–193. Optional alternative procedure; see
urca::ca.po()documentation. - Data: statsmodels macrodata documentation and the supplied data note. Historical FRED archive, not a current-vintage download.
The course keeps for the ordinary equation intercept, for the normalized long-run slope, and for scalar adjustment, continuing Modules 8–9. Estimates carry hats; is a fitted residual; is an innovation. When comparing root conditions, remember that an AR characteristic coefficient inside the unit circle corresponds to a lag-polynomial root outside it.
Key Takeaways — Core
- Cointegration concerns a stationary combination of nonstationary series. Identify that combination, not merely the appearance of the separate levels.
- Economic theory motivates candidate long-run relations and sometimes their coefficients. Statistical cointegration alone does not validate every theoretical restriction or identify a causal effect.
- Engle–Granger estimates a levels relationship and tests its residual with the correct cointegration reference distribution.
- Error correction combines short-run changes with information in a previous deviation. It needs a defensible relationship and dynamic specification.
- A useful empirical analysis can end with unresolved evidence. Reporting that honestly is part of the workflow.
Next: Module 11 turns the scalar dynamic equations into a VAR system. That provides the framework needed later to model several adjusting variables and several long-run relationships together.