Quant Memo
Core

Lag and Rolling-Window Features

Turning a time series into features a model can use means deciding how far back to look and how to summarise what you saw — get the boundary wrong and the model trains on information it wouldn't have had yet.

Prerequisites: Overfitting, Cross-Validation for Time Series

A standard machine learning model treats every row as an independent, interchangeable example. A row of daily stock data is not independent of yesterday's row — today's price is mechanically related to yesterday's. To use a tabular model like a gradient-boosted tree or a linear regression on time series data at all, you first have to convert the sequence into a table where each row still carries a memory of what came before it. That conversion is done with lag features and rolling-window features.

Lags: yesterday as a column

A lag feature is simply a past value of a series, shifted forward and placed as a new column in today's row. return_lag1 is yesterday's return, sitting in today's row; return_lag5 is the return from five trading days ago. A model can now see "what happened recently" as ordinary numeric inputs, without needing to understand sequences at all — the sequential structure has been flattened into the row.

Rolling windows: a summary, not a single point

A single lag only tells you about one specific day. A rolling-window feature summarises a stretch of history ending just before today: a 20-day rolling mean of returns, a 10-day rolling standard deviation (a realized-volatility feature), a 60-day rolling maximum drawdown. These smooth out single-day noise and often carry more signal than any one lag alone, at the cost of reacting more slowly to a genuine regime change.

Worked example

Given a return series r1,r2,r3,r4,r5=1.2%,0.5%,0.8%,2.1%,0.3%r_1, r_2, r_3, r_4, r_5 = 1.2\%, -0.5\%, 0.8\%, -2.1\%, 0.3\%, the row for day 5 would carry:

  • lag1 =2.1%= -2.1\% (day 4's return)
  • lag2 =0.8%= 0.8\% (day 3's return)
  • roll_mean_4 =(1.20.5+0.82.1)/4=0.15%= (1.2 - 0.5 + 0.8 - 2.1)/4 = -0.15\% (mean of days 1–4, not including day 5)
  • roll_std_4 computed the same way over days 1–4

Every one of these numbers is built only from information available strictly before day 5's return is realized. That last clause is the entire discipline this topic is about.

rolling window (days 1–4) target: day 5 window ends here target begins here
A correctly built window stops the instant before the target period starts. Any overlap between the window and the target day is leakage.

Every lag and rolling feature must be computable using only data strictly before the timestamp of the label it predicts. This one rule, applied consistently, is the difference between a feature set that works live and one that only ever worked in the backtest.

Where this goes wrong

The most common bug is an off-by-one on the rolling window: computing a "20-day rolling mean" using df.rolling(20).mean() without shifting it, so that today's row includes today's own return inside its own rolling average. In pandas terms, the fix is typically .rolling(20).mean().shift(1) — shift the whole feature forward by one period so today's row only sees data through yesterday. Skipped, this silently leaks a small piece of the label into the feature: the model performs suspiciously well in backtest and degrades sharply live, because live data has no future value to peek at. This is a specific instance of the Point-in-Time Data discipline, and it interacts directly with Cross-Validation for Time Series: even a correctly-lagged feature can leak across a naive random train/test split, because a rolling window computed near the boundary of a random test fold can still touch training-set days that sit chronologically after some test rows.

Rolling and lag features also change the effective length of your usable dataset. A 60-day rolling feature makes the first 60 rows of your history unusable (there is no full window to compute them from), and if you impute those early rows instead of dropping them, you have manufactured data that never existed — treat the imputation choice with the same care as in Missing Data and Imputation.

Longer windows trade responsiveness for stability; shorter windows and more lags trade stability for a wider feature set that a model can overfit to noise, which is exactly why lag/window choices should be validated inside the same walk-forward loop discussed in Selecting Features Inside the CV Loop, not chosen by eye on the full history.

Choosing window lengths and the multi-horizon habit

A single window length is rarely the right answer. Practitioners typically build several rolling features at once — a 5-day, a 20-day and a 60-day version of the same statistic — because different window lengths pick up different things: a short window is sensitive to a genuine regime change but noisy day to day; a long window is stable but slow to react and can still be dragging around the influence of data from months earlier. Feeding a model several horizons at once and letting it (or a feature-selection step) decide how to weight them is generally more robust than committing to one window length up front, though it multiplies the total feature count and therefore the overfitting surface, which is why the selection step needs to happen honestly, inside the cross-validation loop rather than by eyeballing correlations on the full history.

Cross-sectional lags and window features

Everything above describes a single series lagged against itself, but the same construction applies across assets at a fixed point in time — a stock's rolling 20-day correlation with the sector index, or its rank within a rolling cross-sectional volatility percentile, are both rolling-window features even though the "window" here spans other tickers rather than other dates. These carry a subtler leakage risk: a cross-sectional percentile computed using the full current-day universe implicitly uses information about how every other stock performed today, which is available at the time of prediction only if every input to that ranking was itself known before the prediction was made. Mixing a same-day cross-sectional feature with a next-day return target is safe; mixing it with a same-day target is not.

Related concepts

Practice in interviews

Further reading

  • Lopez de Prado, Advances in Financial Machine Learning, ch. 2
  • Bergmeir & Benítez, On the Use of Cross-Validation for Time Series (2012)
ShareTwitterLinkedIn