Insights
Earnings Implied-Move Database With Realized Outcomes
Alphanume Team · July 20, 2026
Query each earnings event with its pre-event ATM straddle-implied move, post-event realized move, and point-in-time per-ticker history, then keep gross movement evidence separate from trade P&L.
Alphanume's Earnings Implied vs Realized dataset records how an at-the-money straddle priced an earnings move and how the stock actually reacted. Each row carries announcement timing, capture and reaction dates, straddle components, implied move, realized signed and absolute movement, over-under measures, and running ticker history.
The database answers whether realized movement exceeded the pre-earnings implied move under its recorded measurement. It does not provide a complete option trade, because entry spread, skew, contract liquidity, path, exit execution, commissions, and assignment can materially change P&L.
Read the event clocks first
Field | Meaning | Research use |
|---|---|---|
date | Earnings observation date | Event identifier date |
time | BMO or AMC timing | Determines capture and reaction sessions |
capture_date | Date the pre-event straddle was priced | Information cutoff |
reaction_date | Date realized response was measured | Outcome clock |
exp_date | Expiration used for the straddle | Contract horizon context |
Before-market-open and after-market-close events can share a calendar date while using different capture sessions. Join on the explicit capture and reaction fields rather than assuming every earnings date maps to the same close-to-close window.
Query one ticker's history
The endpoint is GET /v1/earnings-move-history. Use a ticker filter for the cleanest first audit, preserve all events, and sort ascending before computing lagged features.
import os
import requests
import pandas as pd
response = requests.get(
"https://api.alphanume.com/v1/earnings-move-history",
headers={"X-API-Key": os.environ["ALPHANUME_API_KEY"]},
params={"ticker": "AAPL"},
timeout=30,
)
response.raise_for_status()
events = pd.DataFrame(response.json()["data"])
events = events.sort_values(["ticker", "date"])
required = {
"implied_move_pct", "realized_abs_move_pct", "move_ratio",
"over_under_pct", "n_events_to_date", "hit_rate_to_date",
}
assert required.issubset(events.columns)Exact date cannot be combined with range filters. Results arrive newest first, then ticker ascending. Free access provides a trailing 20-trading-session window ending one session behind the latest observation, so per-name histories generally require full-history access.
Keep the calculations straight
Metric | Definition | Interpretation |
|---|---|---|
implied_move_pct | ATM straddle divided by spot | Gross move priced at capture |
realized_abs_move_pct | Absolute stock reaction | Observed movement magnitude |
over_under_pct | Implied move minus realized absolute move | Positive means implied exceeded realized |
move_ratio | Realized absolute move divided by implied move | Above one means realized exceeded implied |
overpriced | Realized move below implied move | Event-level comparison, not net trade profit |
A straddle can appear overpriced on the movement comparison and still be unprofitable to sell after spreads, fees, hedging, or adverse path risk. Likewise, realized movement above implied does not establish that a long straddle earned money under an unspecified exit.
Lag the running history
Fields such as n_events_to_date, hit_rate_to_date, and trailing averages reflect events up to and including the row. They are excellent retrospective summaries. For a pre-event screen, shift them by one event so the current realized outcome does not help predict itself.
history_fields = [
"n_events_to_date",
"hit_rate_to_date",
"avg_implied_move_to_date",
"avg_realized_abs_to_date",
"avg_over_under_to_date",
]
for field in history_fields:
events[f"prior_{field}"] = events.groupby("ticker")[field].shift(1)
screenable = events.loc[events["prior_n_events_to_date"] >= 8].copy()The eight-event minimum is an example and should be chosen before results. A small history produces an unstable hit rate, and overlapping company regimes make even a large count nonstationary. Report event count beside every per-name statistic.
For a cross-sectional test, assign the screen before each capture date and evaluate only after its reaction date. Split by event season or calendar time, not by randomly scattering one company's observations across train and test. Cluster uncertainty by ticker and earnings season because repeated events share business characteristics and broad volatility conditions. Keep delisted names and ticker changes under a written identity policy, and show how many events disappear at every join.
Control the trade-level gaps
- Gross observation. Implied versus realized movement omits execution and commissions.
- Path risk. A short-volatility position can experience damaging intraday moves even when the close finishes inside implied.
- Liquidity. Notional volume is context and does not replace quoted spreads or depth.
- Event selection. Results can differ across AMC, BMO, sector, and volatility regimes.
- Current-event leakage. Running history on the current row includes that row after resolution.
The proof page provides published product evidence, while Alphanume Learn: So You Want to Trade Earnings adds directly relevant strategy context. Neither replaces contract-level simulation for a specific implementation.
Audit one name before ranking many
Read the Earnings Move History reference, retrieve one ticker, and verify capture dates, reaction dates, straddle prices, and move calculations for at least eight events. Plot implied and realized absolute movement side by side and label unresolved rows.
Then build a cross-sectional study using only lagged per-name history, a predefined minimum event count, and a later holdout season. Publish gross movement comparisons first, followed by spread and cost sensitivity from an independent option-quote source.