Mirki i Mirabelki, wracam z updatem, tym razem z gatunku "sam sobie strzeliłem w kolano" ( ͡° ͜ʖ ͡°)
Strona /backtest dumnie głosiła "strategia bije benchmark w 97% okien"
No to wziąłem pod nóż własne przemyślenia z backtestu i ten wynik się nie obronił.
Zaczęło się od pytań @Blomex o overfitting, look ahead i brak prawdziwego out-of-sample. Wtedy dorzuciłem tylko sekcję QA, ale...
Wyszło grubo.
1. Co było nie tak
Stary wynik pochodził z pierwszej rekonstrukcji danych: bez prowizji, bez filtrów, które realnie działają w moim scoringu.
Przeliczyłem wszystko od nowa, na pełnych 10 latach, z histerezą i trybem buy-only(prosty filtr gdy bessa tylko kupujemy), przy prowizji 0,5%.
Nowy wynik: strategia bije WIG140TR w "mniejszości" startów walk forward.
Ważne zastrzeżenie, które sam sobie musiałem wbić do głowy -> 97% i dzisiejsze liczby to nie jest ten sam eksperyment
Zmieniły się dane, filtry i uniwersum.
To pokazuje rozjazd między przeliczeniami, a nie prosty "spadek skuteczności z 97% do x%".
2. Potem znalazłem błąd w sposobie liczenia, który działał PRZECIWKO - To jest chyba najciekawsza część całej historii.
Portfel płacił prowizję i podatek Belki, a benchmark nie płacił nic.
Do tego Belkę naliczałem przy każdej sprzedaży, choć realnie rozlicza się ją raz w roku.
Wpływ podatku na wynik był około dwa razy większy niż prowizji, ale ponad połowa tego efektu wynikała ze sposobu liczenia, a nie z realnego kosztu podatkowego
Poprawiłem i NIE zmieniałem strategii.
I wynik walk-forward poprawił się z około 30% do 49% wygranych startów
Czyli nie przegrywa 70:30 -> Bardziej remis
(Mediana nadal jest ujemna, więc nie jest to dowód przewagi)
Ale pokazuje coś niewygodnego -> zanim zaczniesz szukać przewagi, jebnij się w łeb i sprawdź, czy Twój pomiar nie kłamie w którąś stronę
3. Dwa kolejne błędy, tym razem w danych
Momentum w danych historycznych liczyłem na cenach bez korekty o dywidendy.
Spółka dywidendowa dostawała więc sztuczny minus w dniu odcięcia.
Naprawione.
Historia liczy teraz zwrot całkowity.
Skład Top 10 zmienił się w około 1/3 tygodni, zwykle o jedną spółkę.
I ten najgłupszy:
backtest oceniał spółkę, którą mam wykluczoną (GPW, bo jest notowana na własnej giełdzie => dochodziła w historii nawet do #2)
Też poprawione.
4. Bear-extra, czyli jak obaliłem własny pomysł
Dopłata w bessie - gdy WIG140 jest poniżej SMA30 - wyglądała świetnie:
"100% startów powyżej benchmarku"
Tylko że dodatkowy kapitał szedł do portfela, a do benchmarku nie...
Zrobiłem więc test kontrolny.
Ten sam dodatkowy kapitał, ale rozłożony równo w czasie, bez żadnego sygnału.
Wynik:
+3012 zł i 100% startów vs. bear-extra: +2324 zł i 96% startów
Czyli działał sam dodatkowy kapitał, a nie timing.
Hipoteza odrzucona.
I właśnie takie eksperymenty lubię najbardziej.
Bo zamiast znaleźć kolejny powód, dla którego strategia jest zajebista, znalazłem powód, dla którego jeden z jej elementów nie jest potrzebny.
5. Rzecz, która mnie najbardziej zastanowiła
Sprawdziłem, jak wynik rozkłada się na lata.
I okazało się, że niemal cała przewaga widoczna na jednej dziesięcioletniej ścieżce pochodzi z 2020 roku
Po wycięciu 2020 roku z tej konkretnej ścieżki różnica względem benchmarku dramatycznie maleje - z około 500pp. do kilku pp...
I tutaj ważne zastrzeżenie, żeby nie było sprzeczności z punktem 2:
Od stycznia 2017 do dziś:
Walk-forward uruchamia strategię ze 120 różnych dat startowych.
Większość tych startów nie łapie całego efektu 2020.
Dlatego pojedyncza krzywa może wyglądać kosmicznie, a wiele niezależnych startów mówi:
"spokojnie, nie mamy jeszcze dowodu"
To nie znaczy, że strategia nie działa.
Znaczy tylko, że historyczna przewaga jest mocno zależna od konkretnego reżimu rynkowego - w tym przypadku krachu i gwałtownego odbicia.
Średnia z jednej długiej ścieżki może to zamaskować i stworzyć wrażenie stabilnej przewagi.
A stabilnej przewagi jeszcze nie potwierdziłem...
6. Co jest teraz na stronie
Strona to już nie prezentacja wyników, tylko "dziennik walidacji".
Status na górze - bez owijania
Tabela hipotez ze statusami
Znane ograniczenia bez chowania ich w stopce
Stary wynik z 97% zostaje, ale jest oznaczony jako "nieaktualny" <- kasowanie niewygodnych liczb to słaby sport ;)
7. Co dalej, czyli jedyna uczciwa odpowiedź
Backtest odpowiedział na wszystko, na co mógł, chyba?
Rekomendacje analityków mam w danych od 2025 roku, ESPI od lipca 2026.
Przez większość historycznego okresu testuję więc głównie "rdzeń", a nie dokładnie tę samą formułę, która działa dzisiaj.
Żaden kolejny przebieg tego nie naprawi.
Dlatego uruchomiłem "mechaniczny" portfel testowy -> te same reguły, zero uznaniowości, wycena co tydzień, zapisywana od dziś.
Za rok też nie będę pisał "udowodniłem przewagę", bo przy tym rozrzucie rok to nadal za mało.
Ale zacznę mieć dane, które mogą odpowiedzieć na pytania, na które historia już nie odpowie.
Najważniejsze pytanie całego projektu jest dla mnie teraz bardzo proste:
"Czy warto aktywnie zarządzać portfelem według GPW Console, czy lepiej po prostu kupić ETF / indeks i mieć święty spokój?"
Jeżeli odpowiedź ostatecznie będzie brzmiała: "kup ETF i idź na p--o" -> też będę zadowolony.
Bo właśnie po to robię ten eksperyment.
Nie po to, żeby udowodnić, że moja strategia działa.
Tylko żeby sprawdzić, czy w ogóle jest sens aktywnie kombinować zamiast po prostu kupić rynek.
Backtest nie ma udowodnić, że strategia działa; Ma próbować ją obalić!
https://gpwconsole.pl/backtest
Wielkie dzięki @Blomex, bo to Twoje pytania uruchomiły całą tę rozbiórkę.
Gdyby nie one, do dziś miałbym na stronie ładne 97% i spokojne sumienie ( ͡° ͜ʖ ͡°)
Dzięki też wszystkim, którzy czepiali się metodyki w poprzednich wpisach - z tego wychodzą najlepsze poprawki.
#gielda #gpw #programowanie #inwestowanie #tworczoscwlasna #gpwconsole
PS. Jeśli ktoś chce się przyczepić do metodyki - na stronie jest sekcja "co dalej" z listą rzeczy, które sam uważam za niedokończone
Krytyka mile widziana. Na tym to polega i na to liczę ;)
Strona /backtest dumnie głosiła "strategia bije benchmark w 97% okien"
No to wziąłem pod nóż własne przemyślenia z backtestu i ten wynik się nie obronił.
Zaczęło się od pytań @Blomex o overfitting, look ahead i brak prawdziwego out-of-sample. Wtedy dorzuciłem tylko sekcję QA, ale...
Wyszło grubo.
1. Co było nie tak
Stary wynik pochodził z pierwszej rekonstrukcji danych: bez prowizji, bez filtrów, które realnie działają w moim scoringu.
Przeliczyłem wszystko od nowa, na pełnych 10 latach, z histerezą i trybem buy-only(prosty filtr gdy bessa tylko kupujemy), przy prowizji 0,5%.
Nowy wynik: strategia bije WIG140TR w "mniejszości" startów walk forward.
Ważne zastrzeżenie, które sam sobie musiałem wbić do głowy -> 97% i dzisiejsze liczby to nie jest ten sam eksperyment
Zmieniły się dane, filtry i uniwersum.
To pokazuje rozjazd między przeliczeniami, a nie prosty "spadek skuteczności z 97% do x%".
2. Potem znalazłem błąd w sposobie liczenia, który działał PRZECIWKO - To jest chyba najciekawsza część całej historii.
Portfel płacił prowizję i podatek Belki, a benchmark nie płacił nic.
Do tego Belkę naliczałem przy każdej sprzedaży, choć realnie rozlicza się ją raz w roku.
Wpływ podatku na wynik był około dwa razy większy niż prowizji, ale ponad połowa tego efektu wynikała ze sposobu liczenia, a nie z realnego kosztu podatkowego
Poprawiłem i NIE zmieniałem strategii.
I wynik walk-forward poprawił się z około 30% do 49% wygranych startów
Czyli nie przegrywa 70:30 -> Bardziej remis
(Mediana nadal jest ujemna, więc nie jest to dowód przewagi)
Ale pokazuje coś niewygodnego -> zanim zaczniesz szukać przewagi, jebnij się w łeb i sprawdź, czy Twój pomiar nie kłamie w którąś stronę
3. Dwa kolejne błędy, tym razem w danych
Momentum w danych historycznych liczyłem na cenach bez korekty o dywidendy.
Spółka dywidendowa dostawała więc sztuczny minus w dniu odcięcia.
Naprawione.
Historia liczy teraz zwrot całkowity.
Skład Top 10 zmienił się w około 1/3 tygodni, zwykle o jedną spółkę.
I ten najgłupszy:
backtest oceniał spółkę, którą mam wykluczoną (GPW, bo jest notowana na własnej giełdzie => dochodziła w historii nawet do #2)
Też poprawione.
4. Bear-extra, czyli jak obaliłem własny pomysł
Dopłata w bessie - gdy WIG140 jest poniżej SMA30 - wyglądała świetnie:
"100% startów powyżej benchmarku"
Tylko że dodatkowy kapitał szedł do portfela, a do benchmarku nie...
Zrobiłem więc test kontrolny.
Ten sam dodatkowy kapitał, ale rozłożony równo w czasie, bez żadnego sygnału.
Wynik:
+3012 zł i 100% startów vs. bear-extra: +2324 zł i 96% startów
Czyli działał sam dodatkowy kapitał, a nie timing.
Hipoteza odrzucona.
I właśnie takie eksperymenty lubię najbardziej.
Bo zamiast znaleźć kolejny powód, dla którego strategia jest zajebista, znalazłem powód, dla którego jeden z jej elementów nie jest potrzebny.
5. Rzecz, która mnie najbardziej zastanowiła
Sprawdziłem, jak wynik rozkłada się na lata.
I okazało się, że niemal cała przewaga widoczna na jednej dziesięcioletniej ścieżce pochodzi z 2020 roku
Po wycięciu 2020 roku z tej konkretnej ścieżki różnica względem benchmarku dramatycznie maleje - z około 500pp. do kilku pp...
I tutaj ważne zastrzeżenie, żeby nie było sprzeczności z punktem 2:
Od stycznia 2017 do dziś:
Walk-forward uruchamia strategię ze 120 różnych dat startowych.
Większość tych startów nie łapie całego efektu 2020.
Dlatego pojedyncza krzywa może wyglądać kosmicznie, a wiele niezależnych startów mówi:
"spokojnie, nie mamy jeszcze dowodu"
To nie znaczy, że strategia nie działa.
Znaczy tylko, że historyczna przewaga jest mocno zależna od konkretnego reżimu rynkowego - w tym przypadku krachu i gwałtownego odbicia.
Średnia z jednej długiej ścieżki może to zamaskować i stworzyć wrażenie stabilnej przewagi.
A stabilnej przewagi jeszcze nie potwierdziłem...
6. Co jest teraz na stronie
Strona to już nie prezentacja wyników, tylko "dziennik walidacji".
Status na górze - bez owijania
Tabela hipotez ze statusami
Znane ograniczenia bez chowania ich w stopce
Stary wynik z 97% zostaje, ale jest oznaczony jako "nieaktualny" <- kasowanie niewygodnych liczb to słaby sport ;)
7. Co dalej, czyli jedyna uczciwa odpowiedź
Backtest odpowiedział na wszystko, na co mógł, chyba?
Rekomendacje analityków mam w danych od 2025 roku, ESPI od lipca 2026.
Przez większość historycznego okresu testuję więc głównie "rdzeń", a nie dokładnie tę samą formułę, która działa dzisiaj.
Żaden kolejny przebieg tego nie naprawi.
Dlatego uruchomiłem "mechaniczny" portfel testowy -> te same reguły, zero uznaniowości, wycena co tydzień, zapisywana od dziś.
Za rok też nie będę pisał "udowodniłem przewagę", bo przy tym rozrzucie rok to nadal za mało.
Ale zacznę mieć dane, które mogą odpowiedzieć na pytania, na które historia już nie odpowie.
Najważniejsze pytanie całego projektu jest dla mnie teraz bardzo proste:
"Czy warto aktywnie zarządzać portfelem według GPW Console, czy lepiej po prostu kupić ETF / indeks i mieć święty spokój?"
Jeżeli odpowiedź ostatecznie będzie brzmiała: "kup ETF i idź na p--o" -> też będę zadowolony.
Bo właśnie po to robię ten eksperyment.
Nie po to, żeby udowodnić, że moja strategia działa.
Tylko żeby sprawdzić, czy w ogóle jest sens aktywnie kombinować zamiast po prostu kupić rynek.
Backtest nie ma udowodnić, że strategia działa; Ma próbować ją obalić!
https://gpwconsole.pl/backtest
Wielkie dzięki @Blomex, bo to Twoje pytania uruchomiły całą tę rozbiórkę.
Gdyby nie one, do dziś miałbym na stronie ładne 97% i spokojne sumienie ( ͡° ͜ʖ ͡°)
Dzięki też wszystkim, którzy czepiali się metodyki w poprzednich wpisach - z tego wychodzą najlepsze poprawki.
#gielda #gpw #programowanie #inwestowanie #tworczoscwlasna #gpwconsole
PS. Jeśli ktoś chce się przyczepić do metodyki - na stronie jest sekcja "co dalej" z listą rzeczy, które sam uważam za niedokończone
Krytyka mile widziana. Na tym to polega i na to liczę ;)
Filipterka25
2026-09-13 21:35:41
+1
Do tego Belkę naliczałem przy każdej sprzedaży, choć realnie rozlicza się ją raz w roku.
@gpw-console: to akurat dobry odruch - robienie rezerw podatkowych...
Filipterka25
2026-09-13 23:14:57
+1
@gpw-console: no nie, chwila - to akurat nie był błąd jakiś duży. Finalnie kwota podatku znika, ale przed zniknięciem może pracować.
Jak chcesz to uwzględnić w modelu, to albo raz na rok mnożysz wynik razy 0,81, albo jakoś podobnie. Grunt, żeby rezerwa NIE była narażona na straty pomiędzy 31 grudniem a terminem płatności.
Jak chcesz to uwzględnić w modelu, to albo raz na rok mnożysz wynik razy 0,81, albo jakoś podobnie. Grunt, żeby rezerwa NIE była narażona na straty pomiędzy 31 grudniem a terminem płatności.
Blomex
2026-09-14 15:02:28
+1
@gpw-console: fajnie, że przeczytałeś pytania i wyciągnąłeś jakieś wnioski. No i że przy okazji znalazłeś inne buble ( ͡° ͜ʖ ͡°)
gpw-console
[OP]
2026-09-13 21:48:21
+0
@Filipterka25: Dokładnie :D Sam mechanizm jest okej - błędem było traktowanie podatku tak, jakby znikał z portfela przy każdej sprzedaży
gpw-console
[OP]
2026-09-14 10:54:47
+0
@Filipterka25:
Sprawdziłem i muszę Ci podziękować, bo wyszło ciekawiej, niż się spodziewałem - przy okazji znalazłem grubszy błąd niż sam termin płatności...
Poszedłem dodać do modelu opcję rozliczenia w kwietniu zamiast 1 stycznia i napisałem do tego test. Test od razu pokazał, że w moim scenariuszu podatek w ogóle nie schodzi z portfela. Zajrzałem w kod i było:
cash = max(0, cash - podatek)
Czyli jeśli portfel jest w całości zainwestowany i gotówki jest zero, a to normalny stan zaraz po rozdzieleniu kasy - zobowiązanie po prostu znikało. Nie było przeniesione, nie było długiem - znikało.
Na realnych danych to było 26,6k w przebiegu produkcyjnym i 54k przy kapitale jednorazowym.
Najgorsze, że błąd siedział dokładnie w trybie, który miał być bardziej realistyczny. 😅
Efekt na liczbę, którą podałem we wpisie: po poprawce walk-forward wygrywa nie w 49%, tylko w 45% startów, a mediana alfy spada z −123 do −348 zł.
Czyli korekta idzie przeciwko mnie i tak ma być. Poprawię to na stronie.
Co do propozycji mnożenia przez 0,81 - tego bym nie robił, bo opodatkowałoby całą wartość portfela zamiast faktycznie zrealizowanego zysku i pominęłoby rozliczanie strat. Sprawdziłem mój sposób liczenia osobno względem rocznego netowania i rozjazd wyszedł około 0,6%, czyli w tym przypadku pomijalny.
Twój główny punkt dotyczący rezerwy jest trafiony: mój model zabierał podatek 1 stycznia, czyli konserwatywnie. Ale z ciekawości dodałem flagę na ustawowy termin, na tym samym okresie daje to około +15k - wiadomo kasa pracuje dłużej
Domyślnie zostawiam jednak 1 stycznia, bo wolę być po bezpiecznej stronie, trzymanie rezerwy w akcjach do kwietnia może być ryzykowne.
Dzięki! To już nie pierwszy raz kiedy czyjeś pytanie wyciąga błąd, którego sam bym nie zobaczył. ( ͡° ͜ʖ ͡°)
Sprawdziłem i muszę Ci podziękować, bo wyszło ciekawiej, niż się spodziewałem - przy okazji znalazłem grubszy błąd niż sam termin płatności...
Poszedłem dodać do modelu opcję rozliczenia w kwietniu zamiast 1 stycznia i napisałem do tego test. Test od razu pokazał, że w moim scenariuszu podatek w ogóle nie schodzi z portfela. Zajrzałem w kod i było:
cash = max(0, cash - podatek)
Czyli jeśli portfel jest w całości zainwestowany i gotówki jest zero, a to normalny stan zaraz po rozdzieleniu kasy - zobowiązanie po prostu znikało. Nie było przeniesione, nie było długiem - znikało.
Na realnych danych to było 26,6k w przebiegu produkcyjnym i 54k przy kapitale jednorazowym.
Najgorsze, że błąd siedział dokładnie w trybie, który miał być bardziej realistyczny. 😅
Efekt na liczbę, którą podałem we wpisie: po poprawce walk-forward wygrywa nie w 49%, tylko w 45% startów, a mediana alfy spada z −123 do −348 zł.
Czyli korekta idzie przeciwko mnie i tak ma być. Poprawię to na stronie.
Co do propozycji mnożenia przez 0,81 - tego bym nie robił, bo opodatkowałoby całą wartość portfela zamiast faktycznie zrealizowanego zysku i pominęłoby rozliczanie strat. Sprawdziłem mój sposób liczenia osobno względem rocznego netowania i rozjazd wyszedł około 0,6%, czyli w tym przypadku pomijalny.
Twój główny punkt dotyczący rezerwy jest trafiony: mój model zabierał podatek 1 stycznia, czyli konserwatywnie. Ale z ciekawości dodałem flagę na ustawowy termin, na tym samym okresie daje to około +15k - wiadomo kasa pracuje dłużej
Domyślnie zostawiam jednak 1 stycznia, bo wolę być po bezpiecznej stronie, trzymanie rezerwy w akcjach do kwietnia może być ryzykowne.
Dzięki! To już nie pierwszy raz kiedy czyjeś pytanie wyciąga błąd, którego sam bym nie zobaczył. ( ͡° ͜ʖ ͡°)
