STRASMORE/EXPLORE 2,170 QUERIES 22Y EQUITIES · 12Y OPTIONS

2,170 answered market questions

every one with its exact SQL, its result and the date it was computed · free, no signup

Reproducible Backtest in Python, No API Key
A 20/50 moving-average crossover on SPY, year by year, against holdingranking · 2026-08-06 · 9×4Preview: 9 ranked values, smallest first. The same 20/50 rule on five liquid names, 2021 through 2025ranking · 2026-08-06 · 5×4Preview: 5 ranked values, largest first. Same rule, prior-session signal against same-session signal, SPY by yearranking · 2026-08-06 · 9×4Preview: 9 ranked values, smallest first.
A 20/50 moving-average crossover on SPY, year by year, against holding

A 20/50 moving-average crossover on SPY, year by year, against holding

most recentas of ranking 9×4read in context →
A 20/50 moving-average crossover on SPY, year by year, against holding — 9 rows by 4 columns, computed from US exchange, SIP and OPRA data.
yearrule_pcthold_pctcrossover_count
20171619.44
20186.1-6.33
20197.628.75
202017.416.14
202118.7272
2022-24.7-19.56
20239.424.36
20241323.34
202510.416.44
the exact SQL behind every number
WITH daily AS
(
    SELECT
        toDate(toTimeZone(window_start, 'America/New_York')) AS d,
        toFloat64(argMax(close, window_start))               AS px
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker = 'SPY'
      AND window_start >= '2016-01-01 00:00:00'
      AND window_start <  '2026-01-01 05:00:00'
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) >= 570
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) < 960
    GROUP BY d
),
averaged AS
(
    SELECT
        d,
        px,
        avg(px) OVER (ORDER BY d ROWS BETWEEN 19 PRECEDING AND CURRENT ROW) AS fast_ma,
        avg(px) OVER (ORDER BY d ROWS BETWEEN 49 PRECEDING AND CURRENT ROW) AS slow_ma,
        row_number() OVER (ORDER BY d)                                      AS session_no
    FROM daily
),
positioned AS
(
    SELECT
        d,
        px,
        if(session_no >= 50 AND fast_ma > slow_ma, 1, 0) AS long_today,
        lagInFrame(if(session_no >= 50 AND fast_ma > slow_ma, 1, 0), 1)
            OVER (ORDER BY d ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS long_prior,
        lagInFrame(px, 1)
            OVER (ORDER BY d ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS px_prior
    FROM averaged
)
SELECT
    toYear(d)                                                                   AS year,
    round((exp(sum(log(if(long_prior = 1, px / px_prior, 1.0)))) - 1) * 100, 1) AS rule_pct,
    round((exp(sum(log(px / px_prior))) - 1) * 100, 1)                          AS hold_pct,
    countIf(long_today != long_prior)                                           AS crossover_count
FROM positioned
WHERE px_prior > 0
  AND toYear(d) >= 2017
GROUP BY year
ORDER BY year
$