STRASMORE/EXPLORE 2,469 QUERIES

swing_rule

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-22, from market-structure-and-break-of-structure.

as of table 6×5read in context →
swing_rule — 6 rows by 5 columns, computed from US exchange, SIP and OPRA data.
tickersessionsswing_highsswing_lowsbars_per_swing_high
AAPL141512913511
JPM141512412611.4
KO141513013310.9
MSFT141512912911
QQQ141512713711.1
SPY141512113611.7
Rows × columns
6 × 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 swing_rule, derived from the stored result.
ColumnTypeRangeNotes
ticker text 6 distinct values (AAPL, JPM, KO…)
sessions number every row is 1,415
swing_highs number 121 to 130
swing_lows number 126 to 137
bars_per_swing_high number 10.9 to 11.7 US dollars

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.

Run it yourself

This is the exact query behind the result above. Change a ticker, a date or a column and run it against the warehouse — no account, no key. The no-signup tier is smaller than the one this page was computed on; a query that reaches past it comes back saying which plan runs it.

WITH px AS
(
    SELECT
        ticker,
        date,
        toFloat64(high) AS h,
        toFloat64(low)  AS l
    FROM global_markets.stocks_daily_aggs
    WHERE ticker IN ('AAPL', 'JPM', 'KO', 'MSFT', 'QQQ', 'SPY')
      AND date >= '2021-01-01'
      AND date <  '2026-09-01'
),
flagged AS
(
    SELECT
        ticker,
        ((h > max(h) OVER w_prev) AND (h > max(h) OVER w_next)) AS is_swing_high,
        ((l < min(l) OVER w_prev) AND (l < min(l) OVER w_next)) AS is_swing_low,
        count() OVER w_prev AS bars_before,
        count() OVER w_next AS bars_after
    FROM px
    WINDOW
        w_prev AS (PARTITION BY ticker ORDER BY date ROWS BETWEEN 3 PRECEDING AND 1 PRECEDING),
        w_next AS (PARTITION BY ticker ORDER BY date ROWS BETWEEN 1 FOLLOWING AND 3 FOLLOWING)
)
SELECT
    ticker,
    count()                AS sessions,
    countIf(is_swing_high) AS swing_highs,
    countIf(is_swing_low)  AS swing_lows,
    round(count() / greatest(countIf(is_swing_high), 1), 1) AS bars_per_swing_high
FROM flagged
WHERE bars_before = 3
  AND bars_after = 3
GROUP BY ticker
ORDER BY ticker
⌘/Ctrl + Enter