← Alle Insights

MQL5 Praxis-Tipps: stabile Expert Advisors bauen.

MQL5 hat die Reputation, eine alte Sprache zu sein. Stimmt teilweise — aber sie ist für Trading-Bots überraschend mächtig, wenn Sie die Eigenheiten kennen. Hier sind die Lektionen aus zehn Jahren MQL5, die in keinem Tutorial stehen.

1. CTrade-Klasse nutzen, nicht die nativen OrderSend-Funktionen.

Die Standard-Bibliothek hat CTrade in <Trade\Trade.mqh>. Sie kapselt OrderSend, OrderModify, OrderClose mit besserer Fehlerbehandlung und sauberer Syntax. Wer noch direkt OrderSend nutzt, schreibt 2027er-Code im 2008er-Stil.

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

void OnInit() {
    trade.SetExpertMagicNumber(123456);
    trade.SetDeviationInPoints(10);  // 1 pip Slippage erlaubt
    trade.SetTypeFillingBySymbol(_Symbol);
}

void OpenBuy(double lots, double sl, double tp) {
    if (!trade.Buy(lots, _Symbol, 0, sl, tp, "MyEA")) {
        PrintFormat("Buy failed: %d - %s",
                    trade.ResultRetcode(),
                    trade.ResultRetcodeDescription());
    }
}

2. ArraySetAsSeries für jede Indikator-Array.

Wenn Sie CopyBuffer oder eigene Indikator-Arrays nutzen, müssen Sie ArraySetAsSeries(array, true) aufrufen — sonst läuft der Index in die falsche Richtung. Eine der häufigsten Bugs in MQL5-Code von Anfängern.

double atr_buffer[];
ArraySetAsSeries(atr_buffer, true);
int atr_handle = iATR(_Symbol, PERIOD_H1, 14);
CopyBuffer(atr_handle, 0, 0, 3, atr_buffer);
// atr_buffer[0] = aktuelle ATR
// atr_buffer[1] = vor einer Bar
// atr_buffer[2] = vor zwei Bars

3. NormalizeDouble bei jedem Preis.

Forex-Preise haben je nach Symbol unterschiedliche Decimals (5 für EUR/USD, 3 für USD/JPY, 2 für Gold). Wenn Sie einen berechneten Stop-Loss nicht auf die richtigen Decimals normalisieren, gibt der Broker einen Invalid-Stops-Fehler zurück.

double sl = SymbolInfoDouble(_Symbol, SYMBOL_BID) - 100 * _Point;
sl = NormalizeDouble(sl, _Digits);  // KRITISCH
trade.Buy(lots, _Symbol, 0, sl, 0);

4. OnTimer für regelmäßige Tasks, nicht OnTick.

OnTick wird bei jedem Tick aufgerufen — bei volatilen Märkten Tausende Male pro Minute. Wenn Sie Logging, Datenbank-Updates oder externe API-Calls dort machen, wird der EA langsam und kann Ticks verpassen.

Stattdessen: OnTimer mit EventSetTimer(60) (alle 60 Sekunden) oder EventSetMillisecondTimer(500) (alle 500 ms).

5. Bar-Open-Logik mit isNewBar().

Wenn Ihre Strategie nur auf Bar-Open handeln soll: prüfen Sie, ob eine neue Bar entstanden ist. Klassisches Pattern:

bool isNewBar() {
    static datetime last_bar = 0;
    datetime current = iTime(_Symbol, PERIOD_H1, 0);
    if (current != last_bar) {
        last_bar = current;
        return true;
    }
    return false;
}

void OnTick() {
    if (!isNewBar()) return;
    // Nur einmal pro neue Stunde ausführen
    CheckSetup();
}

6. Magic Number für Multi-EA-Setup.

Wenn mehrere EAs auf demselben Konto laufen, muss jeder eine eindeutige Magic Number haben — und seine Positions-Logik darf nur die eigenen Positionen sehen.

bool HasMyPosition(long magic) {
    for (int i = PositionsTotal() - 1; i >= 0; i--) {
        ulong ticket = PositionGetTicket(i);
        if (PositionSelectByTicket(ticket)) {
            if (PositionGetInteger(POSITION_MAGIC) == magic
                && PositionGetString(POSITION_SYMBOL) == _Symbol) {
                return true;
            }
        }
    }
    return false;
}

7. Strategy-Tester-Kompatibilität.

Der MT5-Strategy-Tester hat Eigenheiten:

8. Saubere Fehlerbehandlung.

Jeder Broker hat eigene Limits (max. Spread für Trade, mindest-Abstand für Stops, Trade-Pause während News). Ignorieren Sie nie die Return-Codes:

if (!trade.Buy(lots, _Symbol, 0, sl, tp)) {
    uint retcode = trade.ResultRetcode();
    if (retcode == TRADE_RETCODE_REQUOTE
        || retcode == TRADE_RETCODE_PRICE_OFF) {
        // Markt zu schnell, später nochmal versuchen
        return;
    }
    if (retcode == TRADE_RETCODE_INVALID_STOPS) {
        // Stop-Loss zu nahe am Preis
        PrintFormat("Invalid stops: sl=%f, current=%f", sl,
                    SymbolInfoDouble(_Symbol, SYMBOL_BID));
        return;
    }
    Print("Trade failed: ", retcode, " - ",
          trade.ResultRetcodeDescription());
}

9. Logging und Persistenz.

Print() schreibt ins Journal, aber das wird nach einer Weile rotiert. Für persistentes Logging: eigene Datei in MQL5/Files/:

void LogTrade(string event_type, double price, double sl, double lots) {
    int handle = FileOpen("MyEA_Trades.csv",
                          FILE_WRITE|FILE_READ|FILE_CSV|FILE_COMMON);
    if (handle == INVALID_HANDLE) return;
    FileSeek(handle, 0, SEEK_END);
    FileWrite(handle, TimeCurrent(), _Symbol, event_type,
              price, sl, lots);
    FileClose(handle);
}

10. Was MQL5 nicht kann — und wann Sie Python brauchen.

In solchen Fällen: hybride Setups. Python macht die Schwerarbeit (ML-Modell, Signal- Berechnung), MQL5 führt die Trades aus. Kommunikation über REST-API oder gemeinsame Datei.

Wo Sie weiter lernen.

Die offizielle Dokumentation ist sehr gut. Der Article-Bereich auf mql5.com hat hochwertige Praxis-Artikel (manche Marketing, aber viele ausgezeichnet). Code-Reviews von erfahrenen Entwicklern beschleunigen den Lern-Prozess um Monate.

Sie haben einen EA, der nicht macht, was er soll? Erstgespräch buchen — wir machen einen Code-Review und finden die Fallen.