Chapter 8: Financial Feature Engineering

Point-in-Time Feature Construction and Data Vintages

A feature is only valid for trading if it was knowable at the decision timestamp, not merely true in hindsight.

Point-in-Time Feature Construction and Data Vintages

A feature is only valid for trading if it was knowable at the decision timestamp, not merely true in hindsight.

The Intuition

Point-in-time errors in feature engineering usually do not look like bugs. The values in the table look sensible. The join keys match. The backtest runs. The problem is that financial data lives on more than one clock.

For a quarterly fundamental, the fiscal period may end on March 31, the filing may arrive after the close on May 2, the vendor may publish the parsed value at 17:08, and the first tradable bar may be the next session open. A macro series adds one more complication: the initial release later gets revised, sometimes multiple times. If a feature uses the final revised number on dates before that revision existed, the feature is historically correct but not historically knowable.

That is why point-in-time correctness is the binding constraint for slow-moving features. Momentum or Parkinson volatility are point-in-time by construction because they are deterministic functions of observed prices up to time $t$. Fundamentals, vintages, calendars, and reference data are different. Their values arrive late, persist between releases, and can be overwritten by later versions.

The right question is therefore not "what value belongs to this quarter?" It is "what value could this strategy have attached to this asset at this decision time?"

The Mechanism

For feature construction, three timestamps matter:

  • Valid time: when the fact is about, such as the fiscal quarter or macro reference month.
  • Availability time: when this version became knowable to your system, usually the public release or vendor ingest timestamp.
  • Decision time: when the strategy must act, which may be later than the public release because of session rules, batch latency, or venue hours.

Let $z_j$ denote a candidate record for asset $i$ with value $v_j$ and availability timestamp $a_j$. A feature at decision time $t$ is admissible only if

$$ a_j \le t. $$

For an as-of lookup, the correct record is the most recent admissible one:

$$ j^*(i,t) = \arg\max_j \{a_j : a_j \le t,\ \text{asset}(j)=i\}. $$

The resulting feature value is

$$ x_{i,t} = v_{j^*(i,t)}. $$

That simple maximization rule is the whole logic of a point-in-time join. The difficulty is operational: you need the right timestamp column and you need every historical version, not just the latest value.

For revised data, a bitemporal layout makes the rule explicit:

asset field valid_end known_from known_to value
ABC quarterly_book_equity 2025-03-31 2025-05-02 17:08 2025-08-12 06:40 510
ABC quarterly_book_equity 2025-03-31 2025-08-12 06:40 null 498

Both rows describe the same economic quarter. They differ only in the vintage that was available. A June backtest should see 510; a September backtest should see 498.

How It Works in Practice

As-of joins

The join key for slow-moving features is almost never the period-end date. It is the timestamp when the record became available for use.

$python prices = prices.sort_values(["symbol", "ts"]) fundamentals = fundamentals.sort_values(["symbol", "known_from"]) features = pd.merge_asof( prices, fundamentals, left_on="ts", right_on="known_from", by="symbol", direction="backward", allow_exact_matches=True, ) $

Three details matter:

  • sort both sides by time within asset,
  • join on known_from, not period_end,
  • require backward direction so the match is causal.

Stale values are often correct

A quarterly feature should repeat between releases. A macro vintage should remain constant until the next official publication. Those flat stretches are not missing information; they are the honest state of knowledge. Interpolating, backfilling from future releases, or assigning a new value to every day destroys the release calendar the feature is supposed to respect.

Decision time is stricter than release time

A filing released at 16:05 New York time is public after the equity cash close. If your strategy trades the next regular-session open, the feature becomes tradable on the next session, not at 15:59 and not necessarily at 16:06. PIT correctness therefore needs a market convention, not just a timestamp column.

Worked Example

Suppose you build book-to-market for a daily equity model.

At date $t$, market equity is observable from prices. Book equity is not. The quarter ended on March 31, but the 10-Q arrived on May 2 after the close, the vendor published the parsed record at 17:08, and the next tradable bar is May 5 at 09:30.

The naive construction is

$$ \text{BM}_{i,t}^{\text{naive}} = \frac{\text{BookEquity}_{i,\text{Q1}}}{\text{MarketCap}_{i,t}} $$

for all dates after March 31. That injects more than a month of foresight.

The point-in-time construction is

$$ \text{BM}_{i,t}^{\text{PIT}} = \frac{\text{BookEquity}_{i,\tau(i,t)}}{\text{MarketCap}_{i,t}}, \qquad \tau(i,t)=\max\{j:\text{known\_from}_{i,j}\le t\}, $$

which means:

  • before May 5, use the prior quarter's book value,
  • from May 5 onward, use the new filing,
  • if the company restates in August, do not rewrite June history.

The same logic governs macro vintages. A payroll surprise feature on the June release date must use the first June estimate, not the value that appears in today's downloaded CSV after two revisions.

Diagnostics and Failure Modes

The simplest PIT audits are mechanical:

  • Admissibility audit: verify that every joined record satisfies known_from <= decision_time.
  • Staleness audit: slow-moving features should show repeated values between releases.
  • Vintage audit: historical reruns at different cutoffs should return different records when revisions occurred.
  • Universe audit: constituent membership, identifier mappings, and delistings must also be versioned historically.

Common failure modes are more specific than a generic "look-ahead bias" warning:

  • joining fundamentals on quarter end instead of filing or vendor time,
  • storing only the latest version and destroying revision history,
  • using present-day index membership or ticker mappings in old samples,
  • letting broad tolerances attach stale records across holidays or session gaps,
  • treating vendor file dates as economic availability without validating ingest timing.

Cross-sectional features add one more risk. A same-day sector rank is fine if computed on the historical live universe at that date. It becomes leaky if the panel was first filtered to today's survivors and then ranked retroactively.

Figure Specification

Use one horizontal timeline for a single record with markers for:

  • valid period end,
  • public release,
  • vendor ingest,
  • next tradable bar,
  • later revision vintage.

Below it, draw the two bitemporal rows as bars over known_from to known_to. The figure should make clear that one economic fact can have multiple historical vintages and that only one is admissible at each decision time.

Common Mistakes

WRONG: Treat the economically correct value as automatically admissible for every earlier date in the same period.

CORRECT: A value is admissible only from its availability timestamp onward.

WRONG: Expect a fresh value every day for slow-moving data.

CORRECT: Repeated values between releases are often the signature of a correct PIT feature.

WRONG: Use full-history ticker maps or current constituents with otherwise careful joins.

CORRECT: Reference data and universes need the same historical versioning discipline as fundamentals and macro series.

Connections

  • Book chapters: Ch04 Fundamental and Alternative Data; Ch07 Defining the Learning Task; Ch08 Financial Feature Engineering
  • Related primers: bitemporal data models and as-of queries, walk-forward validation, training-serving skew
  • Why it matters next: every fundamental, macro, calendar, and reference-data feature in the book depends on this admissibility rule; without it, downstream model evaluation is not credible

Register to Read

Sign up for a free account to access all 61 primer articles.

Create Free Account

Already have an account? Sign in