Listing backtests have a reputation for looking wonderful, and it is deserved. The event is discrete, the reaction is fast, and the data is easy to get. That combination is also exactly the recipe for look-ahead, because when the entire return of an event lands inside a few hours, an entry timestamp that is wrong by a few hours is not a rounding error. It is the result.
I have never seen a listing study fail for a subtle statistical reason. They fail because three different clocks got recorded in one field, and by the time anyone asks which clock it was, the answer has been lost.
Three clocks, one field
Separate these before you write any code, because the vocabulary is the discipline.
Announcement time is when the venue published the notice. It belongs to the venue and it is knowable from the venue, not from your system.
Ingest time is when the event became available to you, which is when the row appeared in your store, the webhook landed or the alert fired. It belongs to your infrastructure and it is always later than announcement time.
Effective time is when the market actually changed, meaning trading opened on the new book or, for a delisting, when trading halted. It can be minutes after the announcement or weeks after it, and it is the only one of the three that describes a change in the tradable world.
A fourth stamp is worth keeping even though it is not a clock in the same sense. Observation time, the as-of time for every other field on the row. Market cap, liquidity and holder counts are current values, and their as-of is the moment you read the page, not the moment of the event.
What the ledger's Date column can and cannot settle

Two conclusions follow directly from that screenshot and neither is a criticism of the module, which is built to be read by a person deciding what to do today rather than to reconstruct a past state.
First, a date without a time cannot resolve an intraday event. If the whole reaction happens in the first hours, then entering at any point on the stamped date is a coin flip about whether you were before or after it, and a backtest that assumes the open of that date is assuming the earlier answer every time. That single assumption is enough to manufacture a strong result out of nothing.
Second, the feed serves current state. Which of the three clocks the Date field represents, and whether a row's date can be revised after first publication, are questions to establish empirically rather than assume. Capture the same rows daily for a few weeks and diff them. If a row's date or fields change after first observation, you have restatement, and any study built on a single retrospective pull is reading revised history.
The four conflations and what each one manufactures
Announcement treated as ingest. You use the venue's publication time as your entry time, which credits your strategy with a fill it could not have had, because your system did not know yet. The manufactured edge is exactly your data latency, and it is largest for the venues you are slowest on, which tends to be the venues with the biggest moves.
Ingest treated as announcement. The mirror image, and it is the one that hides a subtler problem. If you backfill history from a provider, the ingest stamps in the backfill are the times the provider had the data, not the times you would have had it. Backfilled history therefore has a different latency profile from live history, and a strategy tuned on backfill will underperform live for reasons that look like alpha decay and are not.
Effective treated as announcement. For upcoming listings the gap between the notice and the open can be days. If you measure the return from announcement but assume you traded at the open, you have collapsed a window during which the price already moved. This is the version that produces the most confident wrong answers, because the return is real, it just was not available to you at the price you recorded.
Observation time treated as event time. Your filter says minimum liquidity of a hundred thousand dollars, and you apply it to the values on the row. But those values are current, so you have selected the tokens that have a hundred thousand dollars of liquidity now, after the listing, which is a beautiful way of selecting the listings that went well. This is the most common look-ahead in the entire category and it does not look like a timestamp bug at all. It looks like a sensible filter.
Point-in-time capture, and what it costs
There is only one fix and it is unglamorous. Store your own observations, append-only, with your own capture timestamp on every row, and never overwrite. The universe as of date d is then the set of rows whose capture time is at or before d, with the field values that were present at that time, and it is reconstructible by query rather than by assumption.
Three rules make the store usable.
- Capture the full accessible universe on each run, not the displayed page. Differencing two top-100 views produces changes that are artefacts of the sort order rather than facts about the world.
- Record all three clocks separately whenever you have them, and record a null rather than a substitute when you do not. A null is a known unknown that a later reviewer can handle. A substituted value is a lie that propagates.
- Alignment rule for entry, written down once and applied everywhere. Entry time is the later of your ingest time and the effective time, plus your measured decision and order latency. Not the announcement. Not the date. That rule is conservative by construction, which is the correct direction to be wrong in.
The cost is that the study cannot start today. If you have no historical point-in-time store, the honest options are to run the study forward from the day capture begins, or to run it on backfilled data with the look-ahead explicitly flagged as unresolved in the write-up. Both are defensible. Presenting backfilled results as if they were point-in-time is not, and it is the thing that ends careers quietly a year later when live performance does not resemble the deck.
Diagnostics that expose a look-ahead you did not know about
Run all five on any listing study before it goes to committee, including studies you did not build.
Delay the entry by one full period and re-run. If most of the result evaporates when you enter an hour or a day later, the result was living inside the timestamp ambiguity. A genuine event effect degrades gradually; a look-ahead falls off a cliff.
Measure the return in the window before your entry timestamp. If the pre-entry window is where the money is, your timestamp is later than the information, which means your fills are fantasy.
Re-run using only rows first observed live, excluding everything backfilled. A large gap between the two versions is a restatement problem, not a sampling one.
Randomise the event dates while holding the universe fixed, and re-run. A properly constructed study should collapse toward zero. If placebo dates still make money, your result is coming from the universe selection rather than from the event, which almost always means a current-value filter has leaked.
Check the universe cardinality by date. Plot how many eligible names your pipeline sees per day across the sample. A count that grows steadily is usually the signature of a store that is really a snapshot of today, filtered by date, in which case the early part of your sample contains only the names that survived to be seen.