STRASMORE/EXPLORE 2,170 QUERIES

A decade of SPY put-call ratios: median, low and high of the daily reading by year

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-08-11, from What Is the Put-Call Ratio? A Data Guide.

as of table 12×5read in context →
A decade of SPY put-call ratios: median, low and high of the daily reading by year — 12 rows by 5 columns, computed from US exchange, SIP and OPRA data.
yearsessionsmedian_ratiolowhigh
20152521.7540.9872.96
20162521.5080.5032.508
20172511.5970.5813.541
20182501.4380.6112.362
20192521.5240.6932.351
20202531.4310.592.282
20212521.3140.6562.167
20222511.3610.9831.927
20232501.2420.6931.716
20242521.1750.8291.897
20252501.1680.5871.793
20261501.2340.7291.751
Rows × columns
12 × 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 A decade of SPY put-call ratios: median, low and high of the daily reading by year, derived from the stored result.
ColumnTypeRangeNotes
year number 2,015 to 2,026
sessions number 150 to 253
median_ratio number 1.168 to 1.754 ratio or rate
low number 0.503 to 0.987 US dollars
high number 1.716 to 3.541 US dollars

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 daily AS (
    SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS day,
           sumIf(toFloat64(volume), substring(ticker, length(ticker) - 8, 1) = 'P')
           / sumIf(toFloat64(volume), substring(ticker, length(ticker) - 8, 1) = 'C') AS ratio
    FROM global_markets.options_minute_aggs
    WHERE ticker LIKE 'O:SPY%'
    GROUP BY day
    HAVING sumIf(toFloat64(volume), substring(ticker, length(ticker) - 8, 1) = 'C') > 0
)
SELECT toYear(day) AS year,
       count() AS sessions,
       round(quantileDeterministic(0.5)(ratio, cityHash64(toString(day))), 3) AS median_ratio,
       round(min(ratio), 3) AS low,
       round(max(ratio), 3) AS high
FROM daily
WHERE toYear(day) >= 2015
GROUP BY year
ORDER BY year

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 the Put-Call Ratio? A Data Guide
Market-wide put-call ratio by day: every listed US option, June 1 through July 10, 2026 series 28×4 Put-call ratio by underlying: five of the most active names, June 2026 ranking 5×3 Put-call ratio by days to expiration: every listed US option, June 2026 ranking 5×4 Index-linked vs single-stock options: put-call ratio by population, June 2026 ranking 3×4 The June 2026 put-call range: median, high and low of the daily market-wide ratio scalar 1×5 Total payout to option holders at each candidate settlement price, SPY July 17 2026 table 36×2 See all 2,170 queries →