piątek, 25 kwietnia 2025

Krzyż śmierci na S&P500 - czy faktycznie ma znaczenie?

 Niektórzy zwrócili niedawno uwagę, że na S&P 500 występuje tzw. krzyż śmierci, czyli 50-dniowa średnia krocząca przecinająca od góry 200-dniową średnią kroczącą:


Na ten sygnał zwrócił m.in. portal xyz.pl , dodając jednak notkę, że analitycy firmy LPL Financial zaobserwowali, że historycznie w ciągu kolejnego miesiąca indeks spadał średnio niecały 1%, a w ciągu 3 miesięcy nawet rósł. Mowa jednak o medianie, a więc średnia i dominanta mogły się sporo różnić. Podanie samej mediany jest niewystarczające.

Sprawdźmy jak to rzeczywiście wyglądało dotychczas. Zaczniemy tak jak ta firma od 1950 r. Trzeba precyzyjnie określić warunki. Nie wystarczy, że SMA50 przetnie SMA200 z góry w dół, bo za chwilę może jeszcze zawrócić i średnie mogą się znowu dotknąć albo w ogóle SMA50 może przebić z dołu w górę. Jeśli taka sytuacja dzieje się w np. w ciągu tygodnia, to sygnału nie będzie. Tak więc określamy minimalne okno tego zdarzenia. Przyjmę cały tydzień, co oznacza 3 kroki do tyłu i 3 kroki do przodu od momentu przecięcia. Taki układ warunków jest już wystarczający, chociaż może nie być przekonujący, bo nawet po przecięciu się obie średnie mogą rosnąć. Tak na intuicję wydaje się, że  powinny spadać. Z drugiej strony już sama nazwa "krzyż" sugeruje dowolne przecięcie, np. rosnącej SM200 i spadkowej SMA50. Z powodu tej różnicy w interpretacji krzyża śmierci, zrobimy 2 sprawdziany:

1) Są warunki: (a) SMA50 <= SMA200, (b) SMA50[t-3] > SMA200[t-3], (c) SMA50[t+3] < SMA200[t+3] ,

2) To samo co p. (1) i dodatkowo warunek, że obie średnie spadają.

Ostatnia sprawa to sam okres przyszłej stopy zwrotu. Sprawdzimy 1-3 miesięczne zwroty do przodu. Za 1 miesiąc przyjmiemy 21 dni, czyli 21 sesji.

Ad 1) Kod w R: 

# 1. Pobieramy dane S&P500, ładujemy pakiety, np. xts.

# 2. Przekształcamy ceny na logarytmy

logCena <- log(sp500)

# 3. Obliczamy 50-dniową i 200-dniową średnią kroczącą

sma50  <- SMA(logCena, n = 50)

sma200 <- SMA(logCena, n = 200)

# 4. Ustalamy wartość przesunięcia (krok) dla warunków – tu krok = 3

krok <- 3

# Warunki dla sygnału

war1 <- sma50 <= sma200

war2 <- lag(sma50, k = krok) > lag(sma200, k = krok)           # SMA50[t-krok] > SMA200[t-krok]

war3 <- lag(sma50, k = -krok) < lag(sma200, k = -krok)                # SMA50[t+krok] < SMA200[t+krok]

war4 <- TRUE

# 5. Tworzymy wektor sygnału: 1, gdy wszystkie warunki są spełnione, NA w przeciwnym przypadku

sygnal <- ifelse(war1 & war2 & war3 & war4, 1, 0)

sygnal <- na.omit(sygnal)

datySygnalow <- index(sygnal)

# Przypisujemy daty wszystkich sygnałów

print("Daty sygnałów (pełny szereg):")

print(datySygnalow)

# 6. Teraz dodajemy warunek, aby wyłapać jedynie moment przejścia z 0 na 1.

# To pozwala zachować tylko pierwszy dzień pojawienia się sygnału.

sygnal_krok1 <- lag(sygnal, k = 1)  # przesunięcie danych o jeden dzień do tyłu (opóźnione)

sygnal_krok1[is.na(sygnal_krok1)] <- 0  # traktujemy pierwszy dzień jako 0

unikalneDatySygnalow <- which(sygnal_krok1 == 0 & sygnal == 1)

unikalneDatySygnalow <- datySygnalow[unikalneDatySygnalow] 

print("Unikalne daty sygnału (przejście z 0 na 1):")

print(unikalneDatySygnalow)

# 7. Obliczamy dzienne logarytmiczne stopy zwrotu

logZwrot <- na.omit(diff(logCena))

# 8. Używamy rollapply do sumowania logarytmicznych stóp zwrotu na horyzoncie 21 sesji,

# ale dnia następnego po sygnale 

oknoPrzyszlosci <- 21

logZwrot_suma <- lag(rollapply(logZwrot,

                           width = oknoPrzyszlosci,

                           FUN = sum,

                           align = "left"), -1)

# 9. Przekształcamy sumy logarytmicznych stóp zwrotu na zwykłe stopy zwrotu: exp(sum) - 1

miesZwrot <- exp(logZwrot_suma) - 1

# 10. Dopasowujemy daty unikalnych sygnałów do obliczonych miesięcznych stóp zwrotu

wyniki <- data.frame(Date = unikalneDatySygnalow,

                     MonthlyReturn = as.numeric(miesZwrot[unikalneDatySygnalow]))

wyniki <- na.omit(wyniki)

print("Wyniki (data unikalnego sygnału + miesięczna stopa zwrotu):")

print(wyniki)

mean(wyniki$MonthlyReturn)

# 11. Rysujemy histogram miesięcznych stóp zwrotu dla wyłapanych sygnałów

h <- hist(wyniki$MonthlyReturn,

     breaks = 7,

     col = "lightblue",

     border = "black",

     main = "Histogram miesięcznych stóp zwrotu",

     xlab = "Stopa zwrotu",

     ylab = "Liczba sygnałów",

     freq = NULL)

# 12. Przekształcamy liczebności na prawdopodobieństwa

czestosc <- h$counts / sum(h$counts)

# 13. Ustawiamy układ dwóch wykresów oraz marginesy

par(mfrow = c(2, 1), mar = c(2, 5, 2.2, 3))

slupkiZwrotow <- 100 * round(h$mids, 3)

# 14. Rysujemy wykres słupkowy prawdopodobieństwa 1-miesięcznych stóp zwrotu

b <- barplot(czestosc,

             names.arg = slupkiZwrotow,

             col       = "lightblue",

             border    = "black",

             main      = "Prawdopodobieństwo 1-miesięcznych stóp zwrotu",

             xlab      = "",

             ylab      = "Prawdopodobieństwo",

             yaxt      = "n",

             space     = 0,

             cex.main  = 0.9)

# 15. Dodajemy oś Y z wartościami prawdopodobieństwa

axis(2, at = round(czestosc, 2), las = 2, cex = 0.7)

# 16. Zaznaczamy dominantę prawdopodobieństwa linią pionową

abline(v  = b[which.max(czestosc)],

       col  = "red",

       lty  = 2,

       lwd  = 2)

# 17. Rysujemy wykres słupkowy skumulowanego prawdopodobieństwa

par(mar = c(4, 5, 1.5, 3))

barplot(cumsum(czestosc),

        names.arg = slupkiZwrotow,

        col       = "lightblue",

        border    = "black",

        main      = "Skumulowane prawdopodobieństwo 1-miesięcznych stóp zwrotu",

        xlab      = "",

        ylab      = "Prawdopodobieństwo",

        yaxt      = "n",

        space     = 0,

        cex.main  = 0.9)

# 18. Dodajemy oś Y z wartościami skumulowanego prawdopodobieństwa

axis(2, at = round(cumsum(czestosc), 2), las = 2, cex = 0.7)

# 19. Dodajemy opis osi X

mtext(text = "Stopa zwrotu, 1-miesięczna (%)", side = 1, padj = 3.5)

# 20. Zaznaczamy dominantę skumulowanego prawdopodobieństwa linią poziomą

abline(h    = cumsum(czestosc)[which.max(czestosc)],

       col  = "red",

       lty  = 2,

       lwd  = 2)

# 21. Zaznaczamy dominantę pojedynczego prawdopodobieństwa linią pionową

abline(v    = b[which.max(czestosc)],

       col  = "red",

       lty  = 2,

       lwd  = 2)



Liczba przypadków = 35.
Otrzymane statystyki:



Okazuje się, że dominuje ujemna stopa zwrotu z medianą -2,5%, ale jej częstość to niecałe 0,4, a  skumulowane prawdopodobieństwo empiryczne 0,57. Oznacza to, że na prawie 60% będzie spadać w kolejnym miesiącu.  Skośność jednak powoduje, że sporo też jest dodatnich stóp zwrotu wokół +2,5%.

Sprawdzamy 2-miesięczne zwroty, tj. oknoPrzyszlosci <- 42.




 W kolejnych dwóch miesiącach po krzyżu śmierci dominowały wzrosty z medianą 2,5%, ale znów skośność powoduje duże ryzyko spadków.

3-miesięczna stopa zwrotu (oknoPrzyszlosci <- 63):




Po 3 miesiącach dostajemy w zasadzie czystą przypadkowość - możliwe są zarówno wzrosty jak i spadki.


Ad 2) Cały kod będzie ten sam, jedynie war4 przyjmuje teraz postać
war4 <- lag(diff(sma50, lag = krok*2 + 1), k = -krok) < 0 & lag(diff(sma200, lag = krok*2 + 1), k = -krok) < 0  # oba spadają


Dostałem 20 przypadków.

a) miesięczne zwroty:


Dostajemy tu zbliżone wyniki do pierwszej definicji krzyża śmierci, z tym, że w tym przypadku prawdopodobieństwa spadków są wyższe niż poprzednio, bo aż 0,75.

b) 2-miesięczne zwroty:


Dominanta praktycznie nie istnieje. Można tylko powiedzieć, że zwroty przechylają się minimalnie na plus.

c) 3-miesięczne zwroty


Prawdopodobieństwo 3-miesięcznych wzrostów jest w tym wypadku obiektywnie większe niż spadków (chociaż okolice do -0% też nie jest takie małe). 

Porównując otrzymane statystyki ze wspomnianym na początku badaniem mogę powiedzieć, że się one pokrywają. Oni dostali średniomiesięcznie (medianę) -1%, ja dostałem dominantę -2,5%, a przy skumulowanym prawdopodobieństwie 0,57, a 0,57*2,5 = 1,43. Podobnie jak oni dostałem także dodatnią 3-miesięczną średnią. 


Naturalne staje się na koniec pytanie, czy bieżący krzyż śmierci spełnia w ogóle określone definicje? Jeśli spełnia drugą, to oczywiście spełnia też pierwszą (bo druga jest mocniejsza), więc sprawdzamy najpierw drugą. Wszystkie sygnały są zapisane w zmiennej unikalneDatySygnalow:

> unikalneDatySygnalow
 [1] "1953-05-11" "1957-09-26" "1960-02-15" "1962-05-08" "1965-07-22" "1968-02-27" "1969-06-23"
 [8] "1977-03-03" "1980-04-22" "1984-02-06" "1987-11-04" "1990-09-07" "1994-04-19" "2000-10-30"
[15] "2010-07-02" "2011-08-12" "2015-08-28" "2016-01-11" "2018-12-07" "2020-03-27" "2025-04-14"

Ostatnia pozycja to właśnie ostatni sygnał z 14 kwietnia. Czyli druga definicja zostaje spełniona. Stąd wnioskujemy, że kolejne 21 sesji będzie spadkowych, z "prawdopodobieństwem" 0,75. To znaczy, że do połowy maja należy spodziewać się generalnie spadków. Kolejne 2 miesiące są niejednoznaczne, z lekkim przechyleniem na plus, a 3 miesiące "powinny" być dodatnie. Trudno mi uwierzyć w to ostatnie, ale wszystkiego trzeba się spodziewać przy takim rozedrganiu, z jakim mamy do czynienia.

środa, 9 kwietnia 2025

Co nam powie Equity Market Volatility Index?

Oczywiście na giełdzie nie ma nic pewnego, ale prognozuję, że kwiecień będzie spadkowy. Spójrzmy na Indeks Zmienności Rynku Akcji USA (US Equity Market Volatility Index) w porównaniu z S&P 500 (od stycznia 2005 do marca 2025):

Największe piki zmienności występowały w końcówce głębokich obsunięć. Mniejsze piki występowały również przy zakończeniu hossy. Wyjątkiem można nazwać rok 2022, ale też z perspektywy czasu nie były to jakieś gigantyczne spadki, co można tłumaczyć większym wpływem geopolityki niż gospodarki. Zupełnie inaczej sprawa się ma w przypadku roku 2020 - krótkie, ale duże załamanie, sztucznie wywołane przez przymusową izolację i zakazy handlu (tzw. lockdown). Pamiętam, jak nie mogłem uwierzyć, że rząd może sobie tak po prostu, z dnia na dzień niemal, zakazać prowadzić działalności gospodarczej. W sumie zobaczmy to z bliska:


Cofnijmy się kolejne kilka lat:

Znowu chwilowy wstrząs w 2011 r. nie był całkowicie naturalny, bo dotyczył państw UE, które musiały poczuć na własnej skórze, do czego prowadzi socjalizm utrzymywany przez długie lata przez frywolne zadłużanie się. Unia w swojej "dobroduszności" postanowiła uratować je, czyli zrefinansować ich niespłacalne długi, tzn. jeszcze je dodłużyć. Koszty tej operacji były niewidoczne z dnia na dzień, dlatego wielu mogło uznać, że Grecji, Włochom czy Hiszpanii upiekło się. I w zasadzie mieli rację, chociaż mogli nie zauważyć, że cała UE od tej chwili zaczęła powoli upadać.  Nasza giełda jest tego świetnym przykładem.

Chociaż jak powiedziałem zmienność 2011 nie była całkiem naturalna, to jednak była skutkiem kryzysu finansowego parę lat wcześniej - a ten miał już duży wpływ na gospodarkę i przez to zmienność była naturalna:


Wróćmy do teraźniejszości, mamy:


Wzrost niepewności, który tu obserwujemy, dopiero się zaczął. Jest pewne, że w kwietniu będzie dużo wyższy, ponieważ cła USA ogłosiły na początku kwietnia, a w kolejnych dniach zaczęły się pojawiać plotki, częściowo zdementowane, że cła będą zmniejszane w trakcie negocjacji. Takie biznesowe podejście, jakby się grało w pokera, zupełnie bez zwracania uwagi na otoczenie, to prawdziwy koszmar dla gospodarki. Światowej, ale w tym wypadku głównie USA, bo amerykańcy przedsiębiorcy nie mogą od niej uciec. A to właśnie zagraniczni inwestorzy będą uciekać z tak niestabilnego otoczenia (stąd dolar spada i będzie dalej spadał). 

Spadki muszą być potężne, bo nakładają dwa negatywne czynniki: cła oraz niepewność, czy za tydzień nie zostaną zniesione. Na efektywnym rynku, ale też czysto psychologicznie, nie będzie wiecznie reakcji na ewentualną zmianę decyzji Trumpa. Ta niepewność musi zostać zdyskontowana w postaci wyższej awersji do ryzyka. Psychologicznie natomiast powiemy, że ten sam bodziec będzie działał z czasem coraz słabiej.

Sama liniowa korelacja stopy zwrotu z EMV wynosi -33%, ale niewiele to mówi, bo ich zależność dużo lepiej pokazuje wykres punktowy:


Ta zależność jest ewidentnie nieliniowa i składa się z dwóch części. Pierwsza połowa zmienności to czyste ryzyko, które generuje albo zysk, albo stratę (zauważmy jak równo wyszło - ok. 11-12% na plus jak i na minus). Druga połowa zmienności jest ujemnie proporcjonalna do zmian procentowych. Dla EMV >= 40 dostajemy same ujemne stopy zwrotu i im większe EMV, tym mocniej ujemna jest zmiana indeksu. W sumie dostajemy wskaźnik mówiący, że jeśli dochodzi do poziomu 40 i przekracza go,  spadki są bardzo prawdopodobne.  

Ale właściwie co to jest ten EMV? Jego konstrukcja jest dość kontrowersyjna, ale wygląda na to, że poprawna. Otóż sam skrót ma dwa znaczenia. Pierwszy pochodzi od jego nazwy, czyli Equity Market Volatility. Drugi to akronim od economy, market, volatility. Każde z tych słów to hasła-skojarzenia,  stanowiące grupę różnych słów związanych z tym hasłem. Np. pod "market" jest m.in. equity, S&P, pod "volatility" jest risk, uncertainty itp. Te słowa-klucze szukane są następnie w prasie (gazetach) i zwyczajnie się je zlicza. Uzyskane liczby odpowiednio się skaluje i dostaje ten sposób wskaźnik dla każdego miesiąca. Ponieważ największe szczyty zmienności dostajemy blisko zakończenia bessy, to prawdopodobnie media napędzają wszechogarniający pesymizm "ulicy", która pod wpływem psychicznej presji najmocniej pozbywa się aktywów blisko dołka. 

Chociaż trudno w to uwierzyć, ale EMV - wskaźnik obliczony tylko w oparciu o zliczanie słów w prasie - ma korelację z VIX ok. 70%. Bo to by znaczyło, że bez danych rynkowych można poprawnie oszacować bieżącą zmienność rynku. Nie jest to może szokujące, ale mocno zaskakujące.

Może się wydawać, że EMV nie jest tak przydatny jak VIX, który podawany jest z dzienną częstością, a nie miesięczną. Tylko zobaczmy jedną rzecz. Porównajmy oba wskaźniki w tym samym okresie po dopasowaniu VIX do miesięcznej częstości:


Zoom od 2019:


W przypadku okresu pandemicznego oba wskaźniki rosły i spadały równolegle, natomiast ostatni wstrząs polityki celnej Trumpa był poprzedzony tak samo szybkim wzrostem EMV, ale już nie VIX. Różnica jest taka, że pierwsze wydarzenie było całkowicie nieprzewidywalne, natomiast drugie było mocno nagłaśniane w mediach. Przypuszczam, że różnica leży w tym, że informacje o cłach były z jednej strony stopniowo odkrywane, a z drugiej ciągle powtarzane, co spowodowało, że rynek zachował się trochę jak powoli gotowana żaba. VIX zaczął gwałtownie rosnąć dopiero w kwietniu, którego tutaj siłą rzeczy nie można było umieścić. Bo dziennie jest skok:


Wygląda więc na to, że EMV przewidział z miesięcznym wyprzedzeniem spadki amerykańskiej giełdy (ponieważ w marcu przekroczył poziom 40), podczas gdy VIX po prostu je odzwierciedla. Spójrzmy też na oba wskaźniki przy zakończeniu hossy 2007:

EMV praktycznie wskazał szczyt S&P500, który korespondował ze szczytem EMV (wartością min. 40). Tego nie można powiedzieć o VIX. Z kolei przy zakończeniu bessy taka rozbieżność nie występuje.