MCP Server live — AI agents can now query 105M+ SEC facts. Connect your agent →
ValueinValuein
quant
advanced
15 min

Point-in-Time Backtesting

Construct survivorship-bias-free factor portfolios using accepted_at timestamps and PIT universe snapshots.

Python SDKDuckDBMCP Server

The Look-Ahead Bias Problem

Most financial datasets don't tell you WHEN a data point became available. If you use a company's 2023 annual results (filed March 2024) in a January 2024 backtest, you've cheated — that information wasn't available yet. Valuein's accepted_at field records the exact SEC acceptance timestamp for every fact, enabling rigorous PIT backtesting.

Filtering by accepted_at

Always filter by accepted_at <= rebalance_date when selecting signals for a historical portfolio.

Python
from valuein_sdk import ValueinClient, ValueinError

rebalance_date = "2022-01-01"

try:
    with ValueinClient() as client:

        # PIT-safe revenue for all companies as of 2022-01-01
        revenue_pit = client.run_query(f"""
          SELECT entity_id, numeric_value AS revenue, accepted_at
          FROM fact
          WHERE standard_concept = 'TotalRevenue'
            AND fiscal_period = 'FY'
            AND accepted_at <= TIMESTAMP '{rebalance_date}'
          QUALIFY ROW_NUMBER() OVER (
            PARTITION BY entity_id ORDER BY accepted_at DESC
          ) = 1
        """)
        print(revenue_pit.head())

except ValueinError as e:
    print(f"Error: {e}")

Survivorship-Bias-Free Universe

Never use today's S&P500 list for historical backtests. Use get_pit_universe to get the exact constituents at each rebalance date.

Python
from valuein_sdk import ValueinClient, ValueinError

# MCP: get the exact S&P500 on Jan 1, 2020
# get_pit_universe(as_of_date="2020-01-01", index="SP500")

# Python SDK — index_membership keys on cik (since migration 0015) and uses
# effective_date / removal_date for the half-open membership window.
try:
    with ValueinClient() as client:
        universe_2020 = client.run_query("""
          SELECT r.cik, r.symbol, r.name, r.sector
          FROM references r
          JOIN index_membership im ON im.cik = r.cik
          WHERE im.index_name = 'SP500'
            AND im.effective_date <= DATE '2020-01-01'
            AND (im.removal_date IS NULL OR im.removal_date > DATE '2020-01-01')
        """)
        print(f"S&P500 universe on 2020-01-01: {len(universe_2020)} companies")

except ValueinError as e:
    print(f"Error: {e}")

Full Monthly Rebalance Loop

Combine PIT universe + PIT signals to build a proper factor backtest.

Python
import pandas as pd
from valuein_sdk import ValueinClient, ValueinError

rebalance_dates = pd.date_range("2018-01-01", "2023-12-01", freq="QS")
portfolio_returns = []

try:
    with ValueinClient() as client:
        for date in rebalance_dates:
            date_str = date.strftime("%Y-%m-%d")

            # Step 1: PIT universe on this date
            universe = client.run_query(f"""
              SELECT r.cik FROM references r
              JOIN index_membership im ON im.cik = r.cik
              WHERE im.index_name = 'SP500'
                AND im.effective_date <= DATE '{date_str}'
                AND (im.removal_date IS NULL OR im.removal_date > DATE '{date_str}')
            """)

            # Step 2: Latest known revenue for universe — fact.entity_id == references.cik
            ids = ','.join(repr(x) for x in universe.cik.tolist())
            signals = client.run_query(f"""
              SELECT entity_id,
                numeric_value AS revenue,
                accepted_at
              FROM fact
              WHERE standard_concept = 'TotalRevenue'
                AND fiscal_period = 'FY'
                AND accepted_at <= TIMESTAMP '{date_str}'
                AND entity_id IN ({ids})
              QUALIFY ROW_NUMBER() OVER (
                PARTITION BY entity_id ORDER BY accepted_at DESC
              ) = 1
            """)

            # Step 3: Rank by revenue growth (simplified)
            signals['rank'] = signals['revenue'].rank(pct=True)
            top_quintile = signals[signals['rank'] > 0.8]
            portfolio_returns.append({'date': date, 'n': len(top_quintile)})

except ValueinError as e:
    print(f"Error: {e}")

Up next

Building a Financial Agent with MCP

Configure Claude or Cursor to query SEC data via the Valuein MCP Server. Write prompts that generate investment research, screen for opportunities, and analyze risk.

Read next