Insights
How to Backtest a Stock Universe That Changes Over Time
Alphanume Team · August 1, 2026
Construct the investable universe independently for every date, using monthly optionability snapshots as intervals and versioned classification snapshots when sector labels matter.
A changing-universe backtest fails when it starts from today's ticker list and projects that list backward. New listings receive imaginary history, acquired or delisted names disappear, and stocks can appear optionable before exchanges listed a chain. Alphanume's Historical Optionable Tickers dataset supplies frozen monthly optionability snapshots for this reconstruction.
Each snapshot is taken on the first trading day of the month and records ticker, the four-gap average across nearby expirations, and a flag for whether that average is under nine days. It identifies listed-chain availability and weekly-style density at that snapshot. It does not provide strikes, quotes, greeks, open interest, volume, or executable liquidity.
Represent membership as intervals
Input | Start | End | Meaning |
|---|---|---|---|
Optionable snapshot row | Snapshot date | Next snapshot date | Chain observed at monthly snapshot |
First appearance | First snapshot containing ticker | Later gap or final covered snapshot | Observed entry into dataset |
Density flag | Snapshot with has_weeklies equal to 1 | Next snapshot | Four-gap average under nine days at that observation |
Classification snapshot | Saved retrieval timestamp | Next saved version | Label known to the research archive |
Monthly intervals are a research convention, not exact listing timestamps. A chain added or removed mid-month is invisible until the next snapshot. Choose whether the first snapshot governs the whole month or only dates after the snapshot, document that rule, and apply it consistently.
Retrieve every monthly snapshot
The endpoint is GET /v1/optionable-tickers. Query a fixed history and paginate with cursor_date plus cursor_ticker. Results are ordered by date descending and ticker descending.
import os
import requests
url = "https://api.alphanume.com/v1/optionable-tickers"
headers = {"X-API-Key": os.environ["ALPHANUME_API_KEY"]}
params = {"date_gte": "2024-01-01", "date_lte": "2026-06-30"}
rows = []
while True:
response = requests.get(url, headers=headers, params=params, timeout=30)
response.raise_for_status()
payload = response.json()
rows.extend(payload["data"])
if not payload["has_more"]:
break
params["cursor_date"] = payload["next_cursor"]["date"]
params["cursor_ticker"] = payload["next_cursor"]["ticker"]Validate that each date is the expected first trading day and that each ticker appears once per snapshot. Save the raw pages before interval construction. A partial page can look like genuine universe shrinkage if pagination is not audited.
Expand snapshots without future leakage
For each signal date, first select the latest global optionable snapshot on or before that date. Then test whether the ticker exists in that exact snapshot. A per-ticker backward join is wrong because it can carry January membership into February even when the ticker is absent from the newer February snapshot.
snapshot_calendar = (
snapshots[["date"]]
.drop_duplicates()
.rename(columns={"date": "snapshot_date"})
.sort_values("snapshot_date")
)
signals = signals.sort_values(["signal_date", "ticker"])
signals = pd.merge_asof(
signals,
snapshot_calendar,
left_on="signal_date",
right_on="snapshot_date",
direction="backward",
tolerance=pd.Timedelta("40D"),
)
membership = snapshots.rename(columns={"date": "snapshot_date"})
eligible = signals.merge(
membership,
on=["snapshot_date", "ticker"],
how="left",
validate="many_to_one",
indicator="membership_join",
)
eligible["is_optionable"] = eligible["membership_join"].eq("both")
eligible["supports_weeklies"] = eligible["has_weeklies"].eq(1)A 40-day tolerance covers normal monthly spacing and should still be paired with a missing-snapshot audit. For a weekly-style density rule, require has_weeklies=1; the field means the four-gap average is under nine days. The avg_days_between field describes the nearby expiration ladder at snapshot time and does not prove a specific expiration.
Version classifications separately
Entry, exit, and optionability are dated in the monthly dataset. Ticker Classification is different: its endpoint has no date field and returns the current Alphanume sector and industry mapping. A historical test cannot call it today and treat the result as the label known years ago.
Classification source | Valid historical use | Label in output |
|---|---|---|
Saved dated snapshots | Join latest saved version on or before signal date | Point-in-time under snapshot cadence |
Current endpoint response | Current-state attribution only | Current Alphanume classification |
Licensed historical taxonomy | Use according to provider contract | Provider-specific label |
If no versioned classification archive exists, omit historical sector controls or describe them honestly as current-state groupings. Do not backfill the current label and call it historical. Also call the taxonomy Alphanume's research classification rather than GICS, SIC, or another provider's system.
Keep membership and execution distinct
- Monthly blind spots. Mid-month chain additions and removals are not observed.
- Listed versus liquid. A chain can exist with unusable spreads or depth.
- Expiration detail. The endpoint has no strikes or exact chain inventory.
- Ticker continuity. Renames, mergers, and reused symbols need a separate identity map.
- Delisted outcomes. A missing later price should remain in the denominator and receive explicit treatment.
Free access covers the trailing 20 trading sessions after a one-trading-session delay, which can contain only one monthly optionability snapshot. Historical entry and exit inference requires deeper snapshot coverage.
Rebuild one month before scaling
Use the Historical Optionable Tickers guide to rebuild one month's universe. Export the raw snapshot, prior and next snapshot dates, inferred membership intervals, weekly subset, missing signal joins, and any saved classification version used.
Compare that universe with the current optionable list and list every added or removed candidate before calculating returns. Then repeat month by month, freezing membership before joining signals and outcomes. This makes the changing choice set visible instead of letting it disappear inside portfolio statistics.