Could You Have Known That Data at the Time? Data Traps to Check Before Running AI Investing Experiments
Evidence and scope — Timing example with artificial data
Literature and an artificial price sequence illustrate data timing and signal delays. The +21% and −19% figures are calculations on that sequence, not actual strategy performance. This example does not validate trading costs or real execution feasibility.
An introduction to auditing backtest data by checking adjusted prices, delistings, filing dates, data vintages, and time zones—and by delaying a closing-price signal by one period
In Part 1, we treated AI not as an automatic stock-picking machine, but as a tool for testing investment ideas. In Part 2, we concluded that the problem, inputs, outputs, and evaluation criteria should come before whichever tool happens to be popular.
So which model should we choose next?
For now, we will postpone that decision. Even with a well-defined problem and clear evaluation criteria, the experiment’s timeline is already broken if its inputs include values that were not available at the time.
When I first downloaded price data, a CSV with no missing values reassured me. If the code ran without errors and the backtest produced high returns, I assumed both the data and the strategy were probably sound.
That changed when I began checking the difference between close and adjusted_close, securities that had disappeared, the actual filing dates of financial statements, revisions to economic indicators, exchange time zones, and signal delays. Corporate actions can involve distinct dates such as announcement, ex-date, record, and payment dates,[S1] while the way adjusted prices are provided may vary by a data vendor’s product and settings.[S4] Using only today’s surviving securities in a historical test can omit delisted securities,[S6] and the end of an accounting period is not the same as the date the financial information was actually filed.[S7]
That is why the first question in my data audit is no longer “Are there any blanks?”
Was this data used in the backtest actually knowable at the moment the trading decision was made?
The tables and code in this article are educational and research examples designed to explain data alignment. They are not recommendations of any security or instructions to place real trades, and they do not guarantee returns.
A Single Data Row Can Hide Several Different Times
Daily data often has just one column named date. In reality, several distinct times exist between the period a value describes and the moment it can be used for an investment decision.
End of the period being measuredperiod_end ↓Time of initial publication or filingrelease_at / filed_at ↓Time the data becomes available through the providervendor_available_at ↓Time the signal is finalizeddecision_at ↓Assumed order submission and execution timesexecution_atThese timestamps may display the same calendar date without referring to the same moment. We need to distinguish the period described by the data, when it was first published, when the researcher could receive it, and when the signal and order were created.
An operational rule for deciding whether a value can be used in a backtest can be expressed as follows.
vendor_available_at <= decision_at < execution_atThe exact conditions will depend on the data delivery channel, market session, and order rules. If the vendor’s delivery time cannot be verified, do not invent one. Record an explicit delay assumption or identify it as a research gap.
Matching a row’s date is not enough. The real task is to trace whether each value was generated, published, and observed before it was used to make and execute a decision.
close and adjusted_close Are Not the Same Price
The Mechanical Price Break Created by a Stock Split
A 2-for-1 stock split doubles the number of shares and halves the per-share price, but the split itself does not cut the value of the shareholder’s position in half.[S2]
| Point in time | Shares held | Raw share price | Position value |
|---|---|---|---|
| Before the split | 1 share | 100 | 100 |
| After a 2-for-1 split | 2 shares | 50 | 100 |
Comparing only the raw closing price of 100 before the split with the raw closing price of 50 afterward produces a return of -50%. That figure, however, may represent a mechanical break caused by the corporate action rather than an economic loss.[S2]
That is as far as the directly established evidence takes us. Changes in features such as moving averages and volatility, failures in outlier rules, and models learning false patterns are potential downstream effects and audit hypotheses arising from that break. Whether they actually occurred must be tested in each pipeline.
- Do feature values break abnormally around the split date?
- Does an outlier-removal rule delete rows containing corporate actions?
- Do model inputs and results change substantially before and after adjustment?
- Is a mechanical break in the raw price being used as though it were a predictive signal?
When you encounter a large price move, check for splits, dividends, spin-offs, rights offerings, and currency or unit errors before concluding that the asset actually crashed.
A Dividend Is Not the Same Kind of Adjustment as a Split
Splits and dividends can both affect a price series, but they are not the same event. The appropriate definition of return also depends on the research objective.
- Price return focuses on changes in price.
- Total return includes reinvested dividends.
- Net total return may incorporate additional assumptions such as withholding taxes.[S3]
Writing only “the strategy’s return” leaves the measured quantity unclear. The appropriate column depends on whether the study concerns pure price changes or holding-period performance with dividends reinvested.
| Item to audit | Columns and information to check |
|---|---|
| Price basis | raw_close, adjusted_close |
| Return basis | price_return_index, total_return_index, net_total_return_index |
| Splits | action_type, split_ratio |
| Dividends | dividend_type, dividend_amount |
| Currency and taxes | Price and dividend currencies, FX basis, tax assumptions |
Price return and total return measure different things and should not be compared as though they use the same basis.[S3]
Do Not Trust the Label “Adjusted Price” by Itself
It is easy to see a column named adjusted_close and assume the problem has been solved. But adjusted close is not a universally defined field calculated identically by every provider.
For example, Alpha Vantage documents an adjusted time series that reflects splits and dividends separately from raw daily OHLCV data. For intraday data, it also allows adjustment and the inclusion of regular versus extended trading hours to be configured independently.[S4] That is one provider’s practice, not evidence that every other provider uses the same adjustment scope or formula.
Check each provider’s documentation to determine:
- Whether adjustments cover splits only
- How cash and special dividends are handled
- Whether spin-offs and rights offerings are reflected
- In which direction historical prices are retroactively adjusted
- Whether volume is adjusted as well
- Whether extended-hours trading is included
- When corrections to historical values are incorporated
Corporate-action data may contain date fields with different meanings, including declaration, announcement, ex-date, record, payment, and last-update dates.[S1] Collapsing all of them into one date mixes together when the event became known, when an entitlement took effect, and when the data was updated.
Record the following in the experiment log:
- Data provider and product or endpoint
- Exact name of the price column
- Scope of the corporate actions reflected
- Adjustment direction and calculation rules
- Regular- and extended-hours settings
- API or file version
- Initial download and subsequent download times
- Checksum of the original file
The public materials reviewed here are not sufficient to determine every provider’s adjustment formula and historical-revision policy. Instead of writing only “adjusted prices were used,” record which provider’s definition you used.
Missing Values and Outliers: Ask Why They Occurred Before Deleting Them
Not Every NaN Means the Same Thing
In a time series, NaN is an observed state, not a cause. Identical-looking blanks may be associated with very different events:
- A market holiday or mismatched trading calendars across assets
- A trading halt or insufficient liquidity
- A period before an IPO or after a delisting
- A delivery delay or collection failure
- A security-identifier change or failed join
- A financial item that was not disclosed or does not apply
You cannot determine the cause of an individual missing value from this list alone. Check the trading calendar, listing status, delivery logs, and source data together.
Deleting every row with missing data is not a neutral cleaning operation either. Requiring complete observations for particular variables can create a non-random subset of the original sample and alter return distributions or relationships among variables, as empirical research has shown.[S5] That evidence does not, however, predetermine the direction or magnitude of bias in every dataset.[S5]
Why is this row blank, and which securities and market conditions disappear from the sample if I remove it?
Forward-Filling Is Also an Assumption
ffill() does more than remove a blank. It adds the assumption that the previous value remains valid at the next timestamp.
Whether that assumption is appropriate must be evaluated separately based on the meaning of the column and its data calendar. A research rule might, for example, retain a financial statement value until the next filing. That does not automatically mean the same treatment is appropriate for price or volume. If the data includes holidays, trading halts, calendar mismatches, or failed joins, test how filling changes the meaning of the rows and the calculation of returns.
Calendars and closing times also matter when combining data from different markets. Whether retaining the previous value on a day when one market is closed represents a valid research state—or merely fabricates an unobserved value—cannot be answered uniformly from the data definition alone.
There is no single best treatment for every missing value. Separate rules for deletion, retention, and imputation according to the cause and research objective, and record:
- The meaning of the column being imputed
- The basis for assuming that the previous value remained valid
- The trading calendar and join rules used
- Observation counts before and after imputation
- Changes in returns, features, and sample composition
- Limitations that could not be resolved
An Outlier May Be an Error—or an Important Event
| Observation | Possibilities to check first | Risk of deleting it automatically |
|---|---|---|
| Large one-day price decline | Split, dividend, currency error, actual decline | Removing a corporate action or genuine extreme event |
| Volume spike | Unit error, split adjustment, actual trading increase | Removing event-day information |
| Zero price or volume | Trading halt, missing-value replacement, vendor convention | Treating distinct states as identical |
| Duplicate timestamps | Multiple exchanges, corrected records, duplicate collection | Arbitrarily losing timing or price information |
A large price break can be caused by a corporate action such as a split.[S1][S2] That does not mean every large move is a corporate action. Compare the raw record with event data and classify the cause.
For missing-value and outlier processing, preserve the original value, detection rule, cause classification, supporting material, values before and after correction, number of affected rows, and reason for the treatment. Compare results before and after processing so the transformation from raw data to the final dataset can be reproduced.
Looking Only at Today’s Survivors Changes the Past
The Security Universe Is Also Time-Dependent Data
Suppose you apply the list of securities that exist today to the entire historical period.
Actual security universe at the time├─ Securities that subsequently survived├─ Securities that were merged or acquired├─ Securities that were delisted└─ Securities affected by bankruptcy or trading haltsHistorical universe reconstructed from the current list└─ Primarily securities that subsequently survivedSecurities that later disappeared are then treated as though they never existed in the past. The future outcome of surviving has become part of the historical selection rule.
A backtest universe should therefore be treated as time-dependent data, not as a fixed list. At each rebalancing date, reconstruct the securities that were actually listed and eligible under the research criteria at that time.
Stopping at the Last Regular Trading Day Can Omit the Final Return
Using today’s security list and omitting delisting returns are related but distinct problems.
If you calculate returns only through a security’s final regular trading day and then remove the row, you may omit the return or consideration associated with the delisting process. Shumway’s original study found that many delisting returns for negative reasons were missing from historical CRSP data and that the omissions could produce substantial bias.[S6]
That study is a historical analysis of U.S. CRSP data beginning in 1962.[S6] It cannot establish that every current provider and market has the same omission. Based on the public information reviewed, I also could not determine the completeness of delisting data in every commercial database as of 2026.
Check the following:
- Permanent security identifier and ticker used at the time
list_date,delist_date,delist_reason- Universe entry and exit dates
- Index or market membership at the time
- Final trading price and delisting return
- Cash or stock consideration
- Merger, acquisition, bankruptcy, and trading-halt status
Record the point-in-time universe construction procedure, whether delisted securities were included, and how final returns and consideration were handled. If the information is unavailable, do not replace it with an arbitrary normal return. Disclose the omission and any substitute assumption.
A history built only from survivors is not the market investors faced at the time.
How This Article Identifies Look-Ahead Bias
For this article, the operational definition of look-ahead bias is using a value for a decision when that value did not become available until after the decision time. It is not limited to directly feeding a future-return column into a model.
Common cases to audit include:
- Calculating a signal from today’s closing price and applying it to today’s close-to-close return
- Assuming that a quarter-end financial value was available from the end of that quarter
- Using subsequently revised economic or financial data as though it had been available in the original release
- Applying today’s security list as the historical universe
- Matching calendar dates while ignoring actual publication times and market closes
Normalization and missing-value imputation can also violate time order, but this research does not provide direct evidence that any particular technique actually incorporated future values in a specific pipeline. Do not declare such an error without examining the calculation window and input rows of that pipeline.
High backtest performance neither proves the presence of look-ahead bias nor confirms correct temporal alignment. To decide, trace each feature’s availability time, join key, within-day sequence, signal-delay rule, and execution-price definition.
Not when was this value measured, but what was the earliest moment I could actually have received it?
The Financial Statements Are Not Yet Known at the Accounting Period-End Date
Consider the following quarterly financial information.
Accounting period-end: 2026-06-30Actual filing: A specific date and time after the period-end2026-06-30 is the end of the period in which the results were measured. It is not the moment when the completed financial statements became known to the market. The SEC’s EDGAR APIs update submission and XBRL data as filings are disseminated, while providing the relevant accounting period and filing-related information separately.[S7]
If you immediately join financial values to price data using period_end, you may pull future information backward in time. Use the value only after the actual filed_at or other verifiable publication timestamp, beginning with the first tradable time defined by the research design.
Metadata to check includes:
fiscal_period_start,fiscal_period_endfiled_at,accepted_at- Filing type and whether it is an amendment
- A permanent document identifier such as the accession number
- Initial and revised values for the same period
- Effective start and end times for each value
A filing released after the market closes cannot be used to make a decision at that same day’s closing price. An SEC submission timestamp alone, however, cannot establish an individual provider’s delivery delay or when the user completed processing the data.[S7] If those details are unavailable, apply a conservative availability time or an explicit delay rule and document the limitation.
The Historical Value You See Today May Not Be the Value Originally Released
Economic Indicators Have Vintages
Do not assume that a historical economic value retrieved from a database today is identical to the figure released at the time.
The default FRED view shows currently available values. To request what was known at a particular historical point, you can use ALFRED’s real-time period information.[S8] FRED vintage dates track when series values were revised or new values were released.[S9]
Observation period≠ Initial release date≠ Vintage date when revisions or new values were releasedReconstructing historical signals with today’s latest values can incorporate revisions that did not yet exist at the time.
GDP Demonstrates Why Revision History Matters
U.S. quarterly GDP is released as advance, second, and third estimates, with later estimates reflecting more detailed and comprehensive data. Annual and comprehensive updates also follow.[S10]
A historical GDP value retrieved today may therefore differ from its initial release. Treating the latest GDP series as though it had been known throughout history introduces hindsight information.
This is an example involving U.S. GDP; it does not imply that every country and indicator uses the same release and revision process. Financial information can also be amended, so initial disclosures and subsequent values should be preserved separately.[S7]
Metadata to check includes:
- Observation date or period
- Initial release date and time
- Vintage date
realtime_start,realtime_end- Revision number and value at each stage
- Seasonal-adjustment, base-year, and definition changes
- Data snapshot time
ALFRED does not contain the original release of every national, economic, or private dataset.[S8][S9] If the historical vintage cannot be obtained, do not describe the latest value as the value known at the time. Explain how that limitation narrows the research question.
The Same Date Can Mean Different Times in Different Markets
The NYSE core trading session runs from 09:30 to 16:00 Eastern Time, and the closing auction occurs at 16:00.[S11] Therefore, if a signal is calculated from that day’s official NYSE closing price and the backtest assumes—without a separate execution rule—that it was already filled at the same closing price, the information and execution order has been reversed.
A date such as 2026-08-31 does not tell you:
- Whether it is local time or UTC
- Whether the bar covers the regular session or includes extended hours
- Whether the price is the official close or simply the last regular trade
- Whether daylight saving time applies
- Whether it was an early-close day
- When the provider delivered the value
The NYSE material supports only the current trading hours of the NYSE and its related markets.[S11] It is not a universal rule for other exchanges, assets, sessions, early closes, or vendor delays. Verify the trading session and calendar for each market separately.
For a time audit, this article checks:
- Time-zone-aware
event_atandreceived_at bar_start,bar_end- Exchange and trading session
- Whether the price is the official close
- Local trading date and converted UTC timestamp
- Time signal calculation was completed
- Order submission and execution times
- Trading-calendar version and early-close indicators
Preserving the source time zone and local trading date, with a UTC conversion where useful, is an audit method proposed in this article. It does not by itself guarantee safe alignment across every market. After joining on date strings, compare the sessions and close sequence for each market to ensure that later information from one market has not been attached to an earlier decision in another.
Matching the date does not mean you matched the time.
A Small Reproducible Experiment: Applying a Closing-Price Signal to the Same Day’s Return
The price series below is synthetic and exists only to demonstrate temporal alignment. Its calculated returns and cumulative values are not actual strategy performance and cannot be generalized to other assets, markets, or periods.
The signal rule is intentionally simple:
If today’s closing price is higher than yesterday’s, use 1; otherwise, use 0.
Synthetic Data
| Date | Close | Daily return | Same-day closing signal |
|---|---|---|---|
| 2026-01-05 | 100.00 | Missing | 0 |
| 2026-01-06 | 110.00 | +10% | 1 |
| 2026-01-07 | 99.00 | -10% | 0 |
| 2026-01-08 | 108.90 | +10% | 1 |
| 2026-01-09 | 98.01 | -10% | 0 |
pct_change() calculates the fractional change between the current row and the previous row.[S12] The +10% on January 6 is therefore the change that has already occurred between the January 5 close and the January 6 close.
The Incorrect Backtest
df["return"] = df["close"].pct_change()df["signal_at_close"] = ( df["close"] > df["close"].shift(1)).astype(int)df["wrong_return"] = ( df["signal_at_close"] * df["return"])signal_at_close cannot be calculated until today’s closing price is final. The return in that same row, however, measures the return from yesterday’s close to today’s close.
Multiplying the two values within the same row amounts to observing today’s increase and then pretending the signal had been held since before that increase began. Appearing in the same row does not mean two values were available at the same time.
Delay the Signal by One Period
df["delayed_signal"] = ( df["signal_at_close"].shift(1))df["aligned_return"] = ( df["delayed_signal"] * df["return"])Using shift(1) aligns the signal calculated in the previous row with the return in the current row.[S12]
| Date | Close | Daily return | Same-day closing signal | Incorrectly applied return | One-period delayed signal | Aligned return |
|---|---|---|---|---|---|---|
| 2026-01-05 | 100.00 | Missing | 0 | Missing | Missing | Missing |
| 2026-01-06 | 110.00 | +10% | 1 | +10% | 0 | 0% |
| 2026-01-07 | 99.00 | -10% | 0 | 0% | 1 | -10% |
| 2026-01-08 | 108.90 | +10% | 1 | +10% | 0 | 0% |
| 2026-01-09 | 98.01 | -10% | 0 | 0% | 1 | -10% |
The incorrectly aligned synthetic cumulative value is:
(1.10 × 1.10) - 1 = +21%The synthetic cumulative value after delaying the signal by one period is:
(0.90 × 0.90) - 1 = -19%The difference between these values does not tell us whether the signal could make money in a real market. The series was deliberately constructed with alternating gains and losses, so neither +21% nor -19% can be generalized as actual strategy performance.
The size of the return is not the point. The test is whether the signal was applied only to a period that began after the signal became complete.
Complete Code You Can Run Yourself
import pandas as pddf = pd.DataFrame( {"close": [100.00, 110.00, 99.00, 108.90, 98.01]}, index=pd.date_range("2026-01-05", periods=5, freq="B"),)df["return"] = df["close"].pct_change()# Use 1 if today's close is higher than the previous day's close.df["signal_at_close"] = ( df["close"] > df["close"].shift(1)).astype(int)# Error: Apply a signal derived from today's close# to the same-day closing-price return that has already occurred.df["wrong_return"] = ( df["signal_at_close"] * df["return"])# Minimal timing correction: Apply the signal calculated through the previous trading day# to the next row's return.df["delayed_signal"] = ( df["signal_at_close"].shift(1))df["aligned_return"] = ( df["delayed_signal"] * df["return"])print(df.round(4))cumulative = ( 1 + df[["wrong_return", "aligned_return"]].fillna(0)).prod() - 1print(cumulative)Using shift(1) is a minimal correction for the error of applying a signal derived from today’s close to a same-day return that has already occurred. It does not prove that an order was actually filled at the next day’s open or at any other price.
Real research must also specify bar completion, signal-calculation completion, assumed order submission and execution, trading halts, unfilled orders, and early-close rules. Transaction costs and risk metrics belong to Part 5, so they are not included in this example.
The purpose of shifting the signal is not to improve performance. It is to preserve the causal order between information availability and execution.
Choosing Only Easily Available Data Changes the Question Itself
Defining the sample based on which data is easiest to obtain can also affect the research result.
- Selecting only assets with long price histories
- Selecting only U.S. large-cap stocks because their API data is convenient
- Selecting only companies with complete financial fields
- Choosing a period that contains only certain market regimes
- Studying only markets with free data
- Applying today’s popular asset classes to the historical sample
These choices are not inherently wrong. Studying only U.S. large-cap stocks or a specific period can be a clearly defined research scope.
The problem arises when a sample constrained by data availability is described as though its conclusions apply to the entire market or to other periods. Empirical evidence shows that requiring complete observations can create a non-random sample.[S5] Because the direction and magnitude of the bias depend on the selection rule and missingness structure, we also should not claim that it always increases measured performance.[S5]
Record the following:
- The target population you intend to explain
- The sample actually used
- Market, asset, and period coverage
- Inclusion and exclusion rules
- Counts of securities and observations by exclusion reason
- How data availability changed the research question
- The limits within which the findings may be generalized
Whether the conveniently available sample is the same population you intended to explain is a separate research question.
How My View Changed: From Clean CSVs to Data Lineage
| Category | My current assessment |
|---|---|
| What I thought before | I believed that a CSV with no missing values and a high backtest return was sufficiently clean. |
| What I verified this time | Raw and adjusted prices are not the same, and splits and dividends have different meanings in return calculations.[S2][S3][S4] Corporate actions involve several different dates,[S1] and deleting rows with missing values can change the sample.[S5] Omitting delisted securities and their final returns can also change the historical market represented by the data.[S6] |
| What I think now | No matter how sophisticated the model is, results are difficult to trust if filing dates, data vintages, universe entries and exits, time zones, and signal delays have not been checked.[S6][S7][S8][S9][S10][S11][S12] |
| What I still do not know | The public materials reviewed were not sufficient to establish every vendor’s adjustment formula, the completeness of current commercial delisting data, vendor delivery delays, or actual execution feasibility. |
What changed as I studied was less my preference in models than the standard of documentation I require before trusting a result.
I used to see a data file only as model input. Now I also examine where each value came from, when it was generated, when I could have received it, and which rows were revised, replaced, or excluded. When something cannot be known, I record a conservative assumption or a limitation on the research scope instead of filling the gap with a plausible story.
A Data-Audit Table for My Own Dataset
The following is not a postmortem checklist for explaining poor results. It is closer to a design document that should be completed before the experiment begins.
| Audit question | Required columns and materials | What to record in the experiment log |
|---|---|---|
| What does the price represent? | Raw and adjusted close; price and total-return indices | Selected column and reason for choosing it |
| What has been adjusted? | Splits, dividends, spin-offs, rights offerings, adjustment factors | Provider definition, version, and download times |
| When did a corporate action become known? | Declaration, announcement, ex-, record, payment, and update dates | Availability date used by the signal |
| Why is the value missing? | Trading calendar, halt status, listing and delisting status, join logs | Counts by cause and processing rules |
| Is the outlier a real event? | Corporate actions, currency and units, volume, duplicate records | Original and corrected values with supporting rationale |
| Is this the universe available at the time? | Entry and exit dates, delisting date and reason, permanent ID | Point-in-time universe construction procedure |
| When was the financial value published? | Period-end date, filing and acceptance timestamps, filing ID | Effective periods of initial and revised values |
| Which vintage is the economic value from? | Release date, vintage date, revision number | Query conditions used to retrieve the contemporaneous value |
| Are the time zones aligned? | Source time zone, local trading date, exchange, session | Conversion rules and market-specific trading calendar |
| Did execution occur after the signal? | Bar close, calculation completion, order and execution times | shift and execution-price rules |
| Why does the sample cover this scope? | Target population, market, asset, period, exclusion reasons | Exclusion flow and limits on generalization |
| Can the result be reproduced? | Code, environment, source files, checksums | Run date, library versions, and settings |
You can start with your own dataset in this order:
- Check the provider’s documentation for the definitions of
closeandadjusted_close.[S4] - Record which date fields exist in the split and dividend data.[S1]
- Group missing values by cause and write down the current deletion and imputation rules.[S5]
- Verify that the dataset contains the point-in-time universe and delisted securities.[S6]
- Confirm that financial data becomes active only after its actual filing—not from
period_end.[S7] - Determine whether economic indicators use the latest revised values or contemporaneous vintages.[S8][S9][S10]
- Record market-specific timestamps, trading sessions, and closing sequences.[S11]
- Compare the existing signal with the result after applying
shift(1).[S12]
Even if the last comparison produces only a small difference, that does not prove the absence of a timing error. Check whether the signal was used only after its information became complete, rather than focusing on the size of the result.
What This Part Will Not Resolve
Correct temporal ordering alone does not make a backtest fair.
Part 4 will separately cover train, validation, and test splits; the difference between random splits and financial time-series splits; walk-forward validation; data snooping; multiple testing; parameter searches; and contamination of the test period. This part stops at the connection that those validation procedures become meaningful only after the input data’s clock has been aligned.
Commissions, spreads, market impact, taxes, and risk metrics belong to Part 5. The synthetic example in this article includes none of them and cannot be interpreted as strategy performance.
Safe LLM use and prompt design will be covered in Parts 10 and 11.
Conclusion: Examine the Data’s History Before the Performance Table
We need to distinguish raw from adjusted prices[S2][S4] and price return from total return.[S3] We should preserve the distinct dates and revision history of corporate actions[S1] and investigate the causes and processing assumptions behind missing values and outliers.[S5]
Do not project today’s survivors backward into the past. Track point-in-time universes and delistings.[S6] Use financial information only after it was actually disclosed, not from the end of its accounting period,[S7] and distinguish the latest economic values from the vintages available at the time.[S8][S9][S10]
Align market-specific time zones, closing times, signal calculation, and execution—not just dates.[S11][S12] Also document whether an easily available sample has quietly replaced the population you intended to study.[S5]
Do not fill unknown vendor delays, adjustment rules, or gaps in data coverage with guesses. Record them as assumptions and research gaps.
Before looking at model performance, trace when each data point was generated, when it became observable, and how it was revised, replaced, or excluded.
A CSV with no missing values and a high backtest return cannot substitute for that audit.
The tables and code in this article are educational and research examples designed to explain data alignment. They do not recommend any security, instruct anyone to place real trades, or guarantee returns. Results from the synthetic data must not be generalized as actual strategy performance.
Once the data’s clock is aligned, the next question is:
How should we divide the training, validation, and test periods in chronological order to create a fair backtest?
Sources
- [S1] Corporate Actions of NYSE Group Listings | NYSE Group, Inc. | 2025-07-14, Version 3.2a | https://beta.nyse.com/publicdocs/nyse/data/NYSE_CorporateActions_Client_Specification.v3.2a.pdf ↩
- [S2] Stock Split | U.S. Securities and Exchange Commission, Investor.gov | No date shown on the page | https://www.investor.gov/introduction-investing/investing-basics/glossary/stock-split ↩
- [S3] S&P Dow Jones Indices: Index Mathematics Methodology | S&P Dow Jones Indices | 2026-08 | https://www.spglobal.com/spdji/en/documents/methodologies/methodology-index-math.pdf ↩
- [S4] API Documentation | Alpha Vantage | No date shown on the page; documentation as of 2026-08-31 | https://www.alphavantage.co/documentation/ ↩
- [S5] Non-random sampling and association tests on realized returns and risk proxies | Anne Beatty, John M. Core, Wayne R. Guay | Published online 2021-03-25 | https://link.springer.com/article/10.1007/s11142-021-09581-0 ↩
- [S6] The Delisting Bias in CRSP Data | Tyler Shumway | 1997 | https://doi.org/10.1111/j.1540-6261.1997.tb03818.x ↩
- [S7] EDGAR Application Programming Interfaces (APIs) | U.S. Securities and Exchange Commission | 2025-04-08 | https://www.sec.gov/search-filings/edgar-application-programming-interfaces ↩
- [S8] FRED API Real-Time Periods | Federal Reserve Bank of St. Louis | No date shown on the page; documentation as of 2026-08-31 | https://fred.stlouisfed.org/docs/api/fred/realtime_period.html ↩
- [S9] fred/series/vintagedates | Federal Reserve Bank of St. Louis | No date shown on the page; documentation as of 2026-08-31 | https://fred.stlouisfed.org/docs/api/fred/series_vintagedates.html ↩
- [S10] Gross Domestic Product Release—Additional Information | U.S. Bureau of Economic Analysis | Page as of 2026-03 | https://www.bea.gov/news/gdp-release-additional-information ↩
- [S11] Trading Information | New York Stock Exchange | No date shown on the page; as of 2026-08-31 | https://beta.nyse.com/trade/trading-information ↩
- [S12] pandas.DataFrame.pct_change | pandas development team | pandas 3.0.4 documentation; checked 2026-08-31 | https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.pct_change.html ↩
Report an error or share feedback
Open a draft with this article’s title and URL. Review the message and recipient before sending.
Open email draftIf no email app opens, copy these details into your usual email service.
Related posts
Quant & Data Research Can Logistic Regression Beat the Baseline? What Makes a Model Comparison Fair
Define timing, data splits, retraining, and cost rules for a fair comparison of logistic regression and baseline strategies, before testing actual performance.
Quant & Data Research Why shift(1) Is Not Enough: A Time Contract for Signals, Execution, and Labels
Separate signal, execution, return, and label timing. Use synthetic unit tests to check information leakage and preprocessing boundaries.