Point-in-Time Accuracy
Financial backtests fail when data is available too early — you use information that wasn't publicly known at the time. Valuein timestamps every fact with accepted_at: the exact moment the SEC accepted the filing. Filter by it and your backtest is safe.
Why Most Databases Introduce Look-Ahead Bias
Most financial databases store data as it exists today, not as it was known historically. A fiscal year 2021 annual report filed in March 2022 is often backdated to December 31, 2021 — making it appear as if that data was available before it was. Backtests built on this data are invalid.
Timeline for a 2022 10-K Filing
Fiscal period ends. Results for the full year are computed internally. The public knows nothing yet.
Company submits the 10-K to SEC EDGAR. Still not indexed in full — processing occurs over hours.
SEC accepts and timestamps the filing. This is the earliest moment any investor could have seen this data. Filter by this.
Now run a dated query against a whole filing history. Everything accepted on or before the as_of date is returned; the later restatement stays walled off — invisible, exactly as it would have been on that day.
The hidden trap
If a data vendor stores this filing against fiscal_year = 2022 with no timestamp, a backtest that says "use 2022 annual data as of Jan 1, 2023" will include it — but the filing wasn't accepted until Feb 15, 2023. Your simulated portfolio used information that didn't exist yet. This introduces look-ahead bias and inflates backtest performance.
The Three Key Fields
accepted_atThe exact UTC timestamp the SEC accepted the filing that disclosed this fact. Each fact row inherits accepted_at from its parent filing on indexing, so the filter works identically on either table. This is your PIT anchor — use it exclusively for backtest-safe queries. It represents the earliest moment any investor could have read this data.
Always filter: WHERE accepted_at <= your_date
filing_dateThe date the SEC received the filing. Very close to accepted_at but lacks the exact time component. Suitable for rough date-range filtering but accepted_at is more precise for PIT analysis.
Safe for range filtering, less precise than accepted_at
report_dateThe fiscal period end date (e.g. December 31 for a calendar-year company). This is NOT a PIT field — using it as a filter introduces look-ahead bias because the data wasn't known until the filing date weeks or months later.
For display purposes only — never use as a PIT filter
Relationships (text)
filingreferencesentityviaentity_id → cik(many → 1)factreferencesentityviaentity_id → cik(many → 1)factreferencesfilingviaaccession_id(many → 1)ratioreferencesentityviaentity_id → cik(many → 1)
Wrong vs. Right Queries
The difference between a biased and a valid backtest often comes down to a single WHERE clause.
-- WRONG: look-ahead bias.-- Meant to rank companies by fiscal 2021 revenue "as of 2022-01-01",-- but most fiscal 2021 10-Ks were filed in February and March 2022,-- and taking the latest vintage pulls in restatements filed years later.SELECT r.symbol, f.numeric_value / 1e9 AS revenue_billionsFROM fact fJOIN "references" r ON r.cik = f.entity_idWHERE r.is_active AND r.is_primary_ticker AND f.standard_concept = 'TotalRevenue' AND f.fiscal_period = 'FY' AND year(f.period_end) = 2021 -- WRONG: the period is not when it was knownQUALIFY ROW_NUMBER() OVER ( PARTITION BY f.entity_id ORDER BY f.accepted_at DESC) = 1ORDER BY revenue_billions DESC;-- RIGHT: point-in-time safe using accepted_at.-- Each company's latest annual revenue the SEC had accepted by 2022-01-01.-- A company whose fiscal 2021 10-K came later shows its fiscal 2020 figure,-- which is exactly what an investor could see that day.SELECT r.symbol, f.period_end, f.numeric_value / 1e9 AS revenue_billions, f.accepted_at -- visible timestampFROM fact fJOIN "references" r ON r.cik = f.entity_idWHERE r.is_active AND r.is_primary_ticker AND f.standard_concept = 'TotalRevenue' AND f.fiscal_period = 'FY' AND f.accepted_at <= TIMESTAMP '2022-01-01 00:00:00' -- RIGHT: PIT filterQUALIFY ROW_NUMBER() OVER ( PARTITION BY f.entity_id ORDER BY f.period_end DESC, f.accepted_at DESC) = 1ORDER BY revenue_billions DESC;Survivorship Bias
Look-ahead bias is temporal — using today's data in the past. Survivorship bias is structural — only analyzing companies that still exist today. Both inflate backtest returns and both are invisible unless your dataset is specifically built to prevent them.
Delisted companies
Valuein tracks all entities including those that were delisted, acquired, or went bankrupt. The Pro and Institutional tiers include 19,000+ entities — active and inactive.
Historical index membership
The index_membership table records exact effective_date / removal_date for each company in each index, with [) interval semantics. A 2010 S&P 500 backtest uses the 2010 constituents, not today's.
PIT universe construction
Use get_pit_universe(as_of_date) to reconstruct the exact investable universe on any historical date — free of additions that happened after.
- ENRNbankrupt · 2001
- LEHbankrupt · 2008
- WAMUseized · 2008
- BBBYbankrupt · 2023
- SIVBfailed · 2023
- FTXcollapsed · 2022
get_pit_universe(date) → the index as it stood, not as it survived
-- Survivorship-free universe: who was in the S&P 500 on 2020-03-01.-- Removal is exclusive: a company removed on that date is not a member.-- Companies removed since then are still here (their data stays on-- Pro and Institutional).SELECT im.cik, im.effective_date, im.removal_dateFROM index_membership imWHERE im.index_name = 'SP500' AND im.effective_date <= DATE '2020-03-01' AND (im.removal_date IS NULL OR im.removal_date > DATE '2020-03-01');PIT in the Python SDK
Create the client with as_of (a timezone-aware datetime) and every point-in-time table — fact, ratio, factor scores, earnings signals, prices and smart money — is filtered to accepted_at <= as_of. Your SQL stays the same.
from datetime import datetime, timezone from valuein_sdk import ValueinClient, ValueinError sql = """SELECT f.period_end, f.numeric_value / 1e9 AS revenue_bn, f.accepted_atFROM fact fJOIN "references" r ON r.cik = f.entity_idWHERE r.symbol = 'AAPL' AND r.is_active AND f.standard_concept = 'TotalRevenue' AND f.fiscal_period = 'FY'QUALIFY ROW_NUMBER() OVER (PARTITION BY f.period_end ORDER BY f.accepted_at DESC) = 1ORDER BY f.period_end DESC""" try: # as_of hides every fact the SEC accepted after 2023-01-01: no SQL filter needed. with ValueinClient(as_of=datetime(2023, 1, 1, tzinfo=timezone.utc)) as client: df = client.run_query(sql) print(df) # every accepted_at is on or before 2023-01-01 # The index as it stood on a past date, delisted members included. print(client.pit_universe("2020-03-01").head())except ValueinError as e: print(f"Error: {e}")Frequently Asked Questions
Ready to build a PIT-safe backtest?
Start with the free sample tier — all PIT fields are included. Register for the free Benchmark tier for full history on S&P 500 constituents, or upgrade for the complete universe of 19,000+ companies.