DEV Community

SIGNUM HQ
SIGNUM HQ

Posted on AI-assisted

How we decide which earnings options records count as pre-announcement

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.

Mobile view of the receipts page: counts for pre-announcement pairs, later reconstructions, unverified timing and missing comparisons

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
}
Enter fullscreen mode Exit fullscreen mode

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

Top comments (1)

Collapse
 
arhancanli profile image
Arhan Canli •

The capturedAt rule is the right call, and the "not a forecast, counts only" framing keeps it honest. Two things I'd add before anyone reads the within/outside column.

First, "within range" should not be expected most of the time. An at-the-money straddle priced over the stock is roughly the market's expected absolute move, and for a normal move that is about 0.8 standard deviations, so a one-day reaction lands inside it only around 58% of the time even if the options are priced perfectly. Your 3 of 4 pre-announcement pairs inside is consistent with that, and with n=4 the 95% Wilson interval for 3/4 is about 30% to 95%, so it says nothing either way yet.

Second, the straddle runs to expiry, so it includes the days after earnings as well as the earnings day. For ASML at ±5.5% against a one-day -3.94%, part of the gap is just the extra days of ordinary variance. If you have two expiries around the release you can strip that out: the variance difference between the expiry after and the expiry before earnings is the event's own share, and that is the number to put next to a close-to-close reaction.

Is the capture time per row stored from the exchange feed or from your own poll? That decides how tight the "unverified" bucket can get.