Migration von MQL4 zu MQL5
Die Migration von MQL4 zu MQL5 ist nicht so einfach, weil sich einiges geändert hat und man offenbar keinen Wert auf Abwärtskompatibilität gelegt hat.
Am Einfachste ist es, wie auch hier bei mindfulFX beschrieben, wenn man sich die MT4Orders.mqh holt. Die zweite Datei mql4_to_mql5.mqh ist hilfreich, jedoch fehlt dort scheinbar auch noch einiges. Ich habe bisher Konstanten, Objekt-Funktionen und Time-Funktionen gefunden, die auch in der erweiterten Datei bei mindfulFX noch fehlen und sammle auf dieser Seite alles, was mir sinnvoll erscheint, um ein erfolgreiches Update von MQL4 zu MQL5 zu erreichen.
Zur Info: Die Inhalte der grauen Quellcodeboxen lassen sich auf iPad, iPhone und Amazon Fire mit dem Finger verschieben, sollte nicht der ganze Inhalt sichtbar sein. Auf anderen Tablets und Mobiltelefonen sollte es analog möglich sein und auf Rechnern wird ein Balken unten sichtbar, der zum Verschieben des Inhaltes dient. Bei mir ist dieser Balken orange.
Fehlende Konstanten
MQL4 Konstanten in MQL5 verfügbar machen, damit der Quellcode nicht komplett neu geschrieben werden muss:
#ifndef OP_BUY
#define OP_BUY 0
#endif
#ifndef OP_SELL
#define OP_SELL 1
#endif
#ifndef OP_BUYLIMIT
#define OP_BUYLIMIT 2
#endif
#ifndef OP_SELLLIMIT
#define OP_SELLLIMIT 3
#endif
#ifndef OP_BUYSTOP
#define OP_BUYSTOP 4
#endif
#ifndef OP_SELLSTOP
#define OP_SELLSTOP 5
#endif
#ifndef MODE_OPEN
#define MODE_OPEN 0
#endif
#ifndef MODE_CLOSE
#define MODE_CLOSE 3
#endif
#ifndef MODE_VOLUME
#define MODE_VOLUME 4
#endif
#ifndef MODE_REAL_VOLUME
#define MODE_REAL_VOLUME 5
#endif
#ifndef MODE_TRADES
#define MODE_TRADES 0
#endif
#ifndef MODE_HISTORY
#define MODE_HISTORY 1
#endif
#ifndef SELECT_BY_POS
#define SELECT_BY_POS 0
#endif
#ifndef SELECT_BY_TICKET
#define SELECT_BY_TICKET 1
#endif
#ifndef DOUBLE_VALUE
#define DOUBLE_VALUE 0
#endif
#ifndef FLOAT_VALUE
#define FLOAT_VALUE 1
#endif
#ifndef LONG_VALUE
#define LONG_VALUE INT_VALUE
#endif
#ifndef CHART_BAR
#define CHART_BAR 0
#endif
#ifndef CHART_CANDLE
#define CHART_CANDLE 1
#endif
#ifndef MODE_ASCEND
#define MODE_ASCEND 0
#endif
#ifndef MODE_DESCEND
#define MODE_DESCEND 1
#endif
#ifndef MODE_LOW
#define MODE_LOW 1
#endif
#ifndef MODE_HIGH
#define MODE_HIGH 2
#endif
#ifndef MODE_TIME
#define MODE_TIME 5
#endif
#ifndef MODE_BID
#define MODE_BID 9
#endif
#ifndef MODE_ASK
#define MODE_ASK 10
#endif
#ifndef MODE_POINT
#define MODE_POINT 11
#endif
#ifndef MODE_DIGITS
#define MODE_DIGITS 12
#endif
#ifndef MODE_SPREAD
#define MODE_SPREAD 13
#endif
#ifndef MODE_STOPLEVEL
#define MODE_STOPLEVEL 14
#endif
#ifndef MODE_LOTSIZE
#define MODE_LOTSIZE 15
#endif
#ifndef MODE_TICKVALUE
#define MODE_TICKVALUE 16
#endif
#ifndef MODE_TICKSIZE
#define MODE_TICKSIZE 17
#endif
#ifndef MODE_SWAPLONG
#define MODE_SWAPLONG 18
#endif
#ifndef MODE_SWAPSHORT
#define MODE_SWAPSHORT 19
#endif
#ifndef MODE_STARTING
#define MODE_STARTING 20
#endif
#ifndef MODE_EXPIRATION
#define MODE_EXPIRATION 21
#endif
#ifndef MODE_TRADEALLOWED
#define MODE_TRADEALLOWED 22
#endif
#ifndef MODE_MINLOT
#define MODE_MINLOT 23
#endif
#ifndef MODE_LOTSTEP
#define MODE_LOTSTEP 24
#endif
#ifndef MODE_MAXLOT
#define MODE_MAXLOT 25
#endif
#ifndef MODE_SWAPTYPE
#define MODE_SWAPTYPE 26
#endif
#ifndef MODE_PROFITCALCMODE
#define MODE_PROFITCALCMODE 27
#endif
#ifndef MODE_MARGINCALCMODE
#define MODE_MARGINCALCMODE 28
#endif
#ifndef MODE_MARGININIT
#define MODE_MARGININIT 29
#endif
#ifndef MODE_MARGINMAINTENANCE
#define MODE_MARGINMAINTENANCE 30
#endif
#ifndef MODE_MARGINHEDGED
#define MODE_MARGINHEDGED 31
#endif
#ifndef MODE_MARGINREQUIRED
#define MODE_MARGINREQUIRED 32
#endif
#ifndef MODE_FREEZELEVEL
#define MODE_FREEZELEVEL 33
#endif
#ifndef EMPTY
#define EMPTY -1
#endif
Fehlende Status-Funktionen
MQL4 Checkup-Funktionen in MQL5 verfügbar machen, damit der Quellcode nicht komplett neu geschrieben werden muss:
#define IsConnected() (bool)TerminalInfoInteger(TERMINAL_CONNECTED)
#define IsDemo() (bool)(AccountInfoInteger(ACCOUNT_TRADE_MODE) == (ENUM_ACCOUNT_TRADE_MODE)ACCOUNT_TRADE_MODE_DEMO)
#define IsDllsAllowed() (bool)TerminalInfoInteger(TERMINAL_DLLS_ALLOWED)
#define IsExpertEnabled() (bool)TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)
#define IsLibrariesAllowed() (bool)MQLInfoInteger(MQL_DLLS_ALLOWED)
#define IsOptimization() (bool)MQLInfoInteger(MQL_OPTIMIZATION)
#define IsTesting() (bool)MQLInfoInteger(MQL_TESTER)
#define IsTradeAllowed() (bool)MQLInfoInteger(MQL_TRADE_ALLOWED)
#define IsTradeContextBusy() false
#define IsVisualMode() (bool)MQLInfoInteger(MQL_VISUAL_MODE)
#define TerminalCompany() TerminalInfoString(TERMINAL_COMPANY)
#define TerminalName() TerminalInfoString(TERMINAL_NAME)
#define TerminalPath() TerminalInfoString(TERMINAL_PATH)
Fehlende Variablen und Zeitreihen
MQL4 "predefined Variables" in MQL5 verfügbar machen, damit der Quellcode nicht komplett neu geschrieben werden muss:
#define Point _Point
#define Digits _Digits
double Point() {
return SymbolInfoDouble(Symbol(), SYMBOL_POINT);
}
int Digits() {
return (int)SymbolInfoInteger(Symbol(), SYMBOL_DIGITS);
}
#define Ask GetAsk()
#define Bid GetBid()
double GetAsk() {
MqlTick lastTick;
SymbolInfoTick(Symbol(), lastTick);
return(lastTick.ask);
}
double GetBid() {
MqlTick lastTick;
SymbolInfoTick(Symbol(), lastTick);
return(lastTick.bid);
}
int MT4Bars(void) {
return(::Bars(_Symbol, _Period));
}
#define Bars (::MT4Bars())
#define DEFINE_TIMESERIE(NAME,FUNC,T) \
class CLASS##NAME \
{ \
public: \
static T Get(const string Symb,const int TimeFrame,const int iShift) \
{ \
T tValue[]; \
\
return((Copy##FUNC((Symb == NULL) ? _Symbol : Symb, _Period, iShift, 1, tValue) > 0) ? tValue[0] : -1); \
} \
\
T operator[](const int iPos) const \
{ \
return(CLASS##NAME::Get(_Symbol, _Period, iPos)); \
} \
}; \
\
CLASS##NAME NAME;
DEFINE_TIMESERIE(Volume, TickVolume,long)
DEFINE_TIMESERIE(Time, Time, datetime)
DEFINE_TIMESERIE(Open, Open, double)
DEFINE_TIMESERIE(High, High, double)
DEFINE_TIMESERIE(Low, Low, double)
DEFINE_TIMESERIE(Close, Close, double)
Fehlende Funktionen zu Datum und Zeit
int Day() {
MqlDateTime tm;
TimeCurrent(tm);
return(tm.day);
}
int DayOfWeek() {
MqlDateTime tm;
TimeCurrent(tm);
return(tm.day_of_week);
}
int DayOfYear() {
MqlDateTime tm;
TimeCurrent(tm);
return(tm.day_of_year);
}
int Hour() {
MqlDateTime tm;
TimeCurrent(tm);
return(tm.hour);
}
int Minute() {
MqlDateTime tm;
TimeCurrent(tm);
return(tm.min);
}
int Month() {
MqlDateTime tm;
TimeCurrent(tm);
return(tm.mon);
}
int Seconds() {
MqlDateTime tm;
TimeCurrent(tm);
return(tm.sec);
}
int Year() {
MqlDateTime tm;
TimeCurrent(tm);
return(tm.year);
}
int TimeDay(datetime date) {
MqlDateTime tm;
TimeToStruct(date,tm);
return(tm.day);
}
int TimeDayOfWeek(datetime date) {
MqlDateTime tm;
TimeToStruct(date,tm);
return(tm.day_of_week);
}
int TimeDayOfYear(datetime date) {
MqlDateTime tm;
TimeToStruct(date,tm);
return(tm.day_of_year);
}
int TimeHour(datetime date) {
MqlDateTime tm;
TimeToStruct(date,tm);
return(tm.hour);
}
int TimeMinute(datetime date) {
MqlDateTime tm;
TimeToStruct(date,tm);
return(tm.min);
}
int TimeMonth(datetime date) {
MqlDateTime tm;
TimeToStruct(date,tm);
return(tm.mon);
}
int TimeSeconds(datetime date) {
MqlDateTime tm;
TimeToStruct(date,tm);
return(tm.sec);
}
int TimeYear(datetime date) {
MqlDateTime tm;
TimeToStruct(date,tm);
return(tm.year);
}
Fehlende Account-Funktionen
MQL4 Account-Funktionen in MQL5 verfügbar machen, damit der Quellcode nicht komplett neu geschrieben werden muss:
double AccountBalance() {
return AccountInfoDouble(ACCOUNT_BALANCE);
}
string AccountCompany() {
return AccountInfoString(ACCOUNT_COMPANY);
}
double AccountCredit() {
return AccountInfoDouble(ACCOUNT_CREDIT);
}
string AccountCurrency() {
return AccountInfoString(ACCOUNT_CURRENCY);
}
double AccountEquity() {
return AccountInfoDouble(ACCOUNT_EQUITY);
}
double AccountFreeMargin() {
return AccountInfoDouble(ACCOUNT_FREEMARGIN);
}
int AccountLeverage() {
return (int)AccountInfoInteger(ACCOUNT_LEVERAGE);
}
double AccountMargin() {
return AccountInfoDouble(ACCOUNT_MARGIN);
}
string AccountName() {
return AccountInfoString(ACCOUNT_NAME);
}
int AccountNumber() {
return (int)AccountInfoInteger(ACCOUNT_LOGIN);
}
double AccountProfit() {
return AccountInfoDouble(ACCOUNT_PROFIT);
}
string AccountServer() {
return AccountInfoString(ACCOUNT_SERVER);
}
double AccountStopoutLevel() {
return AccountInfoDouble(ACCOUNT_MARGIN_SO_SO);
}
int AccountStopoutMode() {
return (int)AccountInfoInteger(ACCOUNT_MARGIN_SO_MODE);
}
double AccountFreeMarginCheck(const string Symb,const int Cmd,const double dVolume) {
double Margin;
return (OrderCalcMargin((ENUM_ORDER_TYPE)Cmd, Symb, dVolume,
SymbolInfoDouble(Symb,(Cmd==ORDER_TYPE_BUY) ? SYMBOL_ASK : SYMBOL_BID),Margin) ?
AccountInfoDouble(ACCOUNT_MARGIN_FREE) - Margin : -1);
}
Für AccountFreeMarginMode() gibt es in MQL5 keine Entsprechung!
Fehlende Funktion MarketInfo()
double MarketInfo(string symbol, int type) {
switch(type) {
case MODE_LOW:
return(SymbolInfoDouble(symbol,SYMBOL_LASTLOW));
case MODE_HIGH:
return(SymbolInfoDouble(symbol,SYMBOL_LASTHIGH));
case MODE_TIME:
return((double)SymbolInfoInteger(symbol,SYMBOL_TIME));
case MODE_BID:
GetBid(); //return(SymbolInfoDouble(symbol,SYMBOL_BID));
case MODE_ASK:
GetAsk(); //return(SymbolInfoDouble(symbol,SYMBOL_ASK));
case MODE_POINT:
return(SymbolInfoDouble(symbol,SYMBOL_POINT));
case MODE_DIGITS:
return((double)SymbolInfoInteger(symbol,SYMBOL_DIGITS));
case MODE_SPREAD:
return((double)SymbolInfoInteger(symbol,SYMBOL_SPREAD));
case MODE_STOPLEVEL:
return((double)SymbolInfoInteger(symbol,SYMBOL_TRADE_STOPS_LEVEL));
case MODE_LOTSIZE:
return(SymbolInfoDouble(symbol,SYMBOL_TRADE_CONTRACT_SIZE));
case MODE_TICKVALUE:
return(SymbolInfoDouble(symbol,SYMBOL_TRADE_TICK_VALUE));
case MODE_TICKSIZE:
return(SymbolInfoDouble(symbol,SYMBOL_TRADE_TICK_SIZE));
case MODE_SWAPLONG:
return(SymbolInfoDouble(symbol,SYMBOL_SWAP_LONG));
case MODE_SWAPSHORT:
return(SymbolInfoDouble(symbol,SYMBOL_SWAP_SHORT));
case MODE_STARTING:
return((double)SymbolInfoInteger(symbol,SYMBOL_START_TIME));
case MODE_EXPIRATION:
return((double)SymbolInfoInteger(symbol,SYMBOL_EXPIRATION_TIME));
case MODE_MINLOT:
return(SymbolInfoDouble(symbol,SYMBOL_VOLUME_MIN));
case MODE_LOTSTEP:
return(SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP));
case MODE_MAXLOT:
return(SymbolInfoDouble(symbol,SYMBOL_VOLUME_MAX));
case MODE_SWAPTYPE:
return((double)SymbolInfoInteger(symbol,SYMBOL_SWAP_MODE));
case MODE_PROFITCALCMODE:
return((double)SymbolInfoInteger(symbol,SYMBOL_TRADE_CALC_MODE));
case MODE_FREEZELEVEL:
return((double)SymbolInfoInteger(symbol,SYMBOL_TRADE_FREEZE_LEVEL));
default:
Print("ERROR MarketInfo: type not found: " + (string)type);
}
return(0);
}
Fehlende StrTo...() und ...ToStr() Funktionen
double StrToDouble(string value) {
return StringToDouble(value);
}
int StrToInteger(string value) {
return (int) StringToInteger(value);
}
datetime StrToTime(string value) {
return StringToTime(value);
}
string CharToStr(int char_code) {
return (string)CharToString((uchar)char_code);
}
string DoubleToStr(double value, int digits) {
return DoubleToString(value, digits);
}
string TimeToStr(datetime value, int mode = TIME_DATE|TIME_MINUTES) {
return TimeToString(value, mode);
}
Objekt-Funktionen
MQL4 Objekt-Funktionen in MQL5 ergänzen, damit der Quellcode nicht komplett neu geschrieben werden muss:
bool ObjectCreate(
string name,
int type,
int window,
datetime time1,
double price1,
datetime time2 = 0,
double price2 = 0,
datetime time3 = 0,
double price3 = 0) {
return(ObjectCreate(0, name,(ENUM_OBJECT)type, window, time1, price1, time2, price2, time3, price3));
}
bool ObjectDelete(string name) {
return(ObjectDelete(0, name));
}
int ObjectFind(string name) {
return(ObjectFind(0, name));
}
string ObjectName(int object_index) {
return ObjectName(0, object_index);
}
bool ObjectSet(string name, int prop_id, double value) {
switch(prop_id) {
case OBJPROP_COLOR:
ObjectSetInteger(0,name,OBJPROP_COLOR,(int)value);return(true);
case OBJPROP_STYLE:
ObjectSetInteger(0,name,OBJPROP_STYLE,(int)value);return(true);
case OBJPROP_WIDTH:
ObjectSetInteger(0,name,OBJPROP_WIDTH,(int)value);return(true);
case OBJPROP_BACK:
ObjectSetInteger(0,name,OBJPROP_BACK,(int)value);return(true);
case OBJPROP_RAY:
ObjectSetInteger(0,name,OBJPROP_RAY_RIGHT,(int)value);return(true);
case OBJPROP_ELLIPSE:
ObjectSetInteger(0,name,OBJPROP_ELLIPSE,(int)value);return(true);
case OBJPROP_SCALE:
ObjectSetDouble(0,name,OBJPROP_SCALE,value);return(true);
case OBJPROP_ANGLE:
ObjectSetDouble(0,name,OBJPROP_ANGLE,value);return(true);
case OBJPROP_ARROWCODE:
ObjectSetInteger(0,name,OBJPROP_ARROWCODE,(int)value);return(true);
case OBJPROP_TIMEFRAMES:
ObjectSetInteger(0,name,OBJPROP_TIMEFRAMES,(int)value);return(true);
case OBJPROP_DEVIATION:
ObjectSetDouble(0,name,OBJPROP_DEVIATION,value);return(true);
case OBJPROP_FONTSIZE:
ObjectSetInteger(0,name,OBJPROP_FONTSIZE,(int)value);return(true);
case OBJPROP_CORNER:
ObjectSetInteger(0,name,OBJPROP_CORNER,(int)value);return(true);
case OBJPROP_XDISTANCE:
ObjectSetInteger(0,name,OBJPROP_XDISTANCE,(int)value);return(true);
case OBJPROP_YDISTANCE:
ObjectSetInteger(0,name,OBJPROP_YDISTANCE,(int)value);return(true);
case OBJPROP_LEVELCOLOR:
ObjectSetInteger(0,name,OBJPROP_LEVELCOLOR,(int)value);return(true);
case OBJPROP_LEVELSTYLE:
ObjectSetInteger(0,name,OBJPROP_LEVELSTYLE,(int)value);return(true);
case OBJPROP_LEVELWIDTH:
ObjectSetInteger(0,name,OBJPROP_LEVELWIDTH,(int)value);return(true);
default:
Print("ERROR ObjectSet: not found ", prop_id);
}
return(false);
}
bool ObjectSetText(string name,
string text,
int font_size,
string font_name = NULL,
color text_color = CLR_NONE) {
bool retValue = true;
int tmpObjType = (int)ObjectGetInteger(0,name,OBJPROP_TYPE);
if (tmpObjType != OBJ_LABEL && tmpObjType != OBJ_TEXT)
return false;
if (StringLen(text) > 0 && font_size > 0) {
if (ObjectSetString(0,name,OBJPROP_TEXT,text) == true && ObjectSetInteger(0,name,OBJPROP_FONTSIZE,font_size) == true) { // set text
if ((StringLen(font_name) > 0) && ObjectSetString(0,name,OBJPROP_FONT,font_name) == false) retValue = false; // set font_name
if (text_color != CLR_NONE && ObjectSetInteger(0,name,OBJPROP_COLOR,text_color) == false) retValue = false; // set text_color
}
return retValue;
}
return false;
}
Händische Ersetzungen
#property strict sollte als erstes entfernt werden, falls man dieses benutzt hat.
Die alte MQL4 start()-Funktion hat sich für Expert-Dateien zu OnTick() geändert. In Indikatoren heißt sie nun OnCalculate(...) und in Script-Dateien wird sie zu OnStart() geändert. Hier sollte man ein wenig aufpassen, da sich auch die Ein- und Rückbabe-Parameter geändert haben können.
An einigen Stellen muss int zu ulong oder long geändert werden, z.B. bei den Ticket-Werten. Das "u" steht für "unsigned" und ermöglicht einen doppelt so großen Zahlenspeicher zu long, beschneidet jedoch negative Zahlen. Es ist nicht ungewöhnlich -1 als Fehler einer int-Funktion als Rückgabewert zu benutzen, so dass sich die Logik ändert, wenn von int zu ulong ersetzt wird! Wenn man unsicher ist, sollte man also lieber "long" statt "ulong" nehmen.
Geänderte Parameter bei StringConcatenate()
StringConcatenate() erwartet nun als ersten Parameter eine Stringvariable, gibt den zusammengesetzten String nicht mehr zurück und erwartet außerdem mindestens 3 Parameter. Daher können folgende händische Änderungen notwendig werden.
MQL4:string obText = StringConcatenate("Param ", param);
MQL5:
string obText;
StringConcatenate(obText, "Param ", param);
MQL4:
obText = StringConcatenate(obText, " mehr Text");
MQL5:
StringConcatenate(obText, " ", "mehr Text");
Ich muss ganz ehrlich sagen, dass ich StringConcatenate() durch diese Umstellung nicht mehr benutze. Das Pluszeichen tut es auch, funktioniert bestens und lesbarer ist es auch:
MQL4 und MQL5:string obText = "Param " + (string)param;
obText += " mehr Text";
Geänderte Parameter bei ArraySort()
Ich habe Beispiele mit ArraySetAsSeries() gefunden und ausprobiert.
ArraySetAsSeries() hat offenbar nur Einfluss auf Arrays des Datentyps datetime und nicht des Datentyps double!
Timeseries bedeutet, dass sich auf
Da ich keinen Einfluss im EA von ArraySetAsSeries() auf ein Array des Datentyps double feststellen konnte und zudem diese Funktion die Ausführung scheinbar langsamer macht, habe ich einen anderen Vorschlag, welcher jedoch die zwei Werte count und start derzeit auch nicht verwendet. Daher könnte man auch direkt ArraySort() und ArrayReverse() benutzen.
Ansonsten muss im Quellcode dann ArraySort(...) zu ArraySortMql4(...) geändert werden:
bool ArraySortMQL4 (double &array[],
int count = WHOLE_ARRAY,
int start = 0,
int direction = MODE_ASCEND) {
bool retValue = ArraySort(array);
if (retValue && MODE_DESCEND == direction) {
retValue = ArrayReverse(array);
}
if (start != 0 || count != WHOLE_ARRAY) {
Print("ERROR ArraySort: count and start are not used!");
}
return retValue;
}
Im Indikator ist das wieder etwas anderes: hier werden ja Buffer-Arrays mit Daten, auch des Typs double, befüllt und wenn man dort mit ArraySetAsSeries() die Richtung umdreht, kann man tatsächlich den Einfluss dieser Funktion direkt beobachten, indem die angezeigte Kurve gespiegelt wird. Im Indikator ist der Index gleichbedeutend mit der Nummer der Bar und erhält so den Zeit-Bezug.
In EAs dagegen versuche ich diese Funktion zu vermeiden, da ich den Eindruck habe, dass die Tests deutlich länger dauern.
Externe Links
MQL5 Reference (deutsch)MQL5 Functions (deutsch)
MQL5 Constants (deutsch)
MQL4 Reference
MQL4 Functions
MQL4 Constants
Möchtest du MQL programmieren lernen? Dieser Kurs macht wirklich Spaß und kostet nur einen Euro. Mein Prädikat: ausgezeichnet und auch nicht langweilig mit Vorwissen:
MQL5 EinsteigerkursMQL4 Einsteigerkurs