How to Diagnose Stationarity in Prices and Returns
Original question
Can you explain the difference between stationarity and non-stationarity in financial time series with practical Python examples?
A weakly stationary time series has a constant mean, a constant finite variance, and a covariance between observations that depends only on their time separation. A random walk in log price is non-stationary because the uncertainty in its level accumulates. Its one-period log returns are stationary in the simplest model with independent, identically distributed shocks.
Imagine opening a chart at a randomly chosen point. If the series is stationary, the model gives you the same unconditional mean and variance wherever you begin. Recent observations can still change your forecast. An autoregressive series might have a constant long-run mean while a high observation today makes a high observation tomorrow more likely. Likewise, a stationary volatility model can assign different conditional variances to quiet and turbulent weeks.
Financial prices can wander far from their starting levels. Measuring the change over one day resets the comparison to a common horizon. This often makes returns a better starting point for analysis, but returns can still undergo structural changes in their unconditional mean or variance.
Take a difference and track what happens to variance
Let Pt denote log price and suppose Pt=Pt−1+εt, with P0=0 and independent shocks of mean zero and variance σ2>0. Expanding the recursion gives
The level's variance grows with t, and the covariance at a fixed lag depends on the starting date. In contrast, ΔPt=εt has variance σ2 and zero covariance at every nonzero lag. A unit-root series whose first difference is stationary is called integrated of order one, or I(1).
A process can also be non-stationary because of a deterministic trend. If Pt=βt+ut with stationary ut, subtracting the trend recovers ut. The distinction matters for forecasting because a shock to a random walk shifts its future expected level permanently, while a transitory shock around a trend can fade.
For the covariance condition, compare observations one step apart early and late in the random walk. P11=P10+ε11, and the new shock is independent of P10, so their covariance is Var(P10)=10σ2. The covariance of P50 and P51 is 50σ2, despite the same one-step lag. ADF and KPSS can help examine a sample, but their different null hypotheses make agreement more informative than a single p-value. A break or an overly short sample can make both tests hard to interpret.
Stationarity does not require every conditional forecast of variance to be constant. A stationary return process can have a stable unconditional variance while today's large shock raises tomorrow's conditional variance. That distinction is important if the price level looks non-stationary and the returns show clusters of large moves. Taking log differences addresses the changing scale of the level under a random-walk model. It need not remove predictable changes in the size of the returns. Check both the returns and their squares before deciding what to model.
Twenty days versus eighty days
Suppose daily log returns have zero mean and a standard deviation of 1%. The random-walk model assigns a standard deviation of 20×1%≈4.47% to cumulative log return after 20 trading days. After 80 days it gives about 8.94%. The daily log return has a 1% standard deviation at both dates. To see the difference in data, plot log prices and first differences, inspect their autocorrelations, and compare several calendar windows for changes.
The augmented Dickey–Fuller test uses a unit root as its null. With real series, select the deterministic terms and lag choices deliberately. KPSS tests a stationarity null and can help distinguish ambiguous cases.