Skip to content
Back to skills

Event Study

ASecurity

Measure abnormal returns around a corporate event for one or many tickers. Three input modes pick the output shape automatically: single ticker + single event renders a sell-side note (with t-stat vs that name's reaction distribution); many tickers + one event class renders a cross-section table; many events + many tickers renders aggregate statistics. Supports earnings (Benzinga or SEC EDGAR fallback), dividend changes, and computed volume spikes out of the box. Generalizes earnings-drilldow...

  • 7 stars
  • 0 votes
  • 0 copies
  • 4 views
  • Added September 6, 2026
developmentgoreacttestingapi

Works with

  • api

Security analysis

A100/100

Pro scans all 12 files and shows the line behind each finding

Scanned September 6, 2026

npx -y skills add rgourley/quant-garage --skill event-study --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Event Study?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Event Study
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/rgourley-event-study/badge)](https://www.skillsdirectory.com/skills/rgourley-event-study)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: event-study
description: Measure abnormal returns around a corporate event for one or many tickers. Three input modes pick the output shape automatically: single ticker + single event renders a sell-side note (with t-stat vs that name's reaction distribution); many tickers + one event class renders a cross-section table; many events + many tickers renders aggregate statistics. Supports earnings (Benzinga or SEC EDGAR fallback), dividend changes, and computed volume spikes out of the box. Generalizes earnings-drilldown's PEAD work to any event class.
---

# event-study

You hand over an event (a date + a class) and either one ticker or a
basket. The skill measures abnormal returns over the event window,
compares each reaction to the name's own history, and aggregates across
the cross-section when the input is wider than one event.

This is the workflow a quant or event-driven PM runs when asking
"did the market actually react to this," "is the cross-section
consistent," or "is this kind of event a tradeable signal." The output
matches the format an analyst already reads: morning-note style for a
single event, screener table for a cross-section, summary stats for an
aggregate.

## When to invoke

- A PM is sizing into a name post-print and wants to know "what's the
  T+5 base rate after a beat like this"
- A quant is testing whether dividend hikes (or cuts) lead to
  measurable abnormal returns across a sector
- A trader saw an unusual volume day on a peer and wants to know
  whether the event class historically resolves
- The user says "event study on X", "what's the average abnormal
  return after Y", "did the market price in Z", or "cross-section
  reaction across mega-cap tech earnings"

## Three modes (determined by input shape)

The same code path runs all three; the shape of `--tickers` and
`--event-date` (vs `--window`) picks the output mode.

### Mode 1: single (single ticker, single event)

Input: `--ticker NVDA --event-date 2026-05-20 --event-class earnings`

Output: a sell-side note with the event window returns, t-stat of this
event's abnormal return vs the name's prior reaction distribution, and
a one-line take. Matches the layout of
[`../earnings-drilldown`](../earnings-drilldown) but generalized to any
event class. See [`references/rendering.md`](./references/rendering.md).

### Mode 2: cross-section (many tickers, one event period)

Input: `--tickers AAPL,NVDA,MSFT,GOOGL,META --event-class earnings --period 2026Q2`

Output: a comparison table (one row per ticker), plus a "Cross-section"
footer with the average T+5 CAR, the median, the t-stat of the average
against zero, and the correlation between surprise magnitude and
reaction.

### Mode 3: aggregate (many tickers, many events)

Input: `--tickers AAPL,NVDA,MSFT,GOOGL,META --event-class earnings --window 2025-06-01..2026-06-24`

Output: only the aggregate statistics. Average CAR by horizon (T+1,
T+3, T+5), t-stat against zero, percentile distribution, n. No
per-event detail in the rendered output (it's in the JSON for UIs).
Used for "is this event class a tradeable signal at all" questions.

## Event classes supported

| Class | Source | Trigger definition |
|---|---|---|
| `earnings` | Benzinga (Tier A) or SEC EDGAR 8-K item 2.02 (Tier B) | Press release date + time |
| `dividend_changes` | `/v3/reference/dividends` | First dividend whose amount differs from the prior payment by ≥1% |
| `large_volume_spike` | computed from `/v2/aggs/ticker/{T}/range/1/day/...` | Days where volume > 3σ of the trailing 30d mean, with a 5-day cooldown |

Each class has its own resolution helper documented in
[`references/event-class-definitions.md`](./references/event-class-definitions.md).
Adding a new event class is a clean PR: implement the resolver, add
a row to the table above, and the skill picks it up.

Out of scope for v1: analyst upgrades/downgrades (the Benzinga
analyst-ratings endpoint wasn't reliably reachable in prior sessions;
queued for v2), index inclusions/exclusions, M&A announcements.

## What you need

- A list of tickers (one or many)
- Either a specific event date or a window
- `MASSIVE_API_KEY` exported

Tiers:

- **Tier A** (full fidelity for earnings): Stocks Starter + Benzinga
  Earnings. True press release dates, consensus, surprise %, allows
  the "surprise vs reaction" correlation column in cross-section.
- **Tier B** (degraded earnings): Stocks Starter only. 8-K item 2.02
  acceptance date as print date; no surprise %, so the cross-section
  drops the surprise-vs-reaction correlation and falls back to
  reaction-sign bucketing. `dividend_changes` and
  `large_volume_spike` run identically on either tier.

## What you get back

Two output layers from one analysis.

**Layer 1: canonical JSON** matching
[`output-schema.json`](./output-schema.json). Discriminated by
`output_mode`: `single`, `cross_section`, or `aggregate`. Each mode
exposes the per-subject `event_window_returns`, `abnormal_returns`,
and `t_stat_vs_history`. Cross-section and aggregate add the
cross-sectional `summary` block, which includes `distribution_shape`
(KDE-derived n_modes, modality label, tail label, skew, excess
kurtosis, sparkline) when n_subjects >= 10 so bimodal or fat-tailed
reactions surface instead of hiding behind a benign mean. UIs and
downstream agents consume this.

**Layer 2: rendered output** in hybrid mode:
- `single` → sell-side note
- `cross_section` → comparison table + cross-section footer
- `aggregate` → summary stats block

See [`references/rendering.md`](./references/rendering.md) for the
full rules.

## How it works

The pipeline is the same regardless of input shape; what changes is
how the rendering layer collapses the result.

1. **Resolve events.** Per
   [`references/event-class-definitions.md`](./references/event-class-definitions.md),
   convert the input (ticker + class + date-or-window) into a list of
   concrete `(ticker, event_date, event_metadata)` tuples.
2. **Pull daily aggregates** for each ticker and SPY across the
   union of event windows plus a 30-day buffer on either side.
3. **Compute abnormal returns** per
   [`references/abnormal-returns.md`](./references/abnormal-returns.md).
   AR = raw_return − SPY_return at each horizon (T0, T+1, T+3, T+5).
   CAR = sum of ARs from T+1 through the horizon.
4. **Compute t-stats** per
   [`references/t-stat-significance.md`](./references/t-stat-significance.md).
   For a single event, compare this event's T+5 CAR to the name's
   prior reaction distribution. For a cross-section, t-stat is the
   mean CAR across events vs zero. Both require n≥8 to be reported
   as significant; below that, the rendered output prints the t-stat
   but marks it "underpowered."
5. **Aggregate cross-sectionally** per
   [`references/cross-section-methodology.md`](./references/cross-section-methodology.md)
   when `n_subjects > 1`. Average CAR, median, t-stat vs zero, and
   the Pearson correlation between event magnitude and reaction.
6. **Detect regime stability** per
   [`references/regime-stability.md`](./references/regime-stability.md):
   for any aggregate-mode result, compare the most-recent 4 events to
   the full window mean and flag when the gap is >1σ. Recent regime
   often differs from headline number.
7. **Generate the take** off the strongest signal: significant
   t-stat, regime shift, or surprise-vs-reaction correlation in
   cross-section.

## Foundations used

- [`massive-api-patterns`](../massive-api-patterns) for REST auth,
  pagination, and the fallback chain.

## Endpoints used

Earnings event class:
- `GET /benzinga/v1/earnings?ticker={T}&limit=20&order=desc&sort=date`
  (Tier A): press release date, time, surprise %, fiscal period.
- `GET https://data.sec.gov/submissions/CIK{cik}.json` (Tier B
  fallback): SEC EDGAR 8-K filings filtered to item 2.02. Free,
  public, no API key required. Same date-resolution logic as
  `earnings-drilldown` Tier B.

Dividend change event class:
- `GET /v3/reference/dividends?ticker={T}&limit=20&order=desc&sort=ex_dividend_date`:
  cash dividend history; the resolver picks the first ex-date where
  the cash amount differs from the prior payment by ≥1%.

Volume spike event class:
- `GET /v2/aggs/ticker/{T}/range/1/day/{from}/{to}`: same daily
  aggregates used for the return computation; the resolver computes
  volume z-score in-memory.

All classes:
- `GET /v2/aggs/ticker/{T}/range/1/day/{from}/{to}`: daily closes
  for the ticker. One call per ticker.
- `GET /v2/aggs/ticker/SPY/range/1/day/{from}/{to}`: daily closes
  for SPY (the benchmark). One call total.

## Doesn't handle (yet)

- **CAPM-style abnormal returns.** The skill uses a simple SPY-naive
  benchmark (AR = raw − SPY). A v2 would estimate per-name beta on
  the 60-day pre-event window and compute AR = raw − (alpha + beta *
  SPY). For mega-caps the difference is small (beta is close to 1);
  for higher-beta names it matters. Schema reserves `model: "spy" |
  "capm"` so the upgrade doesn't break consumers.
- **Multi-day pre-event run-up.** Some event types (M&A leaks,
  guidance pre-announces) show abnormal returns before the official
  event date. The skill measures from T0 forward only.
- **Sample-aware significance.** Below n=8, t-stats are reported but
  marked "underpowered" rather than computing a small-sample
  correction. Bootstrap CIs would be cleaner; queued.
- **Event clustering.** When multiple events fire in the same window
  (e.g. earnings + dividend hike same week), the skill attributes
  the full return to whichever event the user asked about. A cleaner
  treatment would attribute by cross-section dummy; queued.
- **Intraday windows.** Event windows are daily closes only. No
  pre-market or 30-minute reaction measurement.

These are clean PR extensions and welcome contributions.

Files in this skill

  • README.md3.1 KB
  • SKILL.md9.6 KB
  • examples/megacap-q1-2026-earnings.md4.7 KB
  • output-schema.json12.6 KB
  • references/abnormal-returns.md3.8 KB
  • references/cross-section-methodology.md4.2 KB
  • references/distribution-shape.md4 KB
  • references/event-class-definitions.md5.2 KB
  • references/regime-stability.md3.4 KB
  • references/rendering.md6.8 KB
  • references/t-stat-significance.md4.3 KB
  • requires.yml2.4 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.