STRASMORE/EXPLORE 2,170 QUERIES

Minimising strike vs settlement close: SPY monthly expirations, Feb to Jul 2026

Answered against 22 years of US equities and 12 years of US options data and published with the query that produced it. This result is stored as of 2026-07-31, from What Is Max Pain in Options? The Real Math.

as of table 6×5read in context →
Minimising strike vs settlement close: SPY monthly expirations, Feb to Jul 2026 — 6 rows by 5 columns, computed from US exchange, SIP and OPRA data.
expiry_labelmin_payout_strikesettlement_closemax_pain_miss_pctweek_earlier_miss_pct
Feb 20687689.360.341.12
Mar 20652648.540.532.12
Apr 17707710.040.424.32
May 15740739.090.120.21
Jun 18746746.560.071.19
Jul 17746743.20.371.57
Rows × columns
6 × 5
Computed
Completeness
No missing values
Source
US exchange, SIP and OPRA market data
Licence
Strasmore terms · free, no signup
Formats
JSON · CSV · the SQL below

What each column holds

Column definitions for Minimising strike vs settlement close: SPY monthly expirations, Feb to Jul 2026, derived from the stored result.
ColumnTypeRangeNotes
expiry_label text 6 distinct values (Apr 17, Feb 20, Jul 17…)
min_payout_strike number 652 to 746 US dollars
settlement_close number 648.54 to 746.56 US dollars
max_pain_miss_pct number 0.07 to 0.53 percent
week_earlier_miss_pct number 0.21 to 4.32 percent

Computed from Strasmore's warehouse of US exchange, SIP and OPRA market data. Equity prices are delayed; options greeks and implied volatility are end-of-day. This result is stored, not recomputed on load — it is exactly the numbers that were returned on , and the query below is what returned them.

the exact SQL behind every number
WITH expiries AS (
    SELECT arrayJoin([toDate('2026-02-20'), toDate('2026-03-20'), toDate('2026-04-17'),
                      toDate('2026-05-15'), toDate('2026-06-18'), toDate('2026-07-17')]) AS expiry
),
chain AS (
    SELECT toDateOrNull(concat('20', substring(ticker, length(ticker) - 14, 6)))  AS expiry,
           intDiv(toUInt32OrZero(substring(ticker, length(ticker) - 7, 8)), 1000) AS strike,
           substring(ticker, length(ticker) - 8, 1)                              AS opt_type,
           sum(toFloat64(volume))                                                AS contracts
    FROM global_markets.options_minute_aggs
    WHERE ((window_start >= toDateTime('2026-02-16 08:00:00') AND window_start < toDateTime('2026-02-21 04:00:00'))
        OR (window_start >= toDateTime('2026-03-16 08:00:00') AND window_start < toDateTime('2026-03-21 04:00:00'))
        OR (window_start >= toDateTime('2026-04-13 08:00:00') AND window_start < toDateTime('2026-04-18 04:00:00'))
        OR (window_start >= toDateTime('2026-05-11 08:00:00') AND window_start < toDateTime('2026-05-16 04:00:00'))
        OR (window_start >= toDateTime('2026-06-15 08:00:00') AND window_start < toDateTime('2026-06-19 04:00:00'))
        OR (window_start >= toDateTime('2026-07-13 08:00:00') AND window_start < toDateTime('2026-07-18 04:00:00')))
      AND startsWith(ticker, 'O:SPY26')
      AND toDateOrNull(concat('20', substring(ticker, length(ticker) - 14, 6))) IN (SELECT expiry FROM expiries)
    GROUP BY expiry, strike, opt_type
),
curve AS (
    SELECT c1.expiry AS expiry,
           c1.strike AS settle,
           sum(multiIf(c2.opt_type = 'C' AND c2.strike < c1.strike, c2.contracts * (c1.strike - c2.strike),
                       c2.opt_type = 'P' AND c2.strike > c1.strike, c2.contracts * (c2.strike - c1.strike),
                       0)) AS payout
    FROM chain AS c1 INNER JOIN chain AS c2 ON c1.expiry = c2.expiry
    GROUP BY c1.expiry, c1.strike
),
pain AS (
    SELECT expiry, argMin(settle, payout) AS pain_strike FROM curve GROUP BY expiry
),
px AS (
    SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS d,
           argMax(close, window_start)                          AS close_px
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker = 'SPY'
      AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2026-02-05')
      AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-07-17')
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
    GROUP BY d
),
prior AS (
    SELECT expiries.expiry AS expiry, argMax(px.close_px, px.d) AS week_earlier_px
    FROM expiries CROSS JOIN px
    WHERE px.d <= expiries.expiry - 7
    GROUP BY expiries.expiry
)
SELECT formatDateTime(pain.expiry, '%b %e')                                   AS expiry_label,
       pain.pain_strike                                                       AS min_payout_strike,
       round(px.close_px, 2)                                                  AS settlement_close,
       round(abs(px.close_px - pain.pain_strike) / px.close_px * 100, 2)      AS max_pain_miss_pct,
       round(abs(px.close_px - prior.week_earlier_px) / px.close_px * 100, 2) AS week_earlier_miss_pct
FROM pain
INNER JOIN px ON pain.expiry = px.d
INNER JOIN prior ON pain.expiry = prior.expiry
ORDER BY pain.expiry

Run your own version of this

The same 22 years of US equities and 12 years of options data are queryable in SQL or plain English. A free account runs 100 queries a day and takes no card.

More from this analysisWhat Is Max Pain in Options? The Real Math
Total payout to option holders at each candidate settlement price, SPY July 17 2026 table 36×2 SPY contracts traded by strike, July 17 2026 expiry ranking 22×3 The six lowest-payout candidate strikes, SPY July 17 2026 expiry ranking 6×2 Who owns Wednesday: roots by distinct Wednesday-dated expirations, July 6-9, 2026 tape table 8×5 SPY options volume by June 2026 expiration date: a new expiry every session series 21×2 Which tickers carry the most upcoming expirations: option roots by distinct expiration dates in the next six weeks ranking 20×3 See all 2,170 queries →