2) kupuj, gdy leje się krew na rynku złotego.
Zanim zajmę się ich testowaniem, zwrócę uwagę na niezbyt poprawny tytuł. O ile "inwestowanie" rzeczywiście oznacza kupowanie i trzymanie jakiś czas aktywów, to słowo "instrukcja" jest pojęciem szerszym i powinna zawierać oprócz strategii kupowania także sposób sprzedawania. Oczywiście można by dwie przytoczone strategie odwrócić dla momentu sprzedaży, ale intuicja nie wystarczy i autor powinien przynajmniej wspomnieć coś o zbywaniu. W sumie, to wystarczyłoby zmienić tytuł np. na "Prosta strategia inwestowania na GPW" i nie byłoby tematu. Ale zajmijmy się już istotą.
Dokładniej autor proponuje trzymiesięczną średnią rocznej dynamiki produkcji przemysłowej, która spada poniżej zera. Autor nie wskazuje źródła pobranych danych, nie pisze też wprost czy chodzi o polski przemysł - tego trzeba się domyślić z kontekstu. Dane o polskim przemyśle pobrałem ze strony Eurostatu (odnośnik - należy zmienić ustawienia na "Poland", "Seasonally and calendar adjusted data" oraz "Percentage change on previous period"). Aby uzyskać wymagane dane, musiałem surowe dane obrobić:
1. pobrałem miesięczne zmiany procentowe produkcji przemysłowej dla Polski,
2. zamieniłem na logarytmiczne stopy zmian,
3. aby dostać roczną dynamikę, dodałem do siebie kolejnych 12 stóp,
4. uśredniłem 3 kolejne stopy.
Dwie uwagi. Pierwsza dotyczy polskich odpowiedników nazw angielskich. Otóż industry oraz manufacturing są uznawane przez słowniki za synonimy i tłumaczone jako produkcja przemysłowa czy po prostu przemysł, ale jest to ich potoczne rozumienie. W rzeczywistości ten drugi termin oznacza przemysł przetwórczy, inaczej przetwórstwo przemysłowe, które ogólnie mówiąc stanowi produkcję pośrednią między dostarczaniem surowców a wyrobami gotowymi. Tak więc industry jest szerszym pojęciem, a manufacturing jest tylko jego składnikiem.
Po drugie powinniśmy zdać sobie sprawę, że wielkość produkcji nie jest podawana na określony moment, ale w przedziale czasowym. Wynika to zarówno z faktu, że jest ona procesem, a nie stanem oraz z tego, że stanowi ona przychód przedsiębiorstwa produkcyjnego w danym okresie. Natomiast kurs giełdowy można porównać z wartością bilansową, czyli powstającą na dany moment, czyli formalnie jest nieporównywalny z wielkością produkcji. Aby sprowadzić obie wielkości do porównywalności, badamy ich zmiany, np. miesiąc do miesiąca lub rok do roku. Warto więc sobie uświadomić, że analizując produkcję, badamy jakby "zmiany ze zmiany" kapitału gospodarki. Zmiana między jednym momentem a drugim powoduje, że możemy przyrównać okresy do siebie dla dwóch różnych serii i w ten sposób je porównać. Choć to jedynie kwestie techniczne, to rzutują na poprawność wniosków. Z jednej strony informacje ze sfery makro przychodzą z opóźnieniem, z drugiej strony należy uważać, na który okres obliczamy zmiany. Jeśli popełnimy błąd, to błędnie wnioskować, że produkcja wyprzedza giełdę. W kontekście tego badania taki błąd nie jest jednak wielką przeszkodą, bo nie chodzi o idealne przewidzenie momentu dołka, ale okolicy. Zawsze jednak lepiej zachować precyzję.
Eurostat podaje dynamikę (procentową zmianę) produkcji na dany miesiąc i rok, a więc okres dynamiki to (początek miesiąca - koniec miesiąca). Gdybyśmy chcieli te wartości porównać ze stopami zwrotu z WIG, musielibyśmy odjąć zamknięcie w ostatnim dniu miesiąca od analogicznego zamknięcia z poprzedniego miesiąca. Jednak wiemy, że nie chodzi w naszym badaniu o stopy zwrotu, tylko o szacowanie okresu zmian trendu. Wynika z tego, że możemy wykorzystać kod R, jaki napisałem w tym artykule, gdzie porównywałem lokalne ekstrema WIG20 z sWIG80.
To po kolei. Nałożenie WIG i dynamiki przemysłu od marca 2001 do stycznia 2024:
Następnie filtruję oba wykresy (filtr lowess) i oznaczam lokalne ekstrema, przy czym dla przemysłu minimum ma być poniżej zera, a maksimum powyżej.
Pionowe linie zaznaczają dołki - czerwone przemysłu, niebieskie WIGu. Niektóre się nakładają, dlatego nie widać dwóch czerwonych, ale łatwo je usytuować, bo to najbardziej skrajne dna. W sumie mamy tu 4 czerwone dołki i 7 niebieskich. Przeanalizujmy je:
- pierwszy dołek WIGu jest w zasadzie opóźniony w stosunku do przemysłu o ponad pół roku (przemysł dobrze go prognozuje),
- piąty dołek WIGu tak naprawdę nie jest dołkiem, a tempo przemysłu - chociaż spada - nie wskazuje na żadne zwroty (czyli przemysł prawidłowo mówi, aby trzymać),
Sumarycznie okazuje się, że dołki produkcji przemysłowej nie wyprzedzają WIGu, ponieważ jeden raz go wyprzedziły, drugi raz na odwrót, w pozostałych występują w tym samym czasie lub w ogóle ich nie ma. Niemniej rzeczywiście dołek przemysłu ma walor informacyjny i pozwala przewidzieć dalszy kierunek WIGu. Dodatkowa informacja wynika z faktu, że filtr WIGu nie może zastąpić tutaj filtru przemysłu, ponieważ w jednym przypadku dołek WIGu dał fałszywy sygnał - pod koniec roku 2018, kiedy niby było dno, ale wzrost prawie żaden - a tymczasem tempo przemysłu było ciągle dodatnie. Wcześniej, w roku 2016 także filtr WIGu osiąga lokalne minimum, a przemysł nie (tzn. spada, ale jest powyżej zera), co można byłoby uznać za argument przeciwko przemysłowi, gdybyśmy nastawiali się na krótki lub średni termin - była to mocniejsza korekta trwająca niecały rok. W dłuższym terminie nie ma tu błędu i rzeczywiście przemysł prognozuje hossę. De facto dynamika przemysłu także spadała w tym czasie, więc również można było tu zmniejszać pozycję, chociaż opieranie się tylko na tym nie miałoby sensu, choćby dlatego, że mamy inne przykłady, kiedy dynamika ta także spadała, a jednak WIG rósł.
Popatrzmy teraz na ostatnie dołki. Minimum giełdy w 2022 r. wyprzedza lokalne minimum przemysłu o niecały rok. Tzn. dynamika produkcyjna znajduje się nadal pod kreską (choć minimalnie) i nie wiadomo czy w ogóle możemy nazwać dołkiem ten okres, ale istnieje na to szansa. Zobaczmy w zbliżeniu ten obszar:
Oznaczenia analogicznie jak przy dołkach. Spójrzmy:
- pierwszą górkę WIGu zapowiadała wyraźnie zmiana trendu przemysłu (jednak pierwsza górka przemysłu została zignorowana przez WIG),
- drugą górkę WIGu podobnie wskazywała wcześniejsza górka przemysłu, chociaż bessa giełdowa była wyjątkowo krótka, a kolejny dołek przemysłu powstał trochę późno,
- trzecia górka WIGu była sygnalizowana przez sferę realną już ok. rok wcześniej, chociaż sfera ta szybko zawinęła w górę, co z pewnością dawało schizofreniczny obraz; to ten przypadek, gdy bardziej liczyła się umiejętność spekulacji (krótki trend spadkowy),
- przy piątym to przemysł nieco szybciej zareagował od WIGu.
Przy szczytach sytuacja się o tyle komplikuje, że raz giełda zignorowała duże spadki dynamiki produkcji, a przy wielu innych czas spadków indeksu był krótki, tak że w praktyce można było nawet nie zdążyć dobrze wyjść, kiedy trzeba było za chwilę wejść.
Kiedy się wczytamy w treść propozycji, możemy się zawieść. Autor nie wskazuje już warunku zakupu (ani tym bardziej sprzedaży), a jedynie luźno sugeruje, że jeżeli euro w ciągu roku stosunkowo mocno się umocniło do złotego, to możemy rozważać moment zakupu akcji. Podaje pewne liczby, ale niewiele one wnoszą. Jedynie co możemy zrobić, to połączyć poprzedni warunek z warunkiem, aby euro się wzmocniło w ciągu roku. Autor sugeruje min. 10%, ale jakoś nie przekonuje mnie to - nie ma powodu, żeby nie mogłoby być to 5%. Popatrzmy.
I sfiltrujmy też, ale tym razem dołki WIG będą korespondowały z górkami euro.
Niebieskie proste wskazują dołki WIGu, czerwone - górki EUR/PLN. Prawdą jest, że jeżeli maksima euro znajdują się części dodatniej, WIG zaczyna hossę. Tak więc na obecną chwilę warunek ten nie zostaje spełniony, wręcz przeciwnie.
Odwrotna sytuacja, tj. górki na indeksie (niebieska prosta) vs. dołki na walucie (czerwona):
Teraz jasne staje się, dlaczego autor omawianego artykułu pominął kwestię sprzedawania. Obie jego strategie mają potencjał tylko przy znajdowaniu dołków na rynku akcji (tym bardziej należałoby zmienić tytuł). Jako całość nie jest to nawet strategia sensu stricte. Można powiedzieć, że połowicznie poprawna, ale niedokończona. Jeśli chodzi o prognozę na przyszłość, to nie ma jednoznacznego sygnału. Ostatni lokalny dołek przemysłu sugeruje dalsze wzrosty giełdowe, ale nie jest spełniony warunek słabości złotego. W dodatku jeśli przemysł się teraz zagnie i wróci do dołu, to czeka nas co najmniej korekta, a prędzej czy później rynek niedźwiedzia. Wydaje mi się, że lepiej zamiast na polski przemysł spoglądać na zagraniczną produkcję. Na gospodarkę w Polsce wpływa produkcja UE (zob.
Dodatek. Kod w R:
# wykorzystanie funkcji Evan'a Friedlanda (https://stackoverflow.com/questions/6836409/finding-local-maxima-and-minima)
inflect <- function(x, czy_przemysl, threshold){
up <- sapply(1:threshold, function(n) c(x[-(seq(n))], rep(NA, n)))
down <- sapply(-1:-threshold, function(n) c(rep(NA,abs(n)), x[-seq(length(x), length(x) - abs(n) + 1)]))
a <- cbind(x,up,down)
if (czy_przemysl==TRUE) {
list(minima = which(apply(a, 1, min) == a[,1] & a[,1] < 0), maxima = which(apply(a, 1, max) == a[,1] & a[,1] > 0))
} else {
list(minima = which(apply(a, 1, min) == a[,1]), maxima = which(apply(a, 1, max) == a[,1]))
}
}
linieEkstremum <- function(filtr, maks) {
n <- 2
if (deparse(substitute(filtr)) == "filtrPrzemysl") {
czy_przemysl <- TRUE
kolor <- "red"
} else {
czy_przemysl <- FALSE
kolor <- "blue"
}
bottoms <- lapply(1:n, function(th) inflect(filtr, czy_przemysl, threshold = 10)$minima)
tops <- lapply(1:n, function(th) inflect(filtr, czy_przemysl, threshold = 20)$maxima)
for(i in n:n){
if (maks==1) {
#abline(v=tops[[i]], col=kolor)
return(tops[[i]])
} else {
#abline(v=bottoms[[i]], col=kolor)
return(bottoms[[i]])
}
}
}
# potrzebne pakiety
if (require("zoo")==FALSE) {
install.packages("zoo")
}
library(zoo)
if (require("data.table")==FALSE) {
install.packages("data.table")
}
library(data.table)
if (require("lubridate")==FALSE) {
install.packages("lubridate")
}
library(lubridate)
#zamieniam "\" na "/"
sciezka <- r"(C:\Documents\R\testy)"
sciezka <- gsub("\\", "/", sciezka, fixed=T)
setwd(sciezka)
# Przemysł
# link: https://ec.europa.eu/eurostat/databrowser/view/sts_inpr_m__custom_10635116/default/table?lang=en
nazwaPliku1 <- "sts_inpr_m_page_linear.csv"
tabela_przemysl <- fread(nazwaPliku1, tz="")
tabela_przemysl$TIME_PERIOD <- paste0(tabela_przemysl$TIME_PERIOD, "-01")
tabela_przemysl$TIME_PERIOD <- ceiling_date(as.IDate(tabela_przemysl$TIME_PERIOD), "month") - days(1)
tabela_przemysl$TIME_PERIOD <- format(tabela_przemysl$TIME_PERIOD, "%Y-%m")
tabela_przemysl <- tabela_przemysl[, c("TIME_PERIOD", "OBS_VALUE")]
przemysl <- tabela_przemysl[, "OBS_VALUE"]
przemysl_rocznie <- c(rep(NA, 11), rollsum(log(1+przemysl/100)*100, k=12, align="right"))
tabela_przemysl$Przemysl_rocznie <- przemysl_rocznie
tabela_przemysl$SMA3 <- c(rep(NA, 2), round(rollmean(przemysl_rocznie, k=3, align="right"), 4))
# WIG, dane ze stooq.pl
nazwaPliku <- "wig_d.csv"
#tabela_wig <- fread(nazwaPliku, tz="")
# jeżeli jest ostrzeżenie, to prawdopodobnie sa jakieś braki, stosujemy ich wypełnienie
tabela_wig <- fread(nazwaPliku, fill=TRUE, tz="")
tabela_wig$Data <- as.Date(tabela_wig$Data)
# Zakładając, że twoja ramka danych nazywa się tabela_wig, a kolumna z datą to Data
tabela_wig$Data <- as.Date(tabela_wig$Data)
tabela_wig$RokMies <- format(tabela_wig$Data, "%Y-%m")
# Oblicz ostatni dzień każdego miesiąca
ost_dzien_mies_tab <- aggregate(tabela_wig$Data ~ tabela_wig$RokMies, FUN=max)
colnames(ost_dzien_mies_tab) <- c("RokMiesiac", "OstatniaData")
# Wyfiltruj wiersze, gdzie Data to ostatni dzień miesiąca
tabela_wig <- tabela_wig[tabela_wig$Data %in% ost_dzien_mies_tab$OstatniaData, c(1, 5)]
tabela_wig$Data <- format(tabela_wig$Data, "%Y-%m")
# Połączenie danych
dane <- merge(x=tabela_przemysl, y=tabela_wig, by.x="TIME_PERIOD", by.y="Data")
dane <- as.data.frame(na.omit(dane[, c("TIME_PERIOD", "SMA3", "Zamkniecie")]))
colnames(dane) <- c("Okres", "Przemysl_rr", "WIG")
daty <- as.Date(paste0(dane$Okres, "-01"))
rok = year(daty)
mc = month(daty)
przemysl <- ts(dane$Przemysl_rr, start=c(rok[1], mc[1]), frequency=12)
wig <- ts(dane$WIG, start=c(rok[1], mc[1]), frequency=12)
# Wykres
par(mar=c(5, 5, 3, 5))
# Daty, indeks dat
datyWykres <- seq(from=daty[1], to=daty[length(daty)], length.out=year(daty[length(daty)])-year(daty[1]))
indeksWykres <- seq(from=1, to=length(daty), length.out=year(daty[length(daty)])-year(daty[1]))
# Wykres przemysłu
plot(x=index(index(przemysl)), y=przemysl, xlab="Okres", ylab="Dynamika przemysłu (Polska)", lwd=2, xaxt="n", type="l", col="darkred")
axis(side=1, at=indeksWykres, labels=format(datyWykres, "%Y-%m"), cex.axis=0.8)
abline(h=0, col="darkgray")
# Wykres WIG
par(new=TRUE)
plot(x=index(index(przemysl)), y=wig, log="y", axes=FALSE, xlab="", ylab="", col="darkblue", lwd=2, type="l")
axis(side=4)
mtext("WIG", side=4, line=3)
legend(x="topleft", legend=c("Przemysł (Polska)\n log-zmiany % r/r", "WIG"), lty=c(1,1),
lwd=c(2,2), col=c("darkred", "darkblue"), cex=0.7)
# filtr lowess
filtrPrzemysl <- lowess(przemysl, f=0.03)$y
filtrPrzemysl <- ts(filtrPrzemysl, start=c(rok[1], mc[1]), frequency=12)
filtrWig <- lowess(wig, f=0.05)$y
filtrWig <- ts(filtrWig, start=c(rok[1], mc[1]), frequency=12)
# Wykres filtrów
## Filtr przemysłu
plot(x=index(index(filtrPrzemysl)), y=filtrPrzemysl, xlab="Okres", ylab="Filtr przemysłu (Polska)", lwd=2, xaxt="n", type="l", col="darkred")
axis(side=1, at=indeksWykres, labels=format(datyWykres, "%Y-%m"), cex.axis=0.8)
abline(h=0, col="darkgray")
## Filtr WIG
par(new=TRUE)
plot(x=index(index(filtrPrzemysl)), y=filtrWig, log="y", axes=FALSE, xlab="", ylab="", col="darkblue", lwd=2, type="l")
axis(side=4)
mtext("Filtr WIG", side=4, line=3)
## Dołki przemysł
abline(v=linieEkstremum(filtrPrzemysl, maks=0), col="red")
## Dołki WIG
abline(v=linieEkstremum(filtrWig, maks=0), col="blue")
legend(x="topleft", legend=c("Przemysł (Polska)\n log-zmiany % r/r", "WIG"), lty=c(1,1),
lwd=c(2,2), col=c("darkred", "darkblue"), cex=0.5)
## Górki przemysł
abline(v=linieEkstremum(filtrPrzemysl, maks=1), col="red")
## Górki WIG
abline(v=linieEkstremum(filtrWig, maks=1), col="blue")
legend(x="topleft", legend=c("Przemysł (Polska)\n log-zmiany % r/r", "WIG"), lty=c(1,1),
lwd=c(2,2), col=c("darkred", "darkblue"), cex=0.5)
# Zbliżenie
datyWykres <- seq(from=daty[1], to=daty[length(daty)], length.out=length(daty))
indeksWykres <- seq(from=1, to=length(daty), length.out=length(daty))
pocz <- index(index(przemysl))[length(przemysl)-24]
kon <- index(index(przemysl))[length(przemysl)]
plot(x=index(index(przemysl)), y=filtrPrzemysl, xlab="Okres", ylab="Filtr przemysłu (Polska)", lwd=2, xaxt="n", type="l", col="darkred", xlim=c(pocz, kon))
axis(side=1, at=indeksWykres, labels=format(datyWykres, "%Y-%m"), cex.axis=0.8)
abline(h=0, col="grey")
abline(v=indeksWykres, col="grey")
par(new=TRUE)
plot(x=index(index(filtrPrzemysl)), y=filtrWig, log="y", axes=FALSE, xlab="", ylab="", col="darkblue", lwd=2, type="l", xlim=c(pocz, kon))
axis(side=4)
mtext("Filtr WIG", side=4, line=3)
legend(x="bottomleft", legend=c("Przemysł (Polska)\n log-zmiany % r/r", "WIG"), lty=c(1,1),
lwd=c(2,2), col=c("darkred", "darkblue"), cex=0.8)
########
# EUR/PLN, dane ze stooq.pl
nazwaPliku <- "eurpln_d.csv"
#tabela_eur <- fread(nazwaPliku, tz="")
# jeżeli jest ostrzeżenie, to prawdopodobnie sa jakieś braki, stosujemy ich wypełnienie
tabela_eur <- fread(nazwaPliku, fill=TRUE, tz="")
tabela_eur$Data <- as.Date(tabela_eur$Data)
# Zakładając, że twoja ramka danych nazywa się tabela_eur, a kolumna z datą to Data
tabela_eur$Data <- as.Date(tabela_eur$Data)
tabela_eur$RokMies <- format(tabela_eur$Data, "%Y-%m")
# Oblicz ostatni dzień każdego miesiąca
ost_dzien_mies_tab <- aggregate(tabela_eur$Data ~ tabela_eur$RokMies, FUN=max)
colnames(ost_dzien_mies_tab) <- c("RokMiesiac", "OstatniaData")
# Wyfiltruj wiersze, gdzie Data to ostatni dzień miesiąca
tabela_eur <- tabela_eur[tabela_eur$Data %in% ost_dzien_mies_tab$OstatniaData, c(1, 5)]
tabela_eur$Data <- format(tabela_eur$Data, "%Y-%m")
#
eur <- tabela_eur$Zamkniecie
eur_rocznie <- c(rep(NA, 12), 100*diff(eur, lag=12) / head(eur, -12))
tabela_eur$Eur_rocznie <- eur_rocznie
# Połączenie danych
dane <- merge(x=tabela_eur, y=tabela_wig, by="Data")
dane <- as.data.frame(na.omit(dane[, c("Data", "Eur_rocznie", "Zamkniecie.y")]))
colnames(dane) <- c("Okres", "EURPLN_rr", "WIG")
daty <- as.Date(paste0(dane$Okres, "-01"))
rok = year(daty)
mc = month(daty)
eur <- ts(dane$EURPLN_rr, start=c(rok[1], mc[1]), frequency=12)
wig <- ts(dane$WIG, start=c(rok[1], mc[1]), frequency=12)
# Wykres
par(mar=c(5, 5, 3, 5))
# Daty, indeks dat
datyWykres <- seq(from=daty[1], to=daty[length(daty)], length.out=year(daty[length(daty)])-year(daty[1]))
indeksWykres <- seq(from=1, to=length(daty), length.out=year(daty[length(daty)])-year(daty[1]))
# Wykres eurpln
plot(x=index(index(eur)), y=eur, xlab="Okres", ylab="Stopa zmian EUR/PLN", lwd=2, xaxt="n", type="l", col="darkred")
axis(side=1, at=indeksWykres, labels=format(datyWykres, "%Y-%m"), cex.axis=0.8)
abline(h=c(-10, -5, 0, 5, 10), col="darkgray")
par(new=TRUE)
# Wykres WIG
plot(x=index(index(eur)), y=wig, log="y", axes=FALSE, xlab="", ylab="", col="darkblue", lwd=2, type="l")
axis(side=4)
mtext("WIG", side=4, line=3)
legend(x="topleft", legend=c("EUR/PLN zmiany % r/r", "WIG"), lty=c(1,1),
lwd=c(2,2), col=c("darkred", "darkblue"), cex=0.7)
# filtr lowess
filtrEur <- lowess(eur, f=0.03)$y
filtrEur <- ts(filtrEur, start=c(rok[1], mc[1]), frequency=12)
filtrWig <- lowess(wig, f=0.05)$y
filtrWig <- ts(filtrWig, start=c(rok[1], mc[1]), frequency=12)
# Wykres filtrów
## Filtr eur
plot(x=index(index(filtrEur)), y=filtrEur, xlab="Okres", ylab="Filtr zmian EUR/PLN", lwd=2, xaxt="n", type="l", col="darkred")
axis(side=1, at=indeksWykres, labels=format(datyWykres, "%Y-%m"), cex.axis=0.8)
abline(h=0, col="darkgray")
## Filtr WIG
par(new=TRUE)
plot(x=index(index(filtrEur)), y=filtrWig, log="y", axes=FALSE, xlab="", ylab="", col="darkblue", lwd=2, type="l")
axis(side=4)
mtext("Filtr WIG", side=4, line=3)
## Górki eur
abline(v=linieEkstremum(filtrEur, maks=1), col="red")
## Dołki WIG
abline(v=linieEkstremum(filtrWig, maks=0), col="blue")
legend(x="topleft", legend=c("EURPLN zmiany % r/r", "WIG"), lty=c(1,1),
lwd=c(2,2), col=c("darkred", "darkblue"), cex=0.7)
## Dołki eur
abline(v=linieEkstremum(filtrEur, maks=0), col="red")
## Górki WIG
abline(v=linieEkstremum(filtrWig, maks=1), col="blue")
legend(x="topleft", legend=c("EURPLN zmiany % r/r", "WIG"), lty=c(1,1),
lwd=c(2,2), col=c("darkred", "darkblue"), cex=0.7)