options-expiration
v1.0.0
Published
US listed options expiration dates — monthly third-Friday, weekly, 0DTE, quarterly, end-of-month and VIX expiries, shifted correctly for exchange holidays (Good Friday, Juneteenth), plus early-close-aware time to expiry.
Maintainers
Readme
options-expiration
US listed options expiration dates: monthly third-Friday, weekly, daily (0DTE), quarterly, end-of-month and VIX expiries, all shifted for exchange holidays (Good Friday, Juneteenth, July 3, Christmas on a Friday). Also time to expiry in years to the actual settlement instant, accounting for 1 p.m. early closes and DST. Built on us-equity-market-calendar; checked against Cboe's published calendars.
npm install options-expirationWhy
"Third Friday" is the rule people remember and the one their code gets wrong. When the third Friday is a holiday the expiry moves to Thursday: Good Friday on 2022-04-14, Juneteenth on 2026-06-18. That Thursday shift moves the VIX expiry from Wednesday to Tuesday 30 days earlier. A Christmas-Eve weekly settles at 1 p.m., not 4 p.m., so a naive time-to-expiry overstates T by three hours. This package gets these cases right by computing from the NYSE calendar, without a hard-coded date table.
import {
monthlyExpiration, weeklyExpirations, expirations, nextExpiration,
vixExpiration, isQuadWitching, timeToExpiry, tradingDaysToExpiry,
} from "options-expiration";
monthlyExpiration(2026, 6); // "2026-06-18" — Juneteenth Friday → Thursday
monthlyExpiration(2022, 4); // "2022-04-14" — Good Friday → Thursday
vixExpiration(2026, 5); // "2026-05-19" — a Tuesday, because June's SPX expiry is a Thursday
isQuadWitching("2026-06-18"); // true
weeklyExpirations("2026-12-14", "2027-01-10");
// ["2026-12-18", "2026-12-24", "2026-12-31", "2027-01-08"] — Christmas and New Year's on Fridays
expirations("end-of-month", "2024-03-01", "2024-03-31"); // ["2024-03-28"] — Good Friday ends Q1
nextExpiration("2026-06-18", "monthly"); // "2026-06-18" — on or after
nextExpiration("2026-06-18", "monthly", { inclusive: false }); // "2026-07-17"
// PM-settled weekly on Christmas Eve settles at the 13:00 ET early close
timeToExpiry(new Date("2026-12-23T21:00:00Z"), "2026-12-24"); // 21h / 365d = 0.002397…
timeToExpiry(new Date(), "2026-12-18", { settlement: "AM" }); // to the 09:30 ET open
tradingDaysToExpiry("2026-07-02", "2026-07-06"); // 1 — Jul 3 holiday + weekendAPI
Dates are "YYYY-MM-DD" strings or Date objects read by their UTC calendar day (new Date("2026-06-18"), not new Date(2026, 5, 18)). Results are "YYYY-MM-DD" strings. Every function throws RangeError for a malformed date, a month outside 1–12, an unknown schedule, or a year outside MIN_YEAR–MAX_YEAR (1971–2200, the calendar's range).
Single expiries
monthlyExpiration(year, month): the standard monthly expiration. This is the third Friday, or the trading day before it when that Friday is a holiday. Before February 2015, standard contracts technically expired on the Saturday after the third Friday. This function returns the last trading day, which is the date that matters for trading and pricing.endOfMonthExpiration(year, month): the last trading day of the month.vixExpiration(year, month): the Cboe rule for VX futures final settlement and VIX options expiration. Take the third Friday of the following month, or the trading day before it if that Friday is a holiday. Count back 30 days to get a Wednesday (a Tuesday if the Friday shifted). If that day is itself a holiday, use the trading day before it (2024-06-18, when Juneteenth fell on the Wednesday).
Schedules
Schedule is "monthly" | "weekly" | "daily" | "quarterly" | "end-of-month":
| schedule | rule |
|---|---|
| monthly | third Friday, holiday → preceding trading day |
| weekly | every Friday, holiday → preceding trading day |
| daily | every trading day (0DTE) |
| quarterly | last trading day of Mar / Jun / Sep / Dec |
| end-of-month | last trading day of each month |
expirations(schedule, from, to): every expiry in[from, to](both ends inclusive), sorted.weeklyExpirations(from, to): shorthand forexpirations("weekly", from, to).nextExpiration(date, schedule, { inclusive = true }): the first expiry on or afterdate. On an expiration day you get that day back, which is what a 0DTE trader wants. Pass{ inclusive: false }for strictly after.isExpiration(date, schedule): whetherdateis an expiry underschedule.isQuadWitching(date): whetherdateis the monthly expiration in March, June, September or December.
These functions generate dates from rules. They don't record which products listed which expiries, or when they started. SPX dailies, for example, only covered every weekday from 2022, and single-stock weeklies vary by underlying. Check the exchange's product listings for that.
Time to expiry
settlementTime(expiry, { settlement = "PM" })→ the settlement instant as aDate."PM": the session close, 16:00 ET, or 13:00 ET on an early-close day."AM": the session open, 09:30 ET. AM-settled index options (standard SPX) stop trading the day before, and this is the opening print they settle on.- Throws
RangeErrorifexpiryis not a trading day.
timeToExpiry(now, expiry, { settlement = "PM" })→ years from the instantnowto settlement, ACT/365 (elapsed milliseconds ÷ 365 days). DST is handled. Returns0once settled.tradingDaysToExpiry(from, expiry)→ the trading days in(from, expiry]:0on expiration day,1the day before. ThrowsRangeErroriffromis afterexpiry.
Correctness
- Checked against Cboe's own published calendars:
- Holiday shifts tested:
- Good Friday: 2022, 2025 and 2026, plus the end of Q1 2024.
- Juneteenth: on a Friday in 2026, on a Wednesday in 2024 (the VIX exception), and observed on Friday in 2027.
- July 4 on a Friday (2025) and its observed Friday, July 3 (2026).
- Christmas and New Year's on Fridays (2026).
- Thanksgiving week, where the Friday trades with a 1 p.m. close.
- The 2025-01-09 Day of Mourning.
- Holidays and early closes come from
us-equity-market-calendar, which matches the NYSE from 1971 onward. An unscheduled closure (a day of mourning, a hurricane) moves an expiry only once the calendar knows about it, so keep that dependency current. - Exchange discretion: these are the standard rules. An exchange can set a different date for a specific contract by notice, and that notice takes precedence over this package.
Related
The NYSE trading calendar underneath is us-equity-market-calendar. Siblings: options-greeks · futures-calendar. Part of a markets toolkit: sifma-holidays · day-count · fx-value-date.
Author
Built by Moshe Malka — engineering leader in New York City. Studio work at Quentin.Code.
MIT © Moshe Malka
