Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil IX): Kompatibilität mit MQL4 - Datenvorbereitung

1 August 2019, 09:48
Artyom Trishkin
0
154

Inhalt

In den vorherigen Teilen der Artikelserie haben wir die folgenden Werkzeuge für die plattformübergreifende Bibliothek MetaTrader 5 und MetaTrader 4 vorbereitet:

  • Zur Erstellung von User-Case-Funktionen, die einen schnellen Zugriff von Programmen auf beliebige Daten zu beliebigen Aufträgen und Positionen von Hedging- und Netting-Konten ermöglichen.
  • Zur Verfolgung von Ereignissen bei Orders und Positionen — Platzieren, Entfernen und Aktivieren von offenen Orders sowie Öffnen, Schließen und Ändern von Positionen und Orders.

Jetzt ist es an der Zeit, die Bibliothekskompatibilität mit MQL4 zu implementieren, da wir Handelsklassen entwickeln werden und die Bibliothek sowohl in MQL5 als auch in MQL4 korrekt funktionieren sollte.

In diesem Artikel werden wir damit beginnen, die Bibliothek zu verbessern, um ihren plattformübergreifenden Charakter zu implementieren.

MQL4 vs MQL5

Kopieren Sie den gesamten Bibliotheksordner in das entsprechende MetaTrader 4-Verzeichnis \MQL4\Include\DoEasy. Wir nehmen die Test-EAs aus den entsprechenden Ordnern mit MQL5 EAs und speichern sie mit der Erweiterung *.mq4 im EA-Verzeichnis \MQL4\Experts\TestDoEasy (in den der Artikelnummer entsprechenden Ordner, der in diesem Fall Part09 ist).

Suchen Sie das Bibliotheksverzeichnis \MQL4\Include\DoEasy im Navigator des Editors, klicken Sie mit der rechten Maustaste darauf und wählen Sie Kompilieren.


Dadurch werden alle Bibliotheksdateien kompiliert, was zu über zweitausend Kompilierungsfehlern führt:


Wenn wir die angezeigten Fehler analysieren, werden wir sehen, dass ihre überwiegende Mehrheit Konstanten und Enumerationen von MQL5 betreffen, mit denen MQL4 nichts anzufangen weiß. Das bedeutet, dass wir MQL4 über die in der Bibliothek verwendeten Konstanten informieren müssen. Es gibt auch die Fehler einer anderen Art, wie das Fehlen bestimmter Funktionen, was bedeutet, dass wir ihre Betriebslogik mit MQL4-Funktionen umsetzen werden.

Außerdem sind die Auftragssysteme von MQL4 und MQL5 sehr unterschiedlich. Wir müssen für MQL4 eine eigene Ereignisbehandlung implementieren, der sich von dem in MQL5 implementierten unterscheidet, da die Liste der historischen Aufträge in MQL4 viel weniger Daten über Aufträge (und keine Daten über Geschäfte) liefert, was bedeutet, dass wir keine Daten über Aufträge und Geschäfte direkt aus den Klemmenlisten übernehmen können. Hier müssen wir die aufgetretenen Ereignisse in den Listen der aktiven und historischen Marktaufträge logisch vergleichen und die aufgetretenen Ereignisse anhand des Vergleichs definieren.

Verbessern der Bibliothek

Erstellen Sie im Stammverzeichnis der Bibliothek DoEasy die neue Include-Datei ToMQL4.mqh. Hier werden alle notwendigen Konstanten und Aufzählungen für MQL4 beschrieben. Einbinden in die Datei Defines.mqh für die MQL4-Kompilierung am Anfang von Defines.mqh:

//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
//+------------------------------------------------------------------+
//| Include-Dateien                                                  |
//+------------------------------------------------------------------+
#ifdef __MQL4__      
#include "ToMQL4.mqh"
#endif               
//+------------------------------------------------------------------+

Danach kann die gesamte MQL4-Bibliothek sehen, was während der Kompilierung in die Datei ToMQL4.mqh geschrieben wird.

Gehen wir zum Anfang der Fehlerliste in der Registerkarte Fehler der Editor-Toolbox, indem wir NumPad POS1 (Home) drücken oder einfach bis zum Anfang nach oben scrollen. Machen Sie einen Doppelklick auf den allerersten Fehler:


Der Editor bewegt uns zum Fehlerzeile in der Datei Defines.mqh:

//+------------------------------------------------------------------+
//| Liste der möglichen Handelsereignisse auf dem Konto              |
//+------------------------------------------------------------------+
enum ENUM_TRADE_EVENT
  {
   TRADE_EVENT_NO_EVENT = 0,                                // No trading event
   TRADE_EVENT_PENDING_ORDER_PLASED,                        // Pending-Order platziert
   TRADE_EVENT_PENDING_ORDER_REMOVED,                       // Pending-Order entfernt
//--- Mitglieder der Enumeration stimmen mit den Mitgliedern der Enumeration ENUM_DEAL_TYPE überein
//--- (constant order below should not be changed, no constants should be added/deleted)
   TRADE_EVENT_ACCOUNT_CREDIT = DEAL_TYPE_CREDIT,           // Charging credit (3)
   TRADE_EVENT_ACCOUNT_CHARGE,                              // Weitere Lastschrift

Natürlich weiß MQL4 nichts über Deals und deren Typisierung. Dies sollte behoben werden. Öffnen Sie einfach die MQL5-Referenz und suchen Sie mittels DEAL_TYPE_CREDIT nach Daten zu Eigenschaften der Deals:

ID

Beschreibung

Typ

DEAL_TICKET

Ticket des Deals. Eindeutige Nummer für jeden Deal

long

DEAL_ORDER

Order-Nummer des Deals

long

DEAL_TIME

Deal-Zeit

datetime

DEAL_TIME_MSC

Ausführungszeit des Deals in Millisekunden sein 01.01.1970

long

DEAL_TYPE

Deal-Typ

ENUM_DEAL_TYPE

DEAL_ENTRY

Deal-Richtung - Markteintritt, -austritt oder -umkehr

ENUM_DEAL_ENTRY

DEAL_MAGIC

Magicnummer des Deals (siehe ORDER_MAGIC)

long

DEAL_REASON

Ursache oder Quelle für die Ausführung des Deals

ENUM_DEAL_REASON

DEAL_POSITION_ID

Die ID der Position, die das Geschäft eröffnet, geändert oder geschlossen hat. Jede Position hat eine eindeutige ID, die allen Deals zugeordnet ist, die während der Lebensdauer der Position mit dem Symbol ausgeführt werden.

long


In der Tabelle sind wir am meisten an ENUM_DEAL_TYPE interessiert. Folgen wir dem Link und erhalten wir die Liste aller Dealtypen:

ID

Beschreibung

DEAL_TYPE_BUY

Kaufen

DEAL_TYPE_SELL

Verkaufen

DEAL_TYPE_BALANCE

Saldo

DEAL_TYPE_CREDIT

Gutschrift

DEAL_TYPE_CHARGE

Zusätzliche Lastschrift

DEAL_TYPE_CORRECTION

Korrektur

DEAL_TYPE_BONUS

Bonus

DEAL_TYPE_COMMISSION

Zusätzliche Kommission

DEAL_TYPE_COMMISSION_DAILY

Tägliche Kommission

DEAL_TYPE_COMMISSION_MONTHLY

Monatliche Kommission

DEAL_TYPE_COMMISSION_AGENT_DAILY

Tägliche Kommission des Agenten

DEAL_TYPE_COMMISSION_AGENT_MONTHLY

Monatliche Kommission des Agenten

DEAL_TYPE_INTEREST

Zinssatz

DEAL_TYPE_BUY_CANCELED

Stornierter Kauf-Deal. Es kann eine Situation geben, in der ein zuvor abgeschlossener Kauf-Deal storniert wird. In diesem Fall wird die Art des zuvor ausgeführten Deals (DEAL_TYPE_BUY) geändert in DEAL_TYPE_BUY_CANCELED, und sein Gewinn/Verlust wird auf Null gestellt. Zuvor erzielte Gewinne/Verluste werden mit Hilfe einer getrennten Saldenoperation berechnet bzw. entnommen.

DEAL_TYPE_SELL_CANCELED

Stornierter Verkaufs-Deal. Es kann eine Situation geben, in der ein zuvor abgeschlossenes Verkaufs-Deal storniert wird. In diesem Fall wird die Art des zuvor ausgeführten Deals (DEAL_TYPE_SELL) wird in DEAL_TYPE_SELL_CANCELED geändert, und sein Gewinn/Verlust wird auf Null gestellt. Zuvor erzielte Gewinne/Verluste werden mit Hilfe einer getrennten Saldenoperation berechnet bzw. entnommen.

DEAL_DIVIDEND

Dividendenzahlung

DEAL_DIVIDEND_FRANKED

Befreite (nicht zu verteuernde) Dividendenzahlung

DEAL_TAX

Steuerlastschrift


Fügen wir die Dealtypen aus der Enumeration ENUM_DEAL_TYPE zur Datei ToMQL4.mqh hinzu:

//+------------------------------------------------------------------+
//|                                                       ToMQL4.mqh |
//|              Copyright 2017, Artem A. Trishkin, Skype artmedia70 |
//|                         https://www.mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Artem A. Trishkin, Skype artmedia70"
#property link      "https://www.mql5.com/de/users/artmedia70"
#property strict
#ifdef __MQL4__
//+------------------------------------------------------------------+
//| MQL5 deal types                                                  |
//+------------------------------------------------------------------+
enum ENUM_DEAL_TYPE
  {
   DEAL_TYPE_BUY,
   DEAL_TYPE_SELL,
   DEAL_TYPE_BALANCE,
   DEAL_TYPE_CREDIT,
   DEAL_TYPE_CHARGE,
   DEAL_TYPE_CORRECTION,
   DEAL_TYPE_BONUS,
   DEAL_TYPE_COMMISSION,
   DEAL_TYPE_COMMISSION_DAILY,
   DEAL_TYPE_COMMISSION_MONTHLY,
   DEAL_TYPE_COMMISSION_AGENT_DAILY,
   DEAL_TYPE_COMMISSION_AGENT_MONTHLY,
   DEAL_TYPE_INTEREST,
   DEAL_TYPE_BUY_CANCELED,
   DEAL_TYPE_SELL_CANCELED,
   DEAL_DIVIDEND,
   DEAL_DIVIDEND_FRANKED,
   DEAL_TAX
  };
//+------------------------------------------------------------------+
#endif 

Speichern Sie die Datei und kompilieren Sie alle Bibliotheksdateien erneut. Es gibt jetzt weniger Fehler:


Gehen Sie erneut an den Anfang der Fehlerliste und klicken Sie auf den ersten Fehler. Jetzt ist es ENUM_POSITION_TYPE, also fügen wir das hinzu:

//+------------------------------------------------------------------+
//|                                                       ToMQL4.mqh |
//|              Copyright 2017, Artem A. Trishkin, Skype artmedia70 |
//|                         https://www.mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Artem A. Trishkin, Skype artmedia70"
#property link      "https://www.mql5.com/de/users/artmedia70"
#property strict
#ifdef __MQL4__
//+------------------------------------------------------------------+
//| MQL5 deal type                                                   |
//+------------------------------------------------------------------+
enum ENUM_DEAL_TYPE
  {
   DEAL_TYPE_BUY,
   DEAL_TYPE_SELL,
   DEAL_TYPE_BALANCE,
   DEAL_TYPE_CREDIT,
   DEAL_TYPE_CHARGE,
   DEAL_TYPE_CORRECTION,
   DEAL_TYPE_BONUS,
   DEAL_TYPE_COMMISSION,
   DEAL_TYPE_COMMISSION_DAILY,
   DEAL_TYPE_COMMISSION_MONTHLY,
   DEAL_TYPE_COMMISSION_AGENT_DAILY,
   DEAL_TYPE_COMMISSION_AGENT_MONTHLY,
   DEAL_TYPE_INTEREST,
   DEAL_TYPE_BUY_CANCELED,
   DEAL_TYPE_SELL_CANCELED,
   DEAL_DIVIDEND,
   DEAL_DIVIDEND_FRANKED,
   DEAL_TAX
  };
//+------------------------------------------------------------------+
//| Open position direction                                          |
//+------------------------------------------------------------------+
enum ENUM_POSITION_TYPE
  {
   POSITION_TYPE_BUY,
   POSITION_TYPE_SELL
  };
//+------------------------------------------------------------------+
#endif 

Nach dem Kompilieren erhalten wir noch weniger Fehler. Gehen Sie zum ersten Fehler in der Liste, definieren Sie den Grund und fügen Sie die folgende Enumeration hinzu:

//+------------------------------------------------------------------+
//|                                                       ToMQL4.mqh |
//|              Copyright 2017, Artem A. Trishkin, Skype artmedia70 |
//|                         https://www.mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Artem A. Trishkin, Skype artmedia70"
#property link      "https://www.mql5.com/de/users/artmedia70"
#property strict
#ifdef __MQL4__
//+------------------------------------------------------------------+
//| MQL5 deal types                                                  |
//+------------------------------------------------------------------+
enum ENUM_DEAL_TYPE
  {
   DEAL_TYPE_BUY,
   DEAL_TYPE_SELL,
   DEAL_TYPE_BALANCE,
   DEAL_TYPE_CREDIT,
   DEAL_TYPE_CHARGE,
   DEAL_TYPE_CORRECTION,
   DEAL_TYPE_BONUS,
   DEAL_TYPE_COMMISSION,
   DEAL_TYPE_COMMISSION_DAILY,
   DEAL_TYPE_COMMISSION_MONTHLY,
   DEAL_TYPE_COMMISSION_AGENT_DAILY,
   DEAL_TYPE_COMMISSION_AGENT_MONTHLY,
   DEAL_TYPE_INTEREST,
   DEAL_TYPE_BUY_CANCELED,
   DEAL_TYPE_SELL_CANCELED,
   DEAL_DIVIDEND,
   DEAL_DIVIDEND_FRANKED,
   DEAL_TAX
  };
//+------------------------------------------------------------------+
//| Open position direction                                          |
//+------------------------------------------------------------------+
enum ENUM_POSITION_TYPE
  {
   POSITION_TYPE_BUY,
   POSITION_TYPE_SELL
  };
//+------------------------------------------------------------------+
//| Order status                                                     |
//+------------------------------------------------------------------+
enum ENUM_ORDER_STATE
  {
   ORDER_STATE_STARTED,
   ORDER_STATE_PLACED,
   ORDER_STATE_CANCELED,
   ORDER_STATE_PARTIAL,
   ORDER_STATE_FILLED,
   ORDER_STATE_REJECTED,
   ORDER_STATE_EXPIRED,
   ORDER_STATE_REQUEST_ADD,
   ORDER_STATE_REQUEST_MODIFY,
   ORDER_STATE_REQUEST_CANCEL
  };
//+------------------------------------------------------------------+
#endif 

Durch die nächste Kompilierung haben wir den falschen Auftragstyp ORDER_TYPE_BUY_STOP_LIMIT erhalten.
MQL4 verfügt bereits über die Enumeration ENUM_ORDER_TYPE. Wir können ihr keine neuen Konstanten hinzufügen. Fügen wir sie daher als Makro-Substitutionen hinzu.

In MQL5 wird die Konstante ORDER_TYPE_BUY_STOP_LIMIT aus der ENUM_ORDER_TYPE Enumeration auf 6 gesetzt, während in MQL4 ein solcher Auftragstyp existiert. Diese Saldenoperation wird wie ORDER_TYPE_SELL_STOP_LIMIT in MQL5 auf 7 gesetzt, während in MQL4 diese Auftragsart eine Gutschrift ist.

Setzen wir daher höhere Werte für die Konstante ORDER_TYPE_CLOSE_BY für das Schließen einer Order: ORDER_TYPE_CLOSE_BY+1 und entsprechend ORDER_TYPE_CLOSE_BY+2:

//+------------------------------------------------------------------+
//|                                                       ToMQL4.mqh |
//|              Copyright 2017, Artem A. Trishkin, Skype artmedia70 |
//|                         https://www.mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Artem A. Trishkin, Skype artmedia70"
#property link      "https://www.mql5.com/de/users/artmedia70"
#property strict
#ifdef __MQL4__
//+------------------------------------------------------------------+
//| MQL5 deal types                                                  |
//+------------------------------------------------------------------+
enum ENUM_DEAL_TYPE
  {
   DEAL_TYPE_BUY,
   DEAL_TYPE_SELL,
   DEAL_TYPE_BALANCE,
   DEAL_TYPE_CREDIT,
   DEAL_TYPE_CHARGE,
   DEAL_TYPE_CORRECTION,
   DEAL_TYPE_BONUS,
   DEAL_TYPE_COMMISSION,
   DEAL_TYPE_COMMISSION_DAILY,
   DEAL_TYPE_COMMISSION_MONTHLY,
   DEAL_TYPE_COMMISSION_AGENT_DAILY,
   DEAL_TYPE_COMMISSION_AGENT_MONTHLY,
   DEAL_TYPE_INTEREST,
   DEAL_TYPE_BUY_CANCELED,
   DEAL_TYPE_SELL_CANCELED,
   DEAL_DIVIDEND,
   DEAL_DIVIDEND_FRANKED,
   DEAL_TAX
  };
//+------------------------------------------------------------------+
//| Open position direction                                          |
//+------------------------------------------------------------------+
enum ENUM_POSITION_TYPE
  {
   POSITION_TYPE_BUY,
   POSITION_TYPE_SELL
  };
//+------------------------------------------------------------------+
//| Order status                                                     |
//+------------------------------------------------------------------+
enum ENUM_ORDER_STATE
  {
   ORDER_STATE_STARTED,
   ORDER_STATE_PLACED,
   ORDER_STATE_CANCELED,
   ORDER_STATE_PARTIAL,
   ORDER_STATE_FILLED,
   ORDER_STATE_REJECTED,
   ORDER_STATE_EXPIRED,
   ORDER_STATE_REQUEST_ADD,
   ORDER_STATE_REQUEST_MODIFY,
   ORDER_STATE_REQUEST_CANCEL
  };
//+------------------------------------------------------------------+
//| Order types                                                      |
//+------------------------------------------------------------------+
#define ORDER_TYPE_CLOSE_BY         (8) 
#define ORDER_TYPE_BUY_STOP_LIMIT   (9) 
#define ORDER_TYPE_SELL_STOP_LIMIT  (10)
//+------------------------------------------------------------------+
#endif 

Kompilieren wir die gesamte Bibliothek. Nach der Implementierung von Makro-Substitutionen von StopLimit-Ordertypen zeigt der Fehler die Funktionen an, die den korrekten Preis bei einer Orderplatzierung zurückgeben, nämlich die Enumeration ENUM_ORDER_TYPE ohne die Werte 9 und 10, da wir den Wert des Ordertyps in dem Switch-Operator mit dem Aufzählungstyp ENUM_ORDER_TYPE verwenden:

//+------------------------------------------------------------------+
//| Rückgabe des Preises der korrekten Order-Platzierung             |
//| relative zum StopLevel                                           |
//+------------------------------------------------------------------+
double CorrectPricePending(const string symbol_name,const ENUM_ORDER_TYPE order_type,const double price_set,const double price=0,const int spread_multiplier=2)
  {
   double pt=SymbolInfoDouble(symbol_name,SYMBOL_POINT),pp=0;
   int lv=StopLevel(symbol_name,spread_multiplier), dg=(int)SymbolInfoInteger(symbol_name,SYMBOL_DIGITS);
   switch(order_type)
     {
      case ORDER_TYPE_BUY_LIMIT        :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmin(pp-lv*pt,price_set),dg);
      case ORDER_TYPE_BUY_STOP         :  
      case ORDER_TYPE_BUY_STOP_LIMIT   :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmax(pp+lv*pt,price_set),dg);
      case ORDER_TYPE_SELL_LIMIT       :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmax(pp+lv*pt,price_set),dg);
      case ORDER_TYPE_SELL_STOP        :  
      case ORDER_TYPE_SELL_STOP_LIMIT  :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmin(pp-lv*pt,price_set),dg);
      default                          :  Print(DFUN,TextByLanguage("Неправильный тип ордера: ","Invalid order type: "),EnumToString(order_type)); return 0;
     }
  }
//+------------------------------------------------------------------+
//| Rückgabe des Preises der korrekten Order-Platzierung             |
//| relative zum StopLevel                                           |
//+------------------------------------------------------------------+
double CorrectPricePending(const string symbol_name,const ENUM_ORDER_TYPE order_type,const int distance_set,const double price=0,const int spread_multiplier=2)
  {
   double pt=SymbolInfoDouble(symbol_name,SYMBOL_POINT),pp=0;
   int lv=StopLevel(symbol_name,spread_multiplier), dg=(int)SymbolInfoInteger(symbol_name,SYMBOL_DIGITS);
   switch(order_type)
     {
      case ORDER_TYPE_BUY_LIMIT        :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmin(pp-lv*pt,pp-distance_set*pt),dg);
      case ORDER_TYPE_BUY_STOP         :  
      case ORDER_TYPE_BUY_STOP_LIMIT   :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmax(pp+lv*pt,pp+distance_set*pt),dg);
      case ORDER_TYPE_SELL_LIMIT       :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmax(pp+lv*pt,pp+distance_set*pt),dg);
      case ORDER_TYPE_SELL_STOP        :  
      case ORDER_TYPE_SELL_STOP_LIMIT  :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmin(pp-lv*pt,pp-distance_set*pt),dg);
      default                          :  Print(DFUN,TextByLanguage("Неправильный тип ордера: ","Invalid order type: "),EnumToString(order_type)); return 0;
     }
  }
//+------------------------------------------------------------------+

Die Lösung ist einfach — order_type in switch wird umgewandelt in ganze Zahlen:

//+------------------------------------------------------------------+
//| Rückgabe des Preises der korrekten Order-Platzierung             |
//| relative zum StopLevel                                           |
//+------------------------------------------------------------------+
double CorrectPricePending(const string symbol_name,const ENUM_ORDER_TYPE order_type,const double price_set,const double price=0,const int spread_multiplier=2)
  {
   double pt=SymbolInfoDouble(symbol_name,SYMBOL_POINT),pp=0;
   int lv=StopLevel(symbol_name,spread_multiplier), dg=(int)SymbolInfoInteger(symbol_name,SYMBOL_DIGITS);
   switch((int)order_type)
     {
      case ORDER_TYPE_BUY_LIMIT        :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmin(pp-lv*pt,price_set),dg);
      case ORDER_TYPE_BUY_STOP         :  
      case ORDER_TYPE_BUY_STOP_LIMIT   :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmax(pp+lv*pt,price_set),dg);
      case ORDER_TYPE_SELL_LIMIT       :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmax(pp+lv*pt,price_set),dg);
      case ORDER_TYPE_SELL_STOP        :  
      case ORDER_TYPE_SELL_STOP_LIMIT  :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmin(pp-lv*pt,price_set),dg);
      default                          :  Print(DFUN,TextByLanguage("Неправильный тип ордера: ","Invalid order type: "),EnumToString(order_type)); return 0;
     }
  }
//+------------------------------------------------------------------+
//| Rückgabe des Preises der korrekten Order-Platzierung             |
//| relative zum StopLevel                                           |
//+------------------------------------------------------------------+
double CorrectPricePending(const string symbol_name,const ENUM_ORDER_TYPE order_type,const int distance_set,const double price=0,const int spread_multiplier=2)
  {
   double pt=SymbolInfoDouble(symbol_name,SYMBOL_POINT),pp=0;
   int lv=StopLevel(symbol_name,spread_multiplier), dg=(int)SymbolInfoInteger(symbol_name,SYMBOL_DIGITS);
   switch((int)order_type)
     {
      case ORDER_TYPE_BUY_LIMIT        :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmin(pp-lv*pt,pp-distance_set*pt),dg);
      case ORDER_TYPE_BUY_STOP         :  
      case ORDER_TYPE_BUY_STOP_LIMIT   :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmax(pp+lv*pt,pp+distance_set*pt),dg);
      case ORDER_TYPE_SELL_LIMIT       :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmax(pp+lv*pt,pp+distance_set*pt),dg);
      case ORDER_TYPE_SELL_STOP        :  
      case ORDER_TYPE_SELL_STOP_LIMIT  :  pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmin(pp-lv*pt,pp-distance_set*pt),dg);
      default                          :  Print(DFUN,TextByLanguage("Неправильный тип ордера: ","Invalid order type: "),EnumToString(order_type)); return 0;
     }
  }
//+------------------------------------------------------------------+

Kompilieren wir. Jetzt gibt es einen Fehler in der Datei Order.mqh — MQL4 kennt nicht diese Konstanten: ORDER_FILLING_RETURN, ORDER_TIME_GTC, ORDER_REASON_SL, ORDER_REASON_TP und ORDER_REASON_EXPERT.

//+------------------------------------------------------------------+
//| Rückgabe des Ausführungstyps des Restes                          |
//+------------------------------------------------------------------+
long COrder::OrderTypeFilling(void) const
  {
#ifdef __MQL4__
   return (long)ORDER_FILLING_RETURN;
#else 
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ORDER      :
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_TYPE_FILLING);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_TYPE_FILLING);break;
      default                             : res=0;                                                    break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Rückgabe der Laufzeit der Order                                  |
//+------------------------------------------------------------------+
long COrder::OrderTypeTime(void) const
  {
#ifdef __MQL4__
   return (long)ORDER_TIME_GTC;
#else 
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ORDER      :
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_TYPE_TIME);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_TYPE_TIME);break;
      default                             : res=0;                                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Grund oder Quelle der Order                                      |
//+------------------------------------------------------------------+
long COrder::OrderReason(void) const
  {
#ifdef __MQL4__
   return
     (
      this.OrderCloseByStopLoss()   ?  ORDER_REASON_SL      :
      this.OrderCloseByTakeProfit() ?  ORDER_REASON_TP      :  
      this.OrderMagicNumber()!=0    ?  ORDER_REASON_EXPERT  : WRONG_VALUE
     );
#else 
   long res=WRONG_VALUE;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_POSITION   : res=::PositionGetInteger(POSITION_REASON);          break;
      case ORDER_STATUS_MARKET_ORDER      :
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_REASON);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_REASON);break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_REASON);  break;
      default                             : res=WRONG_VALUE;                                    break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

Fügen wir Makro-Substitutionen am Ende der ToMQL4.mqh-Datei hinzu (ich werde hier, um Platz zu sparen, keine vollständige Liste geben):

//+------------------------------------------------------------------+
//| Order types, execution policy, lifetime, reasons                 |
//+------------------------------------------------------------------+
#define ORDER_TYPE_CLOSE_BY         (8)
#define ORDER_TYPE_BUY_STOP_LIMIT   (9)
#define ORDER_TYPE_SELL_STOP_LIMIT  (10)
#define ORDER_FILLING_RETURN        (2)
#define ORDER_TIME_GTC              (0)
#define ORDER_REASON_EXPERT         (3)
#define ORDER_REASON_SL             (4)
#define ORDER_REASON_TP             (5)
//+------------------------------------------------------------------+
#endif 

Eine weitere Zusammenstellung führt uns zu der fehlenden MQL5-Funktion HistoryOrderGetTicket() in der Datei HistoryCollection.mqh der Methode CHistoryCollection::OrderSearch(). Die Codeanalyse schlägt vor, hier bedingte Kompilierungsanweisungen anzuwenden. Lassen Sie uns die Methode ergänzen:

//+------------------------------------------------------------------+
//| Liefert den Typ und das Ticket der "verlorenen" Auftrags zurück  |
//+------------------------------------------------------------------+
ulong CHistoryCollection::OrderSearch(const int start,ENUM_ORDER_TYPE &order_type)
  {
   ulong order_ticket=0;
#ifdef __MQL5__
   for(int i=start-1;i>=0;i--)
     {
      ulong ticket=::HistoryOrderGetTicket(i);
      if(ticket==0)
         continue;
      ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)::HistoryOrderGetInteger(ticket,ORDER_TYPE);
      if(this.IsPresentOrderInList(ticket,type))
         continue;
      order_ticket=ticket;
      order_type=type;
     }
#else 
   for(int i=start-1;i>=0;i--)
     {
      if(!::OrderSelect(i,SELECT_BY_POS,MODE_HISTORY))
         continue;
      ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)::OrderType();
      ulong ticket=::OrderTicket();
      if(ticket==0 || type<ORDER_TYPE_BUY_LIMIT || type>ORDER_TYPE_SELL_STOP)
         continue;
      if(this.IsPresentOrderInList(ticket,type))
         continue;
      order_ticket=ticket;
      order_type=type;
     }
#endif    
   return order_ticket;
  }
//+------------------------------------------------------------------+

Alles, was für MQL5 gedacht ist sind eingerahmt von der Direktive #ifdef __MQL5__. Der Code wird für MQL4 nach der #else Direktive bis zu #endif hinzugefügt.

Der nächste Fehler befindet sich im Klassenkonstruktor CEvent. Ergänzen wir den Code mit den gleichen bedingten Kompilierungsanweisungen:

//+------------------------------------------------------------------+
//| Konstruktor                                                      |
//+------------------------------------------------------------------+
CEvent::CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket) : m_event_code(event_code),m_digits(0)
  {
   this.m_long_prop[EVENT_PROP_STATUS_EVENT]       =  event_status;
   this.m_long_prop[EVENT_PROP_TICKET_ORDER_EVENT] =  (long)ticket;
   this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif;
   this.m_digits_acc=#ifdef __MQL4__ 2 #else (int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS) #endif;
   this.m_chart_id=::ChartID();
  }
//+------------------------------------------------------------------+

Wenn wir ein Hedging-Konto überprüfen, sehen wir uns dem Fehlen eines konstanten Fehlers gegenüber, daher einfach return true auf einmal, da alle Konten im MetaTrader 4 Hedging-Konten sind.
Auch beim Empfangen der Anzahl der Dezimalstellen in der Kontowährung, return 2, da MQL4 diesen Wert nicht erhalten kann.

Die nächste Kompilierung führt uns zur Methode CEventsCollection::NewDealEventHedge() — ein Ereignis für ein MetaTrader 5 Hedging-Konto zu erhalten. Es funktioniert mit Deals, die in MQL4 fehlen. Vorübergehendes Deaktivieren der Methode durch Einschließen des gesamten Methodencodes im Rahmen der bedingten Kompilierung:

Fügen wir die Direktive am Anfang der Methode ein.

//+------------------------------------------------------------------+
//| Create a hedging account event                                   |
//+------------------------------------------------------------------+
void CEventsCollection::NewDealEventHedge(COrder* deal,CArrayObj* list_history,CArrayObj* list_market)
  {
#ifdef __MQL5__
   double ask=::SymbolInfoDouble(deal.Symbol(),SYMBOL_ASK);
   double bid=::SymbolInfoDouble(deal.Symbol(),SYMBOL_BID);
   //--- Markteintritt

und am Ende der Methode

#endif 
  }
//+------------------------------------------------------------------+

Als Nächstes erhalten wir den Fehler in der Methode CEventsCollection::NewDealEventNetto() — das Erstellen eines Ereignisses für ein Netting-Konto. Die Lösung ist die gleiche wie im vorherigen Fall — umgeben wir den gesamten Methodencode NewDealEventNetto() mit der Direktive zur bedingten Kompilierung.

Kompilieren und behandeln wir den Fehler der unbekannten Konstanten DEAL_ENTRY_IN in der Methode CEventsCollection::GetListAllDealsInByPosID(). Hinzufügen der notwendigen Enumeration zur Datei ToMQL4.mqh (wir könnten die bedingte Kompilierung wieder verwenden, um den Code zu deaktivieren, aber wir können diese Aufzählung später benötigen):

//+------------------------------------------------------------------+
//| MQL5 deal types                                                  |
//+------------------------------------------------------------------+
enum ENUM_DEAL_TYPE
  {
   DEAL_TYPE_BUY,
   DEAL_TYPE_SELL,
   DEAL_TYPE_BALANCE,
   DEAL_TYPE_CREDIT,
   DEAL_TYPE_CHARGE,
   DEAL_TYPE_CORRECTION,
   DEAL_TYPE_BONUS,
   DEAL_TYPE_COMMISSION,
   DEAL_TYPE_COMMISSION_DAILY,
   DEAL_TYPE_COMMISSION_MONTHLY,
   DEAL_TYPE_COMMISSION_AGENT_DAILY,
   DEAL_TYPE_COMMISSION_AGENT_MONTHLY,
   DEAL_TYPE_INTEREST,
   DEAL_TYPE_BUY_CANCELED,
   DEAL_TYPE_SELL_CANCELED,
   DEAL_DIVIDEND,
   DEAL_DIVIDEND_FRANKED,
   DEAL_TAX
  };
//+------------------------------------------------------------------+
//| Position change method                                           |
//+------------------------------------------------------------------+
enum ENUM_DEAL_ENTRY
  {
   DEAL_ENTRY_IN,
   DEAL_ENTRY_OUT,
   DEAL_ENTRY_INOUT,
   DEAL_ENTRY_OUT_BY
  };
//+------------------------------------------------------------------+
//| Open position direction                                          |
//+------------------------------------------------------------------+
enum ENUM_POSITION_TYPE
  {
   POSITION_TYPE_BUY,
   POSITION_TYPE_SELL
  };
//+------------------------------------------------------------------+

Als Nächstes kommen wir zu dem bereits bekannten Fehler, bei Prüfen eine Hedging-Kontos, aber jetzt sitzt er im Konstruktor der Klasse Event Collection. Reparieren wir es:

//+------------------------------------------------------------------+
//| Konstruktor                                                      |
//+------------------------------------------------------------------+
CEventsCollection::CEventsCollection(void) : m_trade_event(TRADE_EVENT_NO_EVENT),m_trade_event_code(TRADE_EVENT_FLAG_NO_EVENT)
  {
   this.m_list_events.Clear();
   this.m_list_events.Sort(SORT_BY_EVENT_TIME_EVENT);
   this.m_list_events.Type(COLLECTION_EVENTS_ID);
   this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif;
   this.m_chart_id=::ChartID();
   ::ZeroMemory(this.m_tick);
  }
//+------------------------------------------------------------------+

Als Nächstes implementieren wir die gleiche Korrektur im Klassenkonstruktor CEngine:

//+------------------------------------------------------------------+
//| CEngine Konstruktor                                              |
//+------------------------------------------------------------------+
CEngine::CEngine() : m_first_start(true),m_acc_trade_event(TRADE_EVENT_NO_EVENT)
  {
   ::ResetLastError();
   if(!::EventSetMillisecondTimer(TIMER_FREQUENCY))
      Print(DFUN,"Не удалось создать таймер. Ошибка: ","Could not create timer. Error: ",(string)::GetLastError());
   this.m_list_counters.Sort();
   this.m_list_counters.Clear();
   this.CreateCounter(COLLECTION_COUNTER_ID,COLLECTION_COUNTER_STEP,COLLECTION_PAUSE);
   this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif;
  }
//+------------------------------------------------------------------+

Alles ist eingestellt. Jetzt kann die ganze Bibliothek ohne Fehler kompiliert werden. Jetzt müssen wir es starten. Jetzt müssen wir ihn starten. Da wir einige Methoden mit bedingter Kompilierung deaktiviert haben, müssen wir sie für die Arbeit mit MetaTrader 4 entwickeln.

In MQL5 sind Saldenoperationen Deals. Sie finden Sie in der Liste der historischen Aufträge und Deals. In MQL4 sind Saldenoperationen Aufträge vom Typ ORDER_TYPE_BALANCE (6) und ORDER_TYPE_CREDIT (7). Deshalb habe ich eine eigene Klasse eines Objekts für Saldenoperationen für MQL4 erstellt, die in der Liste der historischen Orders und Positionen gespeichert ist.

Wir erstellen die neue Klasse CHistoryBalance in Verzeichnis \MQL4\Include\DoEasy\Objects\Orders in der Datei HistoryBalance.mqh. COrder sollte eine Basisklasse sein:

//+------------------------------------------------------------------+
//|                                               HistoryBalance.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include-Dateien                                                  |
//+------------------------------------------------------------------+
#include "Order.mqh"
//+------------------------------------------------------------------+
//| Historical balance operation                                     |
//+------------------------------------------------------------------+
class CHistoryBalance : public COrder
  {
public:
   //--- Konstructor
                     CHistoryBalance(const ulong ticket) : COrder(ORDER_STATUS_BALANCE,ticket) {}
   //--- Unterstützte Deal-Eigenschaften (1) real, (2) integer
   virtual bool      SupportProperty(ENUM_ORDER_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_ORDER_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_ORDER_PROP_STRING property);
  };
//+------------------------------------------------------------------+
//| Return 'true' if an order supports a passed                      |
//| Integer-Eigenschaft unterstützt, sonst 'false'                   |
//+------------------------------------------------------------------+
bool CHistoryBalance::SupportProperty(ENUM_ORDER_PROP_INTEGER property)
  {
   if(property==ORDER_PROP_TICKET      ||
      property==ORDER_PROP_TIME_OPEN   || 
      property==ORDER_PROP_STATUS      ||
      property==ORDER_PROP_TYPE        ||
      property==ORDER_PROP_REASON
     ) return true;
   return false;
  }
//+------------------------------------------------------------------+
//| Return 'true' if an order supports a passed                      |
//| Double-Eigenschaft, sonst 'false'                                |
//+------------------------------------------------------------------+
bool CHistoryBalance::SupportProperty(ENUM_ORDER_PROP_DOUBLE property)
  {
   return(property==ORDER_PROP_PROFIT ? true : false);
  }
//+------------------------------------------------------------------+
//| Return 'true' if an order supports a passed                      |
//| String-Eigenschaft, sonst 'false'                                |
//+------------------------------------------------------------------+
bool CHistoryBalance::SupportProperty(ENUM_ORDER_PROP_STRING property)
  {
   if(property==ORDER_PROP_SYMBOL || property==ORDER_PROP_EXT_ID)
      return false;
   return true;
  }
//+------------------------------------------------------------------+

Die Klasse enthält für uns nichts Neues. Wir haben bereits alle historischen Orderklassen im zweiten Teil der Bibliotheksbeschreibung besprochen.

Wir haben zwei Arten von Saldenoperationen — Saldenoperation und die Gutschrift. Dementsprechend haben ihre Typen die Zahlenwerte 6 und 7. Wir werden für beide Typen eine einzige Klasse der Saldenoperationen verwenden und einen bestimmten Typ in der Auftragseigenschaft "Ursache" klären.

Fügen wir der Datei ToMQL4.mqh die zwei fehlenden "Ursachen" der Orders hinzu:

//+------------------------------------------------------------------+
//| Order types, execution policy, lifetime, reasons                 |
//+------------------------------------------------------------------+
#define ORDER_TYPE_CLOSE_BY         (8)
#define ORDER_TYPE_BUY_STOP_LIMIT   (9)
#define ORDER_TYPE_SELL_STOP_LIMIT  (10)
#define ORDER_FILLING_RETURN        (2)
#define ORDER_TIME_GTC              (0)
#define ORDER_REASON_EXPERT         (3)
#define ORDER_REASON_SL             (4)
#define ORDER_REASON_TP             (5)
#define ORDER_REASON_BALANCE        (6)
#define ORDER_REASON_CREDIT         (7)
//+------------------------------------------------------------------+

Da wir eine neue Klasse haben, die von der abstrakten Orderklasse abgeleitet ist, müssen wir die fehlende Funktionsweise in COrder hinzufügen.

Ersetzen wir in der Methode COrder::OrderPositionID() die Rückgabe der Magicnummer für MQL4

//+------------------------------------------------------------------+
//| Rückgabe der Positions-ID                                        |
//+------------------------------------------------------------------+
long COrder::OrderPositionID(void) const
  {
#ifdef __MQL4__
   return ::OrderMagicNumber();
#else

durch die Rückgabe des Tickets (eine Art PositionID für MQL4-Positionen soll später implementiert werden):

//+------------------------------------------------------------------+
//| Rückgabe der Positions-ID                                        |
//+------------------------------------------------------------------+
long COrder::OrderPositionID(void) const
  {
#ifdef __MQL4__
   return ::OrderTicket();
#else

Die Methode, die den Auftragsstatus in MQL4 zurückgibt, gibt immer ORDER_STATE_FILLED aus der Enumeration ENUM_ORDER_STATE zurück, was bei Remote-Pending-Orders nicht der Fall ist. Implementieren wir die Prüfung des Auftragsstatus, und wenn es sich um einen Remote-Pending-Order handelt, geben wir ORDER_STATE_CANCELED zurück.

//+------------------------------------------------------------------+
//| Rückgabe des des Auftragsstatus                                  |
//+------------------------------------------------------------------+
long COrder::OrderState(void) const
  {
#ifdef __MQL4__
   return(this.Status()==ORDER_STATUS_HISTORY_ORDER ? ORDER_STATE_FILLED : ORDER_STATE_CANCELED);
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_STATE); break;
      case ORDER_STATUS_MARKET_ORDER      :
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_STATE);                 break;
      case ORDER_STATUS_MARKET_POSITION   : 
      case ORDER_STATUS_DEAL              : 
      default                             : res=0;                                              break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+

Fügen wir die beiden neu hinzugefügten "Ursachen" zu der Methode hinzu, die die Auftragsursache für MQL4 zurückgibt:

//+------------------------------------------------------------------+
//| Grund oder Quelle der Order                                      |
//+------------------------------------------------------------------+
long COrder::OrderReason(void) const
  {
#ifdef __MQL4__
   return
     (
      this.TypeOrder()==ORDER_TYPE_BALANCE   ?  ORDER_REASON_BALANCE :
      this.TypeOrder()==ORDER_TYPE_CREDIT    ?  ORDER_REASON_CREDIT  :
      this.OrderCloseByStopLoss()            ?  ORDER_REASON_SL      :
      this.OrderCloseByTakeProfit()          ?  ORDER_REASON_TP      :  
      this.OrderMagicNumber()!=0             ?  ORDER_REASON_EXPERT  : WRONG_VALUE
     );
#else 

In unserem Fall gibt die Methode, die das unausgeführtes Volumen zurückgibt, für MQL4 immer eine Order-Losgröße zurück, was für Positionen falsch ist. Für Remot-Pending-Order werden wir eine Lot-Order zurückgeben, aber für Positionen Null:

//+------------------------------------------------------------------+
//| Rückgabe des nicht ausgeführten Volumens                         |
//+------------------------------------------------------------------+
double COrder::OrderVolumeCurrent(void) const
  {
#ifdef __MQL4__
   return(this.Status()==ORDER_STATUS_HISTORY_PENDING ? ::OrderLots() : 0);
#else 

Fügen Sie Beschreibungen der beiden neuen "Ursachen" in der Methode hinzu, die die Ursachenbeschreibung der Order zurückgibt. Bei der Saldenoperationo und der Gutschrift überprüfen wir den Gewinn. Wenn er Null überschreitet, werden die Gelder eingezahlt, andernfalls werden die Gelder abgehoben:

//+------------------------------------------------------------------+
//| Reason description                                               |
//+------------------------------------------------------------------+
string COrder::GetReasonDescription(const long reason) const
  {
#ifdef __MQL4__
   return
     (
      this.IsCloseByStopLoss()            ?  TextByLanguage("Срабатывание StopLoss","Due to StopLoss")                  :
      this.IsCloseByTakeProfit()          ?  TextByLanguage("Срабатывание TakeProfit","Due to TakeProfit")              :
      this.Reason()==ORDER_REASON_EXPERT  ?  TextByLanguage("Выставлен из mql4-программы","Placed from mql4 program")   :
      this.Comment()=="cancelled"         ?  TextByLanguage("Отменён","Cancelled")                                      :
      this.Reason()==ORDER_REASON_BALANCE ?  (
                                              this.Profit()>0 ? TextByLanguage("Пополнение баланса","Deposit of funds on the account balance") :
                                              TextByLanguage("Снятие средств с баланса","Withdrawal from the balance")
                                             )                                                                          :
      this.Reason()==ORDER_REASON_CREDIT  ?  (
                                              this.Profit()>0 ? TextByLanguage("Начисление кредитных средств","Received credit funds") :
                                              TextByLanguage("Изъятие кредитных средств","Withdrawal of credit")
                                             )                                                                          :
                                             
      TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4")
     );
#else 

Außerdem wurden einige kleinere Änderungen vorgenommen. Sie sind zu unbedeutend, um hier beschrieben zu werden. Sie beziehen sich hauptsächlich auf einen Text, der im Journal von MQL5/MQL4 angezeigt wird. Alle Bearbeitungen sind in den Bibliotheksdateien verfügbar, die dem Artikel beigefügt sind.

Nun verbessern wir die Klasse der historischen Kollektion in der Datei HistoryCollection.mqh.
Zuerst muss die neue Klassendatei eingebunden werden:

//+------------------------------------------------------------------+
//| Include-Dateien                                                  |
//+------------------------------------------------------------------+
#include "ListObj.mqh"
#include "..\Services\Select.mqh"
#include "..\Objects\Orders\HistoryOrder.mqh"
#include "..\Objects\Orders\HistoryPending.mqh"
#include "..\Objects\Orders\HistoryDeal.mqh"
#ifdef __MQL4__
#include "..\Objects\Orders\HistoryBalance.mqh"
#endif 
//+------------------------------------------------------------------+

Da wir die Klasse CHistoryBalance nur für die MQL4-Version der Bibliothek benötigen, ist die Datei dieser Klasse in den Anweisungen zur bedingten Kompilierung für MQL4 enthalten.

Jetzt haben wir eine neue Klasse der Saldenoperationen. Um sie zu entwickeln und in die Kollektion zu stellen, müssen wir die Prüfung von Auftragstypen auf die Art eines Saldos und einer Kreditoperation hinzufügen und sie in der Methode Refresh() der Klasse CHistoryCollection für MQL4 zur Kollektion hinzufügen:

//+------------------------------------------------------------------+
//| Aktualisieren der Liste der Aufträge und Deals                   |
//+------------------------------------------------------------------+
void CHistoryCollection::Refresh(void)
  {
#ifdef __MQL4__
   int total=::OrdersHistoryTotal(),i=m_index_order;
   for(; i<total; i++)
     {
      if(!::OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)) continue;
      ENUM_ORDER_TYPE order_type=(ENUM_ORDER_TYPE)::OrderType();
      //--- Closed positions
      if(order_type<ORDER_TYPE_BUY_LIMIT)
        {
         CHistoryOrder *order=new CHistoryOrder(::OrderTicket());
         if(order==NULL) continue;
         if(!this.m_list_all_orders.InsertSort(order))
           {
            ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Could not add order to the list"));
            delete order;
           }
        }
      //--- Balance/credit operations
      else if(order_type>ORDER_TYPE_SELL_STOP)
        {
         CHistoryBalance *order=new CHistoryBalance(::OrderTicket());
         if(order==NULL) continue;
         if(!this.m_list_all_orders.InsertSort(order))
           {
            ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Could not add order to the list"));
            delete order;
           }
        }
      else
        {
         //--- Entfernte Pending-Order
         CHistoryPending *order=new CHistoryPending(::OrderTicket());
         if(order==NULL) continue;
         if(!this.m_list_all_orders.InsertSort(order))
           {
            ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Could not add order to the list"));
            delete order;
           }
        }
     }
//---
   int delta_order=i-m_index_order;
   this.m_index_order=i;
   this.m_delta_order=delta_order;
   this.m_is_trade_event=(this.m_delta_order!=0 ? true : false);
//--- __MQL5__
#else 
Lassen Sie uns einige Korrekturen in der historischen Orderklasse vornehmen:
//+------------------------------------------------------------------+
//| Return 'true' if an order supports the passed                    |
//| Double-Eigenschaft, sonst 'false'                                |
//+------------------------------------------------------------------+
bool CHistoryOrder::SupportProperty(ENUM_ORDER_PROP_DOUBLE property)
  {
   if(
   #ifdef __MQL5__
      property==ORDER_PROP_PROFIT                  || 
      property==ORDER_PROP_PROFIT_FULL             || 
      property==ORDER_PROP_SWAP                    || 
      property==ORDER_PROP_COMMISSION              ||
      property==ORDER_PROP_PRICE_CLOSE             ||
      (
       property==ORDER_PROP_PRICE_STOP_LIMIT       && 
       (
        this.TypeOrder()<ORDER_TYPE_BUY_STOP_LIMIT || 
        this.TypeOrder()>ORDER_TYPE_SELL_STOP_LIMIT  
       )
      )
   #else
      property==ORDER_PROP_PRICE_STOP_LIMIT        && 
      this.Status()==ORDER_STATUS_HISTORY_ORDER
   #endif 
     ) return false;

   return true;
  }
//+------------------------------------------------------------------+

Bisher wurde der Preis einer StopLimit-Order in MQL5 nicht an das Journal weitergegeben. Deshalb habe ich einen Check implementiert: wenn die geprüfte Eigenschaft ein StopLimit-Orderpreis ist, und wenn ein Auftragstyp nicht vom Typ StopLimit ist, wird die Eigenschaft nicht verwendet. Andernfalls handelt es sich um eine StopLimit-Order und die Eigenschaft ist notwendig.
In MQL4 wird
ein StopLimit-Orderpreis nicht für Positionen verwendet.

Damit ist die Verbesserung der ersten Stufe der Kompatibilität mit MQL4 abgeschlossen.

Tests

Zu Testzwecken nehmen wir den ES TestDoEasyPart03_1.mq5 aus \MQL5\Experts\TestDoEasy\Part03 und speichern ihn unter dem Namen TestDoEasyPart09.mq4 im Ordner für MQL4 EAs \MQL4\Experts\TestDoEasyPart09.

Der EA wird ohne Änderungen kompiliert, aber wenn wir einen Blick auf den Code werfen, stellt sich heraus, dass er die Liste der Deals verwendet, die in MQL4 fehlen:

//--- Enumerationen
enum ENUM_TYPE_ORDERS
  {
   TYPE_ORDER_MARKET,   // Marktorder
   TYPE_ORDER_PENDING,  // Pending-Order
   TYPE_ORDER_DEAL      // Deals
  };
//--- Eingabeparameter

//+------------------------------------------------------------------+
//| Initialisierungsfunktion des Experten                            |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Aktualisieren der Historie
   history.Refresh();
//--- get the collection list within the date range
   CArrayObj* list=history.GetListByTime(InpTimeBegin,InpTimeEnd,SELECT_BY_TIME_CLOSE);
   if(list==NULL)
     {
      Print("Could not get collection list");
      return INIT_FAILED;
     }
   int total=list.Total();
   for(int i=0;i<total;i++)
     {
      //--- Abrufen des Auftrags aus der Liste
      COrder* order=list.At(i);
      if(order==NULL) continue;
      //--- Falls es ein Deal ist
      if(order.Status()==ORDER_STATUS_DEAL && InpOrderType==TYPE_ORDER_DEAL)
         order.Print();
      //--- Falls es eine historische Marktorder ist
      if(order.Status()==ORDER_STATUS_HISTORY_ORDER && InpOrderType==TYPE_ORDER_MARKET)
         order.Print();
      //--- Falles es eine entfernte Pending-Order ist
      if(order.Status()==ORDER_STATUS_HISTORY_PENDING && InpOrderType==TYPE_ORDER_PENDING)
         order.Print();
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Ersetzen wir einfach diese Deals durch Saldenoperationen. In diesem Fall verwenden wir die bedingte Kompilierung direkt im EA, was für das Endprodukt nicht korrekt ist, wo alle Aktionen zur Abgrenzung nach Sprachversionen für den Benutzer verborgen sein sollten. Aber in diesem Fall testen wir einfach die Ergebnisse der Bibliotheksverbesserung, also ist das keine große Sache.

Fügen wir kleinere Änderungen am EA-Code hinzu, die MQL5-Deals durch MQL4 Saldenoperationen ersetzen:

//+------------------------------------------------------------------+
//|                                           TestDoEasyPart03_1.mq4 |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
#property version   "1.00"
//--- includes
#include <DoEasy\Collections\HistoryCollection.mqh>
//--- Enumerationen
enum ENUM_TYPE_ORDERS
  {
   TYPE_ORDER_MARKET,   // Marktorder
   TYPE_ORDER_PENDING,  // Pending-Order
#ifdef __MQL5__
   TYPE_ORDER_DEAL      // Deals
#else 
   TYPE_ORDER_BALANCE   // Balance/Credit
#endif 
  };
//--- Eingabeparameter
input ENUM_TYPE_ORDERS  InpOrderType   =  TYPE_ORDER_MARKET;   // Show type:
input datetime          InpTimeBegin   =  0;                   // Start date of required range
input datetime          InpTimeEnd     =  END_TIME;            // End date of required range
//--- Globale Variablen
CHistoryCollection history;
//+------------------------------------------------------------------+
//| Initialisierungsfunktion des Experten                            |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Aktualisieren der Historie
   history.Refresh();
//--- get the collection list within the date range
   CArrayObj* list=history.GetListByTime(InpTimeBegin,InpTimeEnd,SELECT_BY_TIME_CLOSE);
   if(list==NULL)
     {
      Print("Could not get collection list");
      return INIT_FAILED;
     }
   int total=list.Total();
   for(int i=0;i<total;i++)
     {
      //--- Abrufen des Auftrags aus der Liste
      COrder* order=list.At(i);
      if(order==NULL) continue;
   //--- Falls es ein Deal ist
   #ifdef __MQL5__
      if(order.Status()==ORDER_STATUS_DEAL && InpOrderType==TYPE_ORDER_DEAL)
         order.Print();
   #else 
   //--- if this is a balance/credit operation
      if(order.Status()==ORDER_STATUS_BALANCE && InpOrderType==TYPE_ORDER_BALANCE)
         order.Print();
   #endif 
   //--- Falls es eine historische Marktorder ist
      if(order.Status()==ORDER_STATUS_HISTORY_ORDER && InpOrderType==TYPE_ORDER_MARKET)
         order.Print();
   //--- Falles es eine entfernte Pending-Order ist
      if(order.Status()==ORDER_STATUS_HISTORY_PENDING && InpOrderType==TYPE_ORDER_PENDING)
         order.Print();
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Deinitialisierungsfunktion des Experten                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Experten Funktion OnTick                                         |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
  }
//+------------------------------------------------------------------+

Kompilieren und starten wir den EA im Terminal (der Test EA aus dem dritten Artikel funktioniert nur in OnInit(), so dass er die gewünschte historische Kollektionsliste einmalig nach dem Start oder nach Änderung der Liste in den Einstellungen anzeigt).

Bevor wir den EA starten, wählen wir die Option "Ganze Historie" im Kontextmenü der Registerkarte "Kontohistorie" des Terminals, da in MetaTrader 4 die Menge der für Anwendungen verfügbaren Historie von einer in der Registerkarte ausgewählten Größe für die Historie abhängt.

In den Einstellungen wird Saldo/Guthaben ausgewählt und die allererste Einlage wird im Journal angezeigt:


Nun müssen wir prüfen, ob die Suche und Anzeige von geschlossenen Positionen korrekt ist. Seit ich vor kurzem ein MetaTrader 4 Konto eröffnet habe, gab es keinen Handel damit. Ich öffnete einen Verkauf, setzte StopLoss und TakeProfit ein und ging, um Kaffee zu machen. Als ich zurückkam, wurde die Position durch den Stop-Loss geschlossen, woraufhin sich der Markt in Richtung der Verkaufsposition zu bewegen begann. Ja, so ist es immer! :)

Aber jetzt gibt es eine geschlossene Position für den Test.
"Market Orders" wird in den Einstellungen ausgewählt:


Nun überprüfen wir die Liste der entfernten Pending-Orders. Ich habe ein paar Orders platziert und sie danach entfernt.
"Pending-Orders" ist in den Einstellungen ausgewählt:


Die Liste der entfernten Pending-Orders wird ebenfalls angezeigt.

Was kommt als Nächstes?

Im nächsten Artikel werden wir die Möglichkeit implementieren, mit Marktpositionen und aktiven Pending-Orders in MQL4 zu arbeiten.

Alle Dateien der aktuellen Version der Bibliothek sind unten zusammen mit den Dateien der Test-EAs angehängt, die Sie herunterladen und testen können.
Stellen Sie Ihre Fragen, Kommentare und Vorschläge in den Kommentaren.

Zurück zum Inhalt

Frühere Artikel dieser Serie:

Teil 1. Konzept, Datenverwaltung.
Teil 2. Erhebung (Collection) historischer Aufträge und Deals.
Teil 3. Erhebung (Collection) von Marktorders und Positionen, Organisieren der Suche
Teil 4. Handelsereignisse. Konzept.
Teil 5. Klassen und Kollektionen von Handelsereignissen. Senden von Ereignissen an das Programm.
Teil 6. Ereignisse auf Netting-Konten.
Teil 7. Ereignis der Aktivierung einer StopLimit-Order, Vorbereiten der Funktionsweise bei Änderungen von Orders und Positionen.
Teil 8. Ereignisse von Änderungen von Orders und Positionen.


Übersetzt aus dem Russischen von MetaQuotes Software Corp.
Originalartikel: https://www.mql5.com/ru/articles/6651

Beigefügte Dateien |
MQL5.zip (89.98 KB)
MQL4.zip (89.98 KB)
Entwicklung eines plattformübergreifenden Grider-EAs (Teil II): Kursspannenbasiertes Raster in Trendrichtung Entwicklung eines plattformübergreifenden Grider-EAs (Teil II): Kursspannenbasiertes Raster in Trendrichtung

In diesem Artikel werden wir einen Grider-EA für den Handel in einer Trendrichtung innerhalb einer Kursspanne entwickeln. Somit ist der EA vor allem für den Devisen- und Rohstoffmarkt geeignet. Nach den Tests zeigte unser Grider seit 2018 einen Gewinn. Leider gilt dies nicht für den Zeitraum 2014-2018.

Gescheites "Marktgedächtnis" durch Differentiation und Entropieuntersuchung Gescheites "Marktgedächtnis" durch Differentiation und Entropieuntersuchung

Der Anwendungsbereich der Fraktionalen Differenziation ist breit genug. Beispielsweise wird in der Regel eine differenzierte Zeitreihe in maschinelle Lernalgorithmen eingegeben. Das Problem ist, dass es notwendig ist, neue Daten entsprechend der verfügbaren Historie anzuzeigen, die das Modell des maschinellen Lernens erkennen kann. In diesem Artikel werden wir einen originellen Ansatz zur Differenzierung von Zeitreihen betrachten. Der Artikel enthält zusätzlich ein Beispiel für ein selbstoptimierendes Handelssystem, das auf einer erhaltenen differenzierten Reihe basiert.

Entwicklung eines plattformübergreifenden Expert Advisors zur Festlegung von StopLoss und TakeProfit basierend auf den Risikoeinstellungen. Entwicklung eines plattformübergreifenden Expert Advisors zur Festlegung von StopLoss und TakeProfit basierend auf den Risikoeinstellungen.

In diesem Artikel erstellen wir einen Expert Advisor für die automatisierte Berechnung der Losgrößen bei der Eröffnung auf Basis von Risikowerten. Auch der Expert Advisor kann TakeProfit mit dem gewählten Verhältnis zu StopLoss automatisch platzieren. Das heißt, er kann einen TakeProfit basierend auf einem beliebigen Verhältnis berechnen, z.B. 3 zu 1, 4 zu 1 oder einem anderen ausgewählten Wert.

Organisation einer Mailing-Kampagne mit den Google-Services Organisation einer Mailing-Kampagne mit den Google-Services

Ein Händler kann eine Mailing-Kampagne organisieren, um Geschäftsbeziehungen zu anderen Händlern, Abonnenten, Kunden oder Freunden zu pflegen. Außerdem kann es notwendig sein, Screenshots, Logs oder Berichte zu versenden. Dies sind vielleicht nicht die am häufigsten auftretenden Aufgaben, aber eine solche Möglichkeit ist eindeutig von Vorteil. Der Artikel beschäftigt sich mit der gleichzeitigen Nutzung mehrerer Google-Dienste, der Entwicklung einer geeigneten Assembly auf C# und der Integration mit MQL-Tools.