ValueinValuein
advanced
10 min

Point-in-Time Universe Construction

Build a survivorship-bias-free historical S&P 500 universe for any rebalance date.

PITuniversebacktestingDuckDB
## The Survivorship Bias Problem If you use today's S&P 500 list to backtest 2018 strategies, you're including companies that weren't in the index in 2018 — and excluding companies that were delisted since then. This is survivorship bias and it inflates backtest returns. `index_membership` keys on `cik` and uses `effective_date` / `removal_date` as the half-open `[)` membership window. `client.pit_universe(date)` applies that window for you and returns each member's ticker on that date.
python
from datetime import date from valuein_sdk import ValueinClient, ValueinError try:    with ValueinClient() as client:        # pit_universe() reads index_membership: members on that date,        # including companies that have since left the index or delisted.        universe_2020 = client.pit_universe("2020-01-02")        print(f"S&P 500 size on 2020-01-02: {len(universe_2020)} companies")         universe_now = client.pit_universe(date.today().isoformat())        print(f"S&P 500 size today: {len(universe_now)} companies")         # Members today that were not members in 2020 (matched on CIK, not ticker)        added = universe_now[~universe_now["cik"].isin(universe_2020["cik"])]        print(f"Added since 2020: {len(added)} companies")        print(sorted(added["ticker_at_date"].dropna())[:10]) except ValueinError as e:    print(f"Error: {e}")

Try it yourself

Every snippet runs on the free sample with no token. Add one to run this notebook against all 120.5M+ SEC EDGAR facts.