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

MQL5 / Pułapki

Kolejka zdarzeń i zamrożony wykres

Ekspert, wskaźnik i wykres dzielą kolejkę zdarzeń. Długa operacja w jednym miejscu zatrzymuje wszystko pozostałe.

1 fragment kodu z tej strony przeszedł przez kompilator MetaEditor build 6090, 2026-08-05.

Nowicjusze w MQL5 często zakładają, że skoro terminal obsługuje wiele wykresów, z których każdy ma własne EA, to system jest wielowątkowy. Nowsi programiści mogą myśleć, że OnTimer() działa w tle jako osobny wątek (jak w C# czy Javie). Nic bardziej mylnego. Architektura MQL5 dla jednego wykresu jest rygorystycznie jednowątkowa (Single-Threaded Event Loop).

Kolejka Zdarzeń: Wszystko idzie kolejno

Terminal posiada dla każdego wykresu jedną kolejkę zdarzeń. Ticki, wywołania timera, zdarzenia handlowe (OnTrade), a nawet kliknięcia myszką na wykresie (OnChartEvent) są wrzucane do tej samej rury i przetwarzane sekwencyjnie. Jeśli OnTick() wykonuje się przez 2 sekundy (np. w wyniku ciężkiej optymalizacji lub usypiania pętli), OnTimer() nie zostanie wywołany w tym czasie, a kliknięcie na przycisk na wykresie zignorowane lub drastycznie opóźnione. Wykres po prostu się "zawiesi".

Kolejka zdarzeń i zamrożony wykresEkspert, wskaźnik i wykres dzielą kolejkę zdarzeń. Długa operacja w jednym miejscu zatrzymuje wszystko pozostałe. Jednowątkowa pętla zdarzeń MQL5 Tick Timer Click Kolejka Zdarzeń Wątek Główny OnTick() (Zablokowany!) Gdy OnTick trwa długo, zdarzenia czekają w kolejce. Kliknięcia na wykresie nie są rejestrowane.

Funkcja Sleep() to zło w OnTick

Częstym błędem osób tworzących np. strategie sieciowe (Grid/Martingale), które chcą odczekać 5 sekund po otwarciu pozycji, jest użycie Sleep(5000) wewnątrz OnTick(). Choć Sleep() w MQL5 zatrzymuje EA bez pożerania 100% procesora (oddaje czas systemowi), to całkowicie blokuje kolejkę zdarzeń tego wykresu. W tym czasie EA nie zareaguje na nowe ticki, przyciski na wykresie nie będą działać, a wskaźniki na tym wykresie się nie przeliczą.

Rozwiązaniem tego problemu jest przejście na programowanie asynchroniczne oparte na "maszynie stanów" (State Machine) i wykorzystanie OnTimer() jako systemu odmierzania czasu bez blokowania wątku.

Blokowanie wątku głównego
void OnTick()
  {
   if(ConditionToBuy())
     {
      WyslijZlecenie();
      Sleep(5000);          // zawiesza wykres i wszystkie wskaźniki symbolu
      DoSomethingElse();
     }
  }

Poprawna wersja nie czeka, tylko zapamiętuje, na co czeka, i wraca do terminala. Odmierzanie czasu przejmuje timer.

Ta sama logika jako maszyna stanów
enum E_STATE { STATE_IDLE, STATE_WAITING_AFTER_BUY };

E_STATE  currentState  = STATE_IDLE;
datetime waitStartTime = 0;

void OnTick()
  {
   if(currentState != STATE_IDLE)
      return;

   if(!ConditionToBuy())
      return;

   WyslijZlecenie();
   currentState  = STATE_WAITING_AFTER_BUY;
   waitStartTime = TimeCurrent();

   EventSetTimer(1);        // dalsze sprawdzanie przejmuje timer
  }

void OnTimer()
  {
   if(currentState != STATE_WAITING_AFTER_BUY)
      return;

   if(TimeCurrent() - waitStartTime < 5)
      return;               // pięć sekund jeszcze nie minęło

   DoSomethingElse();
   currentState = STATE_IDLE;
   EventKillTimer();
  }