ValueinValuein
Methodology

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.

report_date
Fiscal period end
filing_date
Date filed with SEC
accepted_at
SEC acceptance timestamp

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

Dec 31, 2022
report_date

Fiscal period ends. Results for the full year are computed internally. The public knows nothing yet.

Feb 15, 2023
filing_date

Company submits the 10-K to SEC EDGAR. Still not indexed in full — processing occurs over hours.

Feb 15, 2023 17:42 UTC
accepted_at

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.

query: get_company_fundamentals(as_of="2021-06-30")What a query dated 2021-06-30 returns — and what it is forbidden to see.
as_of · 2021-06-30
10-Q Q2
2020-07-30
10-K FY2020
2021-02-24
as originally reported
10-K/A FY2020
2021-11-09
restatement · filed later
visible at query datefiled later · walled offthe 10-K/A restatement is invisible to this query — no look-ahead

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_at
fact
TIMESTAMPTZ

The 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_date
filing
DATE

The 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_date
filing
DATE

The 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

These tables carry accepted_at; the SDK filters every PIT view to accepted_at <= as_of.
Core financialsDerived
The point-in-time tables. 4 tables, 4 foreign-key relationships. Full text below the diagram.∞1∞1∞1∞1entity● cik name statusfiling● accession_id form_type accepted_at◇ superseded_byfact● fact_id accepted_at value_as_filed value_currentratio◇ entity_id ratio_name accepted_at is_ttm
Relationships (text)
  • filing references entity via entity_id → cik (many → 1)
  • fact references entity via entity_id → cik (many → 1)
  • fact references filing via accession_id (many → 1)
  • ratio references entity via entity_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
sql
-- 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 — PIT-safe
sql
-- 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.

The dead are still in the indexDelisted, bankrupt, merged, taken private — kept, not survivorship-pruned.
  • 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-bias-free universe constructionsql
-- 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.

pit_backtest.pypython
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.