{"slug":"is-a-high-put-call-ratio-bullish","qid":"pcr_and_monthly_moves","label":"Top-decile put/call readings per month, grouped by how the S&P 500 tracker moved","post_title":"Is a High Put/Call Ratio Bullish?","post_url":"/blog/is-a-high-put-call-ratio-bullish#q-pcr_and_monthly_moves","columns":["index_move_bucket","observations","top_decile_reads_avg","typical_pcr_ratio"],"rows":[{"index_move_bucket":"index down 3% or more","observations":12,"top_decile_reads_avg":4.2,"typical_pcr_ratio":0.75},{"index_move_bucket":"index down under 3%","observations":9,"top_decile_reads_avg":1.9,"typical_pcr_ratio":0.64},{"index_move_bucket":"index up under 3%","observations":12,"top_decile_reads_avg":0.6,"typical_pcr_ratio":0.58},{"index_move_bucket":"index up 3% or more","observations":22,"top_decile_reads_avg":1.9,"typical_pcr_ratio":0.62}],"shape":"ranking","sql":"WITH raw AS (\n    SELECT date AS day,\n           sumIf(volume, upper(substring(ticker, length(ticker) - 8, 1)) = 'P') AS put_volume,\n           sumIf(volume, upper(substring(ticker, length(ticker) - 8, 1)) = 'C') AS call_volume\n    FROM global_markets.options_greeks\n    WHERE date >= toDate('2022-01-01')\n      AND date <= toDate('2026-07-31')\n      AND volume > 0\n      AND underlying_symbol IN ('AAPL', 'MSFT', 'NVDA', 'AMZN', 'META',\n                                'TSLA', 'GOOGL', 'JPM', 'KO', 'XOM')\n    GROUP BY day\n    HAVING call_volume > 0\n),\nequity_pcr AS (\n    SELECT day, toFloat64(put_volume) / toFloat64(call_volume) AS pcr\n    FROM raw\n),\ncut AS (\n    SELECT quantileDeterministic(0.90)(pcr, cityHash64(day)) AS p90\n    FROM equity_pcr\n),\nby_month AS (\n    SELECT toStartOfMonth(day) AS month_start,\n           countIf(pcr >= cut.p90) AS extreme_reads,\n           quantileDeterministic(0.5)(pcr, cityHash64(day)) AS median_pcr\n    FROM equity_pcr CROSS JOIN cut\n    GROUP BY month_start\n),\nspy_daily AS (\n    SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS day,\n           toFloat64(argMax(close, window_start)) AS close\n    FROM global_markets.delayed_stocks_minute_aggs\n    WHERE ticker = 'SPY'\n      AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2022-01-01')\n      AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-07-31')\n      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60\n           + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959\n    GROUP BY day\n),\nspy_month AS (\n    SELECT toStartOfMonth(day) AS month_start,\n           100 * (argMax(close, day) / argMin(close, day) - 1) AS move_pct\n    FROM spy_daily\n    GROUP BY month_start\n)\nSELECT multiIf(s.move_pct <= -3, 'index down 3% or more',\n               s.move_pct < 0, 'index down under 3%',\n               s.move_pct < 3, 'index up under 3%',\n               'index up 3% or more') AS index_move_bucket,\n       count() AS observations,\n       round(avg(m.extreme_reads), 1) AS top_decile_reads_avg,\n       round(quantileDeterministic(0.5)(m.median_pcr, cityHash64(m.month_start)), 2) AS typical_pcr_ratio\nFROM by_month AS m\nINNER JOIN spy_month AS s ON m.month_start = s.month_start\nGROUP BY index_move_bucket\nORDER BY min(s.move_pct) ASC","computed_at":"2026-08-03T08:05:36.515959+00:00","elapsed":0.005996392}