
Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XXIII): Handelsklassen - Verifikation der Parameter
Inhalt
- Einstellen der Audiosignale für den Handel
- Kontrolle über fehlerhafte Werte, Automatisierung der Auswahl und Verwendung der Eingaben der Handelsmethode
- Tests
- Was kommt als Nächstes?
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).
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.
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





- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.
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
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.