Point-in-Time Correctness in Feature Pipelines
Making sure every feature a training pipeline computes for a historical date uses only information that was actually available on that date, so backtests don't quietly cheat with future data.
Prerequisites: Point-in-Time Data
A feature pipeline computes inputs — momentum, earnings-quality score, sector-relative valuation — for every stock on every historical date, to train a model. Point-in-time correctness means that the value computed for date uses only data that was genuinely known and available as of , not data that was revised, restated, or simply hadn't been published yet. Getting this wrong is one of the most common and most dangerous sources of an unrealistically good backtest.
Where the leak sneaks in
Corporate fundamentals get restated: a company's Q2 earnings, first reported on July 15th, might be revised on August 20th after an audit adjustment. A feature pipeline that joins "earnings for Q2" to every date in Q2 and Q3 using the final restated number, rather than the number actually publicly known on each date, has quietly given the model early access to a correction it couldn't have had in real time. The same problem hits reporting lags (a filing doesn't become public until weeks after its period ends), index membership (often only knowable in hindsight to the exact rebalance date), and analyst estimates (revisions get backfilled into historical records). None of these show up as an obvious bug — the code runs, the numbers look plausible, and the backtest simply performs better than the strategy ever could have live.
The fix: two timestamps per fact
Point-in-time correctness requires storing, for every fact, both its effective date (the period the fact describes) and its knowledge date (the date it actually became available). A feature pipeline must always join on the knowledge date, never the effective date, when constructing a training row for date : only include facts whose knowledge date is .
Worked example
Building a value factor using book value, a pipeline needs Q1 book value for AcmeCo. The 10-Q for Q1 (effective period ending March 31) is filed and becomes public on May 10th (knowledge date). A naive pipeline that joins "book value effective March 31" to every trading date from April onward leaks the Q1 number into rows for April 1st through May 9th — over five weeks where it wasn't yet known. A point-in-time-correct pipeline instead uses the prior filed book value (Q4's, filed February 1st) through May 9th, switching to the Q1 figure only from May 10th. Running both versions on the same value factor, the naive backtest's Sharpe ratio comes in at 1.4, the corrected version's at 0.9 — the gap is pure lookahead bias, not genuine alpha.
What this means in practice
Every table feeding a feature pipeline should carry an explicit knowledge-date column, and every join into that table should filter on it, not on the effective date. Treat any backtest built on a pipeline that can't answer "which knowledge date did you join on?" as suspect until proven otherwise — this single design choice is responsible for a large share of factors that look great in backtest and disappoint in production.
A feature is point-in-time correct only if it uses data known as of the date being modeled, which requires storing a separate knowledge date from the effective date for every fact and always joining on the former. Violating this creates lookahead bias that inflates backtested performance without any code error being obviously visible.
Restated fundamentals are the classic trap because the final, correct-looking number is the one sitting in most databases by default — there is usually no error message, just a silently better-than-real backtest. Always ask a data vendor explicitly whether their historical tables carry point-in-time knowledge dates or only final restated values.
Related concepts
Practice in interviews
Further reading
- Bailey et al., Advances in Financial Machine Learning, ch. 7