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".
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.
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.
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();
}