Event-Driven and Arbitrage Strategies › Earnings Event Strategies
Quantitative FinanceAll chapters Event-Driven and Arbitrage Strategies › Earnings Event StrategiesOne section · Sections in this chapter5 / 11 ↓
Reading tools Definitions & inline links
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
Earnings announcements represent the most frequent and liquid corporate events. Companies report quarterly, creating a regular calendar of volatility events. The predictable timing combined with substantial price moves makes earnings a natural target for systematic trading.
Earnings Surprise and Price Reaction
The relationship between earnings surprises and stock returns has been studied extensively. The post-earnings announcement drift (PEAD), introduced by Ball and Brown (1968), documents that stocks with positive earnings surprises tend to continue outperforming for weeks after the announcement, while stocks with negative surprises continue underperforming. PEAD has persisted across decades and markets despite extensive academic documentation.
To quantify the surprise, analysts often use Standardized Unexpected Earnings (SUE). This metric acts as a signal-to-noise ratio, scaling the raw earnings beat by the historical volatility of forecast errors to distinguish meaningful surprises from normal variation. Standardization makes earnings surprises comparable because a $0.05 beat means something very different for a stable utility company than for a volatile technology firm:
where:
- : standardized unexpected earnings
- : reported earnings per share
- : consensus analyst estimate
- : standard deviation of historical forecast errors
The numerator measures the raw surprise in dollars per share, while the denominator normalizes this surprise by the typical magnitude of forecast errors for that company. A SUE of 2.0 indicates a positive surprise equal to twice the historical standard deviation of analyst forecast errors for that company.
import numpy as np
import pandas as pd
def calculate_standardized_surprise(
actual_eps, expected_eps, historical_surprise_std
):
"""
Calculate standardized unexpected earnings (SUE).
SUE normalizes the surprise by historical surprise volatility,
making surprises comparable across stocks.
"""
surprise = actual_eps - expected_eps
sue = (
surprise / historical_surprise_std if historical_surprise_std > 0 else 0
)
return sue
# Generate synthetic earnings data
np.random.seed(123)
n_events = 500
# Simulate earnings surprises and subsequent returns
sue_scores = np.random.normal(0, 1, n_events)
# PEAD effect: positive surprises lead to positive drift
announcement_returns = 0.02 * sue_scores + np.random.normal(0, 0.05, n_events)
drift_returns = 0.005 * sue_scores + np.random.normal(
0, 0.03, n_events
) # next 20 days
total_returns = announcement_returns + drift_returns
# Analyze returns by SUE quintile
earnings_df = pd.DataFrame(
{
"sue": sue_scores,
"announcement_return": announcement_returns,
"drift_return": drift_returns,
"total_return": total_returns,
}
)
earnings_df["sue_quintile"] = pd.qcut(
earnings_df["sue"], 5, labels=["Q1 (Worst)", "Q2", "Q3", "Q4", "Q5 (Best)"]
)
quintile_analysis = (
earnings_df.groupby("sue_quintile")
.agg(
{
"sue": "mean",
"announcement_return": "mean",
"drift_return": "mean",
"total_return": "mean",
}
)
.round(4)
)
Returns by Earnings Surprise Quintile
============================================================
sue announcement_return drift_return total_return
sue_quintile
Q1 (Worst) -1.4416 -0.0386 -0.0041 -0.0426
Q2 -0.5695 -0.0067 0.0019 -0.0048
Q3 -0.0480 -0.0008 0.0022 0.0014
Q4 0.4846 0.0071 0.0029 0.0099
Q5 (Best) 1.3813 0.0250 0.0050 0.0301The analysis confirms the PEAD phenomenon: stocks in the highest SUE quintile (Q5) show the strongest immediate announcement returns and continue to drift upward, generating positive post-announcement returns. Conversely, the lowest quintile (Q1) exhibits negative drift, with both the announcement return and the subsequent drift moving downward in tandem.
Earnings Trading Approaches
Several systematic approaches exploit earnings events:
Pre-earnings momentum: Stocks that have exhibited strong momentum leading into earnings tend to have positive surprises. This reflects the market partially anticipating earnings outcomes but leaving room for continued repricing upon actual announcement. A strategy might go long stocks with strong pre-earnings momentum and short those with weak momentum.
Volatility trading around earnings: As we explored in the volatility trading chapter, implied volatility rises before earnings and collapses afterward. Strategies can sell straddles before earnings to collect the higher premiums or buy straddles when implied volatility appears compressed relative to typical realized moves for that security.
PEAD capture: Trading in the direction of the surprise after the announcement captures the drift. The challenge is speed: announcement returns happen in seconds, while the drift unfolds over days to weeks.
def earnings_straddle_analysis(
stock_price,
iv_pre_earnings,
realized_move_pct,
days_to_earnings,
risk_free_rate=0.05,
):
"""
Analyze a short straddle strategy around earnings.
Compares implied volatility premium collected vs. realized move.
"""
# Convert IV to expected move over the period
T = days_to_earnings / 365
expected_move_implied = stock_price * iv_pre_earnings * np.sqrt(T)
# Simple ATM straddle pricing (using Black-Scholes approximation)
# For ATM options: Call ≈ Put ≈ 0.4 * S * σ * sqrt(T)
call_price = 0.4 * stock_price * iv_pre_earnings * np.sqrt(T)
put_price = call_price # ATM approximation
straddle_price = call_price + put_price
# Breakeven move
breakeven_move_pct = straddle_price / stock_price
# P&L from short straddle given realized move
realized_move_dollar = stock_price * abs(realized_move_pct)
straddle_pnl = straddle_price - realized_move_dollar
straddle_return = straddle_pnl / straddle_price
return {
"straddle_price": straddle_price,
"straddle_pct_of_stock": straddle_price / stock_price,
"implied_move": expected_move_implied,
"implied_move_pct": expected_move_implied / stock_price,
"breakeven_move_pct": breakeven_move_pct,
"realized_move_pct": abs(realized_move_pct),
"short_straddle_pnl": straddle_pnl,
"short_straddle_return": straddle_return,
"stock_price": stock_price,
"iv": iv_pre_earnings,
"days": days_to_earnings,
}
# Example: Stock at $100, IV 80% (elevated for earnings), actual move 5%
straddle_result = earnings_straddle_analysis(
stock_price=100,
iv_pre_earnings=0.80, # Annualized IV
realized_move_pct=0.05,
days_to_earnings=2,
)Earnings Straddle Analysis ============================================= Stock Price: $100.00 Pre-Earnings IV: 80% Days to Earnings: 2 Straddle Pricing: Straddle Cost: $4.74 As % of Stock: 4.7% Implied Move: 5.9% Breakeven Move: 4.7% Realized Outcome: Actual Move: 5.0% Short Straddle P&L: $-0.26 Short Straddle Return: -5.5%
The short straddle profits because the realized move of 5% was contained within the breakeven range implied by the option premiums. The pre-earnings implied volatility of 80% priced in a 6.9% move. Since the actual reaction was milder, the volatility risk premium was successfully harvested.
Analyst Revisions and Other Information Events
Beyond scheduled earnings, other information events create trading opportunities:
Analyst estimate revisions: When analysts revise their earnings estimates, stocks often drift in the direction of the revision. The revision captures private information that isn't yet fully reflected in prices.
Index reconstitutions: When indexes like the S&P 500 add or remove stocks, passive funds must buy or sell. This predictable demand creates price pressure that can be traded ahead of the effective date. Research shows stocks added to major indexes experience positive abnormal returns, while deleted stocks suffer negative returns.
Credit rating changes: Rating upgrades and downgrades affect bond prices directly but also signal information about equity value. The impact is asymmetric: downgrades tend to have larger effects than upgrades.
# Simulate index reconstitution effect
np.random.seed(456)
# Event window: -20 to +20 days around announcement
event_window = np.arange(-20, 21)
# Addition effect: price rises as announcement approaches, peaks at effective date
# Reversion afterward
addition_car = np.zeros(len(event_window))
for i, day in enumerate(event_window):
if day <= 0:
addition_car[i] = 0.03 * (1 - np.exp(-0.1 * (day + 20))) # gradual rise
else:
addition_car[i] = 0.03 - 0.01 * (
1 - np.exp(-0.05 * day)
) # partial reversion
# Deletion effect: opposite pattern
deletion_car = -addition_car * 0.8 # slightly smaller magnitude
# Add noise
addition_car += np.random.normal(0, 0.002, len(event_window)).cumsum() * 0.3
deletion_car += np.random.normal(0, 0.002, len(event_window)).cumsum() * 0.3