Lagging Features to Enforce Point-in-Time Safety
Why every feature fed to an alpha model must be shifted so it reflects only what was actually known at the time of the trade decision — otherwise the backtest quietly uses tomorrow's information today.
Prerequisites: Point-in-Time Data
Imagine building a model to predict tomorrow's return using "this quarter's earnings." That sounds fine until you check exactly when the earnings figure became public — often weeks after the quarter ended. If your data table timestamps the earnings number by the quarter it describes rather than the date it was actually released, a model trained on it is silently allowed to see the future: on any day before the real announcement, it's using information that hadn't happened yet from the market's point of view. Lagging is the fix — every feature must be shifted in time so its value on a given date reflects only what was knowable by that date, not what was true about that date.
The idea in one picture
Think of a features table as a set of columns being filled in as a factory line moves forward in time. Some numbers — yesterday's closing price — are filled in almost instantly. Others — reported earnings, revised GDP figures, restated financials — get filled in only after a real-world reporting delay, sometimes a lag of days or months. A model trained without accounting for that delay effectively lets the factory line's later stations reach backward and change what was on the belt earlier. Lagging enforces that the belt only ever moves forward: a feature's value at time in the training table must equal what was actually observable in the real world at time , never a later, truer revision of it.
Formally, if a data field has release timestamp for information describing period (with whenever there's a reporting delay), the point-in-time-safe feature at trading date is
i.e., the most recent period's value whose release date has already passed by . In plain English: look up the newest number you're allowed to know on date , which is often not the newest number that exists.
Worked example: earnings-based feature with a reporting lag
A company's Q2 earnings (period ending June 30) are released on July 28. A naive feature table might record "Q2 EPS" against every trading date in Q2 and July, which means a model backtested on July 10 would see the Q2 EPS figure eighteen days before it existed. The point-in-time-safe version instead lags the field: for all dates from July 1 through July 27, the feature still shows Q1's EPS (the most recent figure actually known), and only from July 28 onward does it switch to Q2's EPS. Suppose Q1 EPS was $1.10 and Q2 EPS came in at $1.35 — a naive backtest trading on July 10 with the $1.35 figure would be reacting to a number that, in live trading, simply did not exist yet, inflating the backtest's apparent skill around earnings dates.
A second common case is index membership: a stock added to an index on March 15 shouldn't appear as a member in February's feature rows just because a static, present-day membership list was joined onto the whole history without a lag or "as of" date attached.
What this means in practice
Point-in-time safety is not a one-time check but a property every feature construction step must preserve, especially for fundamentals, revised macro data, and any field sourced from a vendor's "as reported today" snapshot rather than a true point-in-time history. A backtest built on unlagged features can show an inflated Sharpe ratio that evaporates the moment the model trades live, because live trading never has access to a number before it exists.
It's tempting to think a one-day lag is always enough, but different fields have wildly different real-world reporting delays — some GDP revisions arrive months later, some earnings restatements arrive quarters later. Applying a single blanket lag to every feature is itself a leakage risk; each field needs its own, correctly-sourced release-date lag.
Related concepts
Practice in interviews
Further reading
- de Prado, Advances in Financial Machine Learning, ch. 4