Przejdź do treści
MQL5 · MetaTrader 5 · po polsku
kod odczarowany

MQL5 / Pułapki

Dlaczego martyngał wygląda w backteście doskonale

Mechanizm, przez który podwajanie pozycji daje idealną krzywą w teście. Plus kod wykrywający ten schemat w cudzym ekspercie, bez dostępu do źródeł.

2 fragmenty kodu z tej strony przeszły przez kompilator MetaEditor build 6090, 2026-08-05.

To nie jest strona o tym, czy martyngał ma sens. To strona o tym, dlaczego raport z testu nie jest w stanie pokazać ryzyka, które ta konstrukcja niesie — i jak to zobaczyć mimo wszystko.

Rozróżnienie jest ważne, bo problem nie leży w strategii, tylko w narzędziu pomiarowym.

Mechanizm

Podwajanie po stracie i siatka zleceń mają wspólną cechę: strata pozostaje niezrealizowana, dopóki cała konstrukcja się nie zamknie. Pozycja, która idzie przeciwko tobie, nie trafia do bilansu — jest tylko obsunięciem kapitału. Gdy cena w końcu wróci, wszystko zamyka się jednocześnie z małym zyskiem.

Krzywa bilansu składa się wtedy wyłącznie z wygranych. Nie dlatego, że nie ma strat, tylko dlatego, że strata jest zapisywana w bilansie dopiero w chwili, gdy przestaje być stratą.

Stąd bierze się wykres, który wygląda jak prosta rosnąca linia — i to jest dokładnie ten wykres, który trafia na zrzuty ekranu.

Dwie różne miary obsunięcia

Tester liczy obie i to jest cała odpowiedź:

MiaraCo mierzyCo pokazuje przy martyngale
obsunięcie bilansuspadki zamkniętych wynikówznikome
obsunięcie kapitałuspadki wraz z pozycjami otwartymiprawdziwe

Raport pokazuje obie, ale wykres domyślnie rysuje bilans. Różnica między nimi jest miarą tego, ile ryzyka konstrukcja ukrywa.

Odrzucenie przebiegów, w których kapitał odjeżdża od bilansu
double OnTester()
  {
   double obsuniecieBilansu = TesterStatistics(STAT_BALANCE_DD);
   double obsuniecieKapitalu = TesterStatistics(STAT_EQUITY_DD);

   if(obsuniecieKapitalu <= 0.0)
      return 0.0;

   //--- ile razy prawdziwe obsunięcie przekracza to widoczne w bilansie
   double ukrycie = obsuniecieKapitalu / MathMax(obsuniecieBilansu, 0.01);

   PrintFormat("obsuniecie bilansu %.2f, kapitalu %.2f, stosunek %.1f",
               obsuniecieBilansu, obsuniecieKapitalu, ukrycie);

   //--- przebieg, w którym kapitał spada wielokrotnie głębiej niż bilans,
   //--- nie zasługuje na miejsce w tabeli wyników optymalizacji
   if(ukrycie > 5.0)
      return 0.0;

   return TesterStatistics(STAT_PROFIT) / obsuniecieKapitalu;
  }

Czego tester nie modeluje

Nawet obsunięcie kapitału nie oddaje pełnego ryzyka, bo tester upraszcza trzy rzeczy, które przy takiej konstrukcji decydują o wszystkim:

Wezwanie do uzupełnienia depozytu. Tester zamyka pozycje przy poziomie stop out, ale robi to w warunkach idealnych: bez opóźnienia, bez poślizgu, po cenie z historii. Na rachunku zamknięcie przymusowe odbywa się w chwili największej zmienności i po najgorszych cenach dnia.

Rozszerzenie spreadu. Przy siatce liczba otwartych pozycji rośnie dokładnie wtedy, gdy rynek się rozjeżdża. Tester nalicza spread z historii albo stały — nie modeluje tego, że przy dziesiątej pozycji koszt wejścia bywa kilkukrotnie wyższy.

Limity brokera. Limit wolumenu, limit liczby pozycji i limit zleceń oczekujących w teście zwykle nie istnieją. Na rachunku siatka zatrzymuje się w połowie i to jest najgorszy możliwy moment.

Rozpoznanie w cudzym ekspercie

Kupiony ekspert nie ma źródeł, ale historia rachunku ma wszystko, czego trzeba. Martyngał zostawia charakterystyczny ślad: wolumen rośnie po stracie.

Szuka rosnących wolumenów po stratnych transakcjach
void SprawdzSchematWolumenu(const ulong magic, const int dniWstecz)
  {
   if(!HistorySelect(TimeCurrent() - dniWstecz * 24 * 3600, TimeCurrent()))
     {
      Print("Nie udalo sie wczytac historii.");
      return;
     }

   double poprzedniWolumen = 0.0;
   double poprzedniZysk = 0.0;
   int podwojen = 0;
   int wejsc = 0;

   for(int i = 0; i < HistoryDealsTotal(); i++)
     {
      ulong bilet = HistoryDealGetTicket(i);
      if(bilet == 0)
         continue;
      if(HistoryDealGetInteger(bilet, DEAL_MAGIC) != (long)magic)
         continue;
      if(HistoryDealGetInteger(bilet, DEAL_ENTRY) != DEAL_ENTRY_IN)
         continue;

      double wolumen = HistoryDealGetDouble(bilet, DEAL_VOLUME);
      wejsc++;

      //--- wejscie wiekszym wolumenem tuz po stracie
      if(poprzedniZysk < 0.0 && wolumen > poprzedniWolumen * 1.5)
         podwojen++;

      poprzedniWolumen = wolumen;
      poprzedniZysk = HistoryDealGetDouble(bilet, DEAL_PROFIT);
     }

   PrintFormat("wejsc: %d, w tym po stracie z powiekszonym wolumenem: %d (%.0f%%)",
               wejsc, podwojen, wejsc > 0 ? 100.0 * podwojen / wejsc : 0.0);
  }

Wynik powyżej kilkunastu procent oznacza, że wielkość pozycji zależy od wyniku poprzedniej transakcji, a nie od ryzyka. Nie przesądza to o niczym — przesądza o tym, że raport z bilansu nie opisuje tej strategii.

Powiązane