English Русский 中文 Español 日本語 Português
Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XXIII): Handelsklassen - Verifikation der Parameter

Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XXIII): Handelsklassen - Verifikation der Parameter

MetaTrader 5Beispiele | 16 Januar 2020, 09:17
530 2
Artyom Trishkin
Artyom Trishkin

Inhalt

Wir setzen die Entwicklung der Handelsklasse fort.
Wir haben fertige Handelsmethoden, die mit "sauberen" Bedingungen arbeiten. Wir sind bereits vor der eigentlichen Ausführung in der Lage zu prüfen, ob ein Handelsauftrag an den Server gesendet werden kann (Server-, Terminal- und Programmbeschränkungen werden geprüft). Aber das ist nicht genug. Wir sollten auch die Gültigkeit der Werte überprüfen, die durch Argumente an die Methoden zum Senden von Anfragen an den Server übergeben werden. Zum Beispiel müssen wir sicherstellen, dass eine Stop-Order nicht kleiner als der minimal zulässige Abstand ist, der für ein Symbol festgelegt wurde (StopLevel).

Wenn er tatsächlich kleiner ist, macht es keinen Sinn, einen solchen Auftrag an den Server zu senden, da sie vom Server abgelehnt wird. Um den Server nicht mit absichtlich fehlerhaften Orders zu belasten, werden wir die Gültigkeit von Stop-Orders prüfen und den Fehler zurückgeben, bevor wir die Order an den Server senden.

Außerdem prüfen wir den Mindestabstand, bei dem eine Position nicht geöffnet werden kann, eine Pending-Order nicht entfernt werden kann, sowie die Preise von Stop-Orders und Pending-Order nicht verändert werden können. Dieser Abstand wird durch einen Freeze-Level definiert, der für ein Symbol in Punkten (FreezeLevel) festgelegt ist.


Für den Moment, wenn eine Level-Verletzung erkannt wird, informieren wir einfach über den Fehler und beenden die Handelsmethode mit false.
Im aktuellen Artikel werden wir auch die Geräusche des Sendens einer Anfrage an den Server und die Audiosignale eines Fehlers, der bei der Überprüfung der Werte der Handelsaufträge erkannt wurde oder der vom Server zurückgegeben wird, nachdem der Auftrag bereits gesendet wurde, einstellen und abspielen.


Die Behandlung von ungültigen Werten in einer Handelsorder, sowie die Behandlung von Verarbeitungsfehlern, die vom Server zurückgesendet werden, sollen im nächsten Artikel implementiert werden.

Zunächst werden wir das Basis-Handelsobjekt verbessern, so dass wir in der Lage sind, für jedes Handelsereignis eingestellte Audiosignale abzuspielen. Um die Audiosignale im Test-EA bequemer einzustellen (um zu vermeiden, dass für jedes Ereignis und Symbol ein Sound eingestellt wird), werden wir für alle Handelsaktionen und Symbole die gleichen Standard-Fehler- und Erfolgssignale einstellen.
Wir sind in der Lage, für jedes Handelsereignis und jedes Symbol einen beliebigen Sound einzustellen.

Einstellen der Audiosignale für den Handel

Im vorherigen Artikel haben wir die Methoden für das Abspielen von Audiodateien bei verschiedenen Handelsereignissen implementiert:

//--- Play the sound of (1) opening/placing a specified position/order type,
//--- (2) closing/removal of a specified position/order type, (3) StopLoss modification for a specified position/order type,
//--- (4) TakeProfit modification for a specified position/order type, (5) placement price modification for a specified order type
   void                       PlaySoundOpen(const int action);
   void                       PlaySoundClose(const int action);
   void                       PlaySoundModifySL(const int action);
   void                       PlaySoundModifyTP(const int action);
   void                       PlaySoundModifyPrice(const int action);
//--- Play the error sound of (1) opening/placing a specified position/order type,
//--- (2) closing/removal of a specified position/order type, (3) StopLoss modification for a specified position/order type,
//--- (4) TakeProfit modification for a specified position/order type, (5) placement price modification for a specified order type
   void                       PlaySoundErrorOpen(const int action);
   void                       PlaySoundErrorClose(const int action);
   void                       PlaySoundErrorModifySL(const int action);
   void                       PlaySoundErrorModifyTP(const int action);
   void                       PlaySoundErrorModifyPrice(const int action);


Diese Methoden wurden sich im 'public' Teil der Klasse definiert. Hier werden wir nur zwei Methoden zum Abspielen von Erfolgs- und Fehlersignalen implementieren, während die Daten zu einem Handelsereignis an die neuen Methoden übergeben werden sollen. Dies wird die Implementierung von Audiosignale für verschiedene Handelsereignisse vereinfachen.
Verschieben wir die Methoden in den privaten Bereich der Klasse und ändern wir ihre Implementierung ein wenig:

//+------------------------------------------------------------------+
//| Play the sound of opening/placing                                |
//| a specified position/order type                                  |
//+------------------------------------------------------------------+
void CTradeObj::PlaySoundOpen(const int action)
  {
   switch(action)
     {
      case ORDER_TYPE_BUY              :   if(this.UseSoundOpen(action))   CMessage::PlaySound(this.m_datas.Buy.SoundOpen());             break;
      case ORDER_TYPE_BUY_STOP         :   if(this.UseSoundOpen(action))   CMessage::PlaySound(this.m_datas.BuyStop.SoundOpen());         break;
      case ORDER_TYPE_BUY_LIMIT        :   if(this.UseSoundOpen(action))   CMessage::PlaySound(this.m_datas.BuyLimit.SoundOpen());        break;
      case ORDER_TYPE_BUY_STOP_LIMIT   :   if(this.UseSoundOpen(action))   CMessage::PlaySound(this.m_datas.BuyStopLimit.SoundOpen());    break;
      case ORDER_TYPE_SELL             :   if(this.UseSoundOpen(action))   CMessage::PlaySound(this.m_datas.Sell.SoundOpen());            break;
      case ORDER_TYPE_SELL_STOP        :   if(this.UseSoundOpen(action))   CMessage::PlaySound(this.m_datas.SellStop.SoundOpen());        break;
      case ORDER_TYPE_SELL_LIMIT       :   if(this.UseSoundOpen(action))   CMessage::PlaySound(this.m_datas.SellLimit.SoundOpen());       break;
      case ORDER_TYPE_SELL_STOP_LIMIT  :   if(this.UseSoundOpen(action))   CMessage::PlaySound(this.m_datas.SellStopLimit.SoundOpen());   break;
      default: break;
     }
  }
//+------------------------------------------------------------------+
//| Play the sound of closing/removal of                             |
//| a specified position/order type                                  |
//+------------------------------------------------------------------+
void CTradeObj::PlaySoundClose(const int action)
  {
   switch(action)
     {
      case ORDER_TYPE_BUY              :   if(this.UseSoundClose(action))  CMessage::PlaySound(this.m_datas.Buy.SoundClose());            break;
      case ORDER_TYPE_BUY_STOP         :   if(this.UseSoundClose(action))  CMessage::PlaySound(this.m_datas.BuyStop.SoundClose());        break;
      case ORDER_TYPE_BUY_LIMIT        :   if(this.UseSoundClose(action))  CMessage::PlaySound(this.m_datas.BuyLimit.SoundClose());       break;
      case ORDER_TYPE_BUY_STOP_LIMIT   :   if(this.UseSoundClose(action))  CMessage::PlaySound(this.m_datas.BuyStopLimit.SoundClose());   break;
      case ORDER_TYPE_SELL             :   if(this.UseSoundClose(action))  CMessage::PlaySound(this.m_datas.Sell.SoundClose());           break;
      case ORDER_TYPE_SELL_STOP        :   if(this.UseSoundClose(action))  CMessage::PlaySound(this.m_datas.SellStop.SoundClose());       break;
      case ORDER_TYPE_SELL_LIMIT       :   if(this.UseSoundClose(action))  CMessage::PlaySound(this.m_datas.SellLimit.SoundClose());      break;
      case ORDER_TYPE_SELL_STOP_LIMIT  :   if(this.UseSoundClose(action))  CMessage::PlaySound(this.m_datas.SellStopLimit.SoundClose());  break;
      default: break;
     }
  }
//+------------------------------------------------------------------+
//| Play StopLoss modification sound of                              |
//| a specified position/order type                                  |
//+------------------------------------------------------------------+
void CTradeObj::PlaySoundModifySL(const int action)
  {
   switch(action)
     {
      case ORDER_TYPE_BUY              :   if(this.UseSoundModifySL(action))  CMessage::PlaySound(this.m_datas.Buy.SoundModifySL());            break;
      case ORDER_TYPE_BUY_STOP         :   if(this.UseSoundModifySL(action))  CMessage::PlaySound(this.m_datas.BuyStop.SoundModifySL());        break;
      case ORDER_TYPE_BUY_LIMIT        :   if(this.UseSoundModifySL(action))  CMessage::PlaySound(this.m_datas.BuyLimit.SoundModifySL());       break;
      case ORDER_TYPE_BUY_STOP_LIMIT   :   if(this.UseSoundModifySL(action))  CMessage::PlaySound(this.m_datas.BuyStopLimit.SoundModifySL());   break;
      case ORDER_TYPE_SELL             :   if(this.UseSoundModifySL(action))  CMessage::PlaySound(this.m_datas.Sell.SoundModifySL());           break;
      case ORDER_TYPE_SELL_STOP        :   if(this.UseSoundModifySL(action))  CMessage::PlaySound(this.m_datas.SellStop.SoundModifySL());       break;
      case ORDER_TYPE_SELL_LIMIT       :   if(this.UseSoundModifySL(action))  CMessage::PlaySound(this.m_datas.SellLimit.SoundModifySL());      break;
      case ORDER_TYPE_SELL_STOP_LIMIT  :   if(this.UseSoundModifySL(action))  CMessage::PlaySound(this.m_datas.SellStopLimit.SoundModifySL());  break;
      default: break;
     }
  }
//+------------------------------------------------------------------+
//| Play TakeProfit modification sound of                            |
//| a specified position/order type                                  |
//+------------------------------------------------------------------+
void CTradeObj::PlaySoundModifyTP(const int action)
  {
   switch(action)
     {
      case ORDER_TYPE_BUY              :   if(this.UseSoundModifyTP(action))  CMessage::PlaySound(this.m_datas.Buy.SoundModifyTP());            break;
      case ORDER_TYPE_BUY_STOP         :   if(this.UseSoundModifyTP(action))  CMessage::PlaySound(this.m_datas.BuyStop.SoundModifyTP());        break;
      case ORDER_TYPE_BUY_LIMIT        :   if(this.UseSoundModifyTP(action))  CMessage::PlaySound(this.m_datas.BuyLimit.SoundModifyTP());       break;
      case ORDER_TYPE_BUY_STOP_LIMIT   :   if(this.UseSoundModifyTP(action))  CMessage::PlaySound(this.m_datas.BuyStopLimit.SoundModifyTP());   break;
      case ORDER_TYPE_SELL             :   if(this.UseSoundModifyTP(action))  CMessage::PlaySound(this.m_datas.Sell.SoundModifyTP());           break;
      case ORDER_TYPE_SELL_STOP        :   if(this.UseSoundModifyTP(action))  CMessage::PlaySound(this.m_datas.SellStop.SoundModifyTP());       break;
      case ORDER_TYPE_SELL_LIMIT       :   if(this.UseSoundModifyTP(action))  CMessage::PlaySound(this.m_datas.SellLimit.SoundModifyTP());      break;
      case ORDER_TYPE_SELL_STOP_LIMIT  :   if(this.UseSoundModifyTP(action))  CMessage::PlaySound(this.m_datas.SellStopLimit.SoundModifyTP());  break;
      default: break;
     }
  }
//+------------------------------------------------------------------+
//| Play price modification sound                                    |
//| for a specified order type                                       |
//+------------------------------------------------------------------+
void CTradeObj::PlaySoundModifyPrice(const int action)
  {
   switch(action)
     {
      case ORDER_TYPE_BUY_STOP         :   if(this.UseSoundModifyPrice(action))  CMessage::PlaySound(this.m_datas.BuyStop.SoundModifyPrice());        break;
      case ORDER_TYPE_BUY_LIMIT        :   if(this.UseSoundModifyPrice(action))  CMessage::PlaySound(this.m_datas.BuyLimit.SoundModifyPrice());       break;
      case ORDER_TYPE_BUY_STOP_LIMIT   :   if(this.UseSoundModifyPrice(action))  CMessage::PlaySound(this.m_datas.BuyStopLimit.SoundModifyPrice());   break;
      case ORDER_TYPE_SELL_STOP        :   if(this.UseSoundModifyPrice(action))  CMessage::PlaySound(this.m_datas.SellStop.SoundModifyPrice());       break;
      case ORDER_TYPE_SELL_LIMIT       :   if(this.UseSoundModifyPrice(action))  CMessage::PlaySound(this.m_datas.SellLimit.SoundModifyPrice());      break;
      case ORDER_TYPE_SELL_STOP_LIMIT  :   if(this.UseSoundModifyPrice(action))  CMessage::PlaySound(this.m_datas.SellStopLimit.SoundModifyPrice());  break;
      default: break;
     }
  }
//+------------------------------------------------------------------+
//| Play the error sound of opening/placing                          |
//| a specified position/order type                                  |
//+------------------------------------------------------------------+
void CTradeObj::PlaySoundErrorOpen(const int action)
  {
   switch(action)
     {
      case ORDER_TYPE_BUY              :   if(this.UseSoundOpen(action))   CMessage::PlaySound(this.m_datas.Buy.SoundErrorOpen());              break;
      case ORDER_TYPE_BUY_STOP         :   if(this.UseSoundOpen(action))   CMessage::PlaySound(this.m_datas.BuyStop.SoundErrorOpen());          break;
      case ORDER_TYPE_BUY_LIMIT        :   if(this.UseSoundOpen(action))   CMessage::PlaySound(this.m_datas.BuyLimit.SoundErrorOpen());         break;
      case ORDER_TYPE_BUY_STOP_LIMIT   :   if(this.UseSoundOpen(action))   CMessage::PlaySound(this.m_datas.BuyStopLimit.SoundErrorOpen());     break;
      case ORDER_TYPE_SELL             :   if(this.UseSoundOpen(action))   CMessage::PlaySound(this.m_datas.Sell.SoundErrorOpen());             break;
      case ORDER_TYPE_SELL_STOP        :   if(this.UseSoundOpen(action))   CMessage::PlaySound(this.m_datas.SellStop.SoundErrorOpen());         break;
      case ORDER_TYPE_SELL_LIMIT       :   if(this.UseSoundOpen(action))   CMessage::PlaySound(this.m_datas.SellLimit.SoundErrorOpen());        break;
      case ORDER_TYPE_SELL_STOP_LIMIT  :   if(this.UseSoundOpen(action))   CMessage::PlaySound(this.m_datas.SellStopLimit.SoundErrorOpen());    break;
      default: break;
     }
  }
//+------------------------------------------------------------------+
//| Play the error sound of closing/removal of                       |
//| a specified position/order type                                  |
//+------------------------------------------------------------------+
void CTradeObj::PlaySoundErrorClose(const int action)
  {
   switch(action)
     {
      case ORDER_TYPE_BUY              :   if(this.UseSoundClose(action))  CMessage::PlaySound(this.m_datas.Buy.SoundErrorClose());             break;
      case ORDER_TYPE_BUY_STOP         :   if(this.UseSoundClose(action))  CMessage::PlaySound(this.m_datas.BuyStop.SoundErrorClose());         break;
      case ORDER_TYPE_BUY_LIMIT        :   if(this.UseSoundClose(action))  CMessage::PlaySound(this.m_datas.BuyLimit.SoundErrorClose());        break;
      case ORDER_TYPE_BUY_STOP_LIMIT   :   if(this.UseSoundClose(action))  CMessage::PlaySound(this.m_datas.BuyStopLimit.SoundErrorClose());    break;
      case ORDER_TYPE_SELL             :   if(this.UseSoundClose(action))  CMessage::PlaySound(this.m_datas.Sell.SoundErrorClose());            break;
      case ORDER_TYPE_SELL_STOP        :   if(this.UseSoundClose(action))  CMessage::PlaySound(this.m_datas.SellStop.SoundErrorClose());        break;
      case ORDER_TYPE_SELL_LIMIT       :   if(this.UseSoundClose(action))  CMessage::PlaySound(this.m_datas.SellLimit.SoundErrorClose());       break;
      case ORDER_TYPE_SELL_STOP_LIMIT  :   if(this.UseSoundClose(action))  CMessage::PlaySound(this.m_datas.SellStopLimit.SoundErrorClose());   break;
      default: break;
     }
  }
//+------------------------------------------------------------------+
//| Play StopLoss modification error sound of                        |
//| a specified position/order type                                  |
//+------------------------------------------------------------------+
void CTradeObj::PlaySoundErrorModifySL(const int action)
  {
   switch(action)
     {
      case ORDER_TYPE_BUY              :   if(this.UseSoundModifySL(action))  CMessage::PlaySound(this.m_datas.Buy.SoundErrorModifySL());             break;
      case ORDER_TYPE_BUY_STOP         :   if(this.UseSoundModifySL(action))  CMessage::PlaySound(this.m_datas.BuyStop.SoundErrorModifySL());         break;
      case ORDER_TYPE_BUY_LIMIT        :   if(this.UseSoundModifySL(action))  CMessage::PlaySound(this.m_datas.BuyLimit.SoundErrorModifySL());        break;
      case ORDER_TYPE_BUY_STOP_LIMIT   :   if(this.UseSoundModifySL(action))  CMessage::PlaySound(this.m_datas.BuyStopLimit.SoundErrorModifySL());    break;
      case ORDER_TYPE_SELL             :   if(this.UseSoundModifySL(action))  CMessage::PlaySound(this.m_datas.Sell.SoundErrorModifySL());            break;
      case ORDER_TYPE_SELL_STOP        :   if(this.UseSoundModifySL(action))  CMessage::PlaySound(this.m_datas.SellStop.SoundErrorModifySL());        break;
      case ORDER_TYPE_SELL_LIMIT       :   if(this.UseSoundModifySL(action))  CMessage::PlaySound(this.m_datas.SellLimit.SoundErrorModifySL());       break;
      case ORDER_TYPE_SELL_STOP_LIMIT  :   if(this.UseSoundModifySL(action))  CMessage::PlaySound(this.m_datas.SellStopLimit.SoundErrorModifySL());   break;
      default: break;
     }
  }
//+------------------------------------------------------------------+
//| Play TakeProfit modification error sound of                      |
//| a specified position/order type                                  |
//+------------------------------------------------------------------+
void CTradeObj::PlaySoundErrorModifyTP(const int action)
  {
   switch(action)
     {
      case ORDER_TYPE_BUY              :   if(this.UseSoundModifyTP(action))  CMessage::PlaySound(this.m_datas.Buy.SoundErrorModifyTP());             break;
      case ORDER_TYPE_BUY_STOP         :   if(this.UseSoundModifyTP(action))  CMessage::PlaySound(this.m_datas.BuyStop.SoundErrorModifyTP());         break;
      case ORDER_TYPE_BUY_LIMIT        :   if(this.UseSoundModifyTP(action))  CMessage::PlaySound(this.m_datas.BuyLimit.SoundErrorModifyTP());        break;
      case ORDER_TYPE_BUY_STOP_LIMIT   :   if(this.UseSoundModifyTP(action))  CMessage::PlaySound(this.m_datas.BuyStopLimit.SoundErrorModifyTP());    break;
      case ORDER_TYPE_SELL             :   if(this.UseSoundModifyTP(action))  CMessage::PlaySound(this.m_datas.Sell.SoundErrorModifyTP());            break;
      case ORDER_TYPE_SELL_STOP        :   if(this.UseSoundModifyTP(action))  CMessage::PlaySound(this.m_datas.SellStop.SoundErrorModifyTP());        break;
      case ORDER_TYPE_SELL_LIMIT       :   if(this.UseSoundModifyTP(action))  CMessage::PlaySound(this.m_datas.SellLimit.SoundErrorModifyTP());       break;
      case ORDER_TYPE_SELL_STOP_LIMIT  :   if(this.UseSoundModifyTP(action))  CMessage::PlaySound(this.m_datas.SellStopLimit.SoundErrorModifyTP());   break;
      default: break;
     }
  }
//+------------------------------------------------------------------+
//| Play price modification error sound                              |
//| for a specified order type                                       |
//+------------------------------------------------------------------+
void CTradeObj::PlaySoundErrorModifyPrice(const int action)
  {
   switch(action)
     {
      case ORDER_TYPE_BUY_STOP         :   if(this.UseSoundModifyPrice(action))  CMessage::PlaySound(this.m_datas.BuyStop.SoundErrorModifyPrice());         break;
      case ORDER_TYPE_BUY_LIMIT        :   if(this.UseSoundModifyPrice(action))  CMessage::PlaySound(this.m_datas.BuyLimit.SoundErrorModifyPrice());        break;
      case ORDER_TYPE_BUY_STOP_LIMIT   :   if(this.UseSoundModifyPrice(action))  CMessage::PlaySound(this.m_datas.BuyStopLimit.SoundErrorModifyPrice());    break;
      case ORDER_TYPE_SELL_STOP        :   if(this.UseSoundModifyPrice(action))  CMessage::PlaySound(this.m_datas.SellStop.SoundErrorModifyPrice());        break;
      case ORDER_TYPE_SELL_LIMIT       :   if(this.UseSoundModifyPrice(action))  CMessage::PlaySound(this.m_datas.SellLimit.SoundErrorModifyPrice());       break;
      case ORDER_TYPE_SELL_STOP_LIMIT  :   if(this.UseSoundModifyPrice(action))  CMessage::PlaySound(this.m_datas.SellStopLimit.SoundErrorModifyPrice());   break;
      default: break;
     }
  }
//+------------------------------------------------------------------+


Hier haben wir die Prüfung hinzugefügt, ob eine Audiodatei eines bestimmten Ereignisses gespielt werden darf. Die Audiodatei wird nur abgespielt, wenn das entsprechende Flag gesetzt ist.

Deklarieren Sie im 'public' Teil der Klasse die beiden Methoden zum Abspielen der Audiodateien bei einem Erfolg und bei einem Fehler:

//--- Play a sound of a specified trading event for a set position/order type
   void                       PlaySoundSuccess(const ENUM_ACTION_TYPE action,const int order,bool sl=false,bool tp=false,bool pr=false);
//--- Play an error sound of a specified trading event for a set position/order type
   void                       PlaySoundError(const ENUM_ACTION_TYPE action,const int order,bool sl=false,bool tp=false,bool pr=false);

//--- Set/return the flag of using sounds


Lasst uns ihre Implementierung außerhalb des Klassenkörpers schreiben:

//+------------------------------------------------------------------+
//| Play a sound of a specified trading event                        |
//| a specified position/order type                                  |
//+------------------------------------------------------------------+
void CTradeObj::PlaySoundSuccess(const ENUM_ACTION_TYPE action,const int order,bool sl=false,bool tp=false,bool pr=false)
  {
   if(!this.m_use_sound)
      return;
   switch((int)action)
     {
      //--- Open/set
      case ACTION_TYPE_BUY             :
      case ACTION_TYPE_BUY_LIMIT       :
      case ACTION_TYPE_BUY_STOP        :
      case ACTION_TYPE_BUY_STOP_LIMIT  :
      case ACTION_TYPE_SELL            :
      case ACTION_TYPE_SELL_LIMIT      :
      case ACTION_TYPE_SELL_STOP       :
      case ACTION_TYPE_SELL_STOP_LIMIT :
        this.PlaySoundOpen(order);  
        break;
      //--- Close/remove
      case ACTION_TYPE_CLOSE           :
      case ACTION_TYPE_CLOSE_BY        :
        this.PlaySoundClose(order); 
        break;
      //--- Modification
      case ACTION_TYPE_MODIFY          :
        if(sl) { this.PlaySoundModifySL(order);    return; }
        if(tp) { this.PlaySoundModifyTP(order);    return; }
        if(pr) { this.PlaySoundModifyPrice(order); return; }
        break;
      default:
        break;
     }
  }
//+------------------------------------------------------------------+
//| Play an error sound of a specified trading event                 |
//| a specified position/order type                                  |
//+------------------------------------------------------------------+
void CTradeObj::PlaySoundError(const ENUM_ACTION_TYPE action,const int order,bool sl=false,bool tp=false,bool pr=false)
  {
   if(!this.m_use_sound)
      return;
   switch((int)action)
     {
      //--- Open/set
      case ACTION_TYPE_BUY             :
      case ACTION_TYPE_BUY_LIMIT       :
      case ACTION_TYPE_BUY_STOP        :
      case ACTION_TYPE_BUY_STOP_LIMIT  :
      case ACTION_TYPE_SELL            :
      case ACTION_TYPE_SELL_LIMIT      :
      case ACTION_TYPE_SELL_STOP       :
      case ACTION_TYPE_SELL_STOP_LIMIT :
        this.PlaySoundErrorOpen(order);
        break;
      //--- Close/remove
      case ACTION_TYPE_CLOSE           :
      case ACTION_TYPE_CLOSE_BY        :
        this.PlaySoundErrorClose(order);
        break;
      //--- Modification
      case ACTION_TYPE_MODIFY          :
        if(sl) { this.PlaySoundErrorModifySL(order);    return; }
        if(tp) { this.PlaySoundErrorModifyTP(order);    return; }
        if(pr) { this.PlaySoundErrorModifyPrice(order); return; }
        break;
      default:
        break;
     }
  }
//+------------------------------------------------------------------+


Die Methoden erhalten Handelsereignis und Ordertypen, sowie StopLoss/TakeProfitModifikationsflags und die Preise der Aufträge.
Wenn das allgemeine Flag, das das Abspielen von Audiosignalen für ein Handelsobjekt erlaubt, nicht gesetzt ist,
verlassen wir die Methode — alle Audiosignale sind deaktiviert.
Als Nächstes rufen Sie je nach Art des Handelsvorgangs, die Methoden auf, um die entsprechenden Töne für einen entsprechenden Auftrag zu spielen.
Wenn ein Handelsereignis eine Modifikation ist, dann prüfen Sie zusätzlich die Flags, die genau angeben, was modifiziert wird. (Wenn mehrere Parameter gleichzeitig geändert werden, wird nur der Klang des ersten von ihnen abgespielt)

Auch die Reihenfolge der Argumente in den Handelsmethoden wurde geändert: jetzt sollte ein Kommentar unmittelbar auf eine Magicnummer folgen, die wiederum von einer Veränderung gefolgt wird. Der Grund dafür ist, dass der Kommentar im Vergleich zu einer Slippage öfter für verschiedene Orders gesetzt wird. Deshalb wurden die Plätze von Kommentar und der Veränderung getauscht:

//--- Positionseröffnung
   bool                       OpenPosition(const ENUM_POSITION_TYPE type,
                                           const double volume,
                                           const double sl=0,
                                           const double tp=0,
                                           const ulong magic=ULONG_MAX,
                                           const string comment=NULL,
                                           const ulong deviation=ULONG_MAX);


Dies ist bei allen Handelsmethoden geschehen. Alle Dateien sind unten angehängt.

Die Methode für die Einstellung der standardmäßigen Audiodateien für alle Handelsereignisse, dem Setzen des Flags, das das Abspielen der Audiodateien erlaubt:

//+------------------------------------------------------------------+
//| Allow working with sounds and set standard sounds                |
//+------------------------------------------------------------------+
void CTradeObj::SetSoundsStandart(void)
  {
   this.SetUseSound(true);
   this.m_datas.Buy.UseSoundClose(true);


Zusätzlich gibt es einige kleinere Änderungen in der Klasse zur Vereinfachung der Kompatibilität mit MQL4. Diese werde ich hier nicht berücksichtigen. In den angehängten Dateien finden Sie weitere Informationen.

Damit ist die Verbesserung des Basisobjekts für den Handel abgeschlossen.

Beim Senden von Handelsanforderungen aus dem Programm müssen wir einen Abstand für Pending-Orders festlegen und Stop-Order-Werte definieren. Dazu können wir in den Parametern der Trading Order einen bestimmten Orderpreis übergeben. Alternativ können wir einen Abstand in Preispunkten für die Platzierung einer Pending-Order oder einen Abstand in Punkten von einer Position/Pending-Order, an der Stop-Order-Preise zu finden sind, übergeben.

Wir können sowohl überladene Methoden implementieren, die die Parameter in der realen Darstellung der Preise erhalten, als auch Methoden, die ganzzahlige Distanzwerte in Punkten erhalten. Dies ermöglicht uns die Übergabe an die Methoden der Eröffnung von Positionen/Platzierung von Orders und der Stop-Order-Level/Änderung durchzuführen.

Dies ist jedoch nicht die beste Lösung. Zuerst müssen wir mindestens zwei identische Methoden implementieren, um reelle und ganzzahlige Werte zu erhalten.
Zweitens sind bei dieser Lösung die Parameterkombinationen begrenzt. Wenn wir reale Werte übergeben, sollten sie für jeden Parameter real sein — für die Preise sowohl für den Auftrag als auch für StopLoss/TakeProfit. Die gleiche Beschränkung bleibt auch bei der Übergabe von Entfernungen in Punkten an die Methoden — alle übergebenen Werte sollten ganzzahlig sein.
Die Alternative ist die Implementierung mehrerer Methoden, die alle möglichen Kombinationen von Preisen und Entfernungen enthalten, was nicht praktikabel ist.
Deshalb werden wir eine andere Lösung implementieren: alle Handelsmethoden werden als Vorlage gemacht. Die Typen der Variablen, die zur Übergabe der Orderwerte verwendet werden, werden innerhalb der Methoden definiert. Dies erlaubt uns, die notwendigen Werte in beliebigen Kombinationen an die Methoden zu übergeben, zum Beispiel einen Orderpreis und einen Abstand von einem Stop-Order-Preis in Punkten oder umgekehrt. Dies gibt uns viel mehr Flexibilität bei der Berechnung von Order-Levels.
Innerhalb der Handelsmethoden werden alle eingehenden Werte auf Preiswerte reduziert. Die Werte in Preisen werden in einem Handelsauftrag gesendet.

Einschränkungen für die Durchführung von Handelsgeschäften sind in drei Stufen zu prüfen:

  • Prüfung der Handelseinschränkungen
  • Prüfung der Mittelausstattung für die Eröffnung von Positionen/Orders
  • Prüfen der Parameterwerte durch StopLevel und FreezeLevel

Die ersten beiden Stufen haben wir bereits umgesetzt. Jetzt ist es an der Zeit, Prüfungen durch StopLevel und FreezeLevel hinzuzufügen. Die beiden vorgefertigten Checks (Handelsbeschränkungen und Geldmittelausstattung) werden derzeit direkt aus den Handelsmethoden nacheinander gestartet, was nicht so elegant ist.
Deshalb werden wir eine einheitliche Methode zur Überprüfung aller Einschränkungen implementieren. Sie wird aus den Handelsmethoden heraus gestartet, während alle drei Prüfungen in ihr nacheinander durchgeführt werden. Die Liste der festgestellten Einschränkungen und Fehler wird erstellt. Wenn Fehler entdeckt werden, wird die vollständige Liste der Fehler und Einschränkungen von der Methode an das Journal gesendet und das Flag für das Fehlschlagen aller drei Prüfungen wird zurückgegeben. Andernfalls wird das Erfolgsflag zurückgegeben.

Da fast alle Methoden fertig sind und schon in der Endphase sind, werde ich nicht im Detail auf sie eingehen und stattdessen nur kurze Erklärungen geben.

Kontrolle über fehlerhafte Werte, Automatisierung der Auswahl und Verwendung der Eingaben der Handelsmethode

Die Handelsmethoden der Klasse CTrading erhalten reale Orderpreiswerte. Wir fügen die Möglichkeit hinzu, auch eine Distanz in Punkten anzugeben. Unterstützte Parametertypen, die den Handelsklassen zur Angabe von Preisen oder Distanzen übergeben werden sollen — double, long, ulong, int und uint. Alle anderen Typen werden als ungültig betrachtet.

Der 'private' Teil der Klasse CTrading erhält das globale Flag, das den Sound der Handelsereignisse ermöglicht, und die Kursstruktur, die die an die Handelsmethoden übergebenen und in reale Werte umgewandelten Kurse/Distanzen enthalten soll:

//+------------------------------------------------------------------+
//| Trading class                                                    |
//+------------------------------------------------------------------+
class CTrading
  {
private:
   CAccount            *m_account;           // Pointer to the current account object
   CSymbolsCollection  *m_symbols;           // Pointer to the symbol collection list
   CMarketCollection   *m_market;            // Pointer to the list of the collection of market orders and positions
   CHistoryCollection  *m_history;           // Pointer to the list of the collection of historical orders and deals
   CArrayInt            m_list_errors;       // Error list
   bool                 m_is_trade_enable;   // Flag enabling trading
   bool                 m_use_sound;         // The flag of using sounds of the object trading events
   ENUM_LOG_LEVEL       m_log_level;         // Logging level
//---
   struct SDataPrices
     {
      double            open;                // Open price
      double            limit;               // Limit order price
      double            sl;                  // StopLoss price
      double            tp;                  // TakeProfit price
     };
   SDataPrices          m_req_price;         // Trade request prices
//--- Add the error code to the list


Das globale Flag, der die Audiosignale aktiviert, wirkt sich auf alle Handelsereignisse aus, unabhängig von deren Audiosignale und unabhängig davon, ob das Abspielen eines Audiosignals bei jedem der Events aktiviert ist.

Für Handelsmethoden müssen wir ein Orderobjekt durch sein Ticket erhalten.
Fügen Sie die Methodendeklaration in den 'private' Teil der Klasse ein:

//--- Return an order object by ticket
   COrder              *GetOrderObjByTicket(const ulong ticket);
//--- Return the number of (1) all positions, (2) buy, (3) sell positions


Da die Methoden der Prüfung auf Handelsbeschränkungen und die Mittel zur Eröffnung von Positionen/Platzierung von Aufträgen innerhalb der allgemeinen Methode zur Prüfung von Fehlern arbeiten sollen, werden sie vom 'public' Teil der Klasse in den 'private' verschoben, sowie die Template-Methode für die Preise zur Platzierung von Handelsanfragen, die Methoden, die Flags zurückgeben, die StopLevel und FreezeLevel und die Methode, die prüft, ob Handelsoperationen durch Stop- und Freeze-Level-Abstände erlaubt sind:

//--- Set trading request prices
   template <typename PR,typename SL,typename TP,typename PL> 
   bool                 SetPrices(const ENUM_ORDER_TYPE action,const PR price,const SL sl,const TP tp,const PL limit,const string source_method,CSymbol *symbol_obj);
//--- Return the flag checking the permission to trade by (1) StopLoss, (2) TakeProfit distance, (3) order placement level by a StopLevel-based price
   bool                 CheckStopLossByStopLevel(const ENUM_ORDER_TYPE order_type,const double price,const double sl,const CSymbol *symbol_obj);
   bool                 CheckTakeProfitByStopLevel(const ENUM_ORDER_TYPE order_type,const double price,const double tp,const CSymbol *symbol_obj);
   bool                 CheckPriceByStopLevel(const ENUM_ORDER_TYPE order_type,const double price,const CSymbol *symbol_obj);
//--- Return the flag checking if a distance from a price to (1) StopLoss, (2) TakeProfit, (3) order placement level by FreezeLevel is acceptable
   bool                 CheckStopLossByFreezeLevel(const ENUM_ORDER_TYPE order_type,const double sl,const CSymbol *symbol_obj);
   bool                 CheckTakeProfitByFreezeLevel(const ENUM_ORDER_TYPE order_type,const double tp,const CSymbol *symbol_obj);
   bool                 CheckPriceByFreezeLevel(const ENUM_ORDER_TYPE order_type,const double price,const CSymbol *symbol_obj);
//--- Check trading limitations
   bool                 CheckTradeConstraints(const double volume,
                                              const ENUM_ACTION_TYPE action,
                                              const CSymbol *symbol_obj,
                                              const string source_method,
                                              double sl=0,
                                              double tp=0);
//--- Check if the funds are sufficient
   bool                 CheckMoneyFree(const double volume,const double price,const ENUM_ORDER_TYPE order_type,const CSymbol *symbol_obj,const string source_method);
//--- Check parameter values by StopLevel and FreezeLevel
   bool                 CheckLevels(const ENUM_ACTION_TYPE action,
                                    const ENUM_ORDER_TYPE order_type,
                                    double price,
                                    double limit,
                                    double sl,
                                    double tp,
                                    const CSymbol *symbol_obj,
                                    const string source_method);

public:


Im 'public' Teil der Klasse deklarieren Sie die Methode zur Überprüfung der Handelsverfügbarkeit und der Fehler bei Handelsanfragen, sowie die Methoden zum Setzen von und zur Rückgabe der Flags, die die Audiosignale aktiviert:

public:
//--- Konstructor
                        CTrading();
//--- Get the pointers to the lists (make sure to call the method in program's OnInit() since the symbol collection list is created there)
   void                 OnInit(CAccount *account,CSymbolsCollection *symbols,CMarketCollection *market,CHistoryCollection *history)
                          {
                           this.m_account=account;
                           this.m_symbols=symbols;
                           this.m_market=market;
                           this.m_history=history;
                          }
//--- Return the error list
   CArrayInt           *GetListErrors(void)                                { return &this.m_list_errors; }
//--- Check for errors
   bool                 CheckErrors(const double volume,
                                    const double price,
                                    const ENUM_ACTION_TYPE action,
                                    const ENUM_ORDER_TYPE order_type,
                                    const CSymbol *symbol_obj,
                                    const string source_method,
                                    const double limit=0,
                                    double sl=0,
                                    double tp=0);

//--- Set the following for symbol trading objects:
//--- (1) correct filling policy, (2) filling policy,
//--- (3) correct order expiration type, (4) order expiration type,
//--- (5) magic number, (6) comment, (7) slippage, (8) volume, (9) order expiration date,
//--- (10) the flag of asynchronous sending of a trading request, (11) logging level
   void                 SetCorrectTypeFilling(const ENUM_ORDER_TYPE_FILLING type=ORDER_FILLING_FOK,const string symbol=NULL);
   void                 SetTypeFilling(const ENUM_ORDER_TYPE_FILLING type=ORDER_FILLING_FOK,const string symbol=NULL);
   void                 SetCorrectTypeExpiration(const ENUM_ORDER_TYPE_TIME type=ORDER_TIME_GTC,const string symbol=NULL);
   void                 SetTypeExpiration(const ENUM_ORDER_TYPE_TIME type=ORDER_TIME_GTC,const string symbol=NULL);
   void                 SetMagic(const ulong magic,const string symbol=NULL);
   void                 SetComment(const string comment,const string symbol=NULL);
   void                 SetDeviation(const ulong deviation,const string symbol=NULL);
   void                 SetVolume(const double volume=0,const string symbol=NULL);
   void                 SetExpiration(const datetime expiration=0,const string symbol=NULL);
   void                 SetAsyncMode(const bool mode=false,const string symbol=NULL);
   void                 SetLogLevel(const ENUM_LOG_LEVEL log_level=LOG_LEVEL_ERROR_MSG,const string symbol=NULL);

//--- Set standard sounds (1 symbol=NULL) for trading objects of all symbols, (2 symbol!=NULL) for a symbol trading object
   void                 SetSoundsStandart(const string symbol=NULL);
//--- Set a sound for a specified order/position type and symbol
//--- 'mode' specifies an event a sound is set for
//--- (symbol=NULL) for trading objects of all symbols,
//--- (symbol!=NULL) for a trading object of a specified symbol
   void                 SetSound(const ENUM_MODE_SET_SOUND mode,const ENUM_ORDER_TYPE action,const string sound,const string symbol=NULL);
//--- Set/return the flag enabling sounds
   void                 SetUseSounds(const bool flag);
   bool                 IsUseSounds(void)                            const { return this.m_use_sound; }



Lassen Sie uns alle oben deklarierten Methoden außerhalb des Klassenkörpers implementieren.

Die Methode, die ein Auftragsobjekt per Ticket zurückgibt:

//+------------------------------------------------------------------+
//| Return an order object by ticket                                 |
//+------------------------------------------------------------------+
COrder *CTrading::GetOrderObjByTicket(const ulong ticket)
  {
   CArrayObj *list=this.m_market.GetList();
   list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,ticket,EQUAL);
   if(list==NULL || list.Total()==0)
      return NULL;
   return list.At(0);
  }
//+------------------------------------------------------------------+


Die Methode erhält das gewünschte Ticket, das in den Eigenschaften des Auftragsobjekts hinterlegt ist. Sie erhält die vollständige Liste aller aktiven Aufträge und Positionen und sortiert die Liste nach den Tickets. Wenn kein Auftragsobjekt mit diesem Ticket gefunden wird, geben Sie NULL zurück, sonst geben Sie das einzige Auftragsobjekt aus der Liste zurück.
Wie Sie sich vielleicht erinnern, kann ein Auftragsobjekt entweder durch einen schwebenden Auftrag oder durch eine Position dargestellt werden.
Die Methode liefert ein Objekt zurück, unabhängig davon, ob es sich um einen Auftrag oder eine Position handelt.

Die Template-Methode zur Berechnung und zum Eintragen der Preise der Handelsanfragen in die Struktur m_req_price:

//+------------------------------------------------------------------+
//| Set trading request prices                                       |
//+------------------------------------------------------------------+
template <typename PR,typename SL,typename TP,typename PL> 
bool CTrading::SetPrices(const ENUM_ORDER_TYPE action,const PR price,const SL sl,const TP tp,const PL limit,const string source_method,CSymbol *symbol_obj)
  {
//--- Reset the prices and check the order type. If it is invalid, inform of that and return 'false'
   ::ZeroMemory(this.m_req_price);
   if(action>ORDER_TYPE_SELL_STOP_LIMIT)
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(source_method,CMessage::Text(4003));
      return false;
     }
   
//--- Open/close price
   if(price>0)
     {
      //--- price parameter type (double) - normalize the price up to Digits(), since the price has been passed
      if(typename(price)=="double")
         this.m_req_price.open=::NormalizeDouble(price,symbol_obj.Digits());
      //--- price parameter type (int) - the distance has been passed
      else if(typename(price)=="int" || typename(price)=="uint" || typename(price)=="long" || typename(price)=="ulong")
        {
         //--- Calculate the order price
         switch((int)action)
           {
            //--- Pending order
            case ORDER_TYPE_BUY_LIMIT       :  this.m_req_price.open=::NormalizeDouble(symbol_obj.Ask()-price*symbol_obj.Point(),symbol_obj.Digits());      break;
            case ORDER_TYPE_BUY_STOP        :
            case ORDER_TYPE_BUY_STOP_LIMIT  :  this.m_req_price.open=::NormalizeDouble(symbol_obj.Ask()+price*symbol_obj.Point(),symbol_obj.Digits());      break;
            
            case ORDER_TYPE_SELL_LIMIT      :  this.m_req_price.open=::NormalizeDouble(symbol_obj.BidLast()+price*symbol_obj.Point(),symbol_obj.Digits());  break;
            case ORDER_TYPE_SELL_STOP       :
            case ORDER_TYPE_SELL_STOP_LIMIT :  this.m_req_price.open=::NormalizeDouble(symbol_obj.BidLast()-price*symbol_obj.Point(),symbol_obj.Digits());  break;
            //--- Default - current position open prices
            default  :  this.m_req_price.open=
              (
               this.DirectionByActionType((ENUM_ACTION_TYPE)action)==ORDER_TYPE_BUY ? ::NormalizeDouble(symbol_obj.Ask(),symbol_obj.Digits()) : 
               ::NormalizeDouble(symbol_obj.BidLast(),symbol_obj.Digits())
              ); break;
           }
        }
      //--- unsupported price types - display the message and return 'false'
      else
        {
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(source_method,CMessage::Text(MSG_LIB_TEXT_UNSUPPORTED_PR_TYPE));
         return false;
        }
     }
   //--- If no price is specified, use the current prices
   else
     {
      this.m_req_price.open=
        (
         this.DirectionByActionType((ENUM_ACTION_TYPE)action)==ORDER_TYPE_BUY ? 
         ::NormalizeDouble(symbol_obj.Ask(),symbol_obj.Digits())              : 
         ::NormalizeDouble(symbol_obj.BidLast(),symbol_obj.Digits())
        );
     }
   
//--- StopLimit order price or distance
   if(limit>0)
     {
      //--- limit order price parameter type (double) - normalize the price up to Digits(), since the price has been passed
      if(typename(limit)=="double")
         this.m_req_price.limit=::NormalizeDouble(limit,symbol_obj.Digits());
      //--- limit order price parameter type (int) - the distance has been passed
      else if(typename(limit)=="int" || typename(limit)=="uint" || typename(limit)=="long" || typename(limit)=="ulong")
        {
         //--- Calculate a limit order price
         if(this.DirectionByActionType((ENUM_ACTION_TYPE)action)==ORDER_TYPE_BUY)
            this.m_req_price.limit=::NormalizeDouble(this.m_req_price.open-limit*symbol_obj.Point(),symbol_obj.Digits());
         else
            this.m_req_price.limit=::NormalizeDouble(this.m_req_price.open+limit*symbol_obj.Point(),symbol_obj.Digits());
        }
      //--- unsupported limit order price types - display the message and return 'false'
      else
        {
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(source_method,CMessage::Text(MSG_LIB_TEXT_UNSUPPORTED_PL_TYPE));
         return false;
        }
     }  
     
//--- Order price stop order prices are calculated from
   double price_open=
     (
      (action==ORDER_TYPE_BUY_STOP_LIMIT || action==ORDER_TYPE_SELL_STOP_LIMIT) && limit>0 ? this.m_req_price.limit : this.m_req_price.open
     );
     
//--- StopLoss
   if(sl>0)
     {
      //--- StopLoss parameter type (double) - normalize the price up to Digits(), since the price has been passed
      if(typename(sl)=="double")
         this.m_req_price.sl=::NormalizeDouble(sl,symbol_obj.Digits());
      //--- StopLoss parameter type (int) - calculate the placement distance
      else if(typename(sl)=="int" || typename(sl)=="uint" || typename(sl)=="long" || typename(sl)=="ulong")
        {
         //--- Calculate the StopLoss price
         if(this.DirectionByActionType((ENUM_ACTION_TYPE)action)==ORDER_TYPE_BUY)
            this.m_req_price.sl=::NormalizeDouble(price_open-sl*symbol_obj.Point(),symbol_obj.Digits());
         else
            this.m_req_price.sl=::NormalizeDouble(price_open+sl*symbol_obj.Point(),symbol_obj.Digits());
        }
      //--- unsupported StopLoss types - display the message and return 'false'
      else
        {
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(source_method,CMessage::Text(MSG_LIB_TEXT_UNSUPPORTED_SL_TYPE));
         return false;
        }
      
     }
     
//--- TakeProfit
   if(tp>0)
     {
      //--- TakeProfit parameter type (double) - normalize the price up to Digits(), since the price has been passed
      if(typename(tp)=="double")
         this.m_req_price.tp=::NormalizeDouble(tp,symbol_obj.Digits());
      //--- TakeProfit parameter type (int) - calculate the placement distance
      else if(typename(tp)=="int" || typename(tp)=="uint" || typename(tp)=="long" || typename(tp)=="ulong")
        {
         if(this.DirectionByActionType((ENUM_ACTION_TYPE)action)==ORDER_TYPE_BUY)
            this.m_req_price.tp=::NormalizeDouble(price_open+tp*symbol_obj.Point(),symbol_obj.Digits());
         else
            this.m_req_price.tp=::NormalizeDouble(price_open-tp*symbol_obj.Point(),symbol_obj.Digits());
        }
      //--- unsupported TakeProfit types - display the message and return 'false'
      else
        {
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(source_method,CMessage::Text(MSG_LIB_TEXT_UNSUPPORTED_TP_TYPE));
         return false;
        }
     }
      
//--- All prices are recorded
   return true;
  }
//+------------------------------------------------------------------+


Unabhängig davon, ob die Niveaus als Preisen oder als Abstand an die Handelsmethode übergeben werden, schreibt die Methode die berechneten Preise in die von uns im privaten Abschnitt deklarierte Struktur m_req_price. Übergibt die Methode einen double Wert, wird der Preis mit Digits() auf die Dezimalstellenanzahl des Symbols normiert, dessen Zeiger auf das Objekt an die Methode übergeben wird. Ganzzahlige Werte bedeuten, dass der Abstand übergeben wurde. Die Methode berechnet für diese Distanz einen normierten Preis und schreibt ihn in die Struktur.
Alle Aktionen werden in den Kommentaren des Methodencodes beschrieben.

Die Methoden, die die Gültigkeit von StopLoss, TakeProfit oder den Abständen der Order-Level relativ zum StopLevel zurückgeben:

//+------------------------------------------------------------------+
//| Return the flag checking the validity of the distance            |
//| from the price to StopLoss by StopLevel                          |
//+------------------------------------------------------------------+
bool CTrading::CheckStopLossByStopLevel(const ENUM_ORDER_TYPE order_type,const double price,const double sl,const CSymbol *symbol_obj)
  {
   double lv=symbol_obj.TradeStopLevel()*symbol_obj.Point();
   double pr=(order_type==ORDER_TYPE_BUY ? symbol_obj.BidLast() : order_type==ORDER_TYPE_SELL ? symbol_obj.Ask() : price);
   return(this.DirectionByActionType((ENUM_ACTION_TYPE)order_type)==ORDER_TYPE_BUY ? sl<(pr-lv) : sl>(pr+lv));
  }
//+------------------------------------------------------------------+
//| Return the flag checking the validity of the distance            |
//| from the price to TakeProfit by StopLevel                        |
//+------------------------------------------------------------------+
bool CTrading::CheckTakeProfitByStopLevel(const ENUM_ORDER_TYPE order_type,const double price,const double tp,const CSymbol *symbol_obj)
  {
   double lv=symbol_obj.TradeStopLevel()*symbol_obj.Point();
   double pr=(order_type==ORDER_TYPE_BUY ? symbol_obj.BidLast() : order_type==ORDER_TYPE_SELL ? symbol_obj.Ask() : price);
   return(this.DirectionByActionType((ENUM_ACTION_TYPE)order_type)==ORDER_TYPE_BUY ? tp>(pr+lv) : tp<(pr-lv));
  }
//+------------------------------------------------------------------+
//| Return the flag checking the validity of the order distance      |
//| from the price to the placement level by StopLevel               |
//+------------------------------------------------------------------+
bool CTrading::CheckPriceByStopLevel(const ENUM_ORDER_TYPE order_type,const double price,const CSymbol *symbol_obj)
  {
   double lv=symbol_obj.TradeStopLevel()*symbol_obj.Point();
   double pr=(this.DirectionByActionType((ENUM_ACTION_TYPE)order_type)==ORDER_TYPE_BUY ? symbol_obj.Ask() : symbol_obj.BidLast());
   return
     (
      order_type==ORDER_TYPE_SELL_STOP       ||
      order_type==ORDER_TYPE_SELL_STOP_LIMIT ||
      order_type==ORDER_TYPE_BUY_LIMIT       ?  price<(pr-lv)  :
      order_type==ORDER_TYPE_BUY_STOP        ||
      order_type==ORDER_TYPE_BUY_STOP_LIMIT  ||
      order_type==ORDER_TYPE_SELL_LIMIT      ?  price>(pr+lv)  :
      true
     );
  }
//+------------------------------------------------------------------+


Der Referenzpreis für die Überprüfung einer Orderdistanz und die Rückgabe true bei Überschreitung des minimalen StopLevels wird in den Methoden durch die Orderart definiert. Andernfalls wird false zurückgegeben, was bedeutet, dass die Preise des Auftrags ungültig sind.

Die Methoden, die die Gültigkeit von StopLoss, TakeProfit oder Order-Level-Distanz relativ zum FreezeLevel zurückgeben:

//+------------------------------------------------------------------+
//| Return the flag checking the validity of the                     |
//| distance from the price to StopLoss by FreezeLevel               |
//+------------------------------------------------------------------+
bool CTrading::CheckStopLossByFreezeLevel(const ENUM_ORDER_TYPE order_type,const double sl,const CSymbol *symbol_obj)
  {
   if(symbol_obj.TradeFreezeLevel()==0 || order_type>ORDER_TYPE_SELL)
      return true;
   double lv=symbol_obj.TradeFreezeLevel()*symbol_obj.Point();
   double pr=(order_type==ORDER_TYPE_BUY ? symbol_obj.BidLast() : symbol_obj.Ask());
   return(this.DirectionByActionType((ENUM_ACTION_TYPE)order_type)==ORDER_TYPE_BUY ? sl<(pr-lv) : sl>(pr+lv));
  }
//+------------------------------------------------------------------+
//| Return the flag checking the distance validity of the            |
//| from the price to TakeProfit by FreezeLevel                      |
//+------------------------------------------------------------------+
bool CTrading::CheckTakeProfitByFreezeLevel(const ENUM_ORDER_TYPE order_type,const double tp,const CSymbol *symbol_obj)
  {
   if(symbol_obj.TradeFreezeLevel()==0 || order_type>ORDER_TYPE_SELL)
      return true;
   double lv=symbol_obj.TradeFreezeLevel()*symbol_obj.Point();
   double pr=(order_type==ORDER_TYPE_BUY ? symbol_obj.BidLast() : symbol_obj.Ask());
   return(this.DirectionByActionType((ENUM_ACTION_TYPE)order_type)==ORDER_TYPE_BUY ? tp>(pr+lv) : tp<(pr-lv));
  }
//+------------------------------------------------------------------+
//| Return the flag checking the validity of the distance            |
//| from the price to the order price by FreezeLevel                 |
//+------------------------------------------------------------------+
bool CTrading::CheckPriceByFreezeLevel(const ENUM_ORDER_TYPE order_type,const double price,const CSymbol *symbol_obj)
  {
   if(symbol_obj.TradeFreezeLevel()==0 || order_type<ORDER_TYPE_BUY_LIMIT)
      return true;
   double lv=symbol_obj.TradeFreezeLevel()*symbol_obj.Point();
   double pr=(this.DirectionByActionType((ENUM_ACTION_TYPE)order_type)==ORDER_TYPE_BUY ? symbol_obj.Ask() : symbol_obj.BidLast());
   return
     (
      order_type==ORDER_TYPE_SELL_STOP       ||
      order_type==ORDER_TYPE_SELL_STOP_LIMIT ||
      order_type==ORDER_TYPE_BUY_LIMIT       ?  price<(pr-lv)  :
      order_type==ORDER_TYPE_BUY_STOP        ||
      order_type==ORDER_TYPE_BUY_STOP_LIMIT  ||
      order_type==ORDER_TYPE_SELL_LIMIT      ?  price>(pr+lv)  :
      true
     );
  }
//+------------------------------------------------------------------+


Wie bei der Überprüfung eines Abstandes per StopLevel wird hier der Abstand vom aktuellen Preis für die Orderart zum Preis der Order überprüft.
Ein Freeze-Level von Null bedeutet, es gibt keinen Freeze-Level für das Symbol. Daher überprüfen wir zuerst den Null-Stopp-Level und geben true zurück, wenn das Fehlen eines Freeze-Levels für den Handel bestätigt wird.

Im Gegensatz zum Freeze-Level bedeutet der minimale StopLevel gleich Null, dass der Level gleitend ist und wie gewünscht verwaltet werden sollte. Wir werden dies bei der Implementierung der Behandlung von Fehlern, die vom Handelsserver zurückgegeben werden, in nachfolgenden Artikeln tun.

Die Methode der Überprüfung der Parameterwerte durch StopLevel und FreezeLevel:

//+------------------------------------------------------------------+
//| Check parameter values by StopLevel and FreezeLevel              |
//+------------------------------------------------------------------+
bool CTrading::CheckLevels(const ENUM_ACTION_TYPE action,
                           const ENUM_ORDER_TYPE order_type,
                           double price,
                           double limit,
                           double sl,
                           double tp,
                           const CSymbol *symbol_obj,
                           const string source_method)
  {
//--- the result of conducting all checks
   bool res=true;
//--- StopLevel
//--- If this is not a position closure/order removal
   if(action!=ACTION_TYPE_CLOSE && action!=ACTION_TYPE_CLOSE_BY)
     {
      //--- When placing a pending order
      if(action>ACTION_TYPE_SELL)
        {
         //--- If the placement distance in points is less than StopLevel
         if(!this.CheckPriceByStopLevel(order_type,price,symbol_obj))
           {
            //--- add the error code to the list and write 'false' to the result
            this.AddErrorCodeToList(MSG_LIB_TEXT_PR_LESS_STOP_LEVEL);
            res &=false;
           }
        }
      //--- If StopLoss is present
      if(sl>0)
        {
         //--- If StopLoss distance in points from the open price is less than StopLevel
         double price_open=(action==ACTION_TYPE_BUY_STOP_LIMIT || action==ACTION_TYPE_SELL_STOP_LIMIT ? limit : price);
         if(!this.CheckStopLossByStopLevel(order_type,price_open,sl,symbol_obj))
           {
            //--- add the error code to the list and write 'false' to the result
            this.AddErrorCodeToList(MSG_LIB_TEXT_SL_LESS_STOP_LEVEL);
            res &=false;
           }
        }
      //--- If TakeProfit is present
      if(tp>0)
        {
         double price_open=(action==ACTION_TYPE_BUY_STOP_LIMIT || action==ACTION_TYPE_SELL_STOP_LIMIT ? limit : price);
         //--- If TakeProfit distance in points from the open price is less than StopLevel
         if(!this.CheckTakeProfitByStopLevel(order_type,price_open,tp,symbol_obj))
           {
            //--- add the error code to the list and write 'false' to the result
            this.AddErrorCodeToList(MSG_LIB_TEXT_TP_LESS_STOP_LEVEL);
            res &=false;
           }
        }
     }
//--- FreezeLevel
//--- If this is a position closure/order removal/modification
   if(action>ACTION_TYPE_SELL_STOP_LIMIT)
     {
      //--- If this is a position
      if(order_type<ORDER_TYPE_BUY_LIMIT)
        {
         //--- StopLoss modification
         if(sl>0)
           {
            //--- If the distance from the price to StopLoss is less than FreezeLevel
            if(!this.CheckStopLossByFreezeLevel(order_type,sl,symbol_obj))
              {
               //--- add the error code to the list and write 'false' to the result
               this.AddErrorCodeToList(MSG_LIB_TEXT_SL_LESS_FREEZE_LEVEL);
               res &=false;
              }
           }
         //--- TakeProfit modification
         if(tp>0)
           {
            //--- If the distance from the price to StopLoss is less than FreezeLevel
            if(!this.CheckTakeProfitByFreezeLevel(order_type,tp,symbol_obj))
              {
               //--- add the error code to the list and write 'false' to the result
               this.AddErrorCodeToList(MSG_LIB_TEXT_TP_LESS_FREEZE_LEVEL);
               res &=false;
              }
           }
        }
      //--- If this is a pending order
      else
        {
         //--- Placement price modification
         if(price>0)
           {
            //--- If the distance from the price to the order activation price is less than FreezeLevel
            if(!this.CheckPriceByFreezeLevel(order_type,price,symbol_obj))
              {
               //--- add the error code to the list and write 'false' to the result
               this.AddErrorCodeToList(MSG_LIB_TEXT_PR_LESS_FREEZE_LEVEL);
               res &=false;
              }
           }
        }
     }
   return res;
  }
//+------------------------------------------------------------------+


Abhängig von der Art des durchgeführten Handelsgeschäftes und des Order-/Positionstyps werden die Preisniveaus relativ zum StopLevel und FreezeLevel überprüft. Sind die Preise ungültig, wird der Fehlercode in die Fehlerliste aufgenommen und false zum Ergebnis addiert. Nach Abschluss aller Prüfungen wird das Endergebnis an die aufrufende Methode zurückgegeben.

Die allgemeine Methode, die alle Einschränkungen und Fehler prüft:

//+------------------------------------------------------------------+
//| Check limitations and errors                                     |
//+------------------------------------------------------------------+
bool CTrading::CheckErrors(const double volume,
                           const double price,
                           const ENUM_ACTION_TYPE action,
                           const ENUM_ORDER_TYPE order_type,
                           const CSymbol *symbol_obj,
                           const string source_method,
                           const double limit=0,
                           double sl=0,
                           double tp=0)
  {
//--- the result of conducting all checks
   bool res=true;
//--- Clear the error list
   this.m_list_errors.Clear();
   this.m_list_errors.Sort();
   
//--- Check trading limitations
   res &=this.CheckTradeConstraints(volume,action,symbol_obj,source_method,sl,tp);
//--- Check the funds sufficiency for opening positions/placing orders
   if(action<ACTION_TYPE_CLOSE_BY)
      res &=this.CheckMoneyFree(volume,price,order_type,symbol_obj,source_method);
//--- Check parameter values by StopLevel and FreezeLevel
   res &=this.CheckLevels(action,order_type,price,limit,sl,tp,symbol_obj,source_method);

//--- If there are limitations, display the header and the error list
   if(!res)
     {
      //--- Request was rejected before sending to the server due to:
      int total=this.m_list_errors.Total();
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
        {
         //--- For MQL5, first display the list header followed by the error list
         #ifdef __MQL5__
         ::Print(source_method,CMessage::Text(MSG_LIB_TEXT_REQUEST_REJECTED_DUE));
         for(int i=0;i<total;i++)
            ::Print((total>1 ? string(i+1)+". " : ""),CMessage::Text(m_list_errors.At(i)));
         //--- For MQL4, the journal messages are displayed in the reverse order: the error list in the reverse loop is followed by the list header
         #else    
         for(int i=total-1;i>WRONG_VALUE;i--)
            ::Print((total>1 ? string(i+1)+". " : ""),CMessage::Text(m_list_errors.At(i)));
         ::Print(source_method,CMessage::Text(MSG_LIB_TEXT_REQUEST_REJECTED_DUE));
         #endif 
        }
     }
   return res;
  }
//+------------------------------------------------------------------+


Die Methode zur Überprüfung der Handelseinschränkungen, die Methode zur Überprüfung der Geldmittelausstattung, diejenige zur Eröffnung einer Position oder zur Erteilung einer Pending Order, sowie die Methode zur Überprüfung der Mindestabstände von Stop-Orders nach StopLevel und FreezeLevel werden in der Methode nacheinander aufgerufen.
Das Operationsergebnis jeder der Methoden wird zu dem von der Methode zurückgegebenen Wert addiert
.
Im Falle von Einschränkungen und Fehlern,
wird die vollständige Liste der erkannten Probleme im Journal angezeigt.
Letztendlich wird das Ergebnis aller Prüfungen zurückgegeben.

Die Methode, die das Flag setzt, das die Audiosignale für alle Handelsobjekte aller verwendeten Symbole aktiviert:

//+------------------------------------------------------------------+
//| Set the flag enabling sounds                                     |
//+------------------------------------------------------------------+
void CTrading::SetUseSounds(const bool flag)
  {
   //--- Set the flag enabling sounds
   this.m_use_sound=flag;
   //--- Get the symbol list
   CArrayObj *list=this.m_symbols.GetList();
   if(list==NULL || list.Total()==0)
      return;
   //--- In a loop by the list of symbols
   int total=list.Total();
   for(int i=0;i<total;i++)
     {
      //--- get the next symbol object
      CSymbol *symbol_obj=list.At(i);
      if(symbol_obj==NULL)
         continue;
      //--- get a symbol trading object
      CTradeObj *trade_obj=symbol_obj.GetTradeObj();
      if(trade_obj==NULL)
         continue;
      //--- set the flag enabling sounds for a trading object
      trade_obj.SetUseSound(flag);
     }
  }
//+------------------------------------------------------------------+


Das globale Flag, das die Audiosignale der Handelsklasse ermöglicht, wird in der Methode als Erstes gesetzt. Ähnliche Flags werden dann für jedes Handelsobjekt jedes verwendeten Symbols in einer Schleife über alle verwendeten Symbole gesetzt.

Da wir uns für die Verwendung von Template-Handelsmethoden entschieden haben, um Werte als Preise oder als Abstände übergeben zu können, re-definieren wir zunächst die Handelsmethoden im 'public' Teil der Klasse — setzen der Template-Datentypen für einige Parameter:

//--- Open (1) Buy, (2) Sell position
   template<typename SL,typename TP> 
   bool                 OpenBuy(const double volume,
                                const string symbol,
                                const ulong magic=ULONG_MAX,
                                const SL sl=0,
                                const TP tp=0,
                                const string comment=NULL,
                                const ulong deviation=ULONG_MAX);
   
   template<typename SL,typename TP> 
   bool                 OpenSell(const double volume,
                                 const string symbol,
                                 const ulong magic=ULONG_MAX,
                                 const SL sl=0,
                                 const TP tp=0,
                                 const string comment=NULL,
                                 const ulong deviation=ULONG_MAX);

//--- Modify a position
   template<typename SL,typename TP> 
   bool                 ModifyPosition(const ulong ticket,const SL sl=WRONG_VALUE,const TP tp=WRONG_VALUE);

//--- Close a position (1) fully, (2) partially, (3) by an opposite one
   bool                 ClosePosition(const ulong ticket,const string comment=NULL,const ulong deviation=ULONG_MAX);
   bool                 ClosePositionPartially(const ulong ticket,const double volume,const string comment=NULL,const ulong deviation=ULONG_MAX);
   bool                 ClosePositionBy(const ulong ticket,const ulong ticket_by);

//--- Set (1) BuyStop, (2) BuyLimit, (3) BuyStopLimit pending order
   template<typename PR,typename SL,typename TP>
   bool                 PlaceBuyStop(const double volume,
                                           const string symbol,
                                           const PR price,
                                           const SL sl=0,
                                           const TP tp=0,
                                           const ulong magic=ULONG_MAX,
                                           const string comment=NULL,
                                           const datetime expiration=0,
                                           const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
   template<typename PR,typename SL,typename TP>
   bool                 PlaceBuyLimit(const double volume,
                                           const string symbol,
                                           const PR price,
                                           const SL sl=0,
                                           const TP tp=0,
                                           const ulong magic=ULONG_MAX,
                                           const string comment=NULL,
                                           const datetime expiration=0,
                                           const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
   template<typename PR,typename PL,typename SL,typename TP>
   bool                 PlaceBuyStopLimit(const double volume,
                                           const string symbol,
                                           const PR price_stop,
                                           const PL price_limit,
                                           const SL sl=0,
                                           const TP tp=0,
                                           const ulong magic=ULONG_MAX,
                                           const string comment=NULL,
                                           const datetime expiration=0,
                                           const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);

//--- Set (1) SellStop, (2) SellLimit, (3) SellStopLimit pending order
   template<typename PR,typename SL,typename TP>
   bool                 PlaceSellStop(const double volume,
                                           const string symbol,
                                           const PR price,
                                           const SL sl=0,
                                           const TP tp=0,
                                           const ulong magic=ULONG_MAX,
                                           const string comment=NULL,
                                           const datetime expiration=0,
                                           const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
   template<typename PR,typename SL,typename TP>
   bool                 PlaceSellLimit(const double volume,
                                           const string symbol,
                                           const PR price,
                                           const SL sl=0,
                                           const TP tp=0,
                                           const ulong magic=ULONG_MAX,
                                           const string comment=NULL,
                                           const datetime expiration=0,
                                           const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
   template<typename PR,typename PL,typename SL,typename TP>
   bool                 PlaceSellStopLimit(const double volume,
                                           const string symbol,
                                           const PR price_stop,
                                           const PL price_limit,
                                           const SL sl=0,
                                           const TP tp=0,
                                           const ulong magic=ULONG_MAX,
                                           const string comment=NULL,
                                           const datetime expiration=0,
                                           const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
//--- Modify a pending order
   template<typename PR,typename PL,typename SL,typename TP>
   bool                 ModifyOrder(const ulong ticket,
                                          const PR price=WRONG_VALUE,
                                          const SL sl=WRONG_VALUE,
                                          const TP tp=WRONG_VALUE,
                                          const PL limit=WRONG_VALUE,
                                          datetime expiration=WRONG_VALUE,
                                          ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE);
//--- Remove a pending order
   bool                 DeleteOrder(const ulong ticket);
  };


Implementieren der Methode zum Eröffnen einer Kaufposition:

//+------------------------------------------------------------------+
//| Open Buy position                                                |
//+------------------------------------------------------------------+
template<typename SL,typename TP> 
bool CTrading::OpenBuy(const double volume,
                       const string symbol,
                       const ulong magic=ULONG_MAX,
                       const SL sl=0,
                       const TP tp=0,
                       const string comment=NULL,
                       const ulong deviation=ULONG_MAX)
  {
   ENUM_ACTION_TYPE action=ACTION_TYPE_BUY;
   ENUM_ORDER_TYPE order=ORDER_TYPE_BUY;
//--- Get a symbol object by a symbol name
   CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(symbol);
   if(symbol_obj==NULL)
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_SYM_OBJ));
      return false;
     }
//--- get a trading object from a symbol object
   CTradeObj *trade_obj=symbol_obj.GetTradeObj();
   if(trade_obj==NULL)
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ));
      return false;
     }
//--- Update symbol quotes
   symbol_obj.RefreshRates();
//--- Set the prices
   if(!this.SetPrices(order,0,sl,tp,0,DFUN,symbol_obj))
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_UNSUPPORTED_PRICE_TYPE_IN_REQ));
      return false;
     }
//--- In case of trading limitations, funds insufficiency,
//--- StopLevel or FreezeLevel limitations, play the error sound and exit
   if(!this.CheckErrors(volume,symbol_obj.Ask(),action,ORDER_TYPE_BUY,symbol_obj,DFUN,0,this.m_req_price.sl,this.m_req_price.tp))
     {
      if(this.IsUseSounds())
         trade_obj.PlaySoundError(action,order);
      return false;
     }

//--- Send the request
   bool res=trade_obj.OpenPosition(POSITION_TYPE_BUY,volume,this.m_req_price.sl,this.m_req_price.tp,magic,comment,deviation);
//--- If the request is successful, play the success sound set for a symbol trading object for this type of trading operation
   if(res)
     {
      if(this.IsUseSounds())
         trade_obj.PlaySoundSuccess(action,order);
     }
//--- If the request is not successful, play the error sound set for a symbol trading object for this type of trading operation
   else
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(trade_obj.GetResultRetcode()));
      if(this.IsUseSounds())
         trade_obj.PlaySoundError(action,order);
     }
//--- Return the result of sending a trading request in a symbol trading object
   return res;
  }
//+------------------------------------------------------------------+


Es können Werte der Typen double, long, ulong, int und uint als StopLoss und TakeProfit übergeben werden. Die Methode zum Setzen von Preisen trägt die korrekt berechneten und normalisierten Preise in die Preisstruktur m_req_price ein. Diese berechneten Preise werden wiederum an die Handelsmethode eines Symbols Handelsobjekt übergeben. Die weiteren Aktionen werden in den Codekommentaren erläutert. Ich glaube, sie werden keine Fragen oder Missverständnisse verursachen. In jedem Fall können Sie gerne den Abschnitt Kommentare verwenden.

Die übrigen Handelsmethoden werden auf ähnliche Weise gemacht, deshalb werden wir hier nicht darauf eingehen. Sie können sie immer im Detail in den unten angehängten Bibliotheksdateien studieren.

Damit ist die Verbesserung der Klasse CTrading abgeschlossen.
Wir haben die wichtigsten Änderungen in der Klasse untersucht.
Kleinere Änderungen und Verbesserungen haben wir nicht besprochen, da sie für das Verständnis der Hauptidee nicht notwendig sind. Außerdem sind sie alle in den unten angehängten Dateien enthalten.

Datas.mqh enthält nun die Konstante des Fehlercodes, die im vorigen Artikel fehlte:

//--- CTrading
   MSG_LIB_TEXT_TERMINAL_NOT_TRADE_ENABLED,           // Trade operations are not allowed in the terminal (the AutoTrading button is disabled)
   MSG_LIB_TEXT_EA_NOT_TRADE_ENABLED,                 // EA is not allowed to trade (F7 --> Common --> Allow Automated Trading)
   MSG_LIB_TEXT_ACCOUNT_NOT_TRADE_ENABLED,            // Trading is disabled for the current account
   MSG_LIB_TEXT_ACCOUNT_EA_NOT_TRADE_ENABLED,         // Trading on the trading server side is disabled for EAs on the current account
   MSG_LIB_TEXT_TERMINAL_NOT_CONNECTED,               // No connection to the trade server
   MSG_LIB_TEXT_REQUEST_REJECTED_DUE,                 // Request was rejected before sending to the server due to:
   MSG_LIB_TEXT_NOT_ENOUTH_MONEY_FOR,                 // Insufficient funds for performing a trade
   MSG_LIB_TEXT_MAX_VOLUME_LIMIT_EXCEEDED,            // Exceeded maximum allowed aggregate volume of orders and positions in one direction
   MSG_LIB_TEXT_REQ_VOL_LESS_MIN_VOLUME,              // Request volume is less than the minimum acceptable one
   MSG_LIB_TEXT_REQ_VOL_MORE_MAX_VOLUME,              // Request volume exceeds the maximum acceptable one
   MSG_LIB_TEXT_CLOSE_BY_ORDERS_DISABLED,             // Close by is disabled
   MSG_LIB_TEXT_INVALID_VOLUME_STEP,                  // Request volume is not a multiple of the minimum lot change step gradation
   MSG_LIB_TEXT_CLOSE_BY_SYMBOLS_UNEQUAL,             // Symbols of opposite positions are not equal
   MSG_LIB_TEXT_SL_LESS_STOP_LEVEL,                   // StopLoss in points is less than a value allowed by symbol's StopLevel parameter
   MSG_LIB_TEXT_TP_LESS_STOP_LEVEL,                   // TakeProfit in points is less than a value allowed by symbol's StopLevel parameter
   MSG_LIB_TEXT_PR_LESS_STOP_LEVEL,                   // Order distance in points is less than a value allowed by symbol's StopLevel parameter
   MSG_LIB_TEXT_SL_LESS_FREEZE_LEVEL,                 // The distance from the price to StopLoss is less than a value allowed by symbol's FreezeLevel parameter
   MSG_LIB_TEXT_TP_LESS_FREEZE_LEVEL,                 // The distance from the price to TakeProfit is less than a value allowed by symbol's FreezeLevel parameter
   MSG_LIB_TEXT_PR_LESS_FREEZE_LEVEL,                 // The distance from the price to an order activation level is less than a value allowed by symbol's FreezeLevel parameter
   MSG_LIB_TEXT_UNSUPPORTED_SL_TYPE,                  // Unsupported StopLoss parameter type (should be 'int' or 'double')
   MSG_LIB_TEXT_UNSUPPORTED_TP_TYPE,                  // Unsupported TakeProfit parameter type (should be 'int' or 'double')
   MSG_LIB_TEXT_UNSUPPORTED_PR_TYPE,                  // Unsupported price parameter type (should be 'int' or 'double')
   MSG_LIB_TEXT_UNSUPPORTED_PL_TYPE,                  // Unsupported limit order price parameter type (should be 'int' or 'double')
   MSG_LIB_TEXT_UNSUPPORTED_PRICE_TYPE_IN_REQ,        // Unsupported price parameter type in a request
   
  };

Unten ist der korrespondierende Text:

//--- CEngine
   {"С момента последнего запуска ЕА торговых событий не было","No trade events since the last launch of EA"},
   {"Не удалось получить описание последнего торгового события","Failed to get description of the last trading event"},
   {"Не удалось получить список открытых позиций","Failed to get open positions list"},
   {"Не удалось получить список установленных ордеров","Failed to get pending orders list"},
   {"Нет открытых позиций","No open positions"},
   {"Нет установленных ордеров","No placed orders"},
   {"В терминале нет разрешения на проведение торговых операций (отключена кнопка \"Авто-торговля\")","No permission to conduct trading operations in terminal (\"AutoTrading\" button disabled)"},
   {"Для советника нет разрешения на проведение торговых операций (F7 --> Общие --> \"Разрешить автоматическую торговлю\")","EA does not have permission to conduct trading operations (F7 --> Common --> \"Allow Automatic Trading\")"},
   {"Для текущего счёта запрещена торговля","Trading prohibited for the current account"},
   {"Для советников на текущем счёте запрещена торговля на стороне торгового сервера","From the side of trade server, trading for EA on the current account prohibited"},
   {"Нет связи с торговым сервером","No connection to trading server"},
   {"Запрос отклонён до отправки на сервер по причине:","Request rejected before being sent to server due to:"},
   {"Недостаточно средств для совершения торговой операции","Not enough money to perform trading operation"},
   {"Превышен максимальный совокупный объём ордеров и позиций в одном направлении","Exceeded maximum total volume of orders and positions in one direction"},
   {"Объём в запросе меньше минимально-допустимого","Volume in request less than minimum allowable"},
   {"Объём в запросе больше максимально-допустимого","Volume in request greater than maximum allowable"},
   {"Закрытие встречным запрещено","CloseBy orders prohibited"},
   {"Объём в запросе не кратен минимальной градации шага изменения лота","Volume in request not a multiple of minimum gradation of step for changing lot"},
   {"Символы встречных позиций не равны","Symbols of two opposite positions not equal"},
   {"Размер StopLoss в пунктах меньше разрешённого параметром StopLevel символа","StopLoss in points less than allowed by symbol's StopLevel"},
   {"Размер TakeProfit в пунктах меньше разрешённого параметром StopLevel символа","TakeProfit in points less than allowed by symbol's StopLevel"},
   {"Дистанция установки ордера в пунктах меньше разрешённой параметром StopLevel символа","Distance to place order in points less than allowed by symbol's StopLevel"},
   {"Дистанция от цены до StopLoss меньше разрешённой параметром FreezeLevel символа","Distance from price to StopLoss less than allowed by symbol's FreezeLevel"},
   {"Дистанция от цены до TakeProfit меньше разрешённой параметром FreezeLevel символа","Distance from price to TakeProfit less than allowed by symbol's FreezeLevel"},
   {"Дистанция от цены до цены срабатывания ордера меньше разрешённой параметром FreezeLevel символа","Distance from price to order triggering price less than allowed by symbol's FreezeLevel"},
   {"Неподдерживаемый тип параметра StopLoss (необходимо int или double)","Unsupported StopLoss parameter type (int or double required)"},
   {"Неподдерживаемый тип параметра TakeProfit (необходимо int или double)","Unsupported TakeProfit parameter type (int or double required)"},
   {"Неподдерживаемый тип параметра цены (необходимо int или double)","Unsupported price parameter type (int or double required)"},
   {"Неподдерживаемый тип параметра цены limit-ордера (необходимо int или double)","Unsupported type of price parameter for limit order (int or double required)"},
   {"Неподдерживаемый тип параметра цены в запросе","Unsupported price parameter type in request"},
   
  };


Ich habe mehrere Nutzerberichte über den Fehler erhalten, der beim Empfang des letzten Handelsereignisses festgestellt wurde. Der Test EA, der sich auf die Artikel bezieht und beschreibt, wie man Handelsereignisse empfängt, erhält Daten über das aufgetretene Handelsereignis, indem er den Wert des vorherigen Ereignisses mit dem aktuellen vergleicht. Dies wäre für den Zweck des Tests der Verfolgung von Handelsereignissen durch die Bibliothek ausreichend, da ich nicht beabsichtigte, die unfertige Version der Bibliothek in benutzerdefinierten Anwendungen zu verwenden, wenn ich Artikel über Handelsereignisse schreibe. Es stellte sich jedoch heraus, dass die Beschaffung von Informationen über Trading-Events sehr gefragt ist und es wichtig ist, das letzte aufgetretene Ereignis genau zu kennen.

Die implementierte Methode zum Erhalten eines Handelsereignisses kann einige Ereignisse überspringen. Zum Beispiel, wenn Sie eine Pending Order zweimal hintereinander setzen, wird die zweite nicht im Programm verfolgt (die Bibliothek verfolgt alle Ereignisse), da sie mit der vorletzten übereinstimmt ("Platzieren einer Pending-Order"), obwohl die Orders selbst sich tatsächlich unterscheiden können.

Deshalb werden wir dieses Verhalten korrigieren. Heute werden wir ein einfaches Flag implementieren, das das Programm über ein Ereignis informiert, so dass wir in der Lage sind, Daten über das Ereignis im Programm anzuzeigen. Im nächsten Artikel werden wir das Erhalten von Handelsereignissen im Programm vervollständigen, indem wir eine vollständige Liste aller gleichzeitig aufgetretenen Ereignisse erstellen und sie an das Programm senden. Auf diese Weise können wir nicht nur die Information über das eingetretene Handelsereignis erhalten, sondern auch alle gleichzeitig eingetretenen Ereignisse ansehen, wie es für Konto und Ergebnissee der Symbolkollektion geschieht.

Öffnen Sie die Datei EventsCollection.mqh der Ereigniskollektionen und nehmen Sie alle notwendigen Verbesserungen vor.

Zurücksetzen des Flags des letzten Ereignisses im Klassenkonstruktor:

//+------------------------------------------------------------------+
//| Konstruktor                                                      |
//+------------------------------------------------------------------+
CEventsCollection::CEventsCollection(void) : m_trade_event(TRADE_EVENT_NO_EVENT),m_trade_event_code(TRADE_EVENT_FLAG_NO_EVENT)
  {
   this.m_list_trade_events.Clear();
   this.m_list_trade_events.Sort(SORT_BY_EVENT_TIME_EVENT);
   this.m_list_trade_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();
   this.m_is_event=false;
   ::ZeroMemory(this.m_tick);
  }
//+------------------------------------------------------------------+


Außerdem setzen Sie das Flag des letzten Ereignisses ganz am Anfang der Refresh()-Methode zurück:

//+------------------------------------------------------------------+
//| Aktualisieren der Ereignisliste                                  |
//+------------------------------------------------------------------+
void CEventsCollection::Refresh(CArrayObj* list_history,
                                CArrayObj* list_market,
                                CArrayObj* list_changes,
                                CArrayObj* list_control,
                                const bool is_history_event,
                                const bool is_market_event,
                                const int  new_history_orders,
                                const int  new_market_pendings,
                                const int  new_market_positions,
                                const int  new_deals,
                                const double changed_volume)
  {
//--- Rückkehren, wenn die Liste leer ist
   if(list_history==NULL || list_market==NULL)
      return;
//---
   this.m_is_event=false;
//--- Wenn das Ereignis in der Umgebung des Marktes existiert
   if(is_market_event)
     {


In jeder Methode zum Erstellen eines Handelsereignisses in den Blöcken zum Hinzufügen eines Handelsereignisses in die Liste, des Sendens eines Ereignisses an die Kontrollprogrammkarte und des Setzens des letzten Ereigniswertes:

      //--- Hinzufügen einer Ereignisobjekts, wenn es nicht in der Liste ist
      if(!this.IsPresentEventInList(event))
        {
         this.m_list_trade_events.InsertSort(event);
         //--- Senden einer Nachricht über das Ereignis und setzen des Wertes des letzten Handelsereignisses
         event.SendEvent();
         this.m_trade_event=event.TradeEvent();
        }


fügen wir die Flag-Einstellung für ein Handelsereignis hinzu::

      if(!this.IsPresentEventInList(event))
        {
         this.m_list_trade_events.InsertSort(event);
         //--- Senden einer Nachricht über das Ereignis und setzen des Wertes des letzten Handelsereignisses
         this.m_trade_event=event.TradeEvent();
         this.m_is_event=true;
         event.SendEvent();
        }


Mit Ctrl+F finden Sie bequem alle Stellen zum Setzen des Flags. Geben Sie "event.SendEvent();" (ohne Anführungszeichen) in die Suchzeile ein. Fügen Sie das Setzen des Ereignisflags zu jedem erkannten Codespot hinzu, wie in der obigen Auflistung angezeigt.

Nehmen Sie einige Änderungen in der Datei Engine.mqh vor.

Fügen Sie im 'public' Teil der Klasse CEngine die Methode hinzu, die das Flag eines aufgetretenen Handelsereignisses zurückgibt:

//--- Return the (1) hedge account, (2) working in the tester, (3) account event, (4) symbol event  and (5) trading event flag
   bool                 IsHedge(void)                             const { return this.m_is_hedge;                             }
   bool                 IsTester(void)                            const { return this.m_is_tester;                            }
   bool                 IsAccountsEvent(void)                     const { return this.m_accounts.IsEvent();                   }
   bool                 IsSymbolsEvent(void)                      const { return this.m_symbols.IsEvent();                    }
   bool                 IsTradeEvent(void)                        const { return this.m_events.IsEvent();                     }


Der Block mit den Methoden zur Arbeit mit den Audiodateien enthält die Methode, die das Flag für die Erlaubnis Audiodateien abzuspielen, enthält:

//--- Set standard sounds (symbol==NULL) for a symbol trading object, (symbol!=NULL) for trading objects of all symbols
   void                 SetSoundsStandart(const string symbol=NULL)
                          {
                           this.m_trading.SetSoundsStandart(symbol);
                          }
//--- Set the flag of using sounds
   void                 SetUseSounds(const bool flag) { this.m_trading.SetUseSounds(flag);   }
//--- Set a sound for a specified order/position type and symbol. 'mode' specifies an event a sound is set for
//--- (symbol=NULL) for trading objects of all symbols, (symbol!=NULL) for a trading object of a specified symbol
   void                 SetSound(const ENUM_MODE_SET_SOUND mode,const ENUM_ORDER_TYPE action,const string sound,const string symbol=NULL)
                          {
                           this.m_trading.SetSound(mode,action,sound,symbol);
                          }
//--- Play a sound by its description
   bool                 PlaySoundByDescription(const string sound_description);


Die Methode ruft einfach die gleichnamige Klassenmethode aus CTrading auf, die wir oben besprochen haben.

Setzen Sie das Handelsereignis-Flag zu Beginn der Methode zur Prüfung von Handelsereignissen zurück:

//+------------------------------------------------------------------+
//| Prüfen der Handelsereignisse                                     |
//+------------------------------------------------------------------+
void CEngine::TradeEventsControl(void)
  {
//--- Initialize trading events' flags
   this.m_is_market_trade_event=false;
   this.m_is_history_trade_event=false;
   this.m_events.SetEvent(false);
//--- Aktualisieren der Liste 
   this.m_market.Refresh();
   this.m_history.Refresh();
//--- Aktionen beim ersten Start
   if(this.IsFirstStart())
     {
      this.m_last_trade_event=TRADE_EVENT_NO_EVENT;
      return;
     }
//--- Prüfen der Änderungen des Marktstatus' und der Kontohistorie 
   this.m_is_market_trade_event=this.m_market.IsTradeEvent();
   this.m_is_history_trade_event=this.m_history.IsTradeEvent();

//--- Im Falle irgendeines Ereignisses, werden die Listen, Flags und die Anzahl der neuen Aufträge und Deals an die Kollektion der Ereignisse gesendet und aktualisiert
   int change_total=0;
   CArrayObj* list_changes=this.m_market.GetListChanges();
   if(list_changes!=NULL)
      change_total=list_changes.Total();
   if(this.m_is_history_trade_event || this.m_is_market_trade_event || change_total>0)
     {
      this.m_events.Refresh(this.m_history.GetList(),this.m_market.GetList(),list_changes,this.m_market.GetListControl(),
                            this.m_is_history_trade_event,this.m_is_market_trade_event,
                            this.m_history.NewOrders(),this.m_market.NewPendingOrders(),
                            this.m_market.NewPositions(),this.m_history.NewDeals(),
                            this.m_market.ChangedVolumeValue());
      //--- Abrufen des letzten Handelsereignisses auf dem Konto
      this.m_last_trade_event=this.m_events.GetLastTradeEvent();
     }
  }
//+------------------------------------------------------------------+


Auch die Methoden für die Arbeit mit einer Handelsklasse haben sich geändert. Auch sie haben jetzt Template-Parameter:

//--- Open (1) Buy, (2) Sell position
   template<typename SL,typename TP>
   bool                 OpenBuy(const double volume,
                                const string symbol,
                                const ulong magic=ULONG_MAX,
                                SL sl=0,
                                TP tp=0,
                                const string comment=NULL,
                                const ulong deviation=ULONG_MAX);
   template<typename SL,typename TP>
   bool                 OpenSell(const double volume,
                                 const string symbol,
                                 const ulong magic=ULONG_MAX,
                                 SL sl=0,
                                 TP tp=0,
                                 const string comment=NULL,
                                 const ulong deviation=ULONG_MAX);
//--- Modify a position
   template<typename SL,typename TP>
   bool                 ModifyPosition(const ulong ticket,const SL sl=WRONG_VALUE,const TP tp=WRONG_VALUE);
//--- Close a position (1) fully, (2) partially, (3) by an opposite one
   bool                 ClosePosition(const ulong ticket,const string comment=NULL,const ulong deviation=ULONG_MAX);
   bool                 ClosePositionPartially(const ulong ticket,const double volume,const string comment=NULL,const ulong deviation=ULONG_MAX);
   bool                 ClosePositionBy(const ulong ticket,const ulong ticket_by);
//--- Set (1) BuyStop, (2) BuyLimit, (3) BuyStopLimit pending order
   template<typename PR,typename SL,typename TP>
   bool                 PlaceBuyStop(const double volume,
                                     const string symbol,
                                     const PR price,
                                     const SL sl=0,
                                     const TP tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
   template<typename PR,typename SL,typename TP>
   bool                 PlaceBuyLimit(const double volume,
                                     const string symbol,
                                     const PR price,
                                     const SL sl=0,
                                     const TP tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
   template<typename PR,typename PL,typename SL,typename TP>
   bool                 PlaceBuyStopLimit(const double volume,
                                     const string symbol,
                                     const PR price_stop,
                                     const PL price_limit,
                                     const SL sl=0,
                                     const TP tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
//--- Set (1) SellStop, (2) SellLimit, (3) SellStopLimit pending order
   template<typename PR,typename SL,typename TP>
   bool                 PlaceSellStop(const double volume,
                                     const string symbol,
                                     const PR price,
                                     const SL sl=0,
                                     const TP tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
   template<typename PR,typename SL,typename TP>
   bool                 PlaceSellLimit(const double volume,
                                     const string symbol,
                                     const PR price,
                                     const SL sl=0,
                                     const TP tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
   template<typename PR,typename PL,typename SL,typename TP>
   bool                 PlaceSellStopLimit(const double volume,
                                     const string symbol,
                                     const PR price_stop,
                                     const PL price_limit,
                                     const SL sl=0,
                                     const TP tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
//--- Modify a pending order
   template<typename PR,typename SL,typename TP,typename PL>
   bool                 ModifyOrder(const ulong ticket,
                                    const PR price=WRONG_VALUE,
                                    const SL sl=WRONG_VALUE,
                                    const TP tp=WRONG_VALUE,
                                    const PL stoplimit=WRONG_VALUE,
                                    datetime expiration=WRONG_VALUE,
                                    ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE);
//--- Remove a pending order
   bool                 DeleteOrder(const ulong ticket);


Die Implementierung der Handelsmethoden wurde auch entsprechend der Templatedaten geändert und an die Handelsmethoden der Klasse CTrading übergeben:

//+------------------------------------------------------------------+
//| Open Buy position                                                |
//+------------------------------------------------------------------+
template<typename SL,typename TP>
bool CEngine::OpenBuy(const double volume,const string symbol,const ulong magic=ULONG_MAX,SL sl=0,TP tp=0,const string comment=NULL,const ulong deviation=ULONG_MAX)
  {
   return this.m_trading.OpenBuy(volume,symbol,magic,sl,tp,comment,deviation);
  }
//+------------------------------------------------------------------+
//| Open a Sell position                                             |
//+------------------------------------------------------------------+
template<typename SL,typename TP>
bool CEngine::OpenSell(const double volume,const string symbol,const ulong magic=ULONG_MAX,SL sl=0,TP tp=0,const string comment=NULL,const ulong deviation=ULONG_MAX)
  {
   return this.m_trading.OpenSell(volume,symbol,magic,sl,tp,comment,deviation);
  }
//+------------------------------------------------------------------+
//| Modify a position                                                |
//+------------------------------------------------------------------+
template<typename SL,typename TP>
bool CEngine::ModifyPosition(const ulong ticket,const SL sl=WRONG_VALUE,const TP tp=WRONG_VALUE)
  {
   return this.m_trading.ModifyPosition(ticket,sl,tp);
  }
//+------------------------------------------------------------------+
//| Close a position in full                                         |
//+------------------------------------------------------------------+
bool CEngine::ClosePosition(const ulong ticket,const string comment=NULL,const ulong deviation=ULONG_MAX)
  {
   return this.m_trading.ClosePosition(ticket,comment,deviation);
  }
//+------------------------------------------------------------------+
//| Close a position partially                                       |
//+------------------------------------------------------------------+
bool CEngine::ClosePositionPartially(const ulong ticket,const double volume,const string comment=NULL,const ulong deviation=ULONG_MAX)
  {
   return this.m_trading.ClosePositionPartially(ticket,volume,comment,deviation);
  }
//+------------------------------------------------------------------+
//| Close a position by an opposite one                              |
//+------------------------------------------------------------------+
bool CEngine::ClosePositionBy(const ulong ticket,const ulong ticket_by)
  {
   return this.m_trading.ClosePositionBy(ticket,ticket_by);
  }
//+------------------------------------------------------------------+
//| Place BuyStop pending order                                      |
//+------------------------------------------------------------------+
template<typename PR,typename SL,typename TP>
bool CEngine::PlaceBuyStop(const double volume,
                           const string symbol,
                           const PR price,
                           const SL sl=0,
                           const TP tp=0,
                           const ulong magic=WRONG_VALUE,
                           const string comment=NULL,
                           const datetime expiration=0,
                           const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC)
  {
   return this.m_trading.PlaceBuyStop(volume,symbol,price,sl,tp,magic,comment,expiration,type_time);
  }
//+------------------------------------------------------------------+
//| Place BuyLimit pending order                                     |
//+------------------------------------------------------------------+
template<typename PR,typename SL,typename TP>
bool CEngine::PlaceBuyLimit(const double volume,
                            const string symbol,
                            const PR price,
                            const SL sl=0,
                            const TP tp=0,
                            const ulong magic=WRONG_VALUE,
                            const string comment=NULL,
                            const datetime expiration=0,
                            const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC)
  {
   return this.m_trading.PlaceBuyLimit(volume,symbol,price,sl,tp,magic,comment,expiration,type_time);
  }
//+------------------------------------------------------------------+
//| Place BuyStopLimit pending order                                 |
//+------------------------------------------------------------------+
template<typename PR,typename PL,typename SL,typename TP>
bool CEngine::PlaceBuyStopLimit(const double volume,
                                const string symbol,
                                const PR price_stop,
                                const PL price_limit,
                                const SL sl=0,
                                const TP tp=0,
                                const ulong magic=WRONG_VALUE,
                                const string comment=NULL,
                                const datetime expiration=0,
                                const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC)
  {
   return this.m_trading.PlaceBuyStopLimit(volume,symbol,price_stop,price_limit,sl,tp,magic,comment,expiration,type_time);
  }
//+------------------------------------------------------------------+
//| Place SellStop pending order                                     |
//+------------------------------------------------------------------+
template<typename PR,typename SL,typename TP>
bool CEngine::PlaceSellStop(const double volume,
                            const string symbol,
                            const PR price,
                            const SL sl=0,
                            const TP tp=0,
                            const ulong magic=WRONG_VALUE,
                            const string comment=NULL,
                            const datetime expiration=0,
                            const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC)
  {
   return this.m_trading.PlaceSellStop(volume,symbol,price,sl,tp,magic,comment,expiration,type_time);
  }
//+------------------------------------------------------------------+
//| Place SellLimit pending order                                    |
//+------------------------------------------------------------------+
template<typename PR,typename SL,typename TP>
bool CEngine::PlaceSellLimit(const double volume,
                             const string symbol,
                             const PR price,
                             const SL sl=0,
                             const TP tp=0,
                             const ulong magic=WRONG_VALUE,
                             const string comment=NULL,
                             const datetime expiration=0,
                             const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC)
  {
   return this.m_trading.PlaceSellLimit(volume,symbol,price,sl,tp,magic,comment,expiration,type_time);
  }
//+------------------------------------------------------------------+
//| Place SellStopLimit pending order                                |
//+------------------------------------------------------------------+
template<typename PR,typename PL,typename SL,typename TP>
bool CEngine::PlaceSellStopLimit(const double volume,
                                 const string symbol,
                                 const PR price_stop,
                                 const PL price_limit,
                                 const SL sl=0,
                                 const TP tp=0,
                                 const ulong magic=WRONG_VALUE,
                                 const string comment=NULL,
                                 const datetime expiration=0,
                                 const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC)
  {
   return this.m_trading.PlaceSellStopLimit(volume,symbol,price_stop,price_limit,sl,tp,magic,comment,expiration,type_time);
  }
//+------------------------------------------------------------------+
//| Modify a pending order                                           |
//+------------------------------------------------------------------+
template<typename PR,typename SL,typename TP,typename PL>
bool CEngine::ModifyOrder(const ulong ticket,
                          const PR price=WRONG_VALUE,
                          const SL sl=WRONG_VALUE,
                          const TP tp=WRONG_VALUE,
                          const PL stoplimit=WRONG_VALUE,
                          datetime expiration=WRONG_VALUE,
                          ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE)
  {
   return this.m_trading.ModifyOrder(ticket,price,sl,tp,stoplimit,expiration,type_time);
  }
//+------------------------------------------------------------------+
//| Remove a pending order                                           |
//+------------------------------------------------------------------+
bool CEngine::DeleteOrder(const ulong ticket)
  {
   return this.m_trading.DeleteOrder(ticket);
  }
//+------------------------------------------------------------------+


Damit ist die Verbesserung der Handelsklasse und die Anpassung der erhaltenen Handelsereignisse abgeschlossen.

Tests

Um die aktuelle Bibliotheksversion zu testen, verwenden wir den EA aus dem vorherigen Artikel und speichern ihn in \MQL5\Experts\TestDoEasy\Part23\ unter dem Namen TestDoEasyPart23.mq5.

Bringen wir ein bisschen Ordnung in die Sache. Verschieben wir alle Aktionen, die sich auf die Bibliotheks-Initialisierung beziehen, in die separate Funktion OnInitDoEasy():

//+------------------------------------------------------------------+
//| Initializing DoEasy library                                      |
//+------------------------------------------------------------------+
void OnInitDoEasy()
  {
//--- Check if working with the full list is selected
   used_symbols_mode=InpModeUsedSymbols;
   if((ENUM_SYMBOLS_MODE)used_symbols_mode==SYMBOLS_MODE_ALL)
     {
      int total=SymbolsTotal(false);
      string ru_n="\nКоличество символов на сервере "+(string)total+".\nМаксимальное количество: "+(string)SYMBOLS_COMMON_TOTAL+" символов.";
      string en_n="\nNumber of symbols on server "+(string)total+".\nMaximum number: "+(string)SYMBOLS_COMMON_TOTAL+" symbols.";
      string caption=TextByLanguage("Внимание!","Attention!");
      string ru="Выбран режим работы с полным списком.\nВ этом режиме первичная подготовка списка коллекции символов может занять длительное время."+ru_n+"\nПродолжить?\n\"Нет\" - работа с текущим символом \""+Symbol()+"\"";
      string en="Full list mode selected.\nIn this mode, the initial preparation of the collection symbols list may take a long time."+en_n+"\nContinue?\n\"No\" - working with the current symbol \""+Symbol()+"\"";
      string message=TextByLanguage(ru,en);
      int flags=(MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2);
      int mb_res=MessageBox(message,caption,flags);
      switch(mb_res)
        {
         case IDNO : 
           used_symbols_mode=SYMBOLS_MODE_CURRENT; 
           break;
         default:
           break;
        }
     }
//--- Fill in the array of used symbols
   used_symbols=InpUsedSymbols;
   CreateUsedSymbolsArray((ENUM_SYMBOLS_MODE)used_symbols_mode,used_symbols,array_used_symbols);

//--- Set the type of the used symbol list in the symbol collection
   engine.SetUsedSymbols(array_used_symbols);
//--- Displaying the selected mode of working with the symbol object collection
   Print(engine.ModeSymbolsListDescription(),TextByLanguage(". Number of used symbols: ",". Number of symbols used: "),engine.GetSymbolsCollectionTotal());
   
//--- Create resource text files
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_01",TextByLanguage("Звук упавшей монетки 1","Falling coin 1"),sound_array_coin_01);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_02",TextByLanguage("Звук упавших монеток","Falling coins"),sound_array_coin_02);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_03",TextByLanguage("Звук монеток","Coins"),sound_array_coin_03);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_04",TextByLanguage("Звук упавшей монетки 2","Falling coin 2"),sound_array_coin_04);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_01",TextByLanguage("Звук щелчка по кнопке 1","Button click 1"),sound_array_click_01);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_02",TextByLanguage("Звук щелчка по кнопке 2","Button click 2"),sound_array_click_02);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_03",TextByLanguage("Звук щелчка по кнопке 3","Button click 3"),sound_array_click_03);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_cash_machine_01",TextByLanguage("Звук кассового аппарата","Cash machine"),sound_array_cash_machine_01);
   engine.CreateFile(FILE_TYPE_BMP,"img_array_spot_green",TextByLanguage("Изображение \"Зелёный светодиод\"","Image \"Green Spot lamp\""),img_array_spot_green);
   engine.CreateFile(FILE_TYPE_BMP,"img_array_spot_red",TextByLanguage("Изображение \"Красный светодиод\"","Image \"Red Spot lamp\""),img_array_spot_red);

//--- Pass all existing collections to the trading class
   engine.TradingOnInit();
//--- Set synchronous passing of orders for all used symbols
   engine.TradingSetAsyncMode(false);
//--- Set standard sounds for trading objects of all used symbols
   engine.SetSoundsStandart();
//--- Set the general flag of using sounds
   engine.SetUseSounds(InpUseSounds);
      
//--- Set controlled values for symbols
   //--- Get the list of all collection symbols
   CArrayObj *list=engine.GetListAllUsedSymbols();
   if(list!=NULL && list.Total()!=0)
     {
      //--- In a loop by the list, set the necessary values for tracked symbol properties
      //--- By default, the LONG_MAX value is set to all properties, which means "Do not track this property" 
      //--- It can be enabled or disabled (by setting the value less than LONG_MAX or vice versa - set the LONG_MAX value) at any time and anywhere in the program
      for(int i=0;i<list.Total();i++)
        {
         CSymbol* symbol=list.At(i);
         if(symbol==NULL)
            continue;
         //--- Set control of the symbol price increase by 100 points
         symbol.SetControlBidInc(100*symbol.Point());
         //--- Set control of the symbol price decrease by 100 points
         symbol.SetControlBidDec(100*symbol.Point());
         //--- Set control of the symbol spread increase by 40 points
         symbol.SetControlSpreadInc(40);
         //--- Set control of the symbol spread decrease by 40 points
         symbol.SetControlSpreadDec(40);
         //--- Set control of the current spread by the value of 40 points
         symbol.SetControlSpreadLevel(40);
        }
     }
//--- Set controlled values for the current account
   CAccount* account=engine.GetAccountCurrent();
   if(account!=NULL)
     {
      //--- Set control of the profit increase to 10
      account.SetControlledValueINC(ACCOUNT_PROP_PROFIT,10.0);
      //--- Set control of the funds increase to 15
      account.SetControlledValueINC(ACCOUNT_PROP_EQUITY,15.0);
      //--- Set profit control level to 20
      account.SetControlledValueLEVEL(ACCOUNT_PROP_PROFIT,20.0);
     }
  }
//+------------------------------------------------------------------+


Zuvor wurde der gesamte Inhalt der Funktion in die Funktion OnInit() des EA geschrieben. Jetzt, wenn man die Bibliotheks-Initialisierungs-Aktionen in die separate Funktion verschiebt (wo man alles setzen kann, was man für die EA-Operation benötigt), ist OnInit() sauberer geworden und sieht übersichtlicher aus:

//+------------------------------------------------------------------+
//| Initialisierungsfunktion des Experten                            |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Calling the function displays the list of enumeration constants in the journal 
//--- (the list is set in the strings 22 and 25 of the DELib.mqh file) for checking the constants validity
   //EnumNumbersTest();

//--- Set EA global variables
   prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_";
   for(int i=0;i<TOTAL_BUTT;i++)
     {
      butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i);
      butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i);
     }
   lot=NormalizeLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*2.0));
   magic_number=InpMagic;
   stoploss=InpStopLoss;
   takeprofit=InpTakeProfit;
   distance_pending=InpDistance;
   distance_stoplimit=InpDistanceSL;
   slippage=InpSlippage;
   trailing_stop=InpTrailingStop*Point();
   trailing_step=InpTrailingStep*Point();
   trailing_start=InpTrailingStart;
   stoploss_to_modify=InpStopLossModify;
   takeprofit_to_modify=InpTakeProfitModify;

//--- Initialize DoEasy library
   OnInitDoEasy();
   
//--- Check and remove remaining EA graphical objects
   if(IsPresentObects(prefix))
      ObjectsDeleteAll(0,prefix);

//--- Create the button panel
   if(!CreateButtons(InpButtShiftX,InpButtShiftY))
      return INIT_FAILED;
//--- Set trailing activation button status
   ButtonState(butt_data[TOTAL_BUTT-1].name,trailing_on);

//--- Check playing a standard sound by macro substitution and a custom sound by description
   engine.PlaySoundByDescription(SND_OK);
   Sleep(600);
   engine.PlaySoundByDescription(TextByLanguage("Звук упавшей монетки 2","Falling coin 2"));

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+


Das Arbeiten mit allen Ereignissen aus der Bibliothek in der Funktion OnDoEasyEvent() ist nun auf die gleiche Art und Weise implementiert:

//+------------------------------------------------------------------+
//| Handling DoEasy library events                                   |
//+------------------------------------------------------------------+
void OnDoEasyEvent(const int id,
                   const long &lparam,
                   const double &dparam,
                   const string &sparam)
  {
   int idx=id-CHARTEVENT_CUSTOM;
   string event="::"+string(idx);
   
//--- Retrieve (1) event time milliseconds, (2) reason and (3) source from lparam, as well as (4) set the exact event time
   ushort msc=engine.EventMSC(lparam);
   ushort reason=engine.EventReason(lparam);
   ushort source=engine.EventSource(lparam);
   long time=TimeCurrent()*1000+msc;
   
//--- Handling symbol events
   if(source==COLLECTION_SYMBOLS_ID)
     {
      CSymbol *symbol=engine.GetSymbolObjByName(sparam);
      if(symbol==NULL)
         return;
      //--- Number of decimal places in the event value - in case of a 'long' event, it is 0, otherwise - Digits() of a symbol
      int digits=(idx<SYMBOL_PROP_INTEGER_TOTAL ? 0 : symbol.Digits());
      //--- Event text description
      string id_descr=(idx<SYMBOL_PROP_INTEGER_TOTAL ? symbol.GetPropertyDescription((ENUM_SYMBOL_PROP_INTEGER)idx) : symbol.GetPropertyDescription((ENUM_SYMBOL_PROP_DOUBLE)idx));
      //--- Property change text value
      string value=DoubleToString(dparam,digits);
      
      //--- Check event reasons and display its description in the journal
      if(reason==BASE_EVENT_REASON_INC)
        {
         Print(symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
        }
      if(reason==BASE_EVENT_REASON_DEC)
        {
         Print(symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
        }
      if(reason==BASE_EVENT_REASON_MORE_THEN)
        {
         Print(symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
        }
      if(reason==BASE_EVENT_REASON_LESS_THEN)
        {
         Print(symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
        }
      if(reason==BASE_EVENT_REASON_EQUALS)
        {
         Print(symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
        }
     }   
     
//--- Handling account events
   else if(source==COLLECTION_ACCOUNT_ID)
     {
      CAccount *account=engine.GetAccountCurrent();
      if(account==NULL)
         return;
      //--- Number of decimal places in the event value - in case of a 'long' event, it is 0, otherwise - Digits() of a symbol
      int digits=int(idx<ACCOUNT_PROP_INTEGER_TOTAL ? 0 : account.CurrencyDigits());
      //--- Event text description
      string id_descr=(idx<ACCOUNT_PROP_INTEGER_TOTAL ? account.GetPropertyDescription((ENUM_ACCOUNT_PROP_INTEGER)idx) : account.GetPropertyDescription((ENUM_ACCOUNT_PROP_DOUBLE)idx));
      //--- Property change text value
      string value=DoubleToString(dparam,digits);
      
      //--- Checking event reasons and handling the increase of funds by a specified value,
      
      //--- In case of a property value increase
      if(reason==BASE_EVENT_REASON_INC)
        {
         //--- Display an event in the journal
         Print(account.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
         //--- if this is an equity increase
         if(idx==ACCOUNT_PROP_EQUITY)
           {
            //--- Abrufen der Liste aller offenen Positionen
            CArrayObj* list_positions=engine.GetListMarketPosition();
            //--- Select positions with the profit exceeding zero
            list_positions=CSelect::ByOrderProperty(list_positions,ORDER_PROP_PROFIT_FULL,0,MORE);
            if(list_positions!=NULL)
              {
               //--- Sortieren der Liste nach Gewinn unter Berücksichtigung von Kommission und Swap
               list_positions.Sort(SORT_BY_ORDER_PROFIT_FULL);
               //--- Get the position index with the highest profit
               int index=CSelect::FindOrderMax(list_positions,ORDER_PROP_PROFIT_FULL);
               if(index>WRONG_VALUE)
                 {
                  COrder* position=list_positions.At(index);
                  if(position!=NULL)
                    {
                     //--- Get a ticket of a position with the highest profit and close the position by a ticket
                     engine.ClosePosition(position.Ticket());
                    }
                 }
              }
           }
        }
      //--- Other events are simply displayed in the journal
      if(reason==BASE_EVENT_REASON_DEC)
        {
         Print(account.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
        }
      if(reason==BASE_EVENT_REASON_MORE_THEN)
        {
         Print(account.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
        }
      if(reason==BASE_EVENT_REASON_LESS_THEN)
        {
         Print(account.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
        }
      if(reason==BASE_EVENT_REASON_EQUALS)
        {
         Print(account.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
        }
     } 
     
//--- Handling market watch window events
   else if(idx>MARKET_WATCH_EVENT_NO_EVENT && idx<SYMBOL_EVENTS_NEXT_CODE)
     {
      //--- Market Watch window event
      string descr=engine.GetMWEventDescription((ENUM_MW_EVENT)idx);
      string name=(idx==MARKET_WATCH_EVENT_SYMBOL_SORT ? "" : ": "+sparam);
      Print(TimeMSCtoString(lparam)," ",descr,name);
     }
     
//--- Handling trading events
   else if(idx>TRADE_EVENT_NO_EVENT && idx<TRADE_EVENTS_NEXT_CODE)
     {
      Print(DFUN,engine.GetLastTradeEventDescription());
     }
  }
//+------------------------------------------------------------------+


Hier wird alles in Blöcke je nach einem eingetroffenen Ereignis unterteilt. Die Arbeit mit Handelsereignissen wird durch die Anzeige des Namens des letzten Ereignisses im Journal dargestellt. Wenn Sie auf ein bestimmtes Ereignis reagieren müssen, können Sie hier herausfinden, um welches Ereignis es sich handelt und entscheiden, wie Sie es behandeln wollen. Sie können dies direkt hier tun oder für jedes Handelsereignis ein separates Flag definieren, um die Flags des Handelsereignisses dort zurückzusetzen, statt sie irgendwo im Programm zu behandeln.

Die Funktion OnDoEasyEvent() wird vom der Funktion OnChartEvent() des EAs aufgerufen, wenn Sie außerhalb des Testers arbeiten.
Bei der Arbeit im Tester wird die Funktion EventsHandling() von OnTick() aus aufgerufen:

//+------------------------------------------------------------------+
//| Working with events in the tester                                |
//+------------------------------------------------------------------+
void EventsHandling(void)
  {
//--- If a trading event is present
   if(engine.IsTradeEvent())
     {
      long lparam=0;
      double dparam=0;
      string sparam="";
      OnDoEasyEvent(CHARTEVENT_CUSTOM+engine.LastTradeEvent(),lparam,dparam,sparam);
     }
//--- If there is an account event
   if(engine.IsAccountsEvent())
     {
      //--- Get the list of all account events occurred simultaneously
      CArrayObj* list=engine.GetListAccountEvents();
      if(list!=NULL)
        {
         //--- Get the next event in a loop
         int total=list.Total();
         for(int i=0;i<total;i++)
           {
            //--- take an event from the list
            CEventBaseObj *event=list.At(i);
            if(event==NULL)
               continue;
            //--- Send an event to the event handler
            long lparam=event.LParam();
            double dparam=event.DParam();
            string sparam=event.SParam();
            OnDoEasyEvent(CHARTEVENT_CUSTOM+event.ID(),lparam,dparam,sparam);
           }
        }
     }
//--- If there is a symbol collection event
   if(engine.IsSymbolsEvent())
     {
      //--- Get the list of all symbol events occurred simultaneously
      CArrayObj* list=engine.GetListSymbolsEvents();
      if(list!=NULL)
        {
         //--- Get the next event in a loop
         int total=list.Total();
         for(int i=0;i<total;i++)
           {
            //--- take an event from the list
            CEventBaseObj *event=list.At(i);
            if(event==NULL)
               continue;
            //--- Send an event to the event handler
            long lparam=event.LParam();
            double dparam=event.DParam();
            string sparam=event.SParam();

            OnDoEasyEvent(CHARTEVENT_CUSTOM+event.ID(),lparam,dparam,sparam);
           }
        }
     }
  }
//+------------------------------------------------------------------+


Die Funktion ermöglicht es Ihnen, die Listen der entsprechenden Ereignisse anzusehen und die Parameter des Ereignisses auszufüllen, welches dann an OnDoEasyEvent() gesendet wird. So ordnen wir die Arbeit mit Ereignissen in OnDoEasyEvent() unabhängig davon, wo der EA gestartet wird (im Tester oder nicht). Wenn er im Tester gestartet wird, werden Ereignisse von OnTick() behandelt, wenn er außerhalb des Testers gestartet wird, werden Ereignisse von OnChartEvent() behandelt.

Somit sieht der OnTick() Befehl nun wie folgt aus:

//+------------------------------------------------------------------+
//| Experten Funktion OnTick                                         |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- If working in the tester
   if(MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer();       // Working in the timer
      PressButtonsControl();  // Button pressing control
      EventsHandling();       // Working with events
     }
//--- If the trailing flag is set
   if(trailing_on)
     {
      TrailingPositions();    // Trailing positions
      TrailingOrders();       // Trailing of pending orders
     }
  }
//+------------------------------------------------------------------+


Um die Funktionsweise der Methoden zu überprüfen, die die Gültigkeit der Werte in den Parametern der Handelsorder kontrollieren, müssen wir die Autokorrektur der ungültigen Werte aus dem EA entfernen.
In der Funktion PressButtonEvents() entfernen Sie durch Drücken der Taste alles, was mit der Korrektur der Parameterwerte von Handelsaufträgen zu tun hat:

//+------------------------------------------------------------------+
//| Handle pressing the buttons                                      |
//+------------------------------------------------------------------+
void PressButtonEvents(const string button_name)
  {
   string comment="";
   //--- Konvertieren der Namen der Schaltflächen in die Zeichenketten-ID
   string button=StringSubstr(button_name,StringLen(prefix));
   //--- Falls eine Taste gedrückt wurde
   if(ButtonState(button_name))
     {
      //--- Wenn die Schaltfläche BUTT_BUY geklickt wurde: Eröffnen einer Kaufposition
      if(button==EnumToString(BUTT_BUY))
        {
         //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zu StopLevel
         double sl=stoploss;//CorrectStopLoss(Symbol(),ORDER_TYPE_BUY,0,stoploss);
         double tp=takeprofit;//CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY,0,takeprofit);
         //--- Eröffnen einer Kaufposition
         engine.OpenBuy(lot,Symbol(),magic_number,sl,tp);   // No comment - the default comment is to be set
        }
      //--- Falls die Schaltfläche BUTT_BUY_LIMIT geklickt wurde: Platzieren von BuyLimit
      else if(button==EnumToString(BUTT_BUY_LIMIT))
        {
         //--- Abrufen der korrekten Order-Platzierung relativ zu StopLevel
         double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_LIMIT,distance_pending);
         //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zur Level der Order-Platzierung unter Berücksichtigung von StopLevel
         double sl=stoploss;//CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_LIMIT,price_set,stoploss);
         double tp=takeprofit;//CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_LIMIT,price_set,takeprofit);
         //--- Setzen einer BuyLimit-Order
         engine.PlaceBuyLimit(lot,Symbol(),price_set,sl,tp,magic_number,TextByLanguage("Отложенный BuyLimit","Pending BuyLimit order"));
        }
      //--- Falls die Schaltfläche BUTT_BUY_STOP geklickt wurde: Platzieren von BuyStop
      else if(button==EnumToString(BUTT_BUY_STOP))
        {
         //--- Abrufen der korrekten Order-Platzierung relativ zu StopLevel
         double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_STOP,distance_pending);
         //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zur Level der Order-Platzierung unter Berücksichtigung von StopLevel
         double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_STOP,price_set,stoploss);
         double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_STOP,price_set,takeprofit);
         //--- Setzen einer BuyStop-Order
         engine.PlaceBuyStop(lot,Symbol(),price_set,sl,tp,magic_number,TextByLanguage("Отложенный BuyStop","Pending BuyStop order"));
        }
      //--- Falls die Schaltfläche BUTT_BUY_STOP_LIMIT geklickt wurde: Platzieren von BuyStopLimit
      else if(button==EnumToString(BUTT_BUY_STOP_LIMIT))
        {
         //--- Abrufen der korrekten BuyStop-Order relativ zu StopLevel
         double price_set_stop=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_STOP,distance_pending);
         //--- Berechnen des Preises der BuyLimit-Order relativ zu BuyStop unter Berücksichtigung des StopLevels
         double price_set_limit=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_LIMIT,distance_stoplimit,price_set_stop);
         //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zur Level der Order-Platzierung unter Berücksichtigung von StopLevel
         double sl=stoploss;//CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_STOP,price_set_limit,stoploss);
         double tp=takeprofit;//CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_STOP,price_set_limit,takeprofit);
         //--- Setzen von BuyStopLimit-Order
         engine.PlaceBuyStopLimit(lot,Symbol(),price_set_stop,price_set_limit,sl,tp,magic_number,TextByLanguage("Отложенный BuyStopLimit","Pending BuyStopLimit order"));
        }
      //--- Wenn die Schaltfläche BUTT_SELL geklickt wurde: Eröffnen einer Verkaufsposition
      else if(button==EnumToString(BUTT_SELL))
        {
         //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zu StopLevel
         double sl=stoploss;//CorrectStopLoss(Symbol(),ORDER_TYPE_SELL,0,stoploss);
         double tp=takeprofit;//CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL,0,takeprofit);
         //--- Eröffnen einer Verkaufsposition
         engine.OpenSell(lot,Symbol(),magic_number,sl,tp);  // No comment - the default comment is to be set
        }
      //--- Falls die Schaltfläche BUTT_SELL_LIMIT geklickt wurde: Setzen von SellLimit
      else if(button==EnumToString(BUTT_SELL_LIMIT))
        {
         //--- Abrufen der korrekten Order-Platzierung relativ zu StopLevel
         double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_LIMIT,distance_pending);
         //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zur Level der Order-Platzierung unter Berücksichtigung von StopLevel
         double sl=stoploss;//CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_LIMIT,price_set,stoploss);
         double tp=takeprofit;//CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_LIMIT,price_set,takeprofit);
         //--- Setzen von SellLimit-Order
         engine.PlaceSellLimit(lot,Symbol(),price_set,sl,tp,magic_number,TextByLanguage("Отложенный SellLimit","Pending order SellLimit"));
        }
      //--- Falls die Schaltfläche BUTT_SELL_STOP geklickt wurde: Platzieren von SellStop
      else if(button==EnumToString(BUTT_SELL_STOP))
        {
         //--- Abrufen der korrekten Order-Platzierung relativ zu StopLevel
         double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_STOP,distance_pending);
         //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zur Level der Order-Platzierung unter Berücksichtigung von StopLevel
         double sl=stoploss;//CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_STOP,price_set,stoploss);
         double tp=takeprofit;//CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_STOP,price_set,takeprofit);
         //--- Setzen von SellStop-Order
         engine.PlaceSellStop(lot,Symbol(),price_set,sl,tp,magic_number,TextByLanguage("Отложенный SellStop","Pending SellStop order"));
        }
      //--- Falls die Schaltfläche BUTT_SELL_STOP_LIMIT geklickt wurde: Platzieren von SellStopLimit
      else if(button==EnumToString(BUTT_SELL_STOP_LIMIT))
        {
         //--- Abrufen des Preises von SellStop relativ zu StopLevel
         double price_set_stop=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_STOP,distance_pending);
         //--- Berechnen des Preises der SellLimit-Order relativ zu SellStop unter Berücksichtigung des StopLevels
         double price_set_limit=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_LIMIT,distance_stoplimit,price_set_stop);
         //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zur Level der Order-Platzierung unter Berücksichtigung von StopLevel
         double sl=stoploss;//CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_STOP,price_set_limit,stoploss);
         double tp=takeprofit;//CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_STOP,price_set_limit,takeprofit);
         //--- Setzen der SellStopLimit-Order
         engine.PlaceSellStopLimit(lot,Symbol(),price_set_stop,price_set_limit,sl,tp,magic_number,TextByLanguage("Отложенный SellStopLimit","Pending SellStopLimit order"));
        }
      //--- Wenn die Schaltfläche BUTT_CLOSE_BUY geklickt wurde: Schließen einer Kaufposition mit Maximalgewinn
      else if(button==EnumToString(BUTT_CLOSE_BUY))
        {


Tatsächlich müssen wir nur den Aufruf der Handelsmethoden mit den in den EA-Einstellungen eingestellten Parametern belassen:

//+------------------------------------------------------------------+
//| Handle pressing the buttons                                      |
//+------------------------------------------------------------------+
void PressButtonEvents(const string button_name)
  {
   string comment="";
   //--- Konvertieren der Namen der Schaltflächen in die Zeichenketten-ID
   string button=StringSubstr(button_name,StringLen(prefix));
   //--- Falls eine Taste gedrückt wurde
   if(ButtonState(button_name))
     {
      //--- Wenn die Schaltfläche BUTT_BUY geklickt wurde: Eröffnen einer Kaufposition
      if(button==EnumToString(BUTT_BUY))
        {
         //--- Eröffnen einer Kaufposition
         engine.OpenBuy(lot,Symbol(),magic_number,stoploss,takeprofit);   // No comment - the default comment is to be set
        }
      //--- Falls die Schaltfläche BUTT_BUY_LIMIT geklickt wurde: Platzieren von BuyLimit
      else if(button==EnumToString(BUTT_BUY_LIMIT))
        {
         //--- Setzen einer BuyLimit-Order
         engine.PlaceBuyLimit(lot,Symbol(),distance_pending,stoploss,takeprofit,magic_number,TextByLanguage("Отложенный BuyLimit","Pending BuyLimit order"));
        }
      //--- Falls die Schaltfläche BUTT_BUY_STOP geklickt wurde: Platzieren von BuyStop
      else if(button==EnumToString(BUTT_BUY_STOP))
        {
         //--- Setzen einer BuyStop-Order
         engine.PlaceBuyStop(lot,Symbol(),distance_pending,stoploss,takeprofit,magic_number,TextByLanguage("Отложенный BuyStop","Pending BuyStop order"));
        }
      //--- Falls die Schaltfläche BUTT_BUY_STOP_LIMIT geklickt wurde: Platzieren von BuyStopLimit
      else if(button==EnumToString(BUTT_BUY_STOP_LIMIT))
        {
         //--- Setzen von BuyStopLimit-Order
         engine.PlaceBuyStopLimit(lot,Symbol(),distance_pending,distance_stoplimit,stoploss,takeprofit,magic_number,TextByLanguage("Отложенный BuyStopLimit","Pending BuyStopLimit order"));
        }
      //--- Wenn die Schaltfläche BUTT_SELL geklickt wurde: Eröffnen einer Verkaufsposition
      else if(button==EnumToString(BUTT_SELL))
        {
         //--- Eröffnen einer Verkaufsposition
         engine.OpenSell(lot,Symbol(),magic_number,stoploss,takeprofit);  // No comment - the default comment is to be set
        }
      //--- Falls die Schaltfläche BUTT_SELL_LIMIT geklickt wurde: Setzen von SellLimit
      else if(button==EnumToString(BUTT_SELL_LIMIT))
        {
         //--- Setzen von SellLimit-Order
         engine.PlaceSellLimit(lot,Symbol(),distance_pending,stoploss,takeprofit,magic_number,TextByLanguage("Отложенный SellLimit","Pending SellLimit order"));
        }
      //--- Falls die Schaltfläche BUTT_SELL_STOP geklickt wurde: Platzieren von SellStop
      else if(button==EnumToString(BUTT_SELL_STOP))
        {
         //--- Setzen von SellStop-Order
         engine.PlaceSellStop(lot,Symbol(),distance_pending,stoploss,takeprofit,magic_number,TextByLanguage("Отложенный SellStop","Pending SellStop order"));
        }
      //--- Falls die Schaltfläche BUTT_SELL_STOP_LIMIT geklickt wurde: Platzieren von SellStopLimit
      else if(button==EnumToString(BUTT_SELL_STOP_LIMIT))
        {
         //--- Setzen der SellStopLimit-Order
         engine.PlaceSellStopLimit(lot,Symbol(),distance_pending,distance_stoplimit,stoploss,takeprofit,magic_number,TextByLanguage("Отложенный SellStopLimit","Pending order SellStopLimit"));
        }
      //--- Wenn die Schaltfläche BUTT_CLOSE_BUY geklickt wurde: Schließen einer Kaufposition mit Maximalgewinn
      else if(button==EnumToString(BUTT_CLOSE_BUY))
        {


So wird die Aufgabe der Überprüfung der Parametergültigkeit an die Bibliothek übertragen, während die notwendigen Aufträge innerhalb des EAs gesendet werden. Ich werde nach und nach alle notwendigen Funktionen für ein komfortables Arbeiten hinzufügen.
Der vollständige EA-Code kann in den an den Artikel angehängten Dateien eingesehen werden.

Kompilieren Sie den EA und starten Sie ihn im Tester, wobei Sie in den Parametern Lots auf 10, sowie StopLoss in Punkten und TakeProfit in Punkten auf jeweils 1 Punkt einstellen:


So versuchen wir, eine Position mit einer ungültigen Losgröße zu eröffnen, so dass die Mittel für die Eröffnung nicht ausreichen, und versuchen, den minimalen Stop-Order-Abstand zu verletzen, der durch den StopLevel-Parameter des Symbols geregelt wird:


Der EA zeigt zwei Fehler im Journal an — "Nicht genug Geld, um den Handel durchzuführen" und "StopLoss-Werte verletzen die Anforderungen der StopLevel-Parameter". Wir haben auch TakeProfit auf einen Punkt gesetzt. Warum zeigt der EA keine Informationen auch über diesen Fehler an? Eigentlich gibt es hier keinen Fehler, da Platzierung von TakeProfit und StopLoss Werten, die innerhalb des minimalen SYMBOL_TRADE_STOPS_LEVEL durchgeführt werden, nicht gegen die Regel verstößt:

TakeProfit- und StopLoss-Level sollten mit dem aktuellen Kurs verglichen werden, um die entgegengesetzte Operation durchzuführen.

  • Der Kauf erfolgt zum Ask-Preis — die TakeProfit und StopLoss Niveaus sollten mit dem Bid-Preis verglichen werden.
  • Der Verkauf erfolgt zum Bid-Preis — TakeProfit und StopLoss Niveaus sollten mit dem Ask-Preis verglichen werden.
Der Kauf erfolgt zum Ask-Preis
Der Verkauf erfolgt zum Bid-Preis
TakeProfit >= Bid
StopLoss <= Bid
TakeProfit <= Ask
StopLoss >= Ask

Da wir den Button Position kaufen gedrückt haben, ist der Preis zum Schließen der Bid-Preis, während der Eröffnungspreis der Ask-Preis ist. Stop-Order-Level werden auf der Grundlage des Eröffnungspreises (hier ist es Ask) festgelegt. So liegt TakeProfit bei Ask+1 Punkt, während StopLoss bei Ask-1 Punkt liegt. Das bedeutet, dass der TakeProfit über dem Bid-Preis liegt, von dem der erlaubte Abstand berechnet wird, während der StopLoss nur im Falle eines Null-Spreads innerhalb der Anforderungen liegt. Da der Spread zum Zeitpunkt der Eröffnung etwa zehn Punkte beträgt, fallen wir in die StopLoss-Mindestabstandsbeschränkungen.

Was kommt als Nächstes?

Im nächsten Artikel beginnen wir mit der Implementierung von Handhabungsfehlern beim Versenden von falschen Handelsaufträge sowie von Fehlern, die vom Handelsserver zurückgegeben werden.

Alle Dateien der aktuellen Version der Bibliothek sind unten zusammen mit den Dateien der Test-EAs angehängt, die Sie testen und herunterladen 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
Teil 9. Kompatibilität mit MQL4 - Datenvorbereitung
Teil 10. Kompatibilität mit MQL4 - Ereignisse der Positionseröffnung und Aktivierung von Pending-Orders
Teil 11. Kompatibilität mit MQL4 - Ereignisse des Schließens von Positionen
Teil 12. Objektklasse "Account" und die Kollektion von Konto-Objekten
Teil 13. Das Objekt der Kontoereignisse
Teil 14. Das Symbolobjekt
Teil 15. Die Kollektion der Symbolobjekte
Teil 16. Ereignisse der Kollektionssymbole
Teil 17. Interaktivität von Bibliotheksobjekten
Teil 18. Interaktivität des Kontos und aller anderen Bibliotheksobjekt
Teil 19. Klassenbibliothek für Nachrichten
Teil 20. Erstellen und Speichern von Programmressourcen
Teil 21. Handelsklassen - Plattformübergreifendes Basis-Handelsobjekt
Teil 22. Handelsklassen - Basisklasse des Handels, Verifikation der Einschränkungen

Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/7286

Beigefügte Dateien |
MQL5.zip (3589.61 KB)
MQL4.zip (3589.62 KB)
Letzte Kommentare | Zur Diskussion im Händlerforum (2)
wolf1210
wolf1210 | 20 Jan. 2020 in 20:49

Hallo Artiom

I write to you not knowing how to go to admin. My Problem is this: I havean indicator which is giving fine Signals but only when I refresh the TF in use. If I do Nothing the siganls are not good. But this Refreshing should be done automatically. Do you have an idea? or how can I find it in the codebase? There is Nothing for a search, lol.

Thank you very much

Wolf1210

Artyom Trishkin
Artyom Trishkin | 20 Jan. 2020 in 21:24
wolf1210 :

Hallo Artiom

I write to you not knowing how to go to admin. My Problem is this: I havean indicator which is giving fine Signals but only when I refresh the TF in use. If I do Nothing the siganls are not good. But this Refreshing should be done automatically. Do you have an idea? or how can I find it in the codebase? There is Nothing for a search, lol.

Thank you very much

Wolf1210

An incorrect data calculation has been made in the indicator. I do not think that it will become better if done correctly - most likely, it will be redrawn.

Kontinuierliche Walk-Forward-Optimierung (Teil 1): Arbeiten mit Optimierungsberichten Kontinuierliche Walk-Forward-Optimierung (Teil 1): Arbeiten mit Optimierungsberichten
Der erste Artikel beschäftigt sich mit der Erstellung eines Toolkits für die Arbeit mit Optimierungsberichten, für den Import aus dem Terminal sowie für die Filterung und Sortierung der erhaltenen Daten. MetaTrader 5 ermöglicht das Laden von Optimierungsergebnissen, unser Ziel ist es jedoch, dem Optimierungsbericht unsere eigenen Daten hinzuzufügen.
Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XXII): Handelsklassen - Basisklasse des Handels, Verifikation der Einschränkungen Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XXII): Handelsklassen - Basisklasse des Handels, Verifikation der Einschränkungen
In diesem Artikel beginnen wir mit der Entwicklung der Bibliothek der Basisklasse des Handels und fügen die erste Überprüfung der Berechtigungen zur Durchführung von Handelsoperationen der ersten Version hinzu. Außerdem werden wir die Funktionen und Inhalte der Basishandelsklasse leicht erweitern.
Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XXIV): Handelsklassen - automatische Korrektur ungültiger Parametern Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XXIV): Handelsklassen - automatische Korrektur ungültiger Parametern
In diesem Artikel werden wir einen Blick auf die Behandlung ungültiger Handelsparameter werfen und die Handelsereignisklasse verbessern. Jetzt werden alle Handelsereignisse (sowohl einzelne als auch die gleichzeitig bei einem Tick auftretenden) in Programmen korrekt definiert.
Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XXI): Handelsklassen - Plattformübergreifendes Basis-Handelsobjekt Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XXI): Handelsklassen - Plattformübergreifendes Basis-Handelsobjekt
In diesem Artikel werden wir mit der Entwicklung des neuen Bibliotheksbereichs beginnen - die Handelsklassen. Außerdem werden wir die Entwicklung eines einheitlichen Basisobjekts für den Handel auf den Plattformen MetaTrader 5 und MetaTrader 4 in Betracht ziehen. Wenn ein Auftrag an den Server gesendet wird, bedeutet ein solches Handelsobjekt, dass verifizierte und korrekte Parameter der Handelsanfrage an den Server übergeben werden.