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

MQL5 / Pułapki

CTrade działa u jednego brokera, u drugiego nie

Klasa CTrade używa domyślnych ustawień, których część serwerów nie przyjmuje. Tryb wypełnienia, krok wolumenu i dewiacja dobierane z instrumentu zamiast wpisywane na sztywno.

Kod z tej strony skompilowany i uruchomiony: MetaEditor build 6090, sprawdzone 2026-08-03.

Większość programistów MQL5 szybko odkrywa bibliotekę standardową i klasę CTrade. To niesamowicie wygodne narzędzie. Zamiast pisać dziesiątki linijek kodu wypełniającego strukturę MqlTradeRequest i obsługującego OrderSend, wystarczy napisać trade.Buy(0.1, _Symbol) i gotowe. W testerze strategii oraz na koncie demo u ulubionego brokera wszystko działa jak zegarek. Problem pojawia się, gdy wdrażasz ten sam kod na koncie u innego brokera (lub na innym instrumencie, np. przechodząc z EURUSD na akcje Tesla). Nagle logi zapełniają się błędami: Unsupported filling mode (10030) lub Invalid volume (10014).

Dlaczego tak się dzieje? Odpowiedź leży w architekturze handlowej MQL5. Klasa CTrade nie jest magiczna; pod spodem używa tych samych struktur co Ty. Jeśli nie skonfigurujesz jej jawnie, używa domyślnych wartości, które mogą nie być obsługiwane przez serwer konkretnego brokera. Średniozaawansowany programista musi wiedzieć, jak dostosować EA do środowiska, w którym działa.

1. Tajemnica "Unsupported filling mode" (Błąd 10030)

Każde zlecenie w MQL5 musi mieć określony tryb wypełniania (Filling Mode). Określa on, co ma się stać, jeśli broker nie jest w stanie zrealizować całego wolumenu od razu. MQL5 definiuje trzy główne tryby:

  • ORDER_FILLING_FOK (Fill or Kill) – całość natychmiast albo wcale. Jeśli chcesz kupić 10 lotów, a w arkuszu zleceń jest tylko 5, zlecenie zostanie odrzucone.
  • ORDER_FILLING_IOC (Immediate or Cancel) – kup tyle, ile się da od razu, resztę anuluj.
  • ORDER_FILLING_RETURN – zlecenie zostaje w systemie i czeka na realizację po cenie rynkowej (zwykłe zlecenia oczekujące).

Tutaj pojawia się pułapka. Nie każdy broker obsługuje każdy tryb. Broker A może obsługiwać tylko FOK, a Broker B tylko IOC. Jeśli klasa CTrade ma ustawiony domyślnie tryb FOK, a serwer Brokerów B go nie obsługuje, każde wywołanie trade.Buy() zwróci błąd 10030. Co gorsza, różne instrumenty u tego samego brokera mogą obsługiwać różne tryby!

CTrade działa u jednego brokera, u drugiego nieKlasa CTrade używa domyślnych ustawień, których część serwerów nie przyjmuje. Tryb wypełnienia, krok wolumenu i dewiacja dobierane z instrumentu zamiast wpisywane na sztywno. Dopasowanie trybu wypełniania do środowiska brokera EA (CTrade) Wysyła: FOK Broker A Obsługuje FOK -> Zlecenie OK Broker B Brak FOK! Błąd 10030 Rozwiązanie: Odczytaj z serwera obsługiwany tryb przed wysłaniem zlecenia!

Rozwiązanie: Dynamiczny wybór trybu wypełniania

Zamiast na sztywno ustawiać tryb w OnInit, musimy zapytać serwer o to, jakich trybów wymaga dany symbol. Służy do tego funkcja SymbolInfoInteger(_Symbol, SYMBOL_FILLING_MODE). Zwraca ona maskę bitową (flags), którą musimy rozkodować.

#include <Trade\Trade.mqh>
CTrade trade;

int OnInit() {
    // 1. Pobranie dozwolonych trybów wypełniania dla bieżącego symbolu
    int filling = (int)SymbolInfoInteger(_Symbol, SYMBOL_FILLING_MODE);
    
    // 2. Ustalenie odpowiedniego trybu dla obiektu CTrade
    ENUM_ORDER_TYPE_FILLING useFilling = ORDER_FILLING_RETURN; // domyślnie
    
    // Sprawdzamy bity (flagi) za pomocą operatora bitowego AND (&)
    if((filling & SYMBOL_FILLING_FOK) == SYMBOL_FILLING_FOK) {
        useFilling = ORDER_FILLING_FOK;
    } else if((filling & SYMBOL_FILLING_IOC) == SYMBOL_FILLING_IOC) {
        useFilling = ORDER_FILLING_IOC;
    }
    
    // 3. Ustawienie trybu w klasie CTrade
    trade.SetTypeFilling(useFilling);
    Print("Ustawiono tryb wypełniania: ", EnumToString(useFilling));
    
    return(INIT_SUCCEEDED);
}

2. Błąd "Invalid volume" (10014) i normalizacja lotów

Kolejny błąd wynika z faktu, że każdy instrument (i broker) ma własne reguły dotyczące wielkości pozycji. Programista oblicza wielkość pozycji na podstawie zarządzania ryzykiem, np. wychodzi mu, że ma otworzyć 0.10354 lota. Wywołuje trade.Buy(0.10354). Na koncie FOREX, gdzie krokiem (Volume Step) jest 0.01, to zadziała (zostanie zaokrąglone). Ale na koncie CFD lub Futures, gdzie minimalnym krokiem jest 0.1 lub nawet 1.0 lot, serwer odrzuci zlecenie kodem 10014. Numer 131 spotykany w starszych poradnikach pochodzi z MQL4 i w MetaTraderze 5 nie występuje.

Konieczność normalizacji wolumenu

Zanim wyślesz zlecenie, wolumen musi zostać "sprowadzony" do dozwolonych granic. MQL5 wymaga tu ręcznej interwencji. Musisz sprawdzić trzy parametry symbolu: Minimalny wolumen (SYMBOL_VOLUME_MIN), Maksymalny wolumen (SYMBOL_VOLUME_MAX) oraz krok wolumenu (SYMBOL_VOLUME_STEP).

// Funkcja normalizująca wielkość pozycji do wymogów brokera
double NormalizeVolume(double desiredVolume) {
    double minLot  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
    double maxLot  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
    double stepLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
    
    if(stepLot <= 0) return minLot; // Zabezpieczenie przed dzieleniem przez zero
    
    // 1. Zaokrąglenie w dół do najbliższego kroku
    double normalizedVolume = MathFloor(desiredVolume / stepLot) * stepLot;
    
    // 2. Sprawdzenie limitów
    if(normalizedVolume < minLot) normalizedVolume = minLot;
    if(normalizedVolume > maxLot) normalizedVolume = maxLot;
    
    // 3. Oczyszczenie błędów zmiennoprzecinkowych (np. 0.10000000001 -> 0.1)
    // Używamy 2 cyfr po przecinku dla FOREX, ale bezpieczniej jest policzyć miejsca
    int digits = (int)MathCeil(MathLog10(1.0 / stepLot));
    normalizedVolume = NormalizeDouble(normalizedVolume, digits);
    
    return normalizedVolume;
}

void OpenPosition(double riskPercentage) {
    double calcLot = CalculateLotFromRisk(riskPercentage); // Twoja funkcja matematyczna
    double finalLot = NormalizeVolume(calcLot);
    
    // Teraz to jest w 100% bezpieczne wywołanie
    if(trade.Buy(finalLot, _Symbol)) {
        Print("Otwarto pozycję o wielkości: ", finalLot);
    } else {
        Print("Błąd otwarcia: ", trade.ResultRetcodeDescription());
    }
}

Podsumowanie

Klasa CTrade to potężny akcelerator pisania kodu, ale zwalnia programistę z myślenia o środowisku tylko do momentu pierwszego zderzenia z rzeczywistością różnych dostawców płynności. Średniozaawansowany programista MQL5 nie pyta "dlaczego broker mi to blokuje", ale wie, że musiał najpierw zapytać brokera o reguły (filing mode, lot step) i dopasować do nich swoje żądania. Wdrożenie powyższych dwóch wzorców – dynamicznego ustawiania TypeFilling oraz rygorystycznej normalizacji wolumenu – sprawia, że Twój EA staje się unikalny: działa tak samo dobrze na EURUSD na koncie ECN, jak i na kontraktach na Złoto czy kryptowalutach.

Powiązane