Skip to main content

Point-in-Time Integrity

Ensuring accurate historical analysis without look-ahead bias.

What is Point-in-Time?

Point-in-time (PIT) integrity means historical data reflects exactly what was known at that time, not what we know now after revisions.

PIT Columns

Every product table carries three PIT timestamps. They answer different questions:

ColumnMeaningQuestion it answers
str_effective_atValid time: the business date the row describes, plus the source's publication lag where one appliesFrom when was this knowable to anyone?
str_observed_atTransaction time: when Snowtrail could first have held this valueFrom when did we know it?
str_ingested_atDecision time: when this row was writtenWhen was this row computed?

str_effective_at shifts the business date forward by the source's release lag, so an EIA storage figure describing 2024-03-01 carries an effective date of 2024-03-07, the day it reached the market. For sources with no meaningful lag (weather model runs, same-day grid data) it equals the business date.
str_observed_at is provenance-derived: it is the MAX(str_ingested_at) of the raw rows that actually fed the calculation, propagated through every feature, signal and event layer.

Because it describes the inputs rather than the write, recomputing a row does not move it, rebuilds are idempotent, and a value's knowability does not drift every time the pipeline reruns.
str_ingested_at records when the row was written. A full rebuild rewrites it for every affected row, so it is not a first-publication date and should not be used to infer when a value became available. Use str_observed_at for that.

The as_of Query Parameter

The API exposes an as_of parameter on all product endpoints. It bounds both time axes, and returns a row only if both hold:

str_observed_at  <= as_of     -- we actually held it by then
str_effective_at <= as_of -- it was publicly knowable by then

Bounding both is deliberate as Either alone leaks: filtering only on knowability would return values we had not yet computed, and filtering only on what we held would return values before their source published them. The latter of the two binds, which makes the guarantee conservative by construction.


Where a key has several stored versions, as_of takes the newest surviving version per key, ordering by str_observed_at DESC, str_ingested_at DESC.


Reproducing as_of yourself

The str_ingested_at tiebreaker is not optional. str_observed_at is provenance-derived and therefore not unique per version, measured across sig_system_stress, 16.4% of keys had two rows sharing a stamp but disagreeing on the value. Ordering on str_observed_at alone returns either row arbitrarily, so the same query can give different answers between calls. If you reconstruct as_of against the S3 files, apply the same two-column ordering or your results will diverge from the API.

GET /gbsi_us/system_stress?date_from=2024-01-01&date_to=2024-06-30&as_of=2024-03-15

The query above returns GBSI-US signals between January and June 2024, but only includes records that had become knowable on or before March 15, 2024. Any data point whose source had not yet published by that date is excluded, even if its business date falls within the range.

Backtest-Safe Queries

The as_of parameter is essential for backtesting. Without it, you risk look-ahead bias using data that was not yet available at the time your strategy would have made a decision.

from snowtrail import Snowtrail

client = Snowtrail(api_key="your-api-key")

# Simulate what was known on 2024-03-15
df = client.gbsi_us.system_stress(
date_from="2024-01-01",
date_to="2024-06-30",
as_of="2024-03-15"
)

Walk-Forward Example

To simulate a walk-forward backtest, iterate over decision dates and query with as_of set to each date:

from snowtrail import Snowtrail
from datetime import date, timedelta

client = Snowtrail(api_key="your-api-key")

decision_dates = [date(2024, 1, 1) + timedelta(days=7 * i) for i in range(52)]

for decision_date in decision_dates:
df = client.gbsi_us.system_stress(
date_from=str(decision_date - timedelta(days=90)),
date_to=str(decision_date),
as_of=str(decision_date)
)
# Each iteration sees only what was known at that decision date

What as_of Does and Does Not Do

as_of protects you from the most common source of look-ahead bias: using data before it was available. Publication lags mean a value describing a given business date may not reach the market for days or weeks, and a derived signal may not be computable until later still. Bounding both axes removes exactly that, across every product and every delivery channel.

It does reconstruct superseded values

Snowtrail's derived layers are bitemporal. When a source revises a figure, the recomputed row is appended as a new version rather than overwriting the old one, and each version carries its own str_observed_at. An as_of query returns the version we held at that instant.

So if a source published 100 and later restated it to 105, an as_of preceding the restatement returns 100: the value you would have acted on, while a current query returns 105.

Only genuine changes are stored. A recomputation that reproduces the same value adds no version, so the history records revisions rather than pipeline runs.

Three limits worth knowing

1. The vintage floor. Reconstruction cannot precede the point where raw object versioning began, before that there is no record of what the source looked like.

ProductsFloor
GBSI-EU, WRSI2026-01-13
GBSI-US, WSSI-US, GLMI, PEMI2026-03-02

A query with an earlier as_of does not error. It returns the earliest reconstructable vintage. Treat pre-floor answers as "the oldest state we can prove", not as history.


2. It reconstructs data, not models. as_of returns what the current model computes from the data knowable at that time. It does not resurrect a past model. All rows carry model_version = 1.0; if that changes, prior model vintages are not preserved. This is the honest reading of "deterministic recomputation of raw", and it is a distinction worth being explicit about, because "point in time" alone does not imply it.


3. Raw staging is not versioned. The reconstruction operates on the derived layers. The staging area between raw ingestion and those layers holds only the current state.

If model-vintage preservation is material to your process, contact us and we will scope it against your requirements.