Notebook examples for exploring the Explaining Markets
competition API and its historical data archive — including the disclosure items
an earnings event can carry: the ten call facts, and from 2026Q3 the earnings
preview and three option-implied statistics.
Notebook-first, with the repeated
API / parsing / plotting boilerplate factored into a small examples package so the
notebooks stay short and readable.
| Notebook | What it covers |
|---|---|
notebooks/00_api_quickstart.ipynb |
The read endpoints — events calendar (table + chart), submission health, archive manifest, webhook self-test |
notebooks/01_historical_archive.ipynb |
Download, cache, and load the historical archive, then reproduce the baseline scoring regressions (Koijen & Levy WP, Table 3) with the competition's exact scoring transform |
notebooks/02_earnings_call_facts.ipynb |
Where each event's ten-fact disclosure comes from — the exact prompt, model config, and parsing behind it, and how extraction failures surface |
notebooks/03_earnings_previews.ipynb |
The earnings preview — the result of running Anthropic's Earnings Preview Skill prior to each earnings announcement. Starting Q4 2026, events carry this as a second disclosure item. The notebook covers how it is built, what it looks like, and how it pairs with the facts |
notebooks/04_options_market_data.ipynb |
The option-implied statistics — what the options market priced for each release, why the scorer is blind to a uniform rescaling of predictions, and how the implied move works as an event-specific scale for a prediction. Reproduces the effect on the two reference baselines with the exact scoring transform |
From 2026Q3, an event may also carry an option-implied-stats item (kind="stats"), for stocks with liquid listed options: fewer than half of events. Its content is an object, unlike the facts (a list of strings) and the preview (one string):
from examples.option_stats import option_implied_stats_from_disclosure, statistic_value
stats = option_implied_stats_from_disclosure(record) # dict, or None when absent
move = statistic_value(stats, "implied_absolute_earnings_move") # float, or None unless status is "ok"examples.option_stats documents what the three numbers mean and when they are missing, and scale_by_implied_move turns the move into an event-specific rescaling of a submission's predictions. Notebook 04 walks through both; its regression sections need the live archive (the bundled sample carries the item on its six events but no baseline predictions).
If you parse bundles with your own typed model, make sure an item's content accepts any JSON type. A model that allows only strings and lists of strings rejects the whole bundle, facts included, the first time it meets this item. examples.schemas.DisclosureItem had exactly that annotation until this release.
All five run top-to-bottom. With an API key they hit the live API; without one they
fall back to a small bundled sample (data/sample/), so you can run them — and CI
can execute them — with no credentials.
Submitting predictions is out of scope here.
POST /predictionsplaces a real, scored entry and belongs in a deployed webhook handler. Start from thestarter-modal(orstarter-railway) repos for that.
This repo uses uv. From a clone:
uv sync
cp .env.example .env # then paste your submission's API key (optional)
uv run jupyter labNo key yet? Skip the .env step and everything still runs in sample mode.
.env.example also has an optional ANTHROPIC_API_KEY. It is not a competition
credential — only the last section of notebook 02 uses it, to run the real fact-extraction
call so you can watch it happen. Leave it blank and that section self-skips.
from examples import Client
from examples.frames import events_frame
with Client.from_env() as client:
events = client.events()
events_frame(events).head()notebooks/ the numbered, narrative examples
src/examples/ helper package: config · client · schemas · frames · plotting · archive · scoring · summary · preview · option_stats
scripts/ small runnable scripts (e.g. headless archive download)
tests/ offline unit tests (mocked HTTP + bundled sample data)
data/
sample/ tiny, committed, illustrative data
archive/ gitignored download cache for real archive files
uv run ruff check . # lint
uv run ruff format . # format
uv run mypy # type-check
uv run pytest # offline unit tests
uv run pytest --nbmake notebooks/ # execute the notebooks (sample mode)
uv run pre-commit install # strip notebook outputs + lint on commitCI runs all of the above on every push and pull request. Live tests (-m live)
need EM_API_KEY and are excluded by default.
starter-modal— deploy a webhook handler on Modalstarter-railway— the same, on other platformsOfficial baselines— reference agents to measure yourself against