STRASMORE/EXPLORE 2,401 QUERIES

SPY's change from the crossing-day close, 3, 6 and 12 months after each un-inversion

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-09-19, from What Happens When the Yield Curve Un-Inverts.

as of table 2×5read in context →
SPY's change from the crossing-day close, 3, 6 and 12 months after each un-inversion — 2 rows by 5 columns, computed from US exchange, SIP and OPRA data.
episodespy_at_crossspy_3m_pctspy_6m_pctspy_12m_pct
Mar 2007143.295.57-7.8
Sep 2024550.9510.35.817.8
Rows × columns
2 × 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 SPY's change from the crossing-day close, 3, 6 and 12 months after each un-inversion, derived from the stored result.
ColumnTypeRangeNotes
episode text 2 distinct values (Mar 2007, Sep 2024)
spy_at_cross number 143.29 to 550.95
spy_3m_pct number 5.5 to 10.3 percent
spy_6m_pct number 5.8 to 7 percent
spy_12m_pct number -7.8 to 17.8 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
daily AS
(
    SELECT
        date,
        dateDiff('day', toDate('1970-01-01'), date)   AS dnum,
        ifNull(toFloat64(max(yield_2_year)), 0.)      AS y2,
        ifNull(toFloat64(max(yield_10_year)), 0.)     AS y10,
        y10 - y2                                      AS spread
    FROM global_markets.treasury_yields
    WHERE yield_2_year > 0 AND yield_10_year > 0
    GROUP BY date
),
scored AS
(
    SELECT
        date, dnum, y2, y10, spread,
        count()         OVER (ORDER BY date ROWS BETWEEN 60 PRECEDING AND 1 PRECEDING) AS rows_prior,
        sum(spread < 0) OVER (ORDER BY date ROWS BETWEEN 60 PRECEDING AND 1 PRECEDING) AS neg_prior,
        sum(spread > 0) OVER (ORDER BY date ROWS BETWEEN CURRENT ROW AND 20 FOLLOWING) AS pos_next
    FROM daily
),
candidates AS
(
    SELECT
        date, dnum, y2, y10, spread,
        lagInFrame(dnum, 1, toInt64(-100000)) OVER (ORDER BY dnum ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS prev_dnum
    FROM scored
    WHERE spread > 0 AND rows_prior = 60 AND neg_prior >= 35 AND pos_next >= 19
),
episodes AS
(
    SELECT date AS cross_date, y2, y10, spread
    FROM candidates
    WHERE dnum - prev_dnum > 180
      AND date <= today() - 380
),
spy AS
(
    SELECT date, max(toFloat64(close)) AS px
    FROM global_markets.stocks_daily_aggs
    WHERE ticker = 'SPY'
    GROUP BY date
)
SELECT
    formatDateTime(e.cross_date, '%b %Y')                                                                                        AS episode,
    round(argMinIf(s.px, s.date, s.date >= e.cross_date), 2)                                                                     AS spy_at_cross,
    round((argMinIf(s.px, s.date, s.date >= addDays(e.cross_date, 91))  / argMinIf(s.px, s.date, s.date >= e.cross_date) - 1) * 100, 1) AS spy_3m_pct,
    round((argMinIf(s.px, s.date, s.date >= addDays(e.cross_date, 182)) / argMinIf(s.px, s.date, s.date >= e.cross_date) - 1) * 100, 1) AS spy_6m_pct,
    round((argMinIf(s.px, s.date, s.date >= addDays(e.cross_date, 365)) / argMinIf(s.px, s.date, s.date >= e.cross_date) - 1) * 100, 1) AS spy_12m_pct
FROM episodes AS e
CROSS JOIN spy AS s
WHERE s.date BETWEEN e.cross_date AND addDays(e.cross_date, 380)
GROUP BY e.cross_date
HAVING countIf(s.date <= addDays(e.cross_date, 7)) > 0
   AND countIf(s.date >= addDays(e.cross_date, 365)) > 0
ORDER BY e.cross_date

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 Happens When the Yield Curve Un-Inverts
Which leg did the work: 2-year and 10-year moves over the 91 days before each crossing table 7×9 Every 2s10s un-inversion: the crossing and the spread 3, 6 and 12 months later table 7×8 The 2s10s spread by month, full history series 604×5 The 2s10s over the trailing six months, with the 2-year and 10-year moves that produced it series 26×8 The 2s10s spread, every print of the half table 124×2 Every half-year since 1976: the 2y and 10y change, the twist between them, and the half's lowest 2s10s print table 100×7 See all 2,401 queries →