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:
- OnTimer wird nicht aufgerufen im Standard-Modus. Workaround: in OnTick prüfen, ob Zeit für Timer-Task ist.
- File-I/O funktioniert eingeschränkt. Eigenen Tester-Modus via
MQLInfoInteger(MQL_TESTER)erkennen. - Multi-Symbol-Testing: braucht spezielle Setup in der Bots- und Indicator-Konfig.
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.
- Komplexe Machine-Learning-Modelle (XGBoost, neuronale Netze): kein vernünftiger Support.
- Multi-Account-Management: kein Konzept dafür.
- Externe APIs (News, Sentiment, Datenbanken): WebRequest funktioniert, aber umständlich.
- Tiefe Statistik: alles möglich, aber 5× so viel Code wie in Python.
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.