forward_record
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-13, from can-a-death-cross-be-bullish.
| horizon | case_count | higher_count | higher_pct | all_sessions_higher_pct | avg_return_pct | median_return_pct | worst_pct | best_pct |
|---|---|---|---|---|---|---|---|---|
| 1 month | 11 | 6 | 55 | 66 | 2 | 2.8 | -8.9 | 12.1 |
| 3 months | 11 | 9 | 82 | 72 | 5.6 | 7.1 | -10.1 | 16.4 |
| 6 months | 11 | 8 | 73 | 76 | 10.6 | 11.9 | -11.4 | 27.7 |
| 12 months | 11 | 9 | 82 | 82 | 14.7 | 19.1 | -41.2 | 50.9 |
- Rows × columns
- 4 × 9
- Computed
- Completeness
- No missing values
- Source
- US exchange, SIP and OPRA market data
- Licence
- Strasmore terms · free, no signup
What each column holds
| Column | Type | Range | Notes |
|---|---|---|---|
horizon |
text | 4 distinct values (1 month, 12 months, 3 months…) | |
case_count |
number | every row is 11 | count |
higher_count |
number | 6 to 9 | count |
higher_pct |
number | 55 to 82 | percent |
all_sessions_higher_pct |
number | 66 to 82 | percent |
avg_return_pct |
number | 2 to 14.7 | percent |
median_return_pct |
number | 2.8 to 19.1 | percent |
worst_pct |
number | -41.2 to -8.9 | percent |
best_pct |
number | 12.1 to 50.9 | 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
bars AS
(
SELECT
date,
toFloat64(argMax(close, _ingest_time)) AS px
FROM global_markets.stocks_daily_aggs
WHERE ticker = 'SPY'
GROUP BY date
),
smas AS
(
SELECT
date,
px,
row_number() OVER (ORDER BY date) AS rn,
avg(px) OVER (ORDER BY date ROWS BETWEEN 49 PRECEDING AND CURRENT ROW) AS avg_50,
avg(px) OVER (ORDER BY date ROWS BETWEEN 199 PRECEDING AND CURRENT ROW) AS avg_200,
max(px) OVER (ORDER BY date ROWS BETWEEN 251 PRECEDING AND CURRENT ROW) AS high_1y,
leadInFrame(px, 21) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS px_1m,
leadInFrame(px, 63) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS px_3m,
leadInFrame(px, 126) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS px_6m,
leadInFrame(px, 252) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS px_12m
FROM bars
),
states AS
(
SELECT
*,
(avg_50 < avg_200) AS below,
lagInFrame((avg_50 < avg_200), 1) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS prev_below
FROM smas
),
flags AS
(
SELECT
*,
(rn >= 201 AND below = 1 AND prev_below = 0) AS is_death,
(rn >= 201 AND below = 0 AND prev_below = 1) AS is_golden
FROM states
),
unrolled AS
(
SELECT
is_death,
tupleElement(h, 1) AS horizon_order,
tupleElement(h, 2) AS horizon,
tupleElement(h, 3) AS fwd_px,
px
FROM flags
ARRAY JOIN [(1, '1 month', px_1m), (2, '3 months', px_3m), (3, '6 months', px_6m), (4, '12 months', px_12m)] AS h
WHERE rn >= 201
AND tupleElement(h, 3) > 0
)
SELECT
horizon,
countIf(is_death) AS case_count,
countIf(is_death AND fwd_px > px) AS higher_count,
toInt32(round(100 * countIf(is_death AND fwd_px > px) / countIf(is_death))) AS higher_pct,
toInt32(round(100 * countIf(fwd_px > px) / count())) AS all_sessions_higher_pct,
round(avgIf((fwd_px / px - 1) * 100, is_death), 1) AS avg_return_pct,
round(quantileExactIf((fwd_px / px - 1) * 100, is_death), 1) AS median_return_pct,
round(minIf((fwd_px / px - 1) * 100, is_death), 1) AS worst_pct,
round(maxIf((fwd_px / px - 1) * 100, is_death), 1) AS best_pct
FROM unrolled
GROUP BY horizon_order, horizon
HAVING countIf(is_death) > 0
ORDER BY horizon_order
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 analysiscan-a-death-cross-be-bullish
death_crosses
table 11×6
→
trace_2020
series 126×4
→
whipsaw
ranking 11×4
→
window
scalar 1×11
→
covid_2020
scalar 1×16
→
The 2s10s spread, every print of the half
table 124×2
→
See all 2,225 queries →