High Put/Call Ratio Bullish? See Wetin Data Talk
High put/ call ratio fit look bullish to contrarians. See percentile bands for equity and index options, plus wetin follow extreme readings.
High put/call ratio na the classic contrarian bullish signal: when put buying heavy unusually, people dey take am as sign say crowd don already prepare for market to fall. But to act on that signal get one problem. “High” no get fixed value. A reading of 1.15 for your screen fit be extreme for single-stock options, but normal session for index products.
Wetin the question need na reference distribution. This page provide one for both books, then measure wetin happen after the high readings. The put/call ratio explainer show how dem build the ratio, and everything below start from that definition.
Two notes about the measurement. Every ratio here count contracts wey trade that day, so na volume ratio. Open interest count positions wey still dey open, and e dey operate for another level (volume versus open interest separate the two). The numbers also come from fixed basket: ten household single-stock names against three broad-market ETFs. E no represent whole-market tape, so focus on the shape of the distribution instead of the last decimal.
Wetin count as high put/call ratio?
Percentile dey answer question wey one level no fit answer. The curve below cover every session from January 2022 reach July 2026, with one ratio for each session and each book.
The exact SQL behind every number
WITH book_days AS (
SELECT date AS day,
if(underlying_symbol IN ('SPY', 'QQQ', 'IWM'), 'index_etf', 'equity') AS book,
sumIf(volume, upper(substring(ticker, length(ticker) - 8, 1)) = 'P') AS put_volume,
sumIf(volume, upper(substring(ticker, length(ticker) - 8, 1)) = 'C') AS call_volume
FROM global_markets.options_greeks
WHERE date >= toDate('2022-01-01')
AND date <= toDate('2026-07-31')
AND volume > 0
AND underlying_symbol IN ('SPY', 'QQQ', 'IWM', 'AAPL', 'MSFT', 'NVDA', 'AMZN',
'META', 'TSLA', 'GOOGL', 'JPM', 'KO', 'XOM')
GROUP BY day, book
HAVING call_volume > 0
),
paired AS (
SELECT day,
toFloat64(sumIf(put_volume, book = 'equity'))
/ toFloat64(sumIf(call_volume, book = 'equity')) AS equity_pcr,
toFloat64(sumIf(put_volume, book = 'index_etf'))
/ toFloat64(sumIf(call_volume, book = 'index_etf')) AS index_pcr
FROM book_days
GROUP BY day
HAVING sumIf(call_volume, book = 'equity') > 0
AND sumIf(call_volume, book = 'index_etf') > 0
),
curves AS (
SELECT arrayMap(v -> round(v, 2),
quantilesDeterministic(0.05, 0.10, 0.25, 0.50, 0.75, 0.90, 0.95, 0.99)
(equity_pcr, cityHash64(day))) AS eq,
arrayMap(v -> round(v, 2),
quantilesDeterministic(0.05, 0.10, 0.25, 0.50, 0.75, 0.90, 0.95, 0.99)
(index_pcr, cityHash64(day))) AS idx
FROM paired
)
SELECT z.1 AS percentile,
z.2 AS equity_pcr,
z.3 AS index_etf_pcr
FROM (
SELECT arrayJoin(arrayZip(['p05', 'p10', 'p25', 'p50', 'p75', 'p90', 'p95', 'p99'], eq, idx)) AS z
FROM curves
)For the single-stock basket, median session come out at 0.67 puts per call. The top decile of sessions start from 0.89, while the most extreme one percent pass 1.34. The quiet end still useful for calibration: one session out of twenty finish at or below 0.48.
The ETF column get different distribution for every point on the curve. Its median session read 1.45, above the one-to-one line wey the single-stock median dey well below, and its own top decile start from 1.77. Ratio wey person quote without naming the book almost no get meaning. That 1.15 print mean two different things, depending on the column wey e come from.
Why index ratio wey pass one dey normal, but equity ratio wey pass one no dey normal
If you split the same volume by underlying, the levels go separate clearly.
The exact SQL behind every number
WITH symbol_days AS (
SELECT underlying_symbol AS symbol,
date AS day,
sumIf(volume, upper(substring(ticker, length(ticker) - 8, 1)) = 'P') AS put_volume,
sumIf(volume, upper(substring(ticker, length(ticker) - 8, 1)) = 'C') AS call_volume
FROM global_markets.options_greeks
WHERE date >= toDate('2026-01-01')
AND date <= toDate('2026-07-31')
AND volume > 0
AND underlying_symbol IN ('SPY', 'QQQ', 'IWM', 'AAPL', 'MSFT', 'NVDA', 'AMZN',
'META', 'TSLA', 'GOOGL', 'JPM', 'KO', 'XOM')
GROUP BY symbol, day
HAVING call_volume > 0
)
SELECT symbol,
round(toFloat64(sum(put_volume)) / toFloat64(sum(call_volume)), 2) AS put_call_ratio,
round(100 * countIf(put_volume > call_volume) / count(), 1) AS above_one_pct,
count() AS observations
FROM symbol_days
GROUP BY symbol
ORDER BY put_call_ratio DESCFor the first seven months of 2026, IWM dey top the table with 2.73 puts per call. E finish above one for 98.6% of im 144 sessions. For the bottom, XOM trade 0.38, above one for 2.8% of sessions.
The difference dey come from structure. Broad-market ETF options na where portfolio hedging dey happen. Manager wey hold two hundred positions fit buy index puts instead of puts on every holding. That flow no get matching call trade beside am. Single-stock option volume dey lean the other way. Traders dey buy calls for upside, and write calls against stock wey dem already own. One “total” ratio dey combine both books. That one put am between the two and make am move with the day’s product mix as much as with sentiment.
Another level shift dey important to know. Index and ETF volume now get large share of same-day expiries (0DTE options). This category no dey trade anywhere near this scale when people first write down the familiar thresholds. Old levels no fit apply to tape wey get different composition.
High put/call ratio bullish dey? Wetin happen after the extreme readings
The contrarian case fit test. Arrange every session into five groups based on that day’s equity ratio, then measure how the S&P 500 tracker move over the next 5 and 20 sessions.
The exact SQL behind every number
WITH raw AS (
SELECT date AS day,
sumIf(volume, upper(substring(ticker, length(ticker) - 8, 1)) = 'P') AS put_volume,
sumIf(volume, upper(substring(ticker, length(ticker) - 8, 1)) = 'C') AS call_volume
FROM global_markets.options_greeks
WHERE date >= toDate('2022-01-01')
AND date <= toDate('2026-07-31')
AND volume > 0
AND underlying_symbol IN ('AAPL', 'MSFT', 'NVDA', 'AMZN', 'META',
'TSLA', 'GOOGL', 'JPM', 'KO', 'XOM')
GROUP BY day
HAVING call_volume > 0
),
equity_pcr AS (
SELECT day, toFloat64(put_volume) / toFloat64(call_volume) AS pcr
FROM raw
),
spy_daily AS (
SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS day,
toFloat64(argMax(close, window_start)) AS close
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'SPY'
AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2022-01-01')
AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-08-01')
AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
+ toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
GROUP BY day
),
forward AS (
SELECT day,
close,
any(close) OVER (ORDER BY day ASC ROWS BETWEEN 5 FOLLOWING AND 5 FOLLOWING) AS close_5,
any(close) OVER (ORDER BY day ASC ROWS BETWEEN 20 FOLLOWING AND 20 FOLLOWING) AS close_20
FROM spy_daily
),
joined AS (
SELECT p.day AS day,
p.pcr AS pcr,
100 * (f.close_5 / f.close - 1) AS fwd_5d_pct,
100 * (f.close_20 / f.close - 1) AS fwd_20d_pct
FROM equity_pcr AS p
INNER JOIN forward AS f ON p.day = f.day
WHERE f.close_5 > 0 AND f.close_20 > 0
),
cuts AS (
SELECT quantilesDeterministic(0.2, 0.4, 0.6, 0.8)(pcr, cityHash64(day)) AS q
FROM joined
)
SELECT multiIf(pcr < q[1], 'lowest fifth',
pcr < q[2], 'second fifth',
pcr < q[3], 'middle fifth',
pcr < q[4], 'fourth fifth',
'highest fifth') AS pcr_band,
count() AS observations,
round(min(pcr), 2) AS band_floor_ratio,
round(quantileDeterministic(0.5)(fwd_5d_pct, cityHash64(day)), 2) AS fwd_5d_median_pct,
round(quantileDeterministic(0.5)(fwd_20d_pct, cityHash64(day)), 2) AS fwd_20d_median_pct,
round(quantileDeterministic(0.25)(fwd_20d_pct, cityHash64(day)), 2) AS fwd_20d_p25_pct,
round(quantileDeterministic(0.75)(fwd_20d_pct, cityHash64(day)), 2) AS fwd_20d_p75_pct
FROM joined CROSS JOIN cuts
GROUP BY pcr_band
ORDER BY band_floor_ratio ASCThe quietest fifth of sessions starts at 0.34 and the busiest fifth starts at 0.81, so the top band contain every reading wey contrarian trader go call stretched. Across those 226 sessions, the median 20-session move na 3%, compared with 0.91% after the quietest fifth. After five sessions, the same two bands show 0.82% and 0.3%.
Medians fit hide the part wey matter. Inside the top fifth, the middle half of 20-session outcomes run from -1.82% to 5.71%. Inside the quietest fifth, the middle half run from -0.83% to 2.67%. Those two ranges cover plenty of the same ground. The band wey session enter shifts the center of the distribution, but e hardly makes the range around that center narrower. Na that part rule of thumb dey leave out.
One thing to note about the sample size. January 2022 through July 2026 include one bear market and the recovery wey follow am, with roughly 226 sessions per band. Na real sample, but na still only one period of market history.
Extreme readings dey cluster for market lows?
Dem dey cluster for months wey market dey fall, but na different claim be that. Group every month from 2022 onward based on how the tracker move inside that month, then count how many top-decile put/call readings fall inside each group.
The exact SQL behind every number
WITH raw AS (
SELECT date AS day,
sumIf(volume, upper(substring(ticker, length(ticker) - 8, 1)) = 'P') AS put_volume,
sumIf(volume, upper(substring(ticker, length(ticker) - 8, 1)) = 'C') AS call_volume
FROM global_markets.options_greeks
WHERE date >= toDate('2022-01-01')
AND date <= toDate('2026-07-31')
AND volume > 0
AND underlying_symbol IN ('AAPL', 'MSFT', 'NVDA', 'AMZN', 'META',
'TSLA', 'GOOGL', 'JPM', 'KO', 'XOM')
GROUP BY day
HAVING call_volume > 0
),
equity_pcr AS (
SELECT day, toFloat64(put_volume) / toFloat64(call_volume) AS pcr
FROM raw
),
cut AS (
SELECT quantileDeterministic(0.90)(pcr, cityHash64(day)) AS p90
FROM equity_pcr
),
by_month AS (
SELECT toStartOfMonth(day) AS month_start,
countIf(pcr >= cut.p90) AS extreme_reads,
quantileDeterministic(0.5)(pcr, cityHash64(day)) AS median_pcr
FROM equity_pcr CROSS JOIN cut
GROUP BY month_start
),
spy_daily AS (
SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS day,
toFloat64(argMax(close, window_start)) AS close
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'SPY'
AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2022-01-01')
AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-07-31')
AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
+ toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
GROUP BY day
),
spy_month AS (
SELECT toStartOfMonth(day) AS month_start,
100 * (argMax(close, day) / argMin(close, day) - 1) AS move_pct
FROM spy_daily
GROUP BY month_start
)
SELECT multiIf(s.move_pct <= -3, 'index down 3% or more',
s.move_pct < 0, 'index down under 3%',
s.move_pct < 3, 'index up under 3%',
'index up 3% or more') AS index_move_bucket,
count() AS observations,
round(avg(m.extreme_reads), 1) AS top_decile_reads_avg,
round(quantileDeterministic(0.5)(m.median_pcr, cityHash64(m.month_start)), 2) AS typical_pcr_ratio
FROM by_month AS m
INNER JOIN spy_month AS s ON m.month_start = s.month_start
GROUP BY index_move_bucket
ORDER BY min(s.move_pct) ASCMonths wey tracker finish 3% or more lower average 4.2 top-decile readings each, compared with 1.9 for months wey gain 3% or more. Median daily reading for each group dey for the last column, at 0.75 for the worst group and 0.62 for the best. The two ends of the table get 12 and 22 months.
Extreme put/call readings and falling prices dey show together. Na co-occurrence be this, and the forward numbers for the previous section show wetin e worth. The ratio na same-day measurement of a same-day tape, so the spikes dey happen inside declines, no be before dem start. Low na label wey dem attach afterwards.
Reading level against distribution
Two habits dey come from the numbers above.
First, compare like with like. A reading only get meaning when you compare am with the same book’s own history over a similar period. Na exactly percentile table dey show, unlike threshold wey person just remember.
Second, watch the denominator. The ratio fit fall when call volume surge, even if put buying no change. A fall for the ratio and a spike fit each come from only one side of the fraction moving. So, reading put volume and call volume separately makes the arithmetic clear.
This same discipline apply to every sentiment gauge wey dem quote as bare number. The VIX and a 30% implied volatility print no readable without their own distributions. And contrarian sentiment rules only hold up under data when dem measure the reference level instead of assuming am.
FAQ
High put/call ratio dey bullish?
Contrarian interpretation na say heavy put buying mean crowd don already position for market to fall. When dem compare am with the single-stock basket above, sessions wey readings dey inside the top fifth later show median 20-session move of 3% for the S&P 500 tracker. The calmest fifth show 0.91%. For the top fifth, middle half of outcomes range from -1.82% to 5.71%.
Wetin be normal put/call ratio?
E depend completely on the options wey dem count. For basket of ten familiar single-stock names, median session from 2022 reach July 2026 show 0.67 puts per call. For three broad-market ETFs across the same sessions, median show 1.45.
Why index put/call ratio dey higher than equity put/call ratio?
Broad index and ETF options na the normal instrument for portfolio hedging. Hedger wey buy index puts no get matching call trade beside am. Single-stock volume lean toward calls wey traders buy for upside, plus calls wey dem write against shares wey dem already hold. This one dey keep that book ratio lower.
Put/call ratio fit work for market timing?
The measurement above show the distribution of wetin happen after each fifth of readings. But spread inside any band dey much wider than the gap between band medians. Top-decile readings too dey gather for months wey index finish lower, at 4.2 per month for the worst group.
Wetin be the difference between volume put/call ratio and open interest one?
Volume ratio dey count contracts wey traders execute during one session, so e reset every day and react fast. Open interest ratio dey count positions wey still dey open, and e dey change slowly from its own level. The two no be interchangeable. Threshold wey dem take from one fit misread the other.
How dem build these numbers
- Source: one row for each option contract per session, with the traded volume for that session. Dem separate puts and calls from the OCC contract code. The last eight characters na the strike, while the character before dem na C for call and P for put. Open the SQL under any panel to see the exact table and filters.
- Every ratio for this page na volume ratio. Open interest no enter the computation, so nothing here na open-interest ratio.
- No implied-volatility quality filter dey apply. Contracts enter based on traded volume alone because these panels dey count trading activity, rather than measure volatility.
- The basket na AAPL, MSFT, NVDA, AMZN, META, TSLA, GOOGL, JPM, KO and XOM for the single-stock book, plus SPY, QQQ and IWM for the index-ETF book. Exchange-published ratios cover the whole market and include index products like SPX, so their levels dey elsewhere.
- Index prices na the last regular-session print for each day for the S&P 500 tracker. Forward returns remove the final 20 sessions of the sample because dem never get 20-session future yet. Monthly moves run from the first session close reach the last session close inside each month.
Every panel above dey expand to the SQL wey dey behind am, and the same percentile queries dey run against any underlying wey you dey follow for the Strasmore terminal.