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: each company's latest annual revenue the SEC had
        # accepted by the rebalance date
        revenue_pit = client.run_query(f"""
          SELECT entity_id, period_end, 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 period_end DESC, 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&P 500 list for historical backtests. Use `client.pit_universe()` in the SDK (or the MCP `get_pit_universe` tool) to get the exact constituents at each rebalance date.

Python
from valuein_sdk import ValueinClient, ValueinError

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

# Python SDK: pit_universe() reads index_membership (keyed on cik) with the
# half-open effective_date / removal_date window, delisted members included.
try:
    with ValueinClient() as client:
        universe_2020 = client.pit_universe("2020-01-01", index="SP500")
        print(f"S&P 500 universe on 2020-01-01: {len(universe_2020)} companies")
        print(universe_2020[["cik", "company_name", "ticker_at_date"]].head())

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 (delisted members included)
            universe = client.pit_universe(date_str)
            if universe.empty:
                continue

            # Step 2: latest annual revenue known on this date — fact.entity_id == cik
            ids = ",".join(f"'{cik}'" for cik in universe["cik"])
            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 period_end DESC, accepted_at DESC
              ) = 1
            """)

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

        print(pd.DataFrame(portfolio_returns).tail())

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