Stationarity and unit roots — when statistics on a time series mean anything
▸ Pretest — guess, even if you don't know
You regress Stock A's price on Stock B's price over five years of daily data and get R-squared 0.85 with a tiny p-value. What have you learned?
Stationarity: the license to estimate
Every statistic you compute on a time series — a mean, a volatility, an autocorrelation, a regression beta — is an average over time. Averaging over time only estimates something meaningful if the thing being estimated holds still. That requirement has a name. A series is weakly stationary if:
- its mean is constant over time,
- its variance is constant (and finite) over time,
- the covariance between and depends only on the lag , not on .
The trading question stationarity answers: is the past statistically like the future? If yes, history is a sample you can learn from. If no, your carefully estimated parameters describe a regime that no longer exists, and a model fit on the series produces garbage forecasts with confident-looking standard errors.
Prices are not stationary; returns roughly are
From D1-02, a random walk's variance is — it grows without bound, violating condition 2, and any drift violates condition 1. Prices, being approximately random walks, are non-stationary. Their first difference, though — returns — has roughly constant mean near zero, roughly constant unconditional variance, and autocorrelations that depend only on lag. Returns are approximately weakly stationary.
This is the general recipe: a series whose differences are stationary is called integrated of order one, and differencing (np.diff, or log-price to log-return) is the standard transformation that turns it into something you may legitimately compute statistics on.
Two honest caveats. Volatility clustering (D1-01) means the conditional variance of returns moves around, so returns are not iid — weak stationarity is a workable approximation, not a law. And markets have regimes: a mean and volatility estimated in 2017 described 2017. Stationarity is always an assumption you argue for, never a fact you observe.
Spurious regression: the classic trap
Why exactly does regressing price on price blow up? Both series wander (that is what random walks do), and over any given window two wandering series will drift in some loosely shared way — up-up, down-down, or up-down — so the regression finds a strong "relationship" in-sample. Worse, the regression residuals are themselves non-stationary, which violates the assumptions behind OLS standard errors (A2-06): the t-statistics and p-values are simply wrong, and R-squared stays high no matter how much data you add. Granger and Newbold's simulated independent walks produced about 75% of the time.
The trap wears many trading costumes: "my indicator is 0.9 correlated with the S&P," "these two coins move together" — computed on levels, all meaningless. The fix is mechanical: difference first. Regress returns on returns, changes on changes. (There is one important exception — sometimes a combination of two non-stationary prices is genuinely stationary, which is called cointegration and is the respectable foundation of pairs trading. D1-04 builds the model for that spread.)
Unit roots and the ADF test
Write the suspicion formally with next lesson's model: . If , this is exactly a random walk — the series is said to have a unit root and is non-stationary. If , shocks decay and the series is stationary. The Augmented Dickey–Fuller (ADF) test asks which world you are in. Read it carefully, because the direction trips people up:
- Null hypothesis: the series has a unit root (non-stationary).
- Small p-value → reject the null → evidence the series is stationary.
- Large p-value → you failed to reject; the series may well be a random walk.
In practice it is one line (shown for orientation — the exercise runtime here is numpy-only, so run this locally):
from statsmodels.tsa.stattools import adfuller
stat, pvalue, *_ = adfuller(x) # H0: unit root (non-stationary)
Typical output: SPY prices, p around 0.6 — cannot reject the unit root. SPY returns, p far below 0.01 — stationary. One warning: the ADF test has low power near the boundary, so it often cannot tell (slowly mean-reverting, tradeable) from (random walk, not tradeable). Failing to reject is absence of evidence, not proof of a unit root — and that ambiguity is exactly where many pairs trades go to die.
Try it
A crude but instructive stationarity check: a series whose mean drifts fails the most basic requirement.
Implement two functions. difference(x) returns the first difference of x, using np.diff. is_mean_stable(x) is a crude stationarity check: split x into a first half of len(x) // 2 points and a second half with the rest, and return True when the absolute gap between the two half means is at most 0.5 times the sample standard deviation of the whole series, np.std with ddof=1. Real work uses the ADF test; this builds the intuition that a stationary series has no drifting mean.
⧉ Review cardState the three conditions of weak stationarity and why they matter.
⧉ Review cardWhy are prices non-stationary while returns are roughly stationary?
⧉ Review cardWhat is spurious regression and what is the rule that prevents it?
⧉ Review cardWhat are the null hypothesis and reading of the ADF test?
Teach it
Teach spurious regression to an imaginary junior analyst who just proudly showed you a levels-on-levels regression with R-squared 0.9. In your own words: why do two independent random walks look related, what exactly breaks in the OLS standard errors, and what one-line rule should they follow from now on? Two minutes, out loud.
Predict before the next lesson
Next: autoregressive models (D1-04). Before then, predict: you want a model of a series that gets pulled back toward a long-run mean instead of wandering forever. How would you write next value as a function of the current value plus noise — and what coefficient value would turn your model back into a random walk?
◈ Calibration check
Could you define weak stationarity, explain to a colleague why regressing prices on prices is invalid, and state exactly what the ADF null hypothesis is?
1 = guessing · 5 = could teach it
⏻ End of lesson
Mark it read to book its 4 review cards into your deck.
Sources & further reading
- bookTsay (2010), Analysis of Financial Time Series, 3e — §2
- paperGranger & Newbold (1974), Spurious Regressions in Econometrics, Journal of Econometrics 2