AI disclosure: this article was drafted with generative AI for the SIGNUM HQ team, then checked against the public page it describes.
An earnings story is easy to tell after the fact. "The options market priced a 4% move, the stock fell 5.6%." But when did we record that 4%? Before the release, or after we already knew what happened? If we cannot answer that, the comparison tells a reader nothing. (Those numbers are real: they are the C row further down, and its range was reconstructed after the release, which is exactly why the question matters.)
We built a public page to make the answer inspectable: Earnings verification receipts. This post explains the record-keeping rules behind it, using the week of Oct 12 as the worked example. It is a description of method and limits, not a claim about forecasting skill.
Two numbers, two different horizons
For each scheduled report the page shows:
- Options-implied range: the at-the-money call plus put price divided by the stock price. It describes option pricing through the option's expiry. It is not a SIGNUM forecast.
- Actual reaction: one regular-session close-to-close change. For a before-open release, that is the previous trading close to the release-day close. For an after-close release, the release-day close to the next trading close. Extended-hours prices are excluded.
The horizons differ on purpose, and the page says so: the implied range runs to expiry, the reaction covers one trading day. "Within range" simply means the absolute reaction was no larger than the range.
What counts as "pre-announcement"
The row that matters is the storage timestamp (capturedAt). Only a capture stored before the known release time qualifies. If the company gives no exact time, the rule is deliberately conservative: before 00:00 ET on the release day for a before-open report, or before the regular close for an after-close report.
Everything else is labeled rather than dropped:
| Status | Meaning |
|---|---|
| Pre-announcement record | Stored before the release by the rule above |
| Later reconstruction | Rebuilt from end-of-day chains, or stored after the release |
| Pre-announcement timing unverified | Ambiguous same-day capture |
| No record | Nothing comparable, and the row stays visible |
The conservative rule can leave a genuinely early record in the "unverified" bucket. We accept that, because the opposite error (promoting a record that was really written after the fact) is the one that makes the whole page worthless.
The week of Oct 12 as data
Twelve announcements were scheduled. The page counted 4 pre-announcement pairs, 6 later reconstructions, 1 unverified timing and 1 missing comparison.
| Stock | Options-implied range | Actual reaction | Status |
|---|---|---|---|
| ASML | ±5.5% | -3.94% | Pre-announcement, within |
| MS | ±4.0% | -2.75% | Pre-announcement, within |
| TSM | ±3.7% | -1.80% | Pre-announcement, within |
| BAC | ±4.2% | -4.76% | Pre-announcement, outside |
| C | ±4.0% | -5.60% | Later reconstruction, outside |
| JNJ | ±3.6% | -5.73% | Later reconstruction, outside |
The last two rows are the reason the categories exist. C and JNJ both moved more than their ranges, but their ranges were rebuilt from end-of-day chains, so the page shows them for reference and keeps them out of the pre-announcement counts. Mixing them in would change the tally, and it would change what the tally means.
Guardrails we kept
- Two sources for closes. Both closes come from one data vendor and are checked against a second source with a 0.1% tolerance. Rows without that confirmation are marked.
- Daily bars are final only after the session. A reaction is not computed until the bar has had time to settle.
- Missing stays missing. PLD had no announced release timing, so there is no reaction window and the row shows "No record" rather than a guess.
- Not an immutable ledger. Records are kept for 120 days, and manual reconstruction, price corrections and overwrites are possible. A complete revision history is not available, and the page says that too.
A sketch of the classification
This is an illustration of the rule, not our production code:
type Timing = "bmo" | "amc"; function qualifies( capturedAt: Date, release: { at?: Date; timing: Timing; day: string; regularCloseET: Date }, ): boolean { if (release.at) return capturedAt < release.at; // exact time known return release.timing === "bmo" ? capturedAt < startOfDayET(release.day) // before 00:00 ET on release day : capturedAt < release.regularCloseET; // before the regular close } Anything that fails this check and came from an end-of-day chain becomes a later reconstruction. The unverified bucket exists for captures the rule cannot place on either side.
What the counts are not
The counts do not measure investment performance or prediction accuracy. Four pairs in one week is a very small sample, and an implied range that runs to expiry is not meant to bound a single day. The useful part is the audit trail: for every row you can see the option session, the expiry, the capture timestamp, the source and both closes, and decide for yourself how much weight it deserves.
I work on SIGNUM HQ, a US stock and options research app. The page described here: https://www.signumhq.com/en/receipts/earnings?from=devto_1021

