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

MQL5 / Inżynieria

Architektura obsługi błędów

Dwa równoległe światy błędów: lokalne kody wykonania i kody zwrotne serwera. Lepkie błędy w GetLastError, ponawianie z odstępem i decyzja, kiedy się poddać.

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

Większość programistów MQL5 traktuje obsługę błędów jako dodatek – parę instrukcji if rzucających Print("Błąd: ", GetLastError()). Podejście to jest akceptowalne w skryptach testowych, ale w profesjonalnym algorytmicznym tradingu (zwłaszcza HFT lub na kontach prop tradingu) brak rygorystycznej architektury obsługi błędów to gwarancja utraty kapitału. Zlecenia nie wysyłają się w próżni; podróżują przez warstwy terminala, sieci VPN, mosty płynności i serwery brokera. Na każdym z tych etapów może dojść do awarii. Zrozumienie, skąd pochodzi błąd i jak na niego zareagować, to cecha wyróżniająca kod produkcyjny od amatorskiego.

1. Dwa Równoległe Wszechświaty Błędów

W MQL5 błędy dzielą się fundamentalnie na dwie kategorie, rządzące się różnymi prawami fizyki. Pomylenie ich to najczęstsza przyczyna błędnej diagnozy:

  • Błędy Środowiska (Runtime / Terminal Errors): Zdarzają się lokalnie. Obejmują błędy pamięci (np. 0xC0000005 Access Violation), błędne parametry funkcji wbudowanych, odwrócone indeksowanie tablic czy brak połączenia z serwerem handlowym. Odczytuje się je funkcją GetLastError().
  • Błędy Serwera (Trade Server Errors): Zdarzają się zdalnie. Terminal poprawnie sformułował żądanie, wysłał je przez sieć, ale serwer brokera je odrzucił (np. brak marży, zbyt bliski Stop Loss, rynek zamknięty). Zwracane są w strukturze MqlTradeResult w polu retcode.
Architektura obsługi błędówDwa równoległe światy błędów: lokalne kody wykonania i kody zwrotne serwera. Lepkie błędy w GetLastError, ponawianie z odstępem i decyzja, kiedy się poddać. Cykl życia zlecenia i geneza błędów EA (Kod MQL5) Weryfikacja Terminala MT5 Odrzucenie GetLastError() Serwer Brokera (Ryzyko/Exec) Odrzucenie result.retcode

2. Dziedzictwo C: Pułapka "Lepkich" Błędów w GetLastError()

Funkcja GetLastError() w MQL5 to wrapper nawiązujący do standardu języka C (errno). Działa ona na zasadzie globalnej zmiennej wątku (thread-local storage). Problem architektoniczny polega na tym, że kod błędu nie resetuje się samoczynnie po udanej operacji. Jeśli w linijce 50 wystąpił błąd odczytu pliku, a w linijce 100 wystąpi błąd pobrania ceny, wywołanie GetLastError() w linijce 101 zwróci błąd z linijki 100. Ale jeśli pobranie ceny w linijce 100 zakończy się sukcesem, wywołanie GetLastError() w linijce 101 nadal zwróci stary błąd z linijki 50.

// ANTYPWZORZEC: Fałszywe alarmy
void OnTick() {
    double sum = 0;
    
    // Załóżmy, że tu wystąpił błąd (np. wskaźnik nie gotowy, błąd 4401)
    double val = iCustom(_Symbol, 0, "MyIndi", 0, 0); 
    
    // Później w kodzie operacja udana (np. pobranie czasu)
    datetime t = TimeCurrent(); 
    
    // Sprawdzamy błąd operacji udanej...
    int err = GetLastError();
    if(err != 0) {
        // TU WEJDZIEMY! Zwróci 4401 z poprzedniej operacji!
        Print("Błąd TimeCurrent? To niemożliwe! Kod: ", err);
    }
}

// WZORZEC: Rygorystyczne czyszczenie stosu
void SafeTick() {
    ResetLastError(); // Złota zasada: Zawsze przed ryzykowną operacją
    double val = iCustom(_Symbol, 0, "MyIndi", 0, 0);
    int err = GetLastError();
    if(err != 0) {
        Print("Rzeczywisty błąd iCustom: ", err);
        return;
    }
    // Dalszy kod
}

3. Anatomia retcode: Co odpowiedział serwer?

Kiedy używasz OrderSend() lub biblioteki CTrade, terminal odsyła strukturę MqlTradeResult. Kluczowe jest pole retcode. Wartości z zakresu 10000-10049 to standardowe kody MQL5, powyżej 10050 to niestandardowe kody zwracane przez infrastrukturę brokera (np. odrzucenia przez algorytmy ryzyka).

Profesjonalny EA nie może zakładać, że brak błędu środowiskowego (GetLastError == 0) oznacza otwarcie pozycji. Poniżej przedstawiam klasyfikację kodów retcode pod kątem akcji, jaką należy podjąć:

Klasyfikacja taktyczna kodów serwera:

  • 10009 (TRADE_RETCODE_DONE) oraz 10008 (TRADE_RETCODE_PLACED) - Sukces. Zlecenie zrealizowane lub umieszczone w arkuszu.
  • 10004 (TRADE_RETCODE_REQUOTE), 10021 (TRADE_RETCODE_NO_MONEY), 10027 (TRADE_RETCODE_AUTOTRADING_DISABLED_BY_SERVER) - Błąd przejściowy (Retryable). Można spróbować ponowić zlecenie po krótkim czasie (zaktualizować cenę dla Requote).
  • 10013 (TRADE_RETCODE_INVALID), 10014 (TRADE_RETCODE_INVALID_VOLUME), 10016 (TRADE_RETCODE_INVALID_STOPS) - Błąd logiczny (Fatal). Nigdy nie ponawiaj ślepo tego zlecenia! Zlecenie jest fundamentalnie źle skonstruowane.
  • 10018 (TRADE_RETCODE_MARKET_CLOSED) - Błąd środowiskowy rynkowego. EA powinien przejść w stan "uśpienia" do otwarcia sesji.

4. Implementacja Architektury Retry (Ponowienia)

W środowisku sieciowym błędy typu Requote (10004) czy połączeniowe (np. GetLastError() zwracający 527 (ERR_TRADE_SEND_FAILED)) są nieuniknione. Ślepe wysyłanie zlecenia w pętli to gwarancja zablokania konta za spam serwera. Należy zaimplementować mechanizm wykładniczego wycofywania (Exponential Backoff).

bool SendOrderWithRetry(double volume, double price) {
    MqlTradeRequest request = {};
    MqlTradeResult result = {};
    
    request.action = TRADE_ACTION_DEAL;
    request.symbol = _Symbol;
    request.volume = volume;
    request.type = ORDER_TYPE_BUY;
    request.price = price;
    request.deviation = 20;
    
    int maxRetries = 3;
    int delayMs = 100;
    
    for(int attempt = 0; attempt < maxRetries; attempt++) {
        ResetLastError();
        bool sent = OrderSend(request, result);
        
        // 1. Sprawdzamy błąd sieciowy/środowiskowy
        if(!sent) {
            int err = GetLastError();
            Print("Próba ", attempt+1, " - Błąd sieciowy: ", err);
            Sleep(delayMs);
            delayMs *= 2; // Wykładnicze opóźnienie (100, 200, 400 ms)
            continue;
        }
        
        // 2. Sprawdzamy odpowiedź serwera (retcode)
        if(result.retcode == TRADE_RETCODE_DONE || result.retcode == TRADE_RETCODE_PLACED) {
            return true; // Sukces!
        }
        else if(result.retcode == TRADE_RETCODE_REQUOTE) {
            Print("Requote. Aktualizuję cenę i ponawiam.");
            request.price = SymbolInfoDouble(_Symbol, SYMBOL_ASK); // Aktualizujemy cenę
            Sleep(delayMs);
            continue;
        }
        else {
            // Błędy fatalne (10013, 10016 itp.) - nie ponawiamy, ostrzegamy usera
            Print("Błąd krytyczny serwera: ", result.retcode, " - ", result.comment);
            return false; 
        }
    }
    Print("Nie udało się wysłać zlecenia po ", maxRetries, " próbach.");
    return false;
}

5. Asynchroniczny Koszmar: Błędy w OnTradeTransaction

Jeśli używasz OrderSendAsync(), cała powyższa logika zmienia się diametralnie. Funkcja asynchroniczna wraca niemal natychmiast. Struktura result zawiera jedynie informację "przyjęto do realizacji" (TRADE_RETCODE_PLACED), a nie informację o faktycznym otwarciu/zamknięciu pozycji.

Rzeczywisty błąd (lub sukces) dotrze do Twojego EA ułamek sekundy później (lub kilka sekund, gdy sieć ma opóźnienia) za pośrednictwem funkcji OnTradeTransaction(). Wymaga to utrzymywania w pamięci tzw. maszyny stanów (State Machine) dla oczekujących zleceń.

ulong pendingTicket = 0;

void OnTradeTransaction(const MqlTradeTransaction& trans, const MqlTradeRequest& request, const MqlTradeResult& result) {
    // Interesują nas tylko zlecenia, które wysłaliśmy
    if(trans.order == pendingTicket) {
        
        if(trans.type == TRADE_TRANSACTION_ORDER_DELETE) {
            // Zlecenie usunięte z systemu (zrealizowane lub odrzucone)
            if(trans.order_state == ORDER_STATE_FILLED) {
                Print("Asynchroniczne zlecenie zrealizowane!");
                pendingTicket = 0;
            }
            else if(trans.order_state == ORDER_STATE_REJECTED) {
                // TUTAJ dociera asynchroniczny błąd serwera!
                // result.retcode zawiera przyczynę odrzucenia.
                Print("Zlecenie asynchroniczne ODRZUCONE! Kod: ", result.retcode);
                pendingTicket = 0;
                
                // Tu możemy podjąć decyzję o ponowieniu lub powiadomieniu użytkownika
            }
        }
    }
}

Podsumowanie

Obsługa błędów w MQL5 to nie dodatek, to rdzeń architektury. Podział na błędy środowiskowe (GetLastError) i serwerowe (retcode) wymusza podwójną weryfikację każdej operacji handlowej. Pamiętaj o "lepkich" błędach i używaj ResetLastError() jak tarczy. Implementuj logikę ponawiania (Retry) tylko dla błędów przejściowych, chroniąc serwer przed spamem, a na koniec – jeśli sięgasz po asynchroniczność – przygotuj się na zarządzanie chaosem zdarzeń w OnTradeTransaction. Kod, który przewiduje awarie sieci i odrzucenia brokera, to system, który przetrwa na koncie rzeczywistym.

Powiązane