LifeHack für Händler: ForEach mit #define zubereiten

Vladimir Karputov | 19 März, 2018


 — Worin liegt die Stärke, Bruder?                               
  — Die Stärke, Bruder, liegt in define                     

                                  (c)  fxsaber                  

Sie schreiben immer noch in MQL4 und wollen auf MQL5 umsteigen, wissen aber nicht, wo Sie anfangen sollen? Dann helfen wir Ihnen! Nun gibt es die Möglichkeit, im MetaEditor MQL5 zu arbeiten und dabei die MQL4-Notation zu verwenden (ich möchte anmerken, dass sie etwas früher erschien, und in diesem Artikel möchte ich das Konvertieren von MQL4 Funktionen nach MQL5 ausführlicher beschreiben).

Ein guter Programmierer ist ein fauler Programmierer

Das Erstellen von Expert Advisors oder Handelsrobotern geht immer mit viel Arbeit mit Schleifen einher. Schleifen umringen uns: Iteration über Orders, Trades in der Historie, Objekte im Chart, Symbolen in der Marktübersicht, Balken im Indikatorpuffer. Um das Leben des Programmierers ein bisschen zu erleichtern, wurden zum MetaEditor Snippets hinzugefügt. Wenn Sie anfängliche Zeichen eingeben und Tab drücken, werden sie automatisch zu einem kleinen Codeteil. So funktioniert ein Snippet für die for-Schleife:


Nicht schlecht, aber das erfüllt nicht alle unsere Bedürfnisse. Das einfachste Beispiel: sei es, wir brauchen, über alle Symbole in der Marktübersicht zu iterieren.

   int total=SymbolsTotal(true);
   for(int i=0;i<total;i++)
     { 
      string symbol=SymbolName(i,true);
      PrintFormat("%d. %s",i+1,symbol);
     }

Es wäre toll, einen eigenen Snippet im MetaEditor zu erstellen, der mit fes (for_each_symbol) beginnen und den folgenden Block darstellen würde:


Es gibt aber keine benutzerdefinierten Snippets im MetaEditor, deswegen gehen wir einen anderen Weg — wir beschäftigen uns mit define. Die Makrosubstitution #define wurde von schlauen und faulen Programmierern entwickelt, die damit mehrere Ziele verfolgten. Dazu zählen die Lesbarkeit und Einfachheit des Schreibens eines sich mehrmals wiederholenden Codes.

In vielen Programmiersprachen gibt es außer der klassischen for-Schleife ihre Varianten for(<typename> element:Collection) oder for each (type identifier in expression). Könnte man so einen Code schreiben:

for(ulong order_id in History)
  { 
   Arbeiten mit c order_id  
  }

, wäre das Leben der Programmierer etwas einfacher. Im Internet gibt es sowohl Gegner als auch Befürworter dieses Ansatzes. Hier zeige ich, wie man etwas Ähnliches mithilfe von #define Makros tun kann.

Fangen wir mit einer einfachen Aufgabe an — erhalten wir die Namen aller Symbole in der Marktübersicht. Schreiben wir das folgende Makro:

#define ForEachSymbol(s,i)  string s; int total=SymbolsTotal(true); for(int i=0;i<total;i++,s=SymbolName(i,true))
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  { 
+//---+
   ForEachSymbol(symbol,index)
     { 
      PrintFormat("%d. %s",index,symbol);
     }
  }

Der Compiler versteht diesen Eintrag und gibt keine Fehler aus. Starten wir das Debugging mit F5 und wir sehen, dass etwas schief gelaufen ist:

1. (null)
2. GBPUSD
3. USDCHF
4. USDJPY
...

Das Problem besteht darin, dass der Ausdruck s=SymbolName(i,true) nach der Iteration in der for-Schleife berechnet wird und unsere Variable s in der ersten Iteration nicht initialisiert ist, wenn i=0. Operator der for-Schleife:

Der for-Operator besteht aus drei Ausdrücken und einem ausführbaren Operator:

for(Ausdruck1; Ausdruck2; Ausdruck3)
   Operator;

Ausdruck1 beschreibt die Initialisierung der Schleife. Ausdruck2 überprüft die Bedingungen für das Beenden der Schleife. Wenn er gleich true ist, wird der Operator des Körpers der for-Schleife ausgeführt. Alles wiederholt sich, bis der Ausdruck2 falsch ist. Wenn er gleich false ist, wird die Schleife beendet und die Verwaltung wird dem nächsten Operator übergeben. Der Ausdruck3 wird nach jeder Iteration berechnet.

Das wird ganz einfach gelöst. Nehmen wir einige Korrekturen vor:

#define ForEachSymbol(s,i)  string s=SymbolName(0,true); int total=SymbolsTotal(true); for(int i=1;i<total;i++,s=SymbolName(i,true))
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  { 
+//---+
   ForEachSymbol(symbol,index)
     { 
      PrintFormat("%d. %s",index,symbol); // statt index+1 ist nun einfach index
     }
  }

und wir erhalten das benötigte Ergebnis. Wir haben das Makro ForEachSymbol mit den Parametern symbol und index geschrieben, als ob das eine einfache for-Schleife wäre, und im Körper der Pseudoschleife haben wir mit diesen Variablen gearbeitet, als ob sie bereits deklariert und mit den benötigten Werten initialisiert wurden. Auf diese Weise können wir die benötigten Eigenschaften der Symbole aus der Marktübersicht mithilfe von Funktionen vom Typ SymbolInfoXXX() erhalten. Zum Beispiel, so:

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  { 
+//---+
   ForEachSymbol(symbol,index)
     { 
      //--- Datenaufbereitung
      double spread=SymbolInfoDouble(symbol,SYMBOL_ASK)-SymbolInfoDouble(symbol,SYMBOL_BID);
      double point=SymbolInfoDouble(symbol,SYMBOL_POINT);
      long digits=SymbolInfoInteger(symbol,SYMBOL_DIGITS);
      string str_spread=DoubleToString(spread/point,0);
      string str_point=DoubleToString(point,digits);
      //--- Information ausgeben
      Print(index,". ",symbol," spread=",str_spread," points (",
            digits," digits",", point=",str_point,")");
     }
/* Beispiel für die Ausgabe
        1. EURUSD spread=3 points (5 digits, point=0.00001)
        2. USDCHF spread=8 points (5 digits, point=0.00001)
        3. USDJPY spread=5 points (3 digits, point=0.001)
        4. USDCAD spread=9 points (5 digits, point=0.00001)
        5. AUDUSD spread=5 points (5 digits, point=0.00001)
        6. NZDUSD spread=10 points (5 digits, point=0.00001)
        7. USDSEK spread=150 points (5 digits, point=0.00001)
*/
  }

Nun können wir ein ähnliches Makro auch für die Iteration über grafische Objekte auf dem Chart schreiben:

#define ForEachObject(name,i)   string name=ObjectName(0,0); int total=ObjectsTotal(0); for(int i=1;i<=total;i++,name=ObjectName(0,i-1))
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  { 
+//---+
   ForEachObject(objectname,index)
     { 
      Print(index,": objectname=\"",objectname,"\", objecttype=",
            EnumToString((ENUM_OBJECT)ObjectGetInteger(0,objectname,OBJPROP_TYPE)));
     }
/* Beispiel für die Ausgabe
        1: objectname="H1 Arrow 61067", objecttype=OBJ_ARROW_UP
        2: objectname="H1 Rectangle 31152", objecttype=OBJ_RECTANGLE
        3: objectname="H1 StdDev Channel 56931", objecttype=OBJ_STDDEVCHANNEL
        4: objectname="H1 Trendline 6605", objecttype=OBJ_TREND
*/     
  }

Der String der Definition des Makros ForEachObject ist noch etwas länger geworden, und das erschwert auch das Verständnis des Codes der Ersetzung, der in eine Zeile geschrieben ist. Aber es hat sich herausgestellt, dass auch dieses Problem bereits seit langem gelöst ist: die Definition des Makros kann man mithilfe von '\' in Zeilen teilen. Es sieht wie folgt aus:

#define ForEachObject(name,i)   string name=ObjectName(0,0);   \
   int ob_total=ObjectsTotal(0);                               \
   for(int i=1;i<=ob_total;i++,name=ObjectName(0,i-1))

Für den Compiler sehen alle drei Strings wie eine lange Zeichenkette aus, aber für Programmierer ist es viel einfacher, den Code zu lesen. Es bleibt nur solche Makros für die Arbeit mit Orders, Positionen und Trades zu erstellen.

Beginnen wir mit der Iteration über Orders. Hier wird nur die Funktion HistorySelect() hinzugefügt:

#define ForEachOrder(ticket,i)    HistorySelect(0,TimeCurrent());    \
  ulong ticket=OrderGetTicket(0);                                    \ 
  int or_total=OrdersTotal();                                        \
  for(int i=1;i<or_total;i++,ticket=OrderGetTicket(i))
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  { 
+//---+
   ForEachOrder(orderticket,index)
     { 
      Print(index,": #",orderticket," ",OrderGetString(ORDER_SYMBOL)," ",
            EnumToString((ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE)));
     }
/* Beispiel für die Ausgabe     
   1: #13965457 CADJPY ORDER_TYPE_SELL_LIMIT
   2: #14246567 AUDNZD ORDER_TYPE_SELL_LIMIT
*/ 
  }

Es fällt auf, dass es keine Verarbeitung von Fehlern in diesem Makro (und in den zwei vorherigen) gibt. Was wäre, wenn HistorySelect() false zurückgibt? Man kann nur antworten, dass wir in diesem Fall nicht über alle Orders in der Schleife iterieren können. Darüber hinaus — wer von Euch analysiert denn das Ausführungsergebnis von HistorySelect()? Auf diese Weise gibt es in diesem Makro nichts Verbotenes, ein Programm auch auf gewöhnliche Art zu schreiben.

Aber der größte Nachteil von #define ist, dass die Verwendung von Makrosubstitutionen es nicht erlaubt, den Code zu debuggen. Ich stimme zu, aber wie fxsaber sagt "Ein gut fixierter Patient braucht keine Anästhesie debuggtes Makros braucht kein Debugging".

Ähnlich erstellen wir ein Makro für die Iteration über Positionen:

#define ForEachPosition(ticket,i) HistorySelect(0,TimeCurrent());    \
   ulong ticket=PositionGetTicket(0);                                \
   int po_total=PositionsTotal();                                    \
   for(int i=1;i<=po_total;i++,ticket=PositionGetTicket(i-1))        
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  { 
+//---+
   ForEachPosition(positionid,index)
     { 
      Print(index,": ",PositionGetString(POSITION_SYMBOL)," postionID #",positionid);
     }
/* Beispiel für die Ausgabe 
   1: AUDCAD postionID #13234934
   2: EURNZD postionID #13443909
   3: AUDUSD postionID #14956799
   4: EURUSD postionID #14878673
*/ 

Die Iteration über Trades in der Historie:

#define ForEachDeal(ticket,i) HistorySelect(0,TimeCurrent());        \
   ulong ticket=HistoryDealGetTicket(0);                             \
   int total=HistoryDealsTotal();                                    \
   for(int i=1;i<=total;i++,ticket=HistoryDealGetTicket(i-1))
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  { 
   +//---+
   ForEachDeal(dealticket,index)
     { 
      Print(index,": deal #",dealticket,",  order ticket=",
            HistoryDealGetInteger(dealticket,DEAL_ORDER));
     }
  }

Die Iteration über Orders in der Historie:

#define ForEachHistoryOrder(ticket,i) HistorySelect(0,TimeCurrent());\
   ulong ticket=HistoryOrderGetTicket(0);                            \
   int total=HistoryOrdersTotal();                                   \
   for(int i=1;i<=total;i++,ticket=HistoryOrderGetTicket(i-1))
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  { 
+//---+
   ForEachHistoryOrder(historyorderticket,index)
     { 
      Print(index,": #",historyorderticket);
     }
  }

Sammeln wir alle Makrosubstitutionen in der Datei ForEach.mqh4:

//+------------------------------------------------------------------+
//|                                                      ForEach.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
//+------------------------------------------------------------------+
//| Schleife für die Iteration über Symbole in der Marktübersicht    |
//+------------------------------------------------------------------+
#define ForEachSymbol(symbol,i)  string symbol=SymbolName(0,true);   \
   int os_total=SymbolsTotal(true);                                  \
   for(int i=1;i<os_total;i++,symbol=SymbolName(i,true))
//+-----------------------------------------------------------------------+
//| Schleife für die Iteration über Objekte im Hauptfenster des Charts    |
//+-----------------------------------------------------------------------+   
#define ForEachObject(name,i)   string name=ObjectName(0,0);         \
   int ob_total=ObjectsTotal(0);                                     \
   for(int i=1;i<=ob_total;i++,name=ObjectName(0,i-1))      
//+------------------------------------------------------------------+
//| Schleife für die Iteration über gültige Orders                   |
//+------------------------------------------------------------------+
#define ForEachOrder(ticket,i)    HistorySelect(0,TimeCurrent());    \
   ulong ticket=OrderGetTicket(0);                                   \
   int or_total=OrdersTotal();                                       \
   for(int i=1;i<or_total;i++,ticket=OrderGetTicket(i))   
//+------------------------------------------------------------------+
//| Schleife für die Iteration über offene Positionen                |
//+------------------------------------------------------------------+
#define ForEachPosition(ticket,i) HistorySelect(0,TimeCurrent());    \
   ulong ticket=PositionGetTicket(0);                                \
   int po_total=PositionsTotal();                                    \
   for(int i=1;i<=po_total;i++,ticket=PositionGetTicket(i-1))      
//+------------------------------------------------------------------+
//| Schleife für die Iteration über Trades in der Historie           |
//+------------------------------------------------------------------+
#define ForEachDeal(ticket,i) HistorySelect(0,TimeCurrent());        \
   ulong ticket=HistoryDealGetTicket(0);                             \
   int dh_total=HistoryDealsTotal();                                 \
   for(int i=1;i<=dh_total;i++,ticket=HistoryDealGetTicket(i-1))        
//+------------------------------------------------------------------+
//| Schleife für die Iteration über Orders in der Historie           |
//+------------------------------------------------------------------+
#define ForEachHistoryOrder(ticket,i) HistorySelect(0,TimeCurrent());\
   ulong ticket=HistoryOrderGetTicket(0);                            \
   int oh_total=HistoryOrdersTotal();                                \
   for(int i=1;i<=oh_total;i++,ticket=HistoryOrderGetTicket(i-1))   
//+------------------------------------------------------------------+

Bitte beachten Sie: für jedes Makro mussten wir der Variablen total einen Präfix hinzufügen, damit es keine Konflikte gibt, wenn wir mehr als ein Makro in unserem Code verwenden wollen. Das ist der größte Nachteil dieses Makros: wir deklarieren eine Variable im Makro, auf welche man "von außen" zugreifen kann. Das kann zu Fehlern bei der Kompilierung führen, die schwer zu erkennen sind.

Darüber hinaus gibt der Compiler bei der Verwendung eines parametrischen Makros keine Tooltips aus, wie er das für Funktionen tut. Sie müssen diese sechs Makros aufwendig lernen, wenn Sie sie nutzen wollen. Eigentlich ist es nicht so kompliziert — der erste Parameter ist immer eine Einheit, über welche in der Schleife iteriert wird, der zweite — immer der Index der Schleife, die mit 1 (Eins) beginnt.

Fügen wir auch Makros für eine Iteration in umgekehrter Reihenfolge hinzu. In diesem Fall gehen wir vom Ende der Schleife zu ihrem Anfang vor. Fügen wir dem Namen des Makros den Suffix Back hinzu und nehmen wir kleine Korrekturen vor. Das Makro für die Iteration über Symbolen in der Marktübersicht wird wie folgt aussehen.

#define ForEachSymbolBack(symbol,i) int s_start=SymbolsTotal(true)-1;\
   string symbol=SymbolName(s_start,true);                           \
   for(int i=s_start;i>=0;i--,symbol=SymbolName(i,true))
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  { 
+//---+
   ForEachSymbolBack(symbol,index)
     { 
      //--- Datenaufbereitung
      double spread=SymbolInfoDouble(symbol,SYMBOL_ASK)-SymbolInfoDouble(symbol,SYMBOL_BID);
      double point=SymbolInfoDouble(symbol,SYMBOL_POINT);
      long digits=SymbolInfoInteger(symbol,SYMBOL_DIGITS);
      string str_spread=DoubleToString(spread/point,0);
      string str_point=DoubleToString(point,digits);
      //--- Information ausgeben
      Print(index,". ",symbol," spread=",str_spread," points (",
            digits," digits",", point=",str_point,")");
     }
/* Beispiel für die Ausgabe
   3. USDJPY spread=5 points (3 digits, point=0.001)
   2. USDCHF spread=8 points (5 digits, point=0.00001)
   1. GBPUSD spread=9 points (5 digits, point=0.00001)
   0. EURUSD spread=2 points (5 digits, point=0.00001)
*/
  }

Wie Sie sehen, ändert sich in diesem Fall der Wert der Variablen index von size-1 auf 0. Damit beenden wir die Beschreibung von #define und kommen nun zum Schreiben von Funktionen für einen einfachen Zugriff im MQL4-Stil.

1. Welche Gruppen der MQL4-Funktionen im Artikel beschrieben werden

Hinweis: Solche Variablen wie Point, Digits und Bar müssen Sie selbst durch Point(), Digits() und Bar(Symbol(),Period()) ersetzen.

Nach MQL5 werden die Gruppen MQL4 AccountXXXX, MQL4 MarketInfo, MQL4 Prüfung des Status und MQL4 Vordefinierte Variablen konvertiert. Auf diese Weise werden dem Ordner [date folder]\MQL5\Include\SimpleCall\ vier Dateien hinzugefügt: AccountInfo.mqhMarketInfo.mqhCheck.mqh und Predefined.mqh. Insgesamt befinden sich sieben Dateien für das Konvertieren von MQL4 Funktionen nach MQL5 im Ordner: AccountInfo.mqh, Check.mqh, IndicatorsMQL4.mqh, IndicatorsMQL5.mqh, MarketInfo.mqh, Predefined.mqh und Series.mqh.

Sie müssen alle diese Dateien selbst einbinden. Dabei gibt es eine Einschränkung: die Dateien IndicatorsMQL4.mqh und IndicatorsMQL5.mqh können nicht gleichzeitig eingebunden sein, man muss eine der Dateien auswählen. Deswegen sind zwei Dateien im Ordner vorhanden: SimpleCallMQL4.mqh — sie bindet alle Dateien plus IndicatorsMQL4.mqh ein und SimpleCallMQL5.mqh — sie bindet alle Dateien und IndicatorsMQL5.mqh ein.

Beispiel für das Einbinden basierend auf MACD Sample.mq4

Kopieren wir die Datei MACD Sample.mq4 in den MQL5-Ordner mit Expert Advisors — zum Beispiel, [data folder]\MQL5\Experts\, und ändern wir die Erweiterung der Datei zu mq5. Auf diese Weise erhalten wir die Datei MACD Sample.mq5. Beim Kompilieren bekommen wir 59 Fehler und eine Warnung.

Nun laden wir die Datei SimpleCallMQL4.mqh:

//+------------------------------------------------------------------+
//|                                                  MACD Sample.mq4 |
//|                   Copyright 2005-2014, MetaQuotes Software Corp. |
//|                                              http://www.mql4.com |
//+------------------------------------------------------------------+
#property copyright   "2005-2014, MetaQuotes Software Corp."
#property link        "http://www.mql4.com"
+//---+
#include <SimpleCall\SimpleCallMQL4.mqh>
+//---+
input double TakeProfit    =50;

Kompilieren wir erneut, es werden 39 Fehler und eine Warnung ausgegeben. Nun müssen wir Bars durch Bars(Symbol(),Period()) und Point durch Point() (mithilfe von Ctrl+H) manuell ersetzen. Kompilieren. 35 Fehler, alle betreffen Handelsfunktionen. Aber in diesem Artikel gehen wir nicht auf Handelsfunktionen ein.

1.1 MQL4 AccountXXXX

Die Funktionen MQL4 AccountXXXX werden in der Datei [date folder]\MQL5\Include\SimpleCall\AccountInfo.mqh konvertiert.

Die Tabelle der Entsprechungen zwischen MQL4- und MQL5-Funktionen sieht wie folgt aus:

MQL4 MQL5 AccountInfoXXXX MQL5 CAccountInfo  Hinweise 
 AccountInfoDouble  AccountInfoDouble  CAccountInfo::InfoDouble oder CAccountInfo::double Methoden  
 AccountInfoInteger  AccountInfoInteger  CAccountInfo::InfoInteger oder CAccountInfo::Integer-Methoden  
 AccountInfoString  AccountInfoString  CAccountInfo::InfoString oder CAccountInfo::Text-Methoden  
 AccountBalance  AccountInfoDouble(ACCOUNT_BALANCE) oder   CAccountInfo::Balance  
 AccountCredit  AccountInfoDouble(ACCOUNT_CREDIT) oder   CAccountInfo::Credit  
 AccountCompany  AccountInfoString(ACCOUNT_COMPANY) oder   CAccountInfo::Company  
 AccountCurrency  AccountInfoString(ACCOUNT_CURRENCY) oder   CAccountInfo::Currency  
 AccountEquity  AccountInfoDouble(ACCOUNT_EQUITY) oder   CAccountInfo::Equity  
 AccountFreeMargin  AccountInfoDouble(ACCOUNT_FREEMARGIN) oder   CAccountInfo::FreeMargin  
 AccountFreeMarginCheck  --- /---  CAccountInfo::FreeMarginCheck   
 AccountFreeMarginMode  Keine Entsprechung  Keine Entsprechung  
 AccountLeverage  AccountInfoInteger(ACCOUNT_LEVERAGE)   CAccountInfo::Leverage  In MQL4 hat den Typ int, in MQL5: long
 AccountMargin  AccountInfoDouble(ACCOUNT_MARGIN)  CAccountInfo::Margin  
 AccountName  AccountInfoString(ACCOUNT_NAME)  CAccountInfo::Name  
 AccountNumber  AccountInfoInteger(ACCOUNT_LOGIN)  CAccountInfo::Login  In MQL4 hat den Typ int, in MQL5: long
 AccountProfit  AccountInfoDouble(ACCOUNT_PROFIT)  CAccountInfo::Profit   
 AccountServer  AccountInfoString(ACCOUNT_SERVER)  CAccountInfo::Server  
 AccountStopoutLevel  AccountInfoDouble(ACCOUNT_MARGIN_SO_SO)  CAccountInfo::MarginStopOut  In MQL4 - Typ int, in MQL5 - double
 AccountStopoutMode  AccountInfoInteger(ACCOUNT_MARGIN_SO_MODE)  CAccountInfo::StopoutMode  

Bitte beachten Sie, dass es keine Entsprechung für MQL4 AccountFreeMarginMode in MQL5 gibt. Weiter verwenden Sie MQL4 AccountFreeMarginMode auf eigene Gefahr. Wenn MQL4 AccountFreeMarginMode vorkommt, wird eine Warnung in die Log-Datei ausgegeben und NaN zurückgegeben — "Nicht-Zahl".

Für andere MQL4 AccountXXXX Funktionen gibt es Entsprechungen, und dabei in zwei Varianten: über AccountInfoXXXX oder über die Handelsklasse CAccountInfo. Für AccountInfoDouble, AccountInfoInteger und AccountInfoString gibt es keine Unterschiede zwischen MQL4 und MQL5.

In der Kopfzeile der Datei befindet sich der folgende Code. Wie funktioniert das?

+//---+
#define  OP_BUY                     ORDER_TYPE_BUY
#define  OP_SELL                    ORDER_TYPE_SELL
//--- returns balance value of the current account

Schauen wir in die Hilfe zu #define rein:

Die Direktive #define kann verwendet werden, um Ausdrücken mnemonische Namen zuzuweisen. Es existieren zwei Formen:

#define identifier expression                   // nichtparametrische Form 
#define identifier(par1,... par8) expression    // parametrische Form

Die Direktive #define ersetzt alle weiteren gefundenen Vorkommen von identifier im Quelltext durch expression.

Für unseren Code wird diese Beschreibung wie folgt aussehen (wir haben die nichtparametrische Form verwendet): die Direktive #define ersetzt alle weiteren Vorkommen von OP_BUY im Quelltext durch ORDER_TYPE_BUY. Die Direktive #define ersetzt alle weiteren Vorkommen von OP_SELL im Quelltext durch ORDER_TYPE_SELL. D. h. nachdem Sie die Datei [date folder]\MQL5\Include\SimpleCall\AccountInfo.mqh in Ihren Expert Advisor eingebunden haben, können Sie die MQL4 Typen OP_BUY und OP_SELL wie gewöhnlich verwenden. Der Compiler gibt keine Fehler aus.

Die erste Gruppe von Funktionen, die wir nach MQL5 konvertieren: AccountBalance(), AccountCredit(), AccountCompany(), AccountCurrency(), AccountEquity() und AccountFreeMargin():

//--- returns balance value of the current account
#define  AccountBalance(void)       AccountInfoDouble(ACCOUNT_BALANCE)
//--- returns credit value of the current account
#define  AccountCredit(void)        AccountInfoDouble(ACCOUNT_CREDIT)
//--- returns the brokerage company name where the current account was registered
#define  AccountCompany(void)       AccountInfoString(ACCOUNT_COMPANY)
//--- returns currency name of the current account 
#define  AccountCurrency(void)      AccountInfoString(ACCOUNT_CURRENCY)
//--- returns equity value of the current account
#define  AccountEquity(void)        AccountInfoDouble(ACCOUNT_EQUITY)
//--- returns free margin value of the current account
#define  AccountFreeMargin(void)    AccountInfoDouble(ACCOUNT_MARGIN_FREE)
In MQL4 XXXX(void) Funktionen wird die parametersche Form von #define verwendet, wobei "void" als Parameter dient. Man kann das ganz einfach überprüfen: wenn man "void" entfernt, bekommen wir beim Kompilieren den Fehler "unexpected in macro format parameter list":


unexpected in macro format parameter list

Bei MQL4 AccountFreeMarginCheck machen wir das anders — wir machen aus AccountFreeMarginCheck eine einfache Funktion, innerhalb welcher nur MQL5-Code verwendet wird:

//--- returns free margin that remains after the specified order has been opened 
//---    at the current price on the current account
double   AccountFreeMarginCheck(string symbol,int cmd,double volume)
  { 
   double price=0.0;
   ENUM_ORDER_TYPE trade_operation=(ENUM_ORDER_TYPE)cmd;
   if(trade_operation==ORDER_TYPE_BUY)
      price=SymbolInfoDouble(symbol,SYMBOL_ASK);
   if(trade_operation==ORDER_TYPE_SELL)
      price=SymbolInfoDouble(symbol,SYMBOL_BID);
+//---+
   double margin_check=EMPTY_VALUE;
   double margin=EMPTY_VALUE;
   margin_check=(!OrderCalcMargin(trade_operation,symbol,volume,price,margin))?EMPTY_VALUE:margin;
+//---+
   return(AccountInfoDouble(ACCOUNT_FREEMARGIN)-margin_check);
  }

Wie oben erwähnt, gibt es keine Entsprechung für AccountFreeMarginMode in MQL5, deswegen wenn AccountFreeMarginMode im Code vorkommt, wird eine Warnung ausgegeben und NaN - "Nicht-Zahl" - zurückgegeben:

//--- returns the calculation mode of free margin allowed to open orders on the current account
double AccountFreeMarginMode(void)
  { 
   string text="MQL4 functions \"AccountFreeMarginMode()\" has no analogs in MQL5. Returned \"NAN - not a number\"";
   Alert(text);
   Print(text);
   return(double("nan"));
  }

Mit den anderen MQL4-Funktionen gehen wir ähnlich wie mit den vorherigen vor:

//--- returns leverage of the current account
#define  AccountLeverage(void)      (int)AccountInfoInteger(ACCOUNT_LEVERAGE)
//--- returns margin value of the current account
#define  AccountMargin(void)        AccountInfoDouble(ACCOUNT_MARGIN)
//--- returns the current account name
#define  AccountName(void)          AccountInfoString(ACCOUNT_NAME)
//--- returns the current account number
#define  AccountNumber(void)        (int)AccountInfoInteger(ACCOUNT_LOGIN)
//--- returns profit value of the current account
#define  AccountProfit(void)        AccountInfoDouble(ACCOUNT_PROFIT)
//--- returns the connected server name
#define  AccountServer(void)        AccountInfoString(ACCOUNT_SERVER)
//--- returns the value of the Stop Out level
#define  AccountStopoutLevel(void)  (int)AccountInfoDouble(ACCOUNT_MARGIN_SO_SO)
//--- returns the calculation mode for the Stop Out level
int      AccountStopoutMode(void)
  { 
   ENUM_ACCOUNT_STOPOUT_MODE stopout_mode=(ENUM_ACCOUNT_STOPOUT_MODE)AccountInfoInteger(ACCOUNT_MARGIN_SO_MODE);
   if(stopout_mode==ACCOUNT_STOPOUT_MODE_PERCENT)
      return(0);
   return(1);
  }

1.2. MQL4 MarketInfo

Die Funktionen MQL4 MarketInfotXXXX werden in der Datei [date folder]\MQL5\Include\SimpleCall\MarketInfo.mqh konvertiert.

MQL4 MarketInfo hat den Typ double, aber Entsprechungen für MarketInfo gibt es in MQL5 auch in SymbolInfoInteger() (gibt den Typ long zurück) und in SymbolInfoDouble() (gibt den Typ double zurück). Hier fällt sofort auf, wie unpraktisch die Verwendung der veralteten Funktion MarketInfo ist: Typumwandlung. Am Beispiel der Transformation von MQL5 SYMBOL_DIGITS:

MarketInfo(Symbol(),MODE_DIGITS) <= (double)SymbolInfoInteger(symbol,SYMBOL_DIGITS)

1.2.1 Abweichung von MQL4 MODE_TRADEALLOWED 

In MQL4 ist das ein einfaches Flag bool, während es in MQL5 mehrere Typen von Erlaubnissen/Verboten für ein Symbol geben kann:

ENUM_SYMBOL_TRADE_MODE

Identifier Beschreibung
 SYMBOL_TRADE_MODE_DISABLED  Handel für das Symbol verboten
 SYMBOL_TRADE_MODE_LONGONLY  Nur Buy erlaubt
 SYMBOL_TRADE_MODE_SHORTONLY  Nur Sell erlaubt
 SYMBOL_TRADE_MODE_CLOSEONLY  Nur Schließen von Positionen erlaubt
 YMBOL_TRADE_MODE_FULL  Keine Einschränkungen für Transaktionen

Ich schlage vor, false nur im Fall von SYMBOL_TRADE_MODE_DISABLED zurückzugeben, und für teilweise Einschränkungen oder, wenn es keine Einschränkungen gibt, true zurückzugeben (dabei Alert und Print mit der Warnung über eine teilweise Einschränkung zu erzeugen).

1.2.2. Abweichung von MQL4 MODE_SWAPTYPE

In MQL4 gibt MODE_SWAPTYPE nur vier Werte zurück (Methode der Swap-Berechnung. 0 — in Punkten; 1 — in Basiswährung des Symbols; 2 — in Prozent; 3 — in Marginwährung), während die Aufzählung ENUM_SYMBOL_SWAP_MODE in MQL5 neun Werte beinhaltet und sie überschneiden sich mit den MQL4-Werten:

MQL4 MODE_SWAPTYPE MQL5 ENUM_SYMBOL_SWAP_MODE
 Keine Entsprechung  SYMBOL_SWAP_MODE_DISABLED
 0 - in Punkten  SYMBOL_SWAP_MODE_POINTS
 1 - in Basiswährung des Symbols  SYMBOL_SWAP_MODE_CURRENCY_SYMBOL
 3 - in Margin-Währung  SYMBOL_SWAP_MODE_CURRENCY_MARGIN
 Keine Entsprechung  SYMBOL_SWAP_MODE_CURRENCY_DEPOSIT
 2 - in Prozent  SYMBOL_SWAP_MODE_INTEREST_CURRENT
 2 - in Prozent  SYMBOL_SWAP_MODE_INTEREST_OPEN
 Keine Entsprechung  SYMBOL_SWAP_MODE_REOPEN_CURRENT
 Keine Entsprechung  SYMBOL_SWAP_MODE_REOPEN_BID

In MQL5 gibt es für die Berechnung von Swaps in Prozent zwei Varianten:

/*
SYMBOL_SWAP_MODE_INTEREST_CURRENT
Swaps werden in Jahresprozenten vom Preis des Symbols zum Zeitpunkt der Berechnung kalkuliert (Bankmodus – 360 Tage im Jahr)
SYMBOL_SWAP_MODE_INTEREST_OPEN
Swaps werden in Jahresprozenten vom Eröffnungspreis einer Position auf dem Symbol kalkuliert (Bankmodus – 360 Tage im Jahr)
*/

Wir werden sie als einen Typ betrachten. Für alle anderen MQL5-Varianten, die keine Entsprechungen in MQL4 haben, werden eine Warnung und NaN - "Nicht-Zahl" - zurückgegeben.

1.2.3. Abweichung von MQL4 MODE_PROFITCALCMODE und MODE_MARGINCALCMODE

In MQL4 gibt MODE_PROFITCALCMODE (Methode der Gewinnberechnung) nur drei Werte zurück: 0 — Forex; 1 — CFD; 2 — Futures, und MODE_MARGINCALCMODE (Methode der Margin-Berechnung) — vier: 0 — Forex; 1 — CFD; 2 — Futures; 3 — CFDs auf Indizes. In MQL5 kann man den Typ der Marginberechnung (Aufzählung ENUM_SYMBOL_CALC_MODE) für ein Symbol bestimmen, aber es gibt keine Methode zur Berechnung von Gewinn. Ich vermute, dass der Kalkulationstyp der Margin gleich dem Kalkulationstyp des Gewinns in MQL5 ist, deswegen wird für MQL4 MODE_PROFITCALCMODE und MODE_MARGINCALCMODE ein und der gleiche Wert von MQL5 ENUM_SYMBOL_CALC_MODE zurückgegeben.

Die Aufzählung MQL5 ENUM_SYMBOL_CALC_MODE hat 10 Methoden. Vergleichen wir MQL4 MODE_PROFITCALCMODE und MQL5 ENUM_SYMBOL_CALC_MODE:

MQL4 MODE_PROFITCALCMODE "Forex" <==> MQL5 SYMBOL_CALC_MODE_FOREX

MQL4 MODE_PROFITCALCMODE "CFD" <==> MQL5 SYMBOL_CALC_MODE_CFD

MQL4 MODE_PROFITCALCMODE "Futures" <==> MQL5 SYMBOL_CALC_MODE_FUTURES

Für alle anderen MQL5 Varianten, die keine Entsprechungen in MQL4 haben, wird eine Warnung ausgegeben und NaN - "Nicht-Zahl" - zurückgegeben:

...
#define MODE_PROFITCALCMODE   1000//SYMBOL_TRADE_CALC_MODE
#define MODE_MARGINCALCMODE   1001//SYMBOL_TRADE_CALC_MODE
...
      case MODE_PROFITCALCMODE:
        { 
         ENUM_SYMBOL_CALC_MODE profit_calc_mode=(ENUM_SYMBOL_CALC_MODE)SymbolInfoInteger(symbol,SYMBOL_TRADE_CALC_MODE);
         switch(profit_calc_mode)
           { 
            case  SYMBOL_CALC_MODE_FOREX:
               return((double)0);
            case  SYMBOL_CALC_MODE_FUTURES:
               return((double)2);
            case  SYMBOL_CALC_MODE_CFD:
               return((double)1);
            default :
              { 
               string text="MQL4 MODE_PROFITCALCMODE returned MQL5 "+EnumToString(profit_calc_mode);
               Alert(text);
               Print(text);
               return(double("nan"));
              }
           }
        }
      case MODE_MARGINCALCMODE:
        { 
         ENUM_SYMBOL_CALC_MODE profit_calc_mode=(ENUM_SYMBOL_CALC_MODE)SymbolInfoInteger(symbol,SYMBOL_TRADE_CALC_MODE);
         switch(profit_calc_mode)
           { 
            case  SYMBOL_CALC_MODE_FOREX:
               return((double)0);
            case  SYMBOL_CALC_MODE_FUTURES:
               return((double)2);
            case  SYMBOL_CALC_MODE_CFD:
               return((double)1);
            default :
              { 
               string text="MQL4 MODE_MARGINCALCMODE returned MQL5 "+EnumToString(profit_calc_mode);
               Alert(text);
               Print(text);
               return(double("nan"));
              }
           }
        }

1.3. MQL4 Prüfung des Status

Die MQL4-Funktionen für die Prüfung des Status werden in der Datei [date folder]\MQL5\Include\SimpleCall\Check.mqh konvertiert.

Dazu gehören folgende Elemente:

  • Digits
  • Point
  • IsConnected
  • IsDemo
  • IsDllsAllowed
  • IsExpertEnabled
  • IsLibrariesAllowed
  • IsOptimization
  • IsTesting
  • IsTradeAllowed
  • IsTradeContextBusy
  • IsVisualMode
  • TerminalCompany
  • TerminalName
  • TerminalPath
MQL4 MQL5 MQL5 classes Hinweis
 Digits

 MQL4 erlaubt es, Digits und Digits() gleichzeitig zu verwenden
 Point

 MQL4 erlaubt es, Point und Point() gleichzeitig zu verwenden
 IsConnected  TerminalInfoInteger(TERMINAL_CONNECTED)  CTerminalInfo::IsConnected 
 IsDemo  AccountInfoInteger(ACCOUNT_TRADE_MODE)  CAccountInfo::TradeMode  Es wird einer der Werte der Aufzählung ENUM_ACCOUNT_TRADE_MODE zurückgegeben
 IsDllsAllowed  TerminalInfoInteger(TERMINAL_DLLS_ALLOWED) und MQLInfoInteger(MQL_DLLS_ALLOWED)  CTerminalInfo::IsDLLsAllowed   TERMINAL_DLLS_ALLOWED hat Vorrang, MQL_DLLS_ALLOWED kann ignoriert werden
 IsExpertEnabled  TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)   CTerminalInfo::IsTradeAllowed  Status des Buttons "Autotrading" im Terminal
 IsLibrariesAllowed  MQLInfoInteger(MQL_DLLS_ALLOWED)  -/-  Eine Überprüfung ist sinnlos: wenn das Programm DLL verwendet und Sie verbieten es dem Programm, DLL zu verwenden (Reiter "Abhängigkeiten"), kann das Programm einfach nicht gestartet werden.
 IsOptimization  MQLInfoInteger(MQL_OPTIMIZATION)  -/-  Ein MQL5-Programm hat vier Modi: Debugging-, Profiling-, Test- und Optimierungsmodus.
 IsTesting  MQLInfoInteger(MQL_TESTER)  -/-  Ein MQL5-Programm hat vier Modi: Debugging-, Profiling-, Test- und Optimierungsmodus
 IsTradeAllowed  MQLInfoInteger(MQL_TRADE_ALLOWED)  -/-  Status der Checkbox "Autotrading erlauben" in den Einstellungen des Programms
 IsTradeContextBusy  -/-  -/-  Es wird "false" zurückgegeben
 IsVisualMode  MQLInfoInteger(MQL_VISUAL_MODE)
 Ein MQL5-Programm hat vier Modi: Debugging-, Profiling-, Test- und Optimierungsmodus
 TerminalCompany  TerminalInfoString(TERMINAL_COMPANY)  CTerminalInfo::Company  
 TerminalName  TerminalInfoString(TERMINAL_NAME)  CTerminalInfo::Name  
 TerminalPath  TerminalInfoString(TERMINAL_PATH)  CTerminalInfo::Path  

Digits und Point können nicht implementiert werden, weil MQL4 und MQL5 hier komplett durcheinandergebracht sind. Und zwar: in MQL4 können gleichzeitig Digits und Digits() sowie Point und Point() vorkommen. Ich persönlich weiß nicht, wie man Digits und Digits() über define zu Digits() zusammenführen kann. Alle anderen Punkte erscheinen eindeutig als XXXX() und können durch MQL5-Äquivalente ersetzt werden.

Die Implementierung:

//+------------------------------------------------------------------+
//|                                                        Check.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                                           http://wmua.ru/slesar/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "http://wmua.ru/slesar/"
#property version   "1.003" 
//--- checks connection between client terminal and server
#define IsConnected        (bool)TerminalInfoInteger(TERMINAL_CONNECTED)
//--- checks if the Expert Advisor runs on a demo account
#define IsDemo             (bool)(AccountInfoInteger(ACCOUNT_TRADE_MODE)==(ENUM_ACCOUNT_TRADE_MODE)ACCOUNT_TRADE_MODE_DEMO)
//--- checks if the DLL function call is allowed for the Expert Advisor
#define IsDllsAllowed      (bool)TerminalInfoInteger(TERMINAL_DLLS_ALLOWED) 
//--- checks if Expert Advisors are enabled for running
#define IsExpertEnabled    (bool)TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) 
//--- checks if the Expert Advisor can call library function
#define IsLibrariesAllowed (bool)MQLInfoInteger(MQL_DLLS_ALLOWED)
//--- checks if Expert Advisor runs in the Strategy Tester optimization mode
#define IsOptimization     (bool)MQLInfoInteger(MQL_OPTIMIZATION)
//--- checks if the Expert Advisor runs in the testing mode
#define IsTesting                (bool)MQLInfoInteger(MQL_TESTER)
//--- checks if the Expert Advisor is allowed to trade and trading context is not busy 
#define IsTradeAllowed     (bool)MQLInfoInteger(MQL_TRADE_ALLOWED)
//--- returns the information about trade context 
#define IsTradeContextBusy  false
//--- checks if the Expert Advisor is tested in visual mode 
#define IsVisualMode          (bool)MQLInfoInteger(MQL_VISUAL_MODE)
//--- returns the name of company owning the client terminal 
#define TerminalCompany    TerminalInfoString(TERMINAL_COMPANY)
//--- returns client terminal name
#define TerminalName          TerminalInfoString(TERMINAL_NAME)
//--- returns the directory, from which the client terminal was launched
#define TerminalPath          TerminalInfoString(TERMINAL_PATH)
//+------------------------------------------------------------------+

1.4. MQL4 Vordefinierte Variablen

Implementierung in der Datei [data folder]\MQL5\Include\SimpleCall\Predefined.mqh

MQL4 Vordefinierte Variablen:

  • _Digits
  • _Point
  • _LastError
  • _Period
  • _RandomSeed
  • _StopFlag
  • _Symbol
  • _UninitReason
  • Ask
  • Bars
  • Bid
  • Close
  • Digits
  • High
  • Low
  • Open
  • Point
  • Time
  • Volume

Die vordefinierten Variablen _XXXX werden zu MQL5-Funktionen mithilfe von #define konvertiert:

//--- the _Digits variable stores number of digits after a decimal point,
#define _Digits         Digits()
//--- the _Point variable contains the point size of the current symbol in the quote currency
#define _Point          Point()
//--- the _LastError variable contains code of the last error
#define _LastError      GetLastError()
//--- the _Period variable contains the value of the timeframe of the current chart
#define _Period         Period()
//#define _RandomSeed
//--- the _StopFlag variable contains the flag of the program stop
#define _StopFlag       IsStopped()
//--- the _Symbol variable contains the symbol name of the current chart
#define _Symbol         Symbol()
//--- the _UninitReason variable contains the code of the program uninitialization reason
#define _UninitReason   UninitializeReason()
//#define Bars            Bars(Symbol(),Period());
//#define Digits
//#define Point

Eine Ausnahme gibt es für _RandomSeed - in dieser Variablen wird der aktuelle Status des Pseudozufallszahlengenerators gespeichert. Bars kann auch nicht zu MQL5 konvertiert werden (genauer gesagt, Bars wird manuell ersetzt). Für Digits und Point gibt es keine Lösung. Wie bereits oben erwähnt, können Digits und Digits() sowie Point und Point() gleichzeitig im Text vorkommen.

MQL4 Ask und Bid werden durch die benutzerdefinierten Funktionen GetAsk() und GetBid() ersetzt:

//--- the latest known seller's price (ask price) for the current symbol
#define Ask             GetAsk()
//--- the latest known buyer's price (offer price, bid price) of the current symbol
#define Bid             GetBid()
...
//--- the latest known seller's price (ask price) for the current symbol                  
double GetAsk()
  { 
   MqlTick tick;
   SymbolInfoTick(Symbol(),tick);
   return(tick.ask);
  }
//--- the latest known buyer's price (offer price, bid price) of the current symbol             
double GetBid()
  { 
   MqlTick tick;
   SymbolInfoTick(Symbol(),tick);
   return(tick.bid);
  }

Wir hätten natürlich ein Makro für Ask einfacher schreiben können:

#define Ask SymbolInfoDouble(__Symbol,SYMBOL_ASK)

Aber im Hinweis zu SymbolInfoDouble steht:

Wenn die Funktion für das Erhalten der Information über den letzten Tick verwendet wird, ist es besser, SymbolInfoTick() zu verwenden. Es kann sein, dass es noch kein einziger Kurs für dieses Symbol empfangen wurde, seitdem das Terminal mit den Handelskonto verbunden ist. In diesem Fall wird der abgefragte Wert undefiniert sein.

In den meisten Fällen reicht es, die Funktion SymbolInfoTick() zu verwenden, die es erlaubt, die Werte von Ask, Bid, Last, Volume und die Zeit des Eintreffens des letzten Ticks durch einen Aufruf zu erhalten.

Und nun etwas Neues: verwenden wir 'operator'

Das Schlüsselwort Operator wird für das Überladen des Operators der Indexierung [] verwendet. Dies wird für das Konvertieren von MQL4 Zeitreihen-Arrays (Open[], High[], Low[], Close[], Time[],Volume[]) nach MQL5 benötigt.

Was steht in der Hilfe zu operator:

Das Überladen von Operatoren erlaubt es, Notation (Einträge in Form von einfachen Ausdrücken) auf komplizierte Objekte - Strukturen und Klassen - anzuwenden.

Wenn man von der Hilfe ausgeht, kann man vermuten, dass wir für das Überladen des Operators der Indexierung [] eine Klasse erstellen müssen.

Da wir Folgendes über #define wissen:

Die Direktive #define kann verwendet werden, um Ausdrücken mnemonische Namen zuzuweisen. Es existieren zwei Formen:

#define identifier expression                   // nichtparametrische Form 
#define identifier(par1,... par8) expression    // parametrische Form

Die Direktive #define ersetzt alle weiteren gefundenen Vorkommen von identifier im Quelltext durch expression.

kann man den Code so lesen: die Direktive #define ersetzt alle weiteren gefundenen Vorkommen im Quelltext durch 159 .

#define SeriesVolume(Volume,T) 159
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  { 
   long a=SeriesVolume(Volume,long);
  }

D. h. der Code in OnStart wird wie folgt umgewandelt:

   long a=159;

Schritt 2

Hier wurde eine ganze Klasse in #define eingefügt, 

//+------------------------------------------------------------------+
//|                                                      Test_en.mq5 |
//|                                      Copyright 2012, CompanyName |
//|                                       http://www.companyname.net |
//+------------------------------------------------------------------+
#define SeriesVolume(Volume,T) class CVolume       \
  {                                                \
  public:                                          \
    T operator[](const int i) const                \
    {                                              \
    long val[1];                                   \
    if(CopyTickVolume(Symbol(),Period(),i,1,val)==1)\
      return(val[0]);                              \
    else                                           \
      return(-1);                                  \
    }                                              \
  };                                               \
CVolume Volume;
+//---+
SeriesVolume(Volume,long)
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  { 
   Print(Volume[4]);
  }
//+------------------------------------------------------------------+

Diese Ersetzung kann wie folgt aussehen:

//+------------------------------------------------------------------+
//|                                                      Test_en.mq5 |
//|                                      Copyright 2012, CompanyName |
//|                                       http://www.companyname.net |
//+------------------------------------------------------------------+
class CVolume      
  {                                                 
  public:                                          
    long operator[](const int i) const                
    {                                               
    long val[1];                                   
    if(CopyTickVolume(Symbol(),Period(),i,1,val)==1)
      return(val[0]);                              
    else                                           
      return(-1);                                  
    }                                              
  };                                               
CVolume Volume;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  { 
   Print(Volume[4]);
  }

D. h. In OnStart rufen wir das Objekt Volume der Klasse CVolume, die Methode [], auf, wo der Index i übergeben wird. Nach demselben Prinzip schreiben wir für MQL4-Serien Open, High, Low, Close und Time.

Und zum Schluss bleiben die Funktionen MQL4 iXXX: iOpen, iHigh, iLow, iClose, iTime und iVolume. Für diese verwenden wir die Methode der Deklaration einer benutzerdefinierten Funktion.

Beispiel für iClose:

//--- returns Close price value for the bar of specified symbol with timeframe and shift
double   iClose(
                string                    symbol,              // symbol
                ENUM_TIMEFRAMES           timeframe,           // timeframe
                int                       shift                // shift
                )
  { 
   double result=0.0;
+//---+
   double val[1];
   ResetLastError();
   int copied=CopyClose(symbol,timeframe,shift,1,val);
   if(copied>0)
      result=val[0];
   else
      Print(__FUNCTION__,": CopyClose error=",GetLastError());
+//---+
   return(result);
  }

2. Modifizierungen in den anderen Dateien

In [data folder]\MQL5\Include\SimpleCall\IndicatorsMQL4.mqh sind nun alle MQL4-Namen der Linien in der Kopfzeile geschrieben:

double NaN=double("nan");
#define MODE_MAIN          0   
#define MODE_SIGNAL        1
#define MODE_PLUSDI        1
#define MODE_MINUSDI       2  
#define MODE_GATORJAW      1
#define MODE_GATORTEETH    2
#define MODE_GATORLIPS     3 
#define MODE_UPPER         1
#define MODE_LOWER         2  
#define MODE_TENKANSEN     1     
#define MODE_KIJUNSEN      2                                   
#define MODE_SENKOUSPANA   3                                 
#define MODE_SENKOUSPANB   4                                 
#define MODE_CHIKOUSPAN    5   

In [data folder]\MQL5\Include\SimpleCall\Series.mqh werden entfernt :

#define MODE_OPEN    0
//#define MODE_LOW     1
//#define MODE_HIGH    2
#define MODE_CLOSE   3
#define MODE_VOLUME  4
//#define MODE_TIME    5

Nun sind sie in [data folder]\MQL5\Include\SimpleCall\Header.mqh geschrieben:

//+------------------------------------------------------------------+
//|                                                       Header.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                                           http://wmua.ru/slesar/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "http://wmua.ru/slesar/"
+//---+
#define MODE_LOW     10001
#define MODE_HIGH    10002
#define MODE_TIME    10005

iClose, iHigh, iLow, iOpen, iTime und iVolume sind nun in [data folder]\MQL5\Include\SimpleCall\Predefined.mqh geschrieben.


Fazit

Der vorherige Artikel beschreibt, wie man Aufrufe von Indikatoren im MQL4-Stil schreiben kann und wozu das führen kann. Wir haben die Einfachheit des Schreibens erreicht, aber dafür müssen wir die Verlangsamung der Expert Advisors in Kauf nehmen, in welchen die erstellten Indikatoren nicht verwaltet werden können. In diesem Artikel haben wir weiter nach den Wegen gesucht, wie man das Schreiben eines Codes vereinfachen kann, und haben die Makrosubstitution #define betrachtet.

Als Ergebnis sehen wir, dass man mithilfe der Codes im Anhang fast jeden MQL4 Expert Advisor auch im MetaTrader 5 laufen lassen kann. Man muss nur die richtigen Dateien einbinden, die die erforderlichen Funktionen und vordefinierten Variablen überladen oder hinzufügen.

Für eine komplette Kompatibilität fehlen noch vereinfachte MQL4 Handelsfunktionen. Aber auch dieses Problem kann gelöst werden. Fassen wir die Vor- und Nachteile dieses Ansatzes kurz zusammen:

Nachteile:

  • Einschränkung bei der Verarbeitung des zurückgegebenen Fehlers beim Zugriff auf Indikatoren;
  • Verlangsamung der Testgeschwindigkeit bei einem Zugriff auf gleichzeitig mehrere Indikatoren;
  • die Notwendigkeit, die Indikatorlinien richtig anzugeben, je nach dem ob IndicatorsMQL5.mqh oder IndicatorsMQL4.mqh geladen wurden;
  • kein Debugging für die Makrosubstitution #define;
  • es gibt keinen Tooltip für Argumente der parametrischen Form von #define;
  • potentielle Konflikte zwischen den Variablen, die in Makros deklariert werden.
Vorteile
  • Einfachheit des Code-Schreibens — eine Zeichenkette statt mehrerer;
  • Sichtbarkeit und Prägnanz - je kürzer der Code, desto leichter das Verständnis;
  • Makrosubstitutionen werden im Editor in Rot markiert — so ist es einfacher, benutzerdefinierte Identifier und Funktionen zu unterscheiden;
  • man kann eigene Snippets erstellen.

Ich persönlich betrachte die im Artikel angeführten Methode nämlich als Lifehack und bleibe Befürworter des klassischen MQL5-Ansatzes. Wahrscheinlich helfen diese Artikel denjenigen, die gewöhnt sind im MQL4-Stil zu schreiben, die psychologische Barriere beim Umstieg auf die Plattform MetaTrader 5 zu überwinden, die in allerlei Hinsicht viel bequemer ist.