Strasmore Research
Analizy Matt ConnorAutor: Matt Connor

Czy strategia Sell in May and Go Away działa?

Test S&P 500 z dwóch dekad pokazuje różnicę między sezonami i koszt przełączania pozycji. Sprawdź, czy letnia przerwa rzeczywiście poprawia wynik inwestycji.

„Sell in May and go away” to najbardziej znana reguła kalendarzowa na rynku akcji: sprzedaż pod koniec kwietnia, utrzymywanie gotówki przez lato i ponowny zakup pod koniec października. Na podstawie cen ETF-u replikującego S&P 500, od zamknięcia z ostatniej sesji kwietnia 2005 roku do zamknięcia z ostatniej sesji kwietnia 2026 roku, można stwierdzić, że półrocze od listopada do kwietnia przynosi średnio wyższą stopę zwrotu niż okres od maja do października. Różnica jest realna, ale mniejsza, niż sugeruje to powiedzenie. Okres letni również przynosi średnio zysk, co podważa tę regułę jako strategię inwestycyjną.

Co mówi ta zasada i jak ją testować

To sformułowanie wywodzi się z dawnego zwyczaju londyńskiego, zgodnie z którym po wiosennych spotkaniach wyścigowych opuszczano miasto i wracano do niego jesienią. We współczesnej, testowalnej formie jest to reguła przełączania pozycji oparta na dwóch datach. Szeroki indeks akcji utrzymuje się od ostatniej sesji października do ostatniej sesji kwietnia. Przez pozostałe sześć miesięcy nie utrzymuje się żadnej pozycji.

Do przeprowadzenia testu potrzebne są dwie rzeczy, których samo powiedzenie nie określa: szereg cen oraz zdefiniowane okno czasowe. Wszystkie poniższe wartości opierają się na SPY, najstarszym ETF-ie odzwierciedlającym S&P 500, wycenianym na podstawie minutowych danych z regularnych godzin handlu w tabeli global_markets.delayed_stocks_minute_aggs, od zamknięcia ostatniej sesji kwietnia 2005 roku do zamknięcia ostatniej sesji kwietnia 2026 roku. Miesięczna stopa zwrotu oznacza tu zmianę ostatniej ceny z regularnych godzin handlu względem ceny z poprzedniego miesiąca, bez uwzględnienia dywidend. Dywidendom poświęcono osobną sekcję poniżej, a sposób pomiaru miesięcznych stóp zwrotu wyjaśnia, dlaczego to rozróżnienie zmienia wynik.

Czy strategia „sell in May and go away” działa? Wyniki od 2005 do 2026 roku

ZapytanieTracker S&P 500: maj–październik vs listopad–kwiecień, sezon po sezonie
Dokładny kod SQL dla każdej liczby
WITH monthly AS (
    SELECT toStartOfMonth(toDate(toTimeZone(window_start, 'America/New_York'))) AS month_start,
           argMax(close, window_start) AS month_close
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker = 'SPY'
      AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2005-04-01')
      AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-04-30')
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
    GROUP BY month_start
)
SELECT toYear(apr_m.month_start) AS year,
       round(100 * (toFloat64(oct_m.month_close) / toFloat64(apr_m.month_close) - 1), 2) AS may_oct_pct,
       round(100 * (toFloat64(apr_next.month_close) / toFloat64(oct_m.month_close) - 1), 2) AS nov_apr_pct
FROM monthly AS apr_m
INNER JOIN monthly AS oct_m ON oct_m.month_start = addMonths(apr_m.month_start, 6)
INNER JOIN monthly AS apr_next ON apr_next.month_start = addMonths(apr_m.month_start, 12)
WHERE toMonth(apr_m.month_start) = 4
ORDER BY year
Run this yourself

Każdy punkt zestawia jedno lato z następującą po nim zimą. Wykres obejmuje 21 pełnych par. Zaczyna się od lata 2005, a kończy na zimie, która zakończyła się w kwietniu 2026.

W większości poszczególnych lat obie linie się różnią, a w najgorszym roku poruszają się zgodnie. Od końca kwietnia do końca października 2008 letnie półrocze wyniosło -29.81%, co było najgłębszym odczytem w całej serii. Następująca po nim zima przyniosła -10.03%. Inwestor, który sprzedał akcje w kwietniu, uniknął znacznego spadku, a następnie ponownie zajął pozycję przed ostatnim odcinkiem przeceny. Kalendarz i skala strat zgrały się ze sobą tylko przez połowę tego okresu.

Czy letnia luka osłabła od 2015 roku?

Średnie są tym, co rzeczywiście zakłada ta maksyma, a przedział czasowy stanowi podstawę tych średnich. Podział w 2015 roku daje dwie części o porównywalnej długości, obliczone w ten sam sposób.

ZapytanieRóżnica sezonowa według okresu: średnie półroczne stopy zwrotu i różnica między nimi
Dokładny kod SQL dla każdej liczby
WITH monthly AS (
    SELECT toStartOfMonth(toDate(toTimeZone(window_start, 'America/New_York'))) AS month_start,
           argMax(close, window_start) AS month_close
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker = 'SPY'
      AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2005-04-01')
      AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-04-30')
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
    GROUP BY month_start
),
pairs AS (
    SELECT toYear(apr_m.month_start) AS season_year,
           toFloat64(oct_m.month_close) / toFloat64(apr_m.month_close) - 1 AS may_oct,
           toFloat64(apr_next.month_close) / toFloat64(oct_m.month_close) - 1 AS nov_apr
    FROM monthly AS apr_m
    INNER JOIN monthly AS oct_m ON oct_m.month_start = addMonths(apr_m.month_start, 6)
    INNER JOIN monthly AS apr_next ON apr_next.month_start = addMonths(apr_m.month_start, 12)
    WHERE toMonth(apr_m.month_start) = 4
),
tagged AS (
    SELECT may_oct,
           nov_apr,
           arrayJoin(['All seasons 2005-2025',
                      if(season_year <= 2014, 'First half 2005-2014', 'Second half 2015-2025')]) AS era
    FROM pairs
)
SELECT era,
       count() AS season_count,
       round(100 * avg(may_oct), 2) AS avg_may_oct_pct,
       round(100 * avg(nov_apr), 2) AS avg_nov_apr_pct,
       round(100 * (avg(nov_apr) - avg(may_oct)), 2) AS winter_minus_summer_pct,
       countIf(may_oct > 0) AS summers_positive
FROM tagged
GROUP BY era
ORDER BY era
Run this yourself

We wszystkich 21 sezonach średnia stopa zwrotu od maja do października wyniosła 3.88%, a od listopada do kwietnia 6.06%. Różnica wyniosła 2.18 punktów procentowych. To właśnie cała podstawa tej zasady. Warto zestawić z nią kolumnę dotyczącą lata: 16 tych okresów letnich zakończyło się wyżej niż się rozpoczęło. Oznacza to, że przeciętne wycofanie z rynku prowadziło do pominięcia wzrostu, a nie uniknięcia straty.

Obie części dają różne wyniki. Wcześniejszy okres pokazuje różnicę na poziomie 4.84 punktów, a późniejszy -0.24. Popularne omówienia podają lukę bez wskazania lat, które za nią stoją, choć to właśnie lata są podstawą tego argumentu. Każda statystyka sezonowa jest twierdzeniem dotyczącym określonego odcinka historii, a wybór tego odcinka zależy od osoby, która ją przytacza.

Czy rzeczywiście występuje efekt sierpnia i września?

Sześć miesięcy to zbyt szerokie ujęcie. Podział kalendarza na poszczególne miesiące pozwala sprawdzić, czy letnie okno jest jednolicie słabe, czy też za cały wynik odpowiada tylko jego część.

ZapytanieŚrednia miesięczna stopa zwrotu trackera S&P 500, maj 2005–kwiecień 2026
Dokładny kod SQL dla każdej liczby
WITH monthly AS (
    SELECT toStartOfMonth(toDate(toTimeZone(window_start, 'America/New_York'))) AS month_start,
           argMax(close, window_start) AS month_close
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker = 'SPY'
      AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2005-04-01')
      AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-04-30')
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
    GROUP BY month_start
),
month_returns AS (
    SELECT m1.month_start AS month_start,
           100 * (toFloat64(m1.month_close) / toFloat64(m0.month_close) - 1) AS ret_pct
    FROM monthly AS m1
    INNER JOIN monthly AS m0 ON m0.month_start = addMonths(m1.month_start, -1)
)
SELECT formatDateTime(month_start, '%b') AS label,
       count() AS readings,
       round(avg(ret_pct), 2) AS avg_return_pct,
       round(quantileDeterministic(0.5)(ret_pct, cityHash64(month_start)), 2) AS median_return_pct,
       countIf(ret_pct > 0) AS positive_readings
FROM month_returns
GROUP BY label
ORDER BY min(toMonth(month_start))
Run this yourself

Słabość koncentruje się we wrześniu. Średnia wyniosła -0.75%, a miesiąc zakończył się wzrostem w 12 z 21 lat; mediana wyniosła 0.38%. Średnia dla sierpnia wyniosła 0.28%. W lipcu, przypadającym w środku okresu, w którym inwestor pozostaje poza rynkiem, średnia wyniosła 2.63%, a w październiku, ostatnim miesiącu przed ponownym wejściem zgodnie z regułą, 0.97%.

Efekt w tym oknie ma wąski zakres: obejmuje jeden słabszy okres pod koniec lata, podczas gdy miesiące po obu jego stronach wyglądają podobnie jak pozostała część kalendarza. Wyjście z rynku pod koniec kwietnia w celu pominięcia września oznacza również pominięcie maja, czerwca, lipca i sierpnia.

What the switch actually costs

The seasonal gap is a gross number. A rule has to survive its own frictions.

ZapytanieWzrost wartości jednego dolara: inwestowanie przez cały rok vs tylko od listopada do kwietnia
Dokładny kod SQL dla każdej liczby
WITH monthly AS (
    SELECT toStartOfMonth(toDate(toTimeZone(window_start, 'America/New_York'))) AS month_start,
           argMax(close, window_start) AS month_close
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker = 'SPY'
      AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2005-04-01')
      AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-04-30')
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
    GROUP BY month_start
),
pairs AS (
    SELECT toYear(apr_m.month_start) + 1 AS year,
           toFloat64(oct_m.month_close) / toFloat64(apr_m.month_close) AS summer_ratio,
           toFloat64(apr_next.month_close) / toFloat64(oct_m.month_close) AS winter_ratio
    FROM monthly AS apr_m
    INNER JOIN monthly AS oct_m ON oct_m.month_start = addMonths(apr_m.month_start, 6)
    INNER JOIN monthly AS apr_next ON apr_next.month_start = addMonths(apr_m.month_start, 12)
    WHERE toMonth(apr_m.month_start) = 4
)
SELECT year,
       round(exp(sum(log(summer_ratio * winter_ratio)) OVER (ORDER BY year)), 2) AS buy_and_hold_growth,
       round(exp(sum(log(winter_ratio)) OVER (ORDER BY year)), 2) AS winter_only_growth
FROM pairs
ORDER BY year
Run this yourself

A dollar held through every season from the end of April 2005 grew to $6.21 by the end of April 2026. The same dollar in the market only from November through April, idle the rest of the time, grew to $3.14. Both lines are price only with nothing deducted, which flatters the switching rule. Here is what the chart leaves out.

  • Two trades every year, in every one of the 21 seasons. The ticket is free at most brokers now. The bid ask spread paid on each round trip is not.
  • Tax. In a taxable account the April sale realizes whatever gain built up since November. A November to April holding period runs under a year by construction, which places the gain at short-term rates for a US taxpayer.
  • Idle cash. The winter-only line credits the sidelined money with zero. A real holder would park it, and where to park idle cash covers the instruments people use for that.
  • Dividends, which the next panel prices out.
ZapytanieWypłaty SPY według miesiąca ex-dividend: w okresie maj–październik vs poza nim
Dokładny kod SQL dla każdej liczby
SELECT toYear(ex_dividend_date) AS year,
       round(sumIf(cash_amount, toMonth(ex_dividend_date) >= 5 AND toMonth(ex_dividend_date) <= 10), 3) AS may_oct_dividends_usd,
       round(sumIf(cash_amount, toMonth(ex_dividend_date) < 5 OR toMonth(ex_dividend_date) > 10), 3) AS nov_apr_dividends_usd
FROM global_markets.stocks_dividends
WHERE ticker = 'SPY'
  AND cash_amount > 0
  AND ex_dividend_date >= toDate('2005-01-01')
  AND ex_dividend_date <= toDate('2025-12-31')
GROUP BY year
ORDER BY year
Run this yourself

SPY's ex-dividend dates land in March, June, September and December, so two of the four fall inside the six months a seller sits out. In 2025, $3.592 per share of distributions carried a May to October ex-date, against $3.689 in the other six months. The two columns track each other across the whole chart. Sitting out half the calendar forgoes roughly half the fund's income every year, on top of whatever the price does in those months. The ex-dividend date is the cutoff that decides who gets paid.

One more cost carries no line item. Index returns arrive in clusters, and missing the best days puts numbers on what a handful of absent sessions does to a long-run balance. Six months out of every year is a lot of absent sessions. That is the same arithmetic behind dollar cost averaging as a discipline about time in the market rather than timing.

Dlaczego do znanej reguły kalendarzowej należy podchodzić sceptycznie

Dwanaście miesięcy daje 4 095 niepustych sposobów wyboru podzbioru, w którym można utrzymywać inwestycję. Przeszukanie takiej przestrzeni na podstawie jednej historii cen sprawi, że coś zacznie wyglądać przekonująco. To właściwość samego procesu wyszukiwania.

Reguła Sell in May ma jedną istotną przewagę nad zasadą wydobytą w ubiegłym tygodniu: była znana na długo przed rozpoczęciem analizowanej próby. Oznacza to, że lata przedstawione na tej stronie stanowią test poza próbą, a nie wynik dopasowania. Test wskazuje na niewielką różnicę, skoncentrowaną w jednym miesiącu, w przypadku reguły przełączania, która w tym samym okresie osiąga gorszy wynik niż strategia kup i trzymaj. Błąd wyprzedzania informacji pokazuje, w jaki sposób backtest może niepostrzeżenie poznać odpowiedź z wyprzedzeniem. Reguły kalendarzowe są podręcznikowym przykładem tego problemu: miesiące wybiera się dopiero po poznaniu stóp zwrotu.

Najczęściej zadawane pytania

Czy strategia „sprzedaj w maju i wróć jesienią” nadal działa?

Dla cen SPY od kwietnia 2005 roku do kwietnia 2026 roku średnia stopa zwrotu w okresie od listopada do kwietnia wyniosła 6.06% wobec 3.88% w okresie od maja do października. Oznacza to, że sezonowa różnica utrzymuje się w całej próbie. W letnim półroczu również odnotowano średni wzrost. 16 z 21 takich okresów zakończyło się wyżej niż na początku.

Które miesiące są faktycznie słabsze?

W analizowanym okresie słabość koncentrowała się we wrześniu. Średnia stopa zwrotu wyniosła -0.75%, a wzrost odnotowano w 12 z 21 lat. Lipiec, czyli kolejny miesiąc pomijany przez tę strategię, przyniósł w tym samym okresie średnio 2.63%.

Czy sprzedaż w maju daje lepszy wynik niż strategia kup i trzymaj?

Nie w analizowanej próbie. Jeden dolar utrzymywany nieprzerwanie od końca kwietnia 2005 urósł do $6.21 w kwietniu 2026. W przypadku strategii przełączania się wyłącznie na okres zimowy było to $3.14. Porównanie nie uwzględnia kosztów transakcyjnych, podatków ani dywidend.

Co dzieje się z dywidendami po sprzedaży w maju?

Daty ex-dividend funduszu nie są zawieszane na okres letni. Dwie z czterech kwartalnych dat ex-dividend SPY przypadają w okresie od maja do października. Inwestor, który nie posiada funduszu w dniu ex-dividend, nie otrzymuje tej wypłaty.

Czy wzorzec sezonowy jest tym samym co prognoza?

Nie. Średnia historyczna dla 21 sezonów opisuje wyniki w określonym przedziale czasu. Wyniki poszczególnych lat znacznie różnią się od tej średniej, co pokazuje pierwszy wykres. Sam wybór analizowanego okresu również ma znaczenie.


Każdy panel przedstawia zapisane zapytanie wraz z dołączonym kodem SQL. Na terminalu Strasmore można zmienić ticker, przesunąć przedział czasowy i sprawdzić, czy sezonowa różnica się utrzymuje.

#seasonality#backtesting#sp-500#market-history#quant