Skip to content

feat(tastytrade): a skill for ranking earnings calendar spreads - #23

Merged
walkerhughes merged 2 commits into
mainfrom
tastytrade-earnings-calendars
Aug 4, 2026
Merged

feat(tastytrade): a skill for ranking earnings calendar spreads#23
walkerhughes merged 2 commits into
mainfrom
tastytrade-earnings-calendars

Conversation

@walkerhughes

Copy link
Copy Markdown
Owner

A calendar into earnings is one bet: that the stock moves less than the options say it will. The IV crush is real and usually large, but it is only edge if the profit band is wider than the implied move. Most of the time it isn't, and the usual way to lose money here is to see a 90th-percentile IV rank and a steeply inverted term structure and read that as an opportunity. It is the market pricing a large event correctly.

This adds an earnings-calendars skill to the tastytrade plugin that checks the thing that actually decides the trade.

How it works

scripts/calendars.py fits the vol term structure to base vol plus a one-time event jump, which gives an implied expected move. Every candidate calendar then gets priced against three move regimes: the last four quarters, the chain's own implied jump, and the full history you give it. Quotes are real bid/ask with a quarter-spread per leg per side, because deep-ITM strikes look cheapest at mid and are often quoted 50c wide.

The gate is a wide? column in the ranking: does this structure's profit band cover the implied expected move on both sides. A NO means the trade needs a below-consensus move to pay.

Three commands: fit for the term structure, rank for the ranking, scenario to stress one structure across the move range including the direction it doesn't want.

Choices worth flagging

Standard library only. No numpy, so python3 scripts/calendars.py works with nothing installed. The move distribution is a weighted grid rather than Monte Carlo, which is exact, reproducible run to run, and still gets through 285 structures in about 4 seconds.

The selftest caught a real bug. In the kernel smoothing, each Gaussian's total weight scaled with its own bandwidth, so a wide kernel spanned more grid points and silently outweighed a narrow one. That tilted the whole distribution toward the largest historical moves. Each kernel is now normalised to unit mass. This was ported from a working analysis, so those numbers were slightly pessimistic in the tails.

reference/pltr-2026-08-03.json is real. PLTR quotes captured the afternoon of its Q2 print, so it works as both the worked example in the skill and a regression fixture. fit on it returns an event jump near 13% against a historical rms of 14.8%, the fairly-priced case where no calendar has edge. rank finds 2 of 285 structures positive-EV.

What the skill carries forward

Findings from the analysis it came out of, so they don't get rediscovered:

  • Read the win/loss ratio before the win rate. A 66% win rate with W/L 0.46 loses money, and that is the standard double calendar.
  • Doubles are usually the wrong shape into a big event. A double is roughly twice the short gamma of a single for similar capital, and in the tails both wings lose: the stock blows through one strike and runs away from the other.
  • Extending the back leg barely widens the band, about 0.2 to 0.7pp going from 25 to 46 DTE. The band is set by the extrinsic ratio between the legs, not absolute time. The further month buys flatter regime sensitivity and better fills instead.

Limits

The base-vol slope and the event jump trade off against each other in the fit, so the split is weakly identified. The expected move is robust to about 0.3pp; the skill says not to quote the jump more precisely than that.

The skill never places orders. It is analysis, and analysis is not authorisation.

Testing

make check passes: lint, typecheck, 94 unit tests. python3 scripts/calendars.py --selftest covers the pricer round-trip, put-call parity, recovery of a known jump from a synthetic chain, the move-grid normalisation, and that a calendar is worth more at its strike than away from it.

🤖 Generated with Claude Code

A calendar into earnings is one bet: that the stock moves less than the
options say it will. The IV crush is real and usually large, but it is only
edge if the profit band is wider than the implied move. Usually it isn't.

The skill enforces that as a gate. calendars.py fits the vol term structure
to base vol plus a one-time event jump, then prices every candidate calendar
against three move regimes using real bid/ask, and prints per structure
whether its band actually covers the implied expected move.

Standard library only, so python3 scripts/calendars.py works with no install.
The move distribution is a weighted grid rather than Monte Carlo, which is
exact and reproducible and still runs 285 structures in about 4 seconds.

Writing the selftest caught a bug in the kernel smoothing: each Gaussian's
total weight scaled with its own bandwidth, so wide kernels spanned more grid
points and silently outweighed narrow ones, tilting the distribution toward
the largest historical moves. Each kernel is now normalised to unit mass.

reference/pltr-2026-08-03.json is a real PLTR chain from the afternoon of its
Q2 print. It doubles as a regression fixture: fit should return an event jump
near 13% against a historical rms of 14.8%, which is the fairly-priced case
where no calendar has edge.

The skill never places orders.
The version gate caught the miss: scripts/ and skills/ are shipped payload, so
without the bump every installed copy keeps serving 0.3.0 from cache and never
sees the new skill.

Wiring the selftest into `make check` closes a second hole. Skill scripts live
outside src/, so pytest never collected calendars.py and its assertions were
only running when someone invoked them by hand.
@walkerhughes
walkerhughes merged commit 43b7e69 into main Aug 4, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant