Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XXXIII): Schwebende Handelsanfragen - Entfernen und Ändern von Orders und Positionen unter bestimmten Bedingungen
Artyom Trishkin | 11 Mai, 2020
Inhalt
Konzept
Dieser Artikel vervollständigt den Abschnitt, der dem Handel mit schwebenden Anfragen gewidmet ist. Wir werden die Funktionen zur Entfernung von Pending Orders sowie zur Änderung der StopLoss- und TakeProfit-Preise und der Parameter für Pending Orders entwickeln.
Auf diese Weise werden wir über die gesamte Funktionalität verfügen, die es uns ermöglicht, einfache benutzerdefinierte Strategien bzw. EA-Verhaltenslogiken zu entwickeln, die unter benutzerdefinierten Bedingungen aktiviert werden. Nachdem die grafische Hülle fertig ist, wird uns diese vorbereitende Arbeit die geeigneten Werkzeuge zur Verfügung stellen, z.B. zur Erstellung eines visuellen Konstruktors des EA-Verhaltens direkt aus der EA selbst während ihrer Arbeit (wahrscheinlich werde ich ein einfaches Beispiel machen, wenn die grafische Hülle der Bibliothek fertig ist).
Im Moment sind wir in der Lage, zusätzliche Arten von Pending-Orders zu erstellen. Zum Beispiel ist es möglich, eine StopLimit-Order für MQL4 zu erstellen. Ich werde dies tun, sobald die Bibliotheksfunktionen das abdecken.
Infolgedessen wird es möglich sein, völlig neue Arten von Pending-Orders zu erstellen, wie z.B. BuyTime, SellTime, BuyTimeStop, SellTimeStop, etc.
Es fehlen noch einige grafische Konstruktionen, die uns daran hindern, vollwertige kundenspezifische Aufträge zu erstellen. Wir werden auf diese Aufgabe zurückkommen, sobald wir über die entsprechende Bibliotheksfunktionen verfügen.
Umsetzung
Wie sich herausstellt, verfügen wir nicht über die Funktion, die lediglich den Namen einer Pending-Order anzeigt. Aber wir haben die Funktion OrderTypeDescription(), die deren Beschreibung+Name anzeigt. Das bedeutet, dass wir einfach den Beschreibungstext aus dem Rückgabeergebnis der Funktion entfernen sollten, so dass nur der Auftragsname übrig bleibt.
Verbessern wir die Funktion zur Rückgabe des Auftragsnamens in der \MQL5\Include\DoEasy\Services\DELib.mqh Datei der Dienstfunktionen:
//+------------------------------------------------------------------+ //| Return the order name | //+------------------------------------------------------------------+ string OrderTypeDescription(const ENUM_ORDER_TYPE type,bool as_order=true,bool prefix_for_market_order=true,bool descr=true) { string pref= ( !prefix_for_market_order ? "" : #ifdef __MQL5__ CMessage::Text(MSG_ORD_MARKET) #else/*__MQL4__*/(as_order ? CMessage::Text(MSG_ORD_MARKET) : CMessage::Text(MSG_ORD_POSITION)) #endif ); return ( type==ORDER_TYPE_BUY_LIMIT ? (descr ? CMessage::Text(MSG_ORD_PENDING) : "")+" Buy Limit" : type==ORDER_TYPE_BUY_STOP ? (descr ? CMessage::Text(MSG_ORD_PENDING) : "")+" Buy Stop" : type==ORDER_TYPE_SELL_LIMIT ? (descr ? CMessage::Text(MSG_ORD_PENDING) : "")+" Sell Limit" : type==ORDER_TYPE_SELL_STOP ? (descr ? CMessage::Text(MSG_ORD_PENDING) : "")+" Sell Stop" : #ifdef __MQL5__ type==ORDER_TYPE_BUY_STOP_LIMIT ? (descr ? CMessage::Text(MSG_ORD_PENDING) : "")+" Buy Stop Limit" : type==ORDER_TYPE_SELL_STOP_LIMIT ? (descr ? CMessage::Text(MSG_ORD_PENDING) : "")+" Sell Stop Limit" : type==ORDER_TYPE_CLOSE_BY ? CMessage::Text(MSG_ORD_CLOSE_BY) : #else type==ORDER_TYPE_BALANCE ? CMessage::Text(MSG_LIB_PROP_BALANCE) : type==ORDER_TYPE_CREDIT ? CMessage::Text(MSG_LIB_PROP_CREDIT) : #endif type==ORDER_TYPE_BUY ? pref+" Buy" : type==ORDER_TYPE_SELL ? pref+" Sell" : CMessage::Text(MSG_ORD_UNKNOWN_TYPE) ); } //+------------------------------------------------------------------+
Die Funktion gibt immer den Text "pending order" vor dem Typ der Pending-Order zurück.
Das Flag, das die Notwendigkeit der Anzeige angibt, wurde so implementiert, dass die Funktion die Beschreibung des Auftragstyps ohne den vorläufigen Text anzeigen kann. Wenn das Flag deaktiviert ist (der Wert ist false), wird der vorläufige Text "Pending-Order" nicht angezeigt.
Fixieren Sie in allen Dateien der abgeleiteten Basisobjekte der schwebenden Anfrage, d.h. in den Methoden zur Anzeige eines kurzen Anforderungsnamens, die Anzeige der Kurzbeschreibung einer schwebenden Anfrage - fügen Sie Auftrags-/Positionstyp zum Beschreibungstext der Anfrage hinzu. Danach fügen Sie das Ticket hinzu (falls in der Klasse verfügbar), gefolgt von der ID der schwebenden Anfrage durch Komma getrennt. Die derzeitige Struktur ist nicht perfekt, da zuerst eine Beschreibung der Anfrage angezeigt wird, gefolgt von der ID und dem Ticket.
Die Änderungen in der Objektklasse der schwebenden Anfragen zur Eröffnung einer Position:
//+------------------------------------------------------------------+ //| Return the short request name | //+------------------------------------------------------------------+ string CPendReqOpen::Header(void) { string type=PositionTypeDescription((ENUM_POSITION_TYPE)this.GetProperty(PEND_REQ_PROP_MQL_REQ_TYPE)); return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_OPEN)+" "+type+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID); } //+------------------------------------------------------------------+
Die Änderungen in der Objektklasse der schwebenden Anfrage für die Schließung einer Position:
//+------------------------------------------------------------------+ //| Return the short request name | //+------------------------------------------------------------------+ string CPendReqClose::Header(void) { string type=PositionTypeDescription((ENUM_POSITION_TYPE)this.GetProperty(PEND_REQ_PROP_MQL_REQ_TYPE)); return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_CLOSE)+" "+type+" #"+(string)this.GetProperty(PEND_REQ_PROP_MQL_REQ_POSITION)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID); } //+------------------------------------------------------------------+
Die Änderungen in der Objektklasse der schwebenden Anfrage zur Änderung der StopLoss- und/oder TakeProfit-Ebenen der Position:
//+------------------------------------------------------------------+ //| Return the short request name | //+------------------------------------------------------------------+ string CPendReqSLTP::Header(void) { string type=PositionTypeDescription((ENUM_POSITION_TYPE)this.GetProperty(PEND_REQ_PROP_MQL_REQ_TYPE)); return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_SLTP)+" "+type+" #"+(string)this.GetProperty(PEND_REQ_PROP_MQL_REQ_POSITION)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID); } //+------------------------------------------------------------------+
Die Änderungen in der Objektklasse für schwebende Anfragen zur Erteilung einer Pending-Order:
//+------------------------------------------------------------------+ //| Return the short request name | //+------------------------------------------------------------------+ string CPendReqPlace::Header(void) { string type=OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(PEND_REQ_PROP_MQL_REQ_TYPE),true,false,false); return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_PLACE)+type+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID); } //+------------------------------------------------------------------+
Die Änderungen in der Objektklasse für schwebende Anfragen zum Entfernen einer Pending-Order:
//+------------------------------------------------------------------+ //| Return the short request name | //+------------------------------------------------------------------+ string CPendReqRemove::Header(void) { string type=OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(PEND_REQ_PROP_MQL_REQ_TYPE),true,false,false); return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_REMOVE)+type+" #"+(string)this.GetProperty(PEND_REQ_PROP_MQL_REQ_ORDER)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID); } //+------------------------------------------------------------------+
Die Änderungen in der Objektklasse für schwebende Anfragen zum Ändern der Eigenschaften von Pending-Orders:
//+------------------------------------------------------------------+ //| Return the short request name | //+------------------------------------------------------------------+ string CPendReqModify::Header(void) { string type=OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(PEND_REQ_PROP_MQL_REQ_TYPE),true,false,false); return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_MODIFY)+type+", #"+(string)this.GetProperty(PEND_REQ_PROP_MQL_REQ_ORDER)+" ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID); } //+------------------------------------------------------------------+
Ergänzen Sie in der Datei TradingControl.mqh der Klasse des Handelsmanagements CTradingControl (d.h. in ihrem 'public' Bereich) die Deklaration der Methoden zum Erstellen einer schwebenden Anfrage, um eine Pending Order zu entfernen, zur Änderung der StopLoss/TakeProfit der Position und zur Änderung der Parameter der Pending Order:
//--- Create a pending request (1) for full and partial position closure, (2) for closing a position by an opposite one, (3) for removing an order int CreatePReqClose(const ulong ticket,const double volume=WRONG_VALUE,const string comment=NULL,const ulong deviation=ULONG_MAX); int CreatePReqCloseBy(const ulong ticket,const ulong ticket_by); int CreatePreqDelete(const ulong ticket); //--- Create a pending request to modify (1) position's stop orders, (2) an order template<typename SL,typename TP> int CreatePReqModifyPosition(const ulong ticket,const SL sl=WRONG_VALUE,const TP tp=WRONG_VALUE); template<typename PS,typename PL,typename SL,typename TP> int CreatePReqModifyOrder(const ulong ticket, const PS price=WRONG_VALUE, const SL sl=WRONG_VALUE, const TP tp=WRONG_VALUE, const PL limit=WRONG_VALUE, datetime expiration=WRONG_VALUE, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); //--- Set pending request activation criteria bool SetNewActivationProperties(const uchar id, const ENUM_PEND_REQ_ACTIVATION_SOURCE source, const int property, const double control_value, const ENUM_COMPARER_TYPE comparer_type, const double actual_value); }; //+------------------------------------------------------------------+
Implementieren wir das außerhalb des Klassenkörpers:
//+------------------------------------------------------------------+ //| Create a pending request to remove a pending order | //+------------------------------------------------------------------+ int CTradingControl::CreatePreqDelete(const ulong ticket) { //--- If the global trading ban flag is set, exit and return WRONG_VALUE if(this.IsTradingDisable()) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TRADING_DISABLE)); return WRONG_VALUE; } //--- Set the error flag as "no errors" this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_NO_ERROR; ENUM_ACTION_TYPE action=ACTION_TYPE_CLOSE; //--- Get an order object by ticket COrder *order=this.GetOrderObjByTicket(ticket); if(order==NULL) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR; if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_ORD_OBJ)); return WRONG_VALUE; } ENUM_ORDER_TYPE order_type=(ENUM_ORDER_TYPE)order.TypeOrder(); //--- Get a symbol object by an order ticket CSymbol *symbol_obj=this.GetSymbolObjByOrder(ticket,DFUN); if(symbol_obj==NULL) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR; if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_SYM_OBJ)); return WRONG_VALUE; } //--- get a trading object from a symbol object CTradeObj *trade_obj=symbol_obj.GetTradeObj(); if(trade_obj==NULL) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR; if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return WRONG_VALUE; } //--- Update symbol quotes if(!symbol_obj.RefreshRates()) { trade_obj.SetResultRetcode(10021); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); this.AddErrorCodeToList(10021); // No quotes to handle the request if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(10021)); return WRONG_VALUE; } //--- Look for the least of the possible IDs. If failed to find, return WRONG_VALUE int id=this.GetFreeID(); if(id<1) { //--- No free IDs to create a pending request if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_NO_FREE_IDS)); return WRONG_VALUE; } //--- Set the trading operation type, as well as deleted order's symbol and ticket in the request structure this.m_request.action=TRADE_ACTION_REMOVE; this.m_request.symbol=symbol_obj.Name(); this.m_request.order=ticket; this.m_request.type=order_type; this.m_request.volume=order.Volume(); this.m_request.price=order.PriceOpen(); //--- As a result of creating a pending trading request, return either its ID or -1 if unsuccessful if(this.CreatePendingRequest(PEND_REQ_STATUS_REMOVE,(uchar)id,1,ulong(END_TIME-(ulong)::TimeCurrent()),this.m_request,0,symbol_obj,order)) return id; return WRONG_VALUE; } //+------------------------------------------------------------------+ //| Create a pending request to modify position's stop orders | //+------------------------------------------------------------------+ template<typename SL,typename TP> int CTradingControl::CreatePReqModifyPosition(const ulong ticket,const SL sl=WRONG_VALUE,const TP tp=WRONG_VALUE) { //--- If the global trading ban flag is set, exit and return WRONG_VALUE if(this.IsTradingDisable()) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TRADING_DISABLE)); return WRONG_VALUE; } //--- Set the error flag as "no errors" this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_NO_ERROR; ENUM_ACTION_TYPE action=ACTION_TYPE_MODIFY; //--- Get an order object by ticket COrder *order=this.GetOrderObjByTicket(ticket); if(order==NULL) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR; if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_ORD_OBJ)); return WRONG_VALUE; } ENUM_ORDER_TYPE order_type=(ENUM_ORDER_TYPE)order.TypeOrder(); //--- Get a symbol object by a position ticket CSymbol *symbol_obj=this.GetSymbolObjByPosition(ticket,DFUN); if(symbol_obj==NULL) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR; if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_SYM_OBJ)); return WRONG_VALUE; } //--- Get a trading object from a symbol object CTradeObj *trade_obj=symbol_obj.GetTradeObj(); if(trade_obj==NULL) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR; if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return WRONG_VALUE; } //--- Set the prices //--- If failed to set - write the "internal error" flag, set the error code in the return structure, //--- display the message in the journal and return 'false' if(!this.SetPrices(order_type,0,(sl==WRONG_VALUE ? order.StopLoss() : sl),(tp==WRONG_VALUE ? order.TakeProfit() : tp),0,DFUN,symbol_obj)) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR; trade_obj.SetResultRetcode(10021); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(10021)); // No quotes to process the request return WRONG_VALUE; } //--- Look for the least of the possible IDs. If failed to find, return 'false' int id=this.GetFreeID(); if(id<1) return WRONG_VALUE; //--- Write a type of a conducted operation, as well as a symbol and a ticket of a modified position to the request structure this.m_request.action=TRADE_ACTION_SLTP; this.m_request.symbol=symbol_obj.Name(); this.m_request.position=ticket; this.m_request.type=order_type; this.m_request.volume=order.Volume(); //--- As a result of creating a pending trading request, return either its ID or -1 if unsuccessful if(this.CreatePendingRequest(PEND_REQ_STATUS_SLTP,(uchar)id,1,ulong(END_TIME-(ulong)::TimeCurrent()),this.m_request,0,symbol_obj,order)) return id; return WRONG_VALUE; } //+------------------------------------------------------------------+ //| Create a pending request to modify a pending order | //+------------------------------------------------------------------+ template<typename PS,typename PL,typename SL,typename TP> int CTradingControl::CreatePReqModifyOrder(const ulong ticket, const PS price=WRONG_VALUE, const SL sl=WRONG_VALUE, const TP tp=WRONG_VALUE, const PL limit=WRONG_VALUE, datetime expiration=WRONG_VALUE, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { //--- If the global trading ban flag is set, exit and return WRONG_VALUE if(this.IsTradingDisable()) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TRADING_DISABLE)); return WRONG_VALUE; } //--- Set the error flag as "no errors" this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_NO_ERROR; ENUM_ACTION_TYPE action=ACTION_TYPE_MODIFY; //--- Get an order object by ticket COrder *order=this.GetOrderObjByTicket(ticket); if(order==NULL) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR; if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_ORD_OBJ)); return false; } ENUM_ORDER_TYPE order_type=(ENUM_ORDER_TYPE)order.TypeOrder(); //--- Get a symbol object by an order ticket CSymbol *symbol_obj=this.GetSymbolObjByOrder(ticket,DFUN); if(symbol_obj==NULL) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR; 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) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR; if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } //--- Set the prices //--- If failed to set - write the "internal error" flag, set the error code in the return structure, //--- display the message in the journal and return 'false' if(!this.SetPrices(order_type, (price>0 ? price : order.PriceOpen()), (sl>0 ? sl : sl<0 ? order.StopLoss() : 0), (tp>0 ? tp : tp<0 ? order.TakeProfit() : 0), (limit>0 ? limit : order.PriceStopLimit()), DFUN,symbol_obj)) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR; trade_obj.SetResultRetcode(10021); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(10021)); // No quotes to process the request return false; } //--- Look for the least of the possible IDs. If failed to find, return 'false' int id=this.GetFreeID(); if(id<1) return WRONG_VALUE; //--- Write the magic number, volume, filling type, as well as expiration date and type to the request structure this.m_request.magic=order.GetMagicID((uint)order.Magic()); this.m_request.volume=order.Volume(); this.m_request.type_filling=(type_filling>WRONG_VALUE ? type_filling : order.TypeFilling()); this.m_request.expiration=(expiration>WRONG_VALUE ? expiration : order.TimeExpiration()); this.m_request.type_time=(type_time>WRONG_VALUE ? type_time : order.TypeTime()); //--- Set the trading operation type, as well as modified order's symbol and ticket in the request structure this.m_request.action=TRADE_ACTION_MODIFY; this.m_request.symbol=symbol_obj.Name(); this.m_request.order=ticket; this.m_request.type=order_type; //--- As a result of creating a pending trading request, return either its ID or -1 if unsuccessful if(this.CreatePendingRequest(PEND_REQ_STATUS_MODIFY,(uchar)id,1,ulong(END_TIME-(ulong)::TimeCurrent()),this.m_request,0,symbol_obj,order)) return id; return WRONG_VALUE; } //+------------------------------------------------------------------+
Die Logik der Methoden ist absolut identisch mit den zuvor geschaffenen Methoden zur Generierung schwebender Anfragen zur Eröffnung/Schließung von Positionen/Platzierung Pending-Orders unter bestimmten Bedingungen. Der Code ist ausführlich kommentiert, so dass es keinen Sinn macht, auf diese Methoden nochmals einzugehen.
Lassen Sie uns nun den Zugang aus dem Programm zu den erstellten Methoden hinzufügen. Dazu schreiben wir den Aufruf dieser Methoden aus den Methoden der Bibliotheks-Basisobjektklasse.
Fügen wir in der Klasse CEngine die Deklaration der Methoden zur Erstellung von schwebenden Anfragen hinzu, um einen Pending Order zu entfernen, modifizieren wir die StopLoss/TakeProfit der Position und wir modifizieren die Parameter der Pending Order:
//--- Create a pending request for closing a position (1) fully, (2) partially, (3) by an opposite one, (4) for removing an order int ClosePositionPending(const ulong ticket,const string comment=NULL,const ulong deviation=ULONG_MAX); int ClosePositionPartiallyPending(const ulong ticket,const double volume,const string comment=NULL,const ulong deviation=ULONG_MAX); int ClosePositionByPending(const ulong ticket,const ulong ticket_by); int DeleteOrderPending(const ulong ticket); //--- Create a pending request to modify (1) a position, (2) an order template<typename SL,typename TP> int ModifyPositionPending(const ulong ticket,const SL sl=WRONG_VALUE,const TP tp=WRONG_VALUE,const string comment=NULL); template<typename PR,typename SL,typename TP,typename PL> int ModifyOrderPending(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, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
Implementieren wir die deklarierte Methoden außerhalb des Klassenkörpers:
//+------------------------------------------------------------------+ //| Create a pending request to remove a pending order | //+------------------------------------------------------------------+ int CEngine::DeleteOrderPending(const ulong ticket) { return this.m_trading.CreatePreqDelete(ticket); } //+------------------------------------------------------------------+ //| Create a pending request to modify a position | //+------------------------------------------------------------------+ template<typename SL,typename TP> int CEngine::ModifyPositionPending(const ulong ticket,const SL sl=WRONG_VALUE,const TP tp=WRONG_VALUE,const string comment=NULL) { return this.m_trading.CreatePReqModifyPosition(ticket,sl,tp); } //+------------------------------------------------------------------+ //| Create a pending request to modify an order | //+------------------------------------------------------------------+ template<typename PR,typename SL,typename TP,typename PL> int CEngine::ModifyOrderPending(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, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.CreatePReqModifyOrder(ticket,price,sl,tp,stoplimit,expiration,type_time,type_filling); } //+------------------------------------------------------------------+
Die Methoden geben einfach das Ergebnis des Aufrufs der entsprechenden Methoden zum Erstellen schwebender Anfragen der CTradingControl-Klasse zurück, die wir zuvor geschrieben haben.
Dies sind alle Verbesserungen, die erforderlich sind, um die Funktionsweise für die Arbeit mit schwebenden Anfragen hinzuzufügen, um Aufträge zu entfernen und Aufträge und Positionen zu modifizieren.
Außerdem wurden einige kleinere Änderungen in den Codes der verbesserten Klassen vorgenommen (Namen der Vorlagenparameter). Sie beziehen sich jedoch nur auf die visuelle Wahrnehmung des Methodencodes, so dass es keinen Sinn macht, sie hier zu berücksichtigen.
Tests
Um die erstellte Funktionen auszuführen, verwenden wir den EA aus dem vorherigen Artikel und speichern ihn in \MQL5\Experts\TestDoEasy\Part34\ unter dem Namen TestDoEasyPart34.mq5.
Wie die vorherigen EAs, die zum Testen der schwebenden Anfragen gedacht waren, werden wir die Schaltflächen zur Aktivierung der Modi der schwebenden Anfragen erstellen - für die Schaltflächen zum Entfernen aller Pending-Orders (sie löschen), zum Schließen aller Positionen (alle schließen) und zum Setzen von StopLoss und TakeProfit für Orders und Positionen ohne Stop-Level (StopLoss setzen und TakeProfit setzen).
Da das Drücken dieser Schaltflächen zur Stapelverarbeitung aller bestehenden Aufträge und Positionen führt, ermöglicht uns die Aktivierung der entsprechenden Aktivierungsschaltflächen die Überprüfung der schwebenden Anfragen für mehrere Aufträge und Positionen gleichzeitig.
In der aktuellen Version sind die Schaltflächen für die Handelsverwaltung des Handelspanels des Test-EAs nicht sehr praktisch — die Schaltfläche zum Abheben von verdientem Geld befindet sich zwischen den Schaltflächen zum Entfernen von Orders und Schließen von Positionen und den Schaltflächen zum Platzieren von Stop-Orders. Lassen Sie diese Schaltfläche weiter unten platzieren — nach der Schaltfläche von TakeProfit. Ändern wir dazu einfach die Position der Konstanten in der Aufzählung aller Schaltflächen des Handelspanels des Test-EAs:
//+------------------------------------------------------------------+ //| TestDoEasyPart34.mq5 | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/de/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/de/users/artmedia70" #property version "1.00" //--- includes #include <DoEasy\Engine.mqh> //--- enums enum ENUM_BUTTONS { BUTT_BUY, BUTT_BUY_LIMIT, BUTT_BUY_STOP, BUTT_BUY_STOP_LIMIT, BUTT_CLOSE_BUY, BUTT_CLOSE_BUY2, BUTT_CLOSE_BUY_BY_SELL, BUTT_SELL, BUTT_SELL_LIMIT, BUTT_SELL_STOP, BUTT_SELL_STOP_LIMIT, BUTT_CLOSE_SELL, BUTT_CLOSE_SELL2, BUTT_CLOSE_SELL_BY_BUY, BUTT_DELETE_PENDING, BUTT_CLOSE_ALL, BUTT_SET_STOP_LOSS, BUTT_SET_TAKE_PROFIT, BUTT_PROFIT_WITHDRAWAL, BUTT_TRAILING_ALL }; #define TOTAL_BUTT (20)
Die Liste der globalen Variablen empfängt die Flags, die die Zustände der Schaltflächen anzeigen, die die Modi der Arbeit mit schwebenden Anfragen aktivieren, um Pending-Orders zu entfernen, Positionen zu schließen und Stop-Levels von Orders und Positionen zu modifizieren, außerdem werden zwei Variablen zur Speicherung der Werte von Point und Digits (Dezimalstellen) des aktuellen Symbols hinzu:
//--- global variables CEngine engine; SDataButt butt_data[TOTAL_BUTT]; string prefix; double lot; double withdrawal=(InpWithdrawal<0.1 ? 0.1 : InpWithdrawal); ushort magic_number; uint stoploss; uint takeprofit; uint distance_pending; uint distance_stoplimit; uint distance_pending_request; uint bars_delay_pending_request; uint slippage; bool trailing_on; bool pressed_pending_buy; bool pressed_pending_buy_limit; bool pressed_pending_buy_stop; bool pressed_pending_buy_stoplimit; bool pressed_pending_close_buy; bool pressed_pending_close_buy2; bool pressed_pending_close_buy_by_sell; bool pressed_pending_sell; bool pressed_pending_sell_limit; bool pressed_pending_sell_stop; bool pressed_pending_sell_stoplimit; bool pressed_pending_close_sell; bool pressed_pending_close_sell2; bool pressed_pending_close_sell_by_buy; bool pressed_pending_delete_all; bool pressed_pending_close_all; bool pressed_pending_sl; bool pressed_pending_tp; double trailing_stop; double trailing_step; uint trailing_start; uint stoploss_to_modify; uint takeprofit_to_modify; int used_symbols_mode; string used_symbols; string array_used_symbols[]; bool testing; uchar group1; uchar group2; double g_point; int g_digits; //+------------------------------------------------------------------+
In OnInit() des EAs weisen wir die Werte für Point und Digits des aktuellen Symbols den entsprechenden Variablen zu:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ 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)+"_"; testing=engine.IsTester(); 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; distance_pending_request=(InpDistancePReq<5 ? 5 : InpDistancePReq); bars_delay_pending_request=(InpBarsDelayPReq<1 ? 1 : InpBarsDelayPReq); g_point=SymbolInfoDouble(NULL,SYMBOL_POINT); g_digits=(int)SymbolInfoInteger(NULL,SYMBOL_DIGITS); //--- Initialize random group numbers group1=0; group2=0; srand(GetTickCount()); //--- 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); //--- Reset states of the buttons for working using pending requests for(int i=0;i<14;i++) { ButtonState(butt_data[i].name+"_PRICE",false); ButtonState(butt_data[i].name+"_TIME",false); } //--- 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); } //+------------------------------------------------------------------+
In der Funktion zum Erzeugen von Schaltflächen verbessern wir den Code, um zusätzliche Schaltflächen für die Aktivierung des Arbeitsmodus mit schwebenden Anfragen zu erzeugen:
//+------------------------------------------------------------------+ //| Create the buttons panel | //+------------------------------------------------------------------+ bool CreateButtons(const int shift_x=20,const int shift_y=0) { int h=18,w=82,offset=2,wpt=14; int cx=offset+shift_x+wpt*2+2,cy=offset+shift_y+(h+1)*(TOTAL_BUTT/2)+3*h+1; int x=cx,y=cy; int shift=0; for(int i=0;i<TOTAL_BUTT;i++) { x=x+(i==7 ? w+2 : 0); if(i==TOTAL_BUTT-6) x=cx; y=(cy-(i-(i>6 ? 7 : 0))*(h+1)); if(!ButtonCreate(butt_data[i].name,x,y,(i<TOTAL_BUTT-6 ? w : w*2+2),h,butt_data[i].text,(i<4 ? clrGreen : i>6 && i<11 ? clrRed : clrBlue))) { Alert(TextByLanguage("Не удалось создать кнопку \"","Could not create button \""),butt_data[i].text); return false; } } h=18; offset=2; cx=offset+shift_x; cy=offset+shift_y+(h+1)*(TOTAL_BUTT/2)+3*h+1; x=cx; y=cy; shift=0; for(int i=0;i<18;i++) { y=(cy-(i-(i>6 ? 7 : 0))*(h+1)); if(!ButtonCreate(butt_data[i].name+"_PRICE",((i>6 && i<14) || i>17 ? x+wpt*2+w*2+5 : x),y,wpt,h,"P",(i<4 ? clrGreen : i>6 && i<11 ? clrChocolate : clrBlue))) { Alert(TextByLanguage("Не удалось создать кнопку \"","Could not create button \""),butt_data[i].text+" \"P\""); return false; } if(!ButtonCreate(butt_data[i].name+"_TIME",((i>6 && i<14) || i>17 ? x+wpt*2+w*2+5+wpt+1 : x+wpt+1),y,wpt,h,"T",(i<4 ? clrGreen : i>6 && i<11 ? clrChocolate : clrBlue))) { Alert(TextByLanguage("Не удалось создать кнопку \"","Could not create button \""),butt_data[i].text+" \"T\""); return false; } } ChartRedraw(0); return true; } //+------------------------------------------------------------------+
Alle Änderungen betreffen nur die Verwaltung von einer Seriennummer eines erstellten Buttons, um die notwendigen Koordinaten einzustellen.
Die wichtigsten Änderungen im EA beziehen sich auf die Funktion zur Handhabung des Drückens der Knöpfe des EA-Handelspanels.
Fügen wir die Codes für den Umgang mit gedrückten Tasten des Handelspanels hinzu:
//+------------------------------------------------------------------+ //| Handle pressing the buttons | //+------------------------------------------------------------------+ void PressButtonEvents(const string button_name) { bool comp_magic=true; // Temporary variable selecting the composite magic number with random group IDs string comment=""; //--- Convert button name into its string ID string button=StringSubstr(button_name,StringLen(prefix)); //--- Random group 1 and 2 numbers within the range of 0 - 15 group1=(uchar)Rand(); group2=(uchar)Rand(); uint magic=(comp_magic ? engine.SetCompositeMagicNumber(magic_number,group1,group2) : magic_number); //--- If the button is pressed if(ButtonState(button_name)) { //--- If the BUTT_BUY button is pressed: Open Buy position if(button==EnumToString(BUTT_BUY)) { //--- If the pending request creation buttons are not pressed, open Buy if(!pressed_pending_buy) engine.OpenBuy(lot,Symbol(),magic,stoploss,takeprofit); // No comment - the default comment is to be set //--- Otherwise, create a pending request for opening a Buy position else { int id=engine.OpenBuyPending(lot,Symbol(),magic,stoploss,takeprofit); if(id>0) { //--- set the pending request activation price and time, as well as activation parameters double ask=SymbolInfoDouble(NULL,SYMBOL_ASK); double price_activation=NormalizeDouble(ask-distance_pending_request*g_point,g_digits); ulong time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_BUY,EQUAL_OR_LESS,ask,TimeCurrent()); } } } //--- If the BUTT_BUY_LIMIT button is pressed: Place BuyLimit else if(button==EnumToString(BUTT_BUY_LIMIT)) { //--- If the pending request creation buttons are not pressed, set BuyLimit if(!pressed_pending_buy_limit) engine.PlaceBuyLimit(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Отложенный BuyLimit","Pending BuyLimit order")); //--- Otherwise, create a pending request to place a BuyLimit order with the placement distance //--- and set the conditions depending on active buttons else { int id=engine.PlaceBuyLimitPending(lot,Symbol(),distance_pending,stoploss,takeprofit,magic); if(id>0) { //--- set the pending request activation price and time, as well as activation parameters double ask=SymbolInfoDouble(NULL,SYMBOL_ASK); double price_activation=NormalizeDouble(ask-distance_pending_request*g_point,g_digits); ulong time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_BUY_LIMIT,EQUAL_OR_LESS,ask,TimeCurrent()); } } } //--- If the BUTT_BUY_STOP button is pressed: Set BuyStop else if(button==EnumToString(BUTT_BUY_STOP)) { //--- If the pending request creation buttons are not pressed, set BuyStop if(!pressed_pending_buy_stop) engine.PlaceBuyStop(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Отложенный BuyStop","Pending BuyStop order")); //--- Otherwise, create a pending request to place a BuyStop order with the placement distance //--- and set the conditions depending on active buttons else { int id=engine.PlaceBuyStopPending(lot,Symbol(),distance_pending,stoploss,takeprofit,magic); if(id>0) { //--- set the pending request activation price and time, as well as activation parameters double ask=SymbolInfoDouble(NULL,SYMBOL_ASK); double price_activation=NormalizeDouble(ask-distance_pending_request*g_point,g_digits); ulong time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_BUY_STOP,EQUAL_OR_LESS,ask,TimeCurrent()); } } } //--- If the BUTT_BUY_STOP_LIMIT button is pressed: Set BuyStopLimit else if(button==EnumToString(BUTT_BUY_STOP_LIMIT)) { //--- If the pending request creation buttons are not pressed, set BuyStopLimit if(!pressed_pending_buy_stoplimit) engine.PlaceBuyStopLimit(lot,Symbol(),distance_pending,distance_stoplimit,stoploss,takeprofit,magic,TextByLanguage("Отложенный BuyStopLimit","Pending BuyStopLimit order")); //--- Otherwise, create a pending request to place a BuyStopLimit order with the placement distances //--- and set the conditions depending on active buttons else { int id=engine.PlaceBuyStopLimitPending(lot,Symbol(),distance_pending,distance_stoplimit,stoploss,takeprofit,magic); if(id>0) { //--- set the pending request activation price and time, as well as activation parameters double ask=SymbolInfoDouble(NULL,SYMBOL_ASK); double price_activation=NormalizeDouble(ask-distance_pending_request*g_point,g_digits); ulong time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_BUY_STOP_LIMIT,EQUAL_OR_LESS,ask,TimeCurrent()); } } } //--- If the BUTT_SELL button is pressed: Open Sell position else if(button==EnumToString(BUTT_SELL)) { //--- If the pending request creation buttons are not pressed, open Sell if(!pressed_pending_sell) engine.OpenSell(lot,Symbol(),magic,stoploss,takeprofit); // No comment - the default comment is to be set //--- Otherwise, create a pending request for opening a Sell position else { int id=engine.OpenSellPending(lot,Symbol(),magic,stoploss,takeprofit); if(id>0) { //--- set the pending request activation price and time, as well as activation parameters double bid=SymbolInfoDouble(NULL,SYMBOL_BID); double price_activation=NormalizeDouble(bid+distance_pending_request*g_point,g_digits); ulong time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_SELL,EQUAL_OR_MORE,bid,TimeCurrent()); } } } //--- If the BUTT_SELL_LIMIT button is pressed: Set SellLimit else if(button==EnumToString(BUTT_SELL_LIMIT)) { //--- If the pending request creation buttons are not pressed, set SellLimit if(!pressed_pending_sell_limit) engine.PlaceSellLimit(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Отложенный SellLimit","Pending SellLimit order")); //--- Otherwise, create a pending request to place a SellLimit order with the placement distance //--- and set the conditions depending on active buttons else { int id=engine.PlaceSellLimitPending(lot,Symbol(),distance_pending,stoploss,takeprofit,magic); if(id>0) { //--- set the pending request activation price and time, as well as activation parameters double bid=SymbolInfoDouble(NULL,SYMBOL_BID); double price_activation=NormalizeDouble(bid+distance_pending_request*g_point,g_digits); ulong time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_SELL_LIMIT,EQUAL_OR_MORE,bid,TimeCurrent()); } } } //--- If the BUTT_SELL_STOP button is pressed: Set SellStop else if(button==EnumToString(BUTT_SELL_STOP)) { //--- If the pending request creation buttons are not pressed, set SellStop if(!pressed_pending_sell_stop) engine.PlaceSellStop(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Отложенный SellStop","Pending SellStop order")); //--- Otherwise, create a pending request to place a SellStop order with the placement distance //--- and set the conditions depending on active buttons else { int id=engine.PlaceSellStopPending(lot,Symbol(),distance_pending,stoploss,takeprofit,magic); if(id>0) { //--- set the pending request activation price and time, as well as activation parameters double bid=SymbolInfoDouble(NULL,SYMBOL_BID); double price_activation=NormalizeDouble(bid+distance_pending_request*g_point,g_digits); ulong time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_SELL_STOP,EQUAL_OR_MORE,bid,TimeCurrent()); } } } //--- If the BUTT_SELL_STOP_LIMIT button is pressed: Set SellStopLimit else if(button==EnumToString(BUTT_SELL_STOP_LIMIT)) { //--- If the pending request creation buttons are not pressed, set SellStopLimit if(!pressed_pending_sell_stoplimit) engine.PlaceSellStopLimit(lot,Symbol(),distance_pending,distance_stoplimit,stoploss,takeprofit,magic,TextByLanguage("Отложенный SellStopLimit","Pending SellStopLimit order")); //--- Otherwise, create a pending request to place a SellStopLimit order with the placement distances //--- and set the conditions depending on active buttons else { int id=engine.PlaceSellStopLimitPending(lot,Symbol(),distance_pending,distance_stoplimit,stoploss,takeprofit,magic); if(id>0) { //--- set the pending request activation price and time, as well as activation parameters double bid=SymbolInfoDouble(NULL,SYMBOL_BID); double price_activation=NormalizeDouble(bid+distance_pending_request*g_point,g_digits); ulong time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_SELL_STOP_LIMIT,EQUAL_OR_MORE,bid,TimeCurrent()); } } } //--- If the BUTT_CLOSE_BUY button is pressed: Close Buy with the maximum profit else if(button==EnumToString(BUTT_CLOSE_BUY)) { //--- Get the list of all open positions CArrayObj* list=engine.GetListMarketPosition(); //--- Select only Buy positions from the list and for the current symbol only list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL); list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- Sort the list by profit considering commission and swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Buy position with the maximum profit int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { //--- Get the Buy position object and close a position by ticket COrder* position=list.At(index); if(position!=NULL) { //--- If the pending request creation buttons are not pressed, close a position if(!pressed_pending_close_buy) engine.ClosePosition((ulong)position.Ticket()); //--- Otherwise, create a pending request for closing a position by ticket //--- and set the conditions depending on active buttons else { int id=engine.ClosePositionPending(position.Ticket()); if(id>0) { //--- set the pending request activation price and time, as well as activation parameters double bid=SymbolInfoDouble(NULL,SYMBOL_BID); double price_activation=NormalizeDouble(bid+distance_pending_request*g_point,g_digits); ulong time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_CLOSE_BUY,EQUAL_OR_MORE,bid,TimeCurrent()); } } } } } //--- If the BUTT_CLOSE_BUY2 button is pressed: Close the half of the Buy with the maximum profit else if(button==EnumToString(BUTT_CLOSE_BUY2)) { //--- Get the list of all open positions CArrayObj* list=engine.GetListMarketPosition(); //--- Select only Buy positions from the list and for the current symbol only list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL); list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- Sort the list by profit considering commission and swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Buy position with the maximum profit int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { //--- Get the Buy position object and close a position by ticket COrder* position=list.At(index); if(position!=NULL) { //--- If the pending request creation buttons are not pressed, close a position by ticket if(!pressed_pending_close_buy2) engine.ClosePositionPartially((ulong)position.Ticket(),position.Volume()/2.0); //--- Otherwise, create a pending request for closing a position partially by ticket //--- and set the conditions depending on active buttons else { int id=engine.ClosePositionPartiallyPending(position.Ticket(),position.Volume()/2.0); if(id>0) { //--- set the pending request activation price and time, as well as activation parameters double bid=SymbolInfoDouble(NULL,SYMBOL_BID); double price_activation=NormalizeDouble(bid+distance_pending_request*g_point,g_digits); ulong time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_CLOSE_BUY2,EQUAL_OR_MORE,bid,TimeCurrent()); } } } } } //--- If the BUTT_CLOSE_BUY_BY_SELL button is pressed: Close Buy with the maximum profit by the opposite Sell with the maximum profit else if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)) { //--- In case of a hedging account if(engine.IsHedge()) { CArrayObj *list_buy=NULL, *list_sell=NULL; //--- Get the list of all open positions CArrayObj* list=engine.GetListMarketPosition(); if(list==NULL) return; //--- Select only current symbol positions from the list list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL); //--- Select only Buy positions from the list list_buy=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); if(list_buy==NULL) return; //--- Sort the list by profit considering commission and swap list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Buy position with the maximum profit int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL); //--- Select only Sell positions from the list list_sell=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); if(list_sell==NULL) return; //--- Sort the list by profit considering commission and swap list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Sell position with the maximum profit int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL); if(index_buy>WRONG_VALUE && index_sell>WRONG_VALUE) { //--- Select the Buy position with the maximum profit COrder* position_buy=list_buy.At(index_buy); //--- Select the Sell position with the maximum profit COrder* position_sell=list_sell.At(index_sell); if(position_buy!=NULL && position_sell!=NULL) { //--- If the pending request creation buttons are not pressed, close positions by ticket if(!pressed_pending_close_buy_by_sell) engine.ClosePositionBy((ulong)position_buy.Ticket(),(ulong)position_sell.Ticket()); //--- Otherwise, create a pending request for closing a Buy position by an opposite Sell one //--- and set the conditions depending on active buttons else { int id=engine.ClosePositionByPending(position_buy.Ticket(),position_sell.Ticket()); if(id>0) { //--- set the pending request activation price and time, as well as activation parameters double bid=SymbolInfoDouble(NULL,SYMBOL_BID); double price_activation=NormalizeDouble(bid+distance_pending_request*g_point,g_digits); ulong time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_CLOSE_BUY_BY_SELL,EQUAL_OR_MORE,bid,TimeCurrent()); } } } } } } //--- If the BUTT_CLOSE_SELL button is pressed: Close Sell with the maximum profit else if(button==EnumToString(BUTT_CLOSE_SELL)) { //--- Get the list of all open positions CArrayObj* list=engine.GetListMarketPosition(); //--- Select only Sell positions from the list and for the current symbol only list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL); list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- Sort the list by profit considering commission and swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Sell position with the maximum profit int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { //--- Get the Sell position object and close a position by ticket COrder* position=list.At(index); if(position!=NULL) { //--- If the pending request creation buttons are not pressed, close a position if(!pressed_pending_close_sell) engine.ClosePosition((ulong)position.Ticket()); //--- Otherwise, create a pending request for closing a position by ticket //--- and set the conditions depending on active buttons else { int id=engine.ClosePositionPending(position.Ticket()); if(id>0) { //--- set the pending request activation price and time, as well as activation parameters double ask=SymbolInfoDouble(NULL,SYMBOL_ASK); double price_activation=NormalizeDouble(ask-distance_pending_request*g_point,g_digits); ulong time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_CLOSE_SELL,EQUAL_OR_LESS,ask,TimeCurrent()); } } } } } //--- If the BUTT_CLOSE_SELL2 button is pressed: Close the half of the Sell with the maximum profit else if(button==EnumToString(BUTT_CLOSE_SELL2)) { //--- Get the list of all open positions CArrayObj* list=engine.GetListMarketPosition(); //--- Select only Sell positions from the list and for the current symbol only list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL); list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- Sort the list by profit considering commission and swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Sell position with the maximum profit int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { //--- Get the Sell position object and close a position by ticket COrder* position=list.At(index); if(position!=NULL) { //--- If the pending request creation buttons are not pressed, close a position by ticket if(!pressed_pending_close_sell2) engine.ClosePositionPartially((ulong)position.Ticket(),position.Volume()/2.0); //--- Otherwise, create a pending request for closing a position partially by ticket //--- and set the conditions depending on active buttons else { int id=engine.ClosePositionPartiallyPending(position.Ticket(),position.Volume()/2.0); if(id>0) { //--- set the pending request activation price and time, as well as activation parameters double ask=SymbolInfoDouble(NULL,SYMBOL_ASK); double price_activation=NormalizeDouble(ask-distance_pending_request*g_point,g_digits); ulong time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_CLOSE_SELL2,EQUAL_OR_LESS,ask,TimeCurrent()); } } } } } //--- If the BUTT_CLOSE_SELL_BY_BUY button is pressed: Close Sell with the maximum profit by the opposite Buy with the maximum profit else if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)) { //--- In case of a hedging account if(engine.IsHedge()) { CArrayObj *list_buy=NULL, *list_sell=NULL; //--- Get the list of all open positions CArrayObj* list=engine.GetListMarketPosition(); if(list==NULL) return; //--- Select only current symbol positions from the list list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL); //--- Select only Sell positions from the list list_sell=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); if(list_sell==NULL) return; //--- Sort the list by profit considering commission and swap list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Sell position with the maximum profit int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL); //--- Select only Buy positions from the list list_buy=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); if(list_buy==NULL) return; //--- Sort the list by profit considering commission and swap list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Buy position with the maximum profit int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL); if(index_sell>WRONG_VALUE && index_buy>WRONG_VALUE) { //--- Select the Sell position with the maximum profit COrder* position_sell=list_sell.At(index_sell); //--- Select the Buy position with the maximum profit COrder* position_buy=list_buy.At(index_buy); if(position_sell!=NULL && position_buy!=NULL) { //--- If the pending request creation buttons are not pressed, close positions by ticket if(!pressed_pending_close_sell_by_buy) engine.ClosePositionBy((ulong)position_sell.Ticket(),(ulong)position_buy.Ticket()); //--- Otherwise, create a pending request for closing a Sell position by an opposite Buy one //--- and set the conditions depending on active buttons else { int id=engine.ClosePositionByPending(position_sell.Ticket(),position_buy.Ticket()); if(id>0) { //--- set the pending request activation price and time, as well as activation parameters double ask=SymbolInfoDouble(NULL,SYMBOL_ASK); double price_activation=NormalizeDouble(ask-distance_pending_request*g_point,g_digits); ulong time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_CLOSE_SELL_BY_BUY,EQUAL_OR_LESS,ask,TimeCurrent()); } } } } } } //--- If the BUTT_CLOSE_ALL is pressed: Close all positions starting with the one with the least profit else if(button==EnumToString(BUTT_CLOSE_ALL)) { //--- Get the list of all open positions CArrayObj* list=engine.GetListMarketPosition(); //--- Select only current symbol positions from the list list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL); if(list!=NULL) { //--- Sort the list by profit considering commission and swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); int total=list.Total(); //--- In the loop from the position with the least profit for(int i=0;i<total;i++) { COrder* position=list.At(i); if(position==NULL) continue; //--- If the pending request creation buttons are not pressed, close each position by its ticket if(!pressed_pending_close_all) engine.ClosePosition((ulong)position.Ticket()); //--- Otherwise, create a pending request for closing each position //--- and set the conditions depending on active buttons else { int id=engine.ClosePositionPending(position.Ticket()); if(id>0) { //--- set the pending request activation price and time, as well as activation parameters double price=SymbolInfoDouble(NULL,SYMBOL_BID); double price_activation=NormalizeDouble(position.PriceOpen()+distance_pending_request*g_point,g_digits); ENUM_COMPARER_TYPE comparer=EQUAL_OR_MORE; if(position.TypeOrder()==POSITION_TYPE_SELL) { price=SymbolInfoDouble(NULL,SYMBOL_ASK); price_activation=NormalizeDouble(position.PriceOpen()-distance_pending_request*g_point,g_digits); comparer=EQUAL_OR_LESS; } ulong time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_CLOSE_ALL,comparer,price,TimeCurrent()); } } } } } //--- If the BUTT_DELETE_PENDING button is pressed: Remove pending orders starting from the oldest one else if(button==EnumToString(BUTT_DELETE_PENDING)) { //--- Get the list of all orders CArrayObj* list=engine.GetListMarketPendings(); //--- Select only current symbol orders from the list list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL); if(list!=NULL) { //--- Sort the list by placement time list.Sort(SORT_BY_ORDER_TIME_OPEN); int total=list.Total(); //--- In a loop from an order with the longest time for(int i=total-1;i>=0;i--) { COrder* order=list.At(i); if(order==NULL) continue; //--- If the pending request creation buttons are not pressed, remove each order by its ticket if(!pressed_pending_delete_all) engine.DeleteOrder((ulong)order.Ticket()); //--- Otherwise, create a pending request for removing each order //--- and set the conditions depending on active buttons else { int id=engine.DeleteOrderPending(order.Ticket()); if(id>0) { //--- set the pending request activation price and time, as well as activation parameters double price=SymbolInfoDouble(NULL,SYMBOL_ASK); double price_activation=NormalizeDouble(order.PriceOpen()+(distance_pending+distance_pending_request)*g_point,g_digits); ENUM_COMPARER_TYPE comparer=EQUAL_OR_MORE; if(order.TypeByDirection()==ORDER_TYPE_SELL) { price=SymbolInfoDouble(NULL,SYMBOL_BID); price_activation=NormalizeDouble(order.PriceOpen()-(distance_pending+distance_pending_request)*g_point,g_digits); comparer=EQUAL_OR_LESS; } ulong time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_DELETE_PENDING,comparer,price,TimeCurrent()); } } } } } //--- If the BUTT_SET_STOP_LOSS button is pressed: Place StopLoss to all orders and positions where it is not present if(button==EnumToString(BUTT_SET_STOP_LOSS)) { SetStopLoss(); } //--- If the BUTT_SET_TAKE_PROFIT button is pressed: Place TakeProfit to all orders and positions where it is not present if(button==EnumToString(BUTT_SET_TAKE_PROFIT)) { SetTakeProfit(); } //--- If the BUTT_PROFIT_WITHDRAWAL button is pressed: Withdraw funds from the account if(button==EnumToString(BUTT_PROFIT_WITHDRAWAL)) { //--- If the program is launched in the tester if(MQLInfoInteger(MQL_TESTER)) { //--- Emulate funds withdrawal TesterWithdrawal(withdrawal); } } //--- Wait for 1/10 of a second Sleep(100); //--- "Unpress" the button (if this is neither a trailing button, nor the buttons enabling pending requests) if(button!=EnumToString(BUTT_TRAILING_ALL) && StringFind(button,"_PRICE")<0 && StringFind(button,"_TIME")<0) ButtonState(button_name,false); //--- If the BUTT_TRAILING_ALL button or the buttons enabling pending requests are pressed else { //--- Set the active button color for the button enabling trailing if(button==EnumToString(BUTT_TRAILING_ALL)) { ButtonState(button_name,true); trailing_on=true; } //--- Buying //--- Set the active button color for the button enabling pending requests for opening Buy by price or time if(button==EnumToString(BUTT_BUY)+"_PRICE" || button==EnumToString(BUTT_BUY)+"_TIME") { ButtonState(button_name,true); pressed_pending_buy=true; } //--- Set the active button color for the button enabling pending requests for placing BuyLimit by price or time if(button==EnumToString(BUTT_BUY_LIMIT)+"_PRICE" || button==EnumToString(BUTT_BUY_LIMIT)+"_TIME") { ButtonState(button_name,true); pressed_pending_buy_limit=true; } //--- Set the active button color for the button enabling pending requests for placing BuyStop by price or time if(button==EnumToString(BUTT_BUY_STOP)+"_PRICE" || button==EnumToString(BUTT_BUY_STOP)+"_TIME") { ButtonState(button_name,true); pressed_pending_buy_stop=true; } //--- Set the active button color for the button enabling pending requests for placing BuyStopLimit by price or time if(button==EnumToString(BUTT_BUY_STOP_LIMIT)+"_PRICE" || button==EnumToString(BUTT_BUY_STOP_LIMIT)+"_TIME") { ButtonState(button_name,true); pressed_pending_buy_stoplimit=true; } //--- Set the active button color for the button enabling pending requests for closing Buy by price or time if(button==EnumToString(BUTT_CLOSE_BUY)+"_PRICE" || button==EnumToString(BUTT_CLOSE_BUY)+"_TIME") { ButtonState(button_name,true); pressed_pending_close_buy=true; } //--- Set the active button color for the button enabling pending requests for closing 1/2 Buy by price or time if(button==EnumToString(BUTT_CLOSE_BUY2)+"_PRICE" || button==EnumToString(BUTT_CLOSE_BUY2)+"_TIME") { ButtonState(button_name,true); pressed_pending_close_buy2=true; } //--- Set the active button color for the button enabling pending requests for closing Buy by an opposite Sell by price or time if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_PRICE" || button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_TIME") { ButtonState(button_name,true); pressed_pending_close_buy_by_sell=true; } //--- Selling //--- Set the active button color for the button enabling pending requests for opening Sell by price or time if(button==EnumToString(BUTT_SELL)+"_PRICE" || button==EnumToString(BUTT_SELL)+"_TIME") { ButtonState(button_name,true); pressed_pending_sell=true; } //--- Set the active button color for the button enabling pending requests for placing SellLimit by price or time if(button==EnumToString(BUTT_SELL_LIMIT)+"_PRICE" || button==EnumToString(BUTT_SELL_LIMIT)+"_TIME") { ButtonState(button_name,true); pressed_pending_sell_limit=true; } //--- Set the active button color for the button enabling pending requests for placing SellStop by price or time if(button==EnumToString(BUTT_SELL_STOP)+"_PRICE" || button==EnumToString(BUTT_SELL_STOP)+"_TIME") { ButtonState(button_name,true); pressed_pending_sell_stop=true; } //--- Set the active button color for the button enabling pending requests for placing SellStopLimit by price or time if(button==EnumToString(BUTT_SELL_STOP_LIMIT)+"_PRICE" || button==EnumToString(BUTT_SELL_STOP_LIMIT)+"_TIME") { ButtonState(button_name,true); pressed_pending_sell_stoplimit=true; } //--- Set the active button color for the button enabling pending requests for closing Sell by price or time if(button==EnumToString(BUTT_CLOSE_SELL)+"_PRICE" || button==EnumToString(BUTT_CLOSE_SELL)+"_TIME") { ButtonState(button_name,true); pressed_pending_close_sell=true; } //--- Set the active button color for the button enabling pending requests for closing 1/2 Sell by price or time if(button==EnumToString(BUTT_CLOSE_SELL2)+"_PRICE" || button==EnumToString(BUTT_CLOSE_SELL2)+"_TIME") { ButtonState(button_name,true); pressed_pending_close_sell2=true; } //--- Set the active button color for the button enabling pending requests for closing Sell by an opposite Buy by price or time if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_PRICE" || button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_TIME") { ButtonState(button_name,true); pressed_pending_close_sell_by_buy=true; } //--- Set the active button color for the button enabling pending requests for removing orders by price or time if(button==EnumToString(BUTT_DELETE_PENDING)+"_PRICE" || button==EnumToString(BUTT_DELETE_PENDING)+"_TIME") { ButtonState(button_name,true); pressed_pending_delete_all=true; } //--- Set the active button color for the button enabling pending requests for closing positions by price or time if(button==EnumToString(BUTT_CLOSE_ALL)+"_PRICE" || button==EnumToString(BUTT_CLOSE_ALL)+"_TIME") { ButtonState(button_name,true); pressed_pending_close_all=true; } //--- Set the active button color for the button enabling pending requests for placing StopLoss by price or time if(button==EnumToString(BUTT_SET_STOP_LOSS)+"_PRICE" || button==EnumToString(BUTT_SET_STOP_LOSS)+"_TIME") { ButtonState(button_name,true); pressed_pending_sl=true; } //--- Set the active button color for the button enabling pending requests for placing TakeProfit by price or time if(button==EnumToString(BUTT_SET_TAKE_PROFIT)+"_PRICE" || button==EnumToString(BUTT_SET_TAKE_PROFIT)+"_TIME") { ButtonState(button_name,true); pressed_pending_tp=true; } } //--- re-draw the chart ChartRedraw(); } //--- Return a color for the inactive buttons else { //--- trailing button if(button==EnumToString(BUTT_TRAILING_ALL)) { ButtonState(button_name,false); trailing_on=false; } //--- Buying //--- the button enabling pending requests for opening Buy by price if(button==EnumToString(BUTT_BUY)+"_PRICE") { ButtonState(button_name,false); pressed_pending_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY)+"_TIME")); } //--- the button enabling pending requests for opening Buy by time if(button==EnumToString(BUTT_BUY)+"_TIME") { ButtonState(button_name,false); pressed_pending_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY)+"_PRICE")); } //--- the button enabling pending requests for placing BuyLimit by price if(button==EnumToString(BUTT_BUY_LIMIT)+"_PRICE") { ButtonState(button_name,false); pressed_pending_buy_limit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_LIMIT)+"_TIME")); } //--- the button enabling pending requests for placing BuyLimit by time if(button==EnumToString(BUTT_BUY_LIMIT)+"_TIME") { ButtonState(button_name,false); pressed_pending_buy_limit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_LIMIT)+"_PRICE")); } //--- the button enabling pending requests for placing BuyStop by price if(button==EnumToString(BUTT_BUY_STOP)+"_PRICE") { ButtonState(button_name,false); pressed_pending_buy_stop=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_STOP)+"_TIME")); } //--- the button enabling pending requests for placing BuyStop by time if(button==EnumToString(BUTT_BUY_STOP)+"_TIME") { ButtonState(button_name,false); pressed_pending_buy_stop=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_STOP)+"_PRICE")); } //--- the button enabling pending requests for placing BuyStopLimit by price if(button==EnumToString(BUTT_BUY_STOP_LIMIT)+"_PRICE") { ButtonState(button_name,false); pressed_pending_buy_stoplimit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_STOP_LIMIT)+"_TIME")); } //--- the button enabling pending requests for placing BuyStopLimit by time if(button==EnumToString(BUTT_BUY_STOP_LIMIT)+"_TIME") { ButtonState(button_name,false); pressed_pending_buy_stoplimit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_STOP_LIMIT)+"_PRICE")); } //--- the button enabling pending requests for closing Buy by price if(button==EnumToString(BUTT_CLOSE_BUY)+"_PRICE") { ButtonState(button_name,false); pressed_pending_close_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY)+"_TIME")); } //--- the button enabling pending requests for closing Buy by time if(button==EnumToString(BUTT_CLOSE_BUY)+"_TIME") { ButtonState(button_name,false); pressed_pending_close_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY)+"_PRICE")); } //--- the button enabling pending requests for closing 1/2 Buy by price if(button==EnumToString(BUTT_CLOSE_BUY2)+"_PRICE") { ButtonState(button_name,false); pressed_pending_close_buy2=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY2)+"_TIME")); } //--- the button enabling pending requests for closing 1/2 Buy by time if(button==EnumToString(BUTT_CLOSE_BUY2)+"_TIME") { ButtonState(button_name,false); pressed_pending_close_buy2=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY2)+"_PRICE")); } //--- the button enabling pending requests for closing Buy by an opposite Sell by price if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_PRICE") { ButtonState(button_name,false); pressed_pending_close_buy_by_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_TIME")); } //--- the button enabling pending requests for closing Buy by an opposite Sell by time if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_TIME") { ButtonState(button_name,false); pressed_pending_close_buy_by_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_PRICE")); } //--- Selling //--- the button enabling pending requests for opening Sell by price if(button==EnumToString(BUTT_SELL)+"_PRICE") { ButtonState(button_name,false); pressed_pending_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL)+"_TIME")); } //--- the button enabling pending requests for opening Sell by time if(button==EnumToString(BUTT_SELL)+"_TIME") { ButtonState(button_name,false); pressed_pending_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL)+"_PRICE")); } //--- the button enabling pending requests for placing SellLimit by price if(button==EnumToString(BUTT_SELL_LIMIT)+"_PRICE") { ButtonState(button_name,false); pressed_pending_sell_limit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_LIMIT)+"_TIME")); } //--- the button enabling pending requests for placing SellLimit by time if(button==EnumToString(BUTT_SELL_LIMIT)+"_TIME") { ButtonState(button_name,false); pressed_pending_sell_limit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_LIMIT)+"_PRICE")); } //--- the button enabling pending requests for placing SellStop by price if(button==EnumToString(BUTT_SELL_STOP)+"_PRICE") { ButtonState(button_name,false); pressed_pending_sell_stop=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_STOP)+"_TIME")); } //--- the button enabling pending requests for placing SellStop by time if(button==EnumToString(BUTT_SELL_STOP)+"_TIME") { ButtonState(button_name,false); pressed_pending_sell_stop=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_STOP)+"_PRICE")); } //--- the button enabling pending requests for placing SellStopLimit by price if(button==EnumToString(BUTT_SELL_STOP_LIMIT)+"_PRICE") { ButtonState(button_name,false); pressed_pending_sell_stoplimit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_STOP_LIMIT)+"_TIME")); } //--- the button enabling pending requests for placing SellStopLimit by time if(button==EnumToString(BUTT_SELL_STOP_LIMIT)+"_TIME") { ButtonState(button_name,false); pressed_pending_sell_stoplimit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_STOP_LIMIT)+"_PRICE")); } //--- the button enabling pending requests for closing Sell by price if(button==EnumToString(BUTT_CLOSE_SELL)+"_PRICE") { ButtonState(button_name,false); pressed_pending_close_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL)+"_TIME")); } //--- the button enabling pending requests for closing Sell by time if(button==EnumToString(BUTT_CLOSE_SELL)+"_TIME") { ButtonState(button_name,false); pressed_pending_close_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL)+"_PRICE")); } //--- the button enabling pending requests for closing 1/2 Sell by price if(button==EnumToString(BUTT_CLOSE_SELL2)+"_PRICE") { ButtonState(button_name,false); pressed_pending_close_sell2=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL2)+"_TIME")); } //--- the button enabling pending requests for closing 1/2 Sell by time if(button==EnumToString(BUTT_CLOSE_SELL2)+"_TIME") { ButtonState(button_name,false); pressed_pending_close_sell2=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL2)+"_PRICE")); } //--- the button enabling pending requests for closing Sell by an opposite Buy by price if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_PRICE") { ButtonState(button_name,false); pressed_pending_close_sell_by_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_TIME")); } //--- the button enabling pending requests for closing Sell by an opposite Buy by time if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_TIME") { ButtonState(button_name,false); pressed_pending_close_sell_by_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_PRICE")); } //--- the button enabling pending requests for removing orders by price if(button==EnumToString(BUTT_DELETE_PENDING)+"_PRICE") { ButtonState(button_name,false); pressed_pending_delete_all=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_DELETE_PENDING)+"_TIME")); } //--- the button enabling pending requests for removing orders by time if(button==EnumToString(BUTT_DELETE_PENDING)+"_TIME") { ButtonState(button_name,false); pressed_pending_delete_all=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_DELETE_PENDING)+"_PRICE")); } //--- the button enabling pending requests for closing positions by price if(button==EnumToString(BUTT_CLOSE_ALL)+"_PRICE") { ButtonState(button_name,false); pressed_pending_close_all=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_ALL)+"_TIME")); } //--- the button enabling pending requests for closing positions by time if(button==EnumToString(BUTT_CLOSE_ALL)+"_TIME") { ButtonState(button_name,false); pressed_pending_close_all=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_ALL)+"_PRICE")); } //--- the button enabling pending requests for placing StopLoss by price if(button==EnumToString(BUTT_SET_STOP_LOSS)+"_PRICE") { ButtonState(button_name,false); pressed_pending_sl=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SET_STOP_LOSS)+"_TIME")); } //--- the button enabling pending requests for placing StopLoss by time if(button==EnumToString(BUTT_SET_STOP_LOSS)+"_TIME") { ButtonState(button_name,false); pressed_pending_sl=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SET_STOP_LOSS)+"_PRICE")); } //--- the button enabling pending requests for placing TakeProfit by price if(button==EnumToString(BUTT_SET_TAKE_PROFIT)+"_PRICE") { ButtonState(button_name,false); pressed_pending_tp=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SET_TAKE_PROFIT)+"_TIME")); } //--- the button enabling pending requests for placing TakeProfit by time if(button==EnumToString(BUTT_SET_TAKE_PROFIT)+"_TIME") { ButtonState(button_name,false); pressed_pending_tp=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SET_TAKE_PROFIT)+"_PRICE")); } //--- re-draw the chart ChartRedraw(); } } //+------------------------------------------------------------------+
In den Funktionen für das Platzieren von StopLoss und TakeProfit fügen wir die Code-Blöcke für die Erstellung von schwebenden Anfragen hinzu, um StopLoss/TakeProfit für alle Orders und Positionen zu setzen:
//+------------------------------------------------------------------+ //| Set StopLoss to all orders and positions | //+------------------------------------------------------------------+ void SetStopLoss(void) { if(stoploss_to_modify==0) return; //--- Set StopLoss to all positions where it is absent //--- Get the list of all positions CArrayObj* list=engine.GetListMarketPosition(); //--- Select only current symbol positions from the list list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL); //--- select positions with zero StopLoss from the list list=CSelect::ByOrderProperty(list,ORDER_PROP_SL,0,EQUAL); if(list==NULL) return; int total=list.Total(); for(int i=total-1;i>=0;i--) { COrder* position=list.At(i); if(position==NULL) continue; double sl=CorrectStopLoss(position.Symbol(),position.TypeByDirection(),0,stoploss_to_modify); //--- If the pending request creation buttons are not pressed, set StopLoss for each position by its ticket if(!pressed_pending_sl) engine.ModifyPosition((ulong)position.Ticket(),sl,-1); //--- Otherwise, create a pending request for setting StopLoss for each position //--- and set the conditions depending on active buttons else { int id=engine.ModifyPositionPending(position.Ticket(),sl,-1); if(id>0) { //--- set the pending request activation price and time, as well as activation parameters double price=SymbolInfoDouble(NULL,SYMBOL_BID); double price_activation=NormalizeDouble(position.PriceOpen()+distance_pending_request*g_point,g_digits); ENUM_COMPARER_TYPE comparer=EQUAL_OR_MORE; if(position.TypeByDirection()==ORDER_TYPE_SELL) { price=SymbolInfoDouble(NULL,SYMBOL_ASK); price_activation=NormalizeDouble(position.PriceOpen()-distance_pending_request*g_point,g_digits); comparer=EQUAL_OR_LESS; } ulong time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_SET_STOP_LOSS,comparer,price,TimeCurrent()); } } } //--- Set StopLoss to all pending orders where it is absent //--- Get the list of all orders list=engine.GetListMarketPendings(); //--- Select only current symbol positions from the list list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL); //--- select orders with zero StopLoss from the list list=CSelect::ByOrderProperty(list,ORDER_PROP_SL,0,EQUAL); if(list==NULL) return; total=list.Total(); for(int i=total-1;i>=0;i--) { COrder* order=list.At(i); if(order==NULL) continue; double sl=CorrectStopLoss(order.Symbol(),(ENUM_ORDER_TYPE)order.TypeOrder(),order.PriceOpen(),stoploss_to_modify); //--- If the pending request creation buttons are not pressed, set StopLoss for each order by its ticket if(!pressed_pending_sl) engine.ModifyOrder((ulong)order.Ticket(),-1,sl,-1,-1); //--- Otherwise, create a pending request for setting StopLoss for each order //--- and set the conditions depending on active buttons else { int id=engine.ModifyOrderPending(order.Ticket(),-1,sl,-1,-1); if(id>0) { //--- set the pending request activation price and time, as well as activation parameters double price=SymbolInfoDouble(NULL,SYMBOL_ASK); double price_activation=NormalizeDouble(order.PriceOpen()+(distance_pending+distance_pending_request)*g_point,g_digits); ENUM_COMPARER_TYPE comparer=EQUAL_OR_MORE; if(order.TypeByDirection()==ORDER_TYPE_SELL) { price=SymbolInfoDouble(NULL,SYMBOL_BID); price_activation=NormalizeDouble(order.PriceOpen()-(distance_pending+distance_pending_request)*g_point,g_digits); comparer=EQUAL_OR_LESS; } ulong time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_SET_STOP_LOSS,comparer,price,TimeCurrent()); } } } } //+------------------------------------------------------------------+ //| Set TakeProfit to all orders and positions | //+------------------------------------------------------------------+ void SetTakeProfit(void) { if(takeprofit_to_modify==0) return; //--- Set TakeProfit to all positions where it is absent //--- Get the list of all positions CArrayObj* list=engine.GetListMarketPosition(); //--- Select only current symbol positions from the list list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL); //--- select positions with zero TakeProfit from the list list=CSelect::ByOrderProperty(list,ORDER_PROP_TP,0,EQUAL); if(list==NULL) return; int total=list.Total(); for(int i=total-1;i>=0;i--) { COrder* position=list.At(i); if(position==NULL) continue; double tp=CorrectTakeProfit(position.Symbol(),position.TypeByDirection(),0,takeprofit_to_modify); //--- If the pending request creation buttons are not pressed, set TakeProfit for each position by its ticket if(!pressed_pending_tp) engine.ModifyPosition((ulong)position.Ticket(),-1,tp); //--- Otherwise, create a pending request for setting TakeProfit for each position //--- and set the conditions depending on active buttons else { int id=engine.ModifyPositionPending(position.Ticket(),-1,tp); if(id>0) { //--- set the pending request activation price and time, as well as activation parameters double price=SymbolInfoDouble(NULL,SYMBOL_BID); double price_activation=NormalizeDouble(position.PriceOpen()+distance_pending_request*g_point,g_digits); ENUM_COMPARER_TYPE comparer=EQUAL_OR_MORE; if(position.TypeByDirection()==ORDER_TYPE_SELL) { price=SymbolInfoDouble(NULL,SYMBOL_ASK); price_activation=NormalizeDouble(position.PriceOpen()-distance_pending_request*g_point,g_digits); comparer=EQUAL_OR_LESS; } ulong time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_SET_TAKE_PROFIT,comparer,price,TimeCurrent()); } } } //--- Set TakeProfit to all pending orders where it is absent //--- Get the list of all orders list=engine.GetListMarketPendings(); //--- Select only current symbol orders from the list list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL); //--- select orders with zero TakeProfit from the list list=CSelect::ByOrderProperty(list,ORDER_PROP_TP,0,EQUAL); if(list==NULL) return; total=list.Total(); for(int i=total-1;i>=0;i--) { COrder* order=list.At(i); if(order==NULL) continue; double tp=CorrectTakeProfit(order.Symbol(),(ENUM_ORDER_TYPE)order.TypeOrder(),order.PriceOpen(),takeprofit_to_modify); //--- If the pending request creation buttons are not pressed, set TakeProfit for each order by its ticket if(!pressed_pending_sl) engine.ModifyOrder((ulong)order.Ticket(),-1,-1,tp,-1); //--- Otherwise, create a pending request for setting TakeProfit for each order //--- and set the conditions depending on active buttons else { int id=engine.ModifyOrderPending(order.Ticket(),-1,-1,tp,-1); if(id>0) { //--- set the pending request activation price and time, as well as activation parameters double price=SymbolInfoDouble(NULL,SYMBOL_ASK); double price_activation=NormalizeDouble(order.PriceOpen()+(distance_pending+distance_pending_request)*g_point,g_digits); ENUM_COMPARER_TYPE comparer=EQUAL_OR_MORE; if(order.TypeByDirection()==ORDER_TYPE_SELL) { price=SymbolInfoDouble(NULL,SYMBOL_BID); price_activation=NormalizeDouble(order.PriceOpen()-(distance_pending+distance_pending_request)*g_point,g_digits); comparer=EQUAL_OR_LESS; } ulong time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_SET_TAKE_PROFIT,comparer,price,TimeCurrent()); } } } } //+------------------------------------------------------------------+
Die Codeblöcke für das Erstellen von schwebenden Anfragen sind in allen betrachteten Funktionen logisch ähnlich. Sie sind im Code ausführlich kommentiert, so dass sie zum unabhängigen Studium überlassen bleiben. Wenn Sie Fragen haben, können Sie diese gerne in den Kommentaren stellen.
Kompilieren Sie den EA und starten Sie ihn im Tester im visuellen Modus. Um das Entfernen von Orders sowie die Änderung von Orders und Positionen zu überprüfen, eröffnen wir zwei Verkaufspositionen und platzieren einen Pending-Verkaufsorder ohne StopLoss- und TakeProfit-Preise. Als Nächstes erstellen wir schwebende Anfragen zur Änderung der Stop-Level der Orders und Positionen gemäß dem Preis. Wir warten auf die Aktivierung der schwebenden Anfragen und die Platzierung spezifizierter Stop-Preise und entfernen Orders und Positionen.
Eröffnen wir dann zwei Kaufpositionen und platzieren einen Pending-Kauforder. Danach erstellen wir schwebende Anfragen, um Orders zu entfernen und Positionen gemäß der Zeit zu schließen.
Wie wir sehen können, wurden die Stopp-Level am Schnittpunkt eines bestimmten Preisniveaus für die Aktivierung einer schwebenden Anfrage festgelegt. Die Positionen wurden nach einer bestimmten Zeit geschlossen, und die Order wurde entfernt.
Die Arbeit ist aber noch nicht abgeschlossen. Es gibt Probleme bei der gleichzeitigen Erstellung mehrerer schwebender Anfragen für dasselbe Ticket, da diese Anfragen nicht immer korrekt ausgeführt werden. Derzeit funktioniert die Logik nur dann korrekt, wenn es für jede Position oder jeden Auftrag eine schwebende Anfrage gibt. Nachdem eine schwebende Anfrage aktiviert, ausgeführt und entfernt wurde, ist es möglich, eine neue schwebende Anfrage für diese Position oder diesen Auftrag zu erstellen (falls sie noch aktiv sind).
Ich plane, dieses Problem schrittweise zusammen mit der Weiterentwicklung der Bibliotheksfunktionalität zu beheben, sobald ich bereits über einige grafische Bibliotheksobjekte verfüge.
Was kommt als Nächstes?
Im nächsten Artikel werden wir mit der Entwicklung der Bibliotheksfunktionalität für die Speicherung, Handhabung und den Empfang von Preisdaten beginnen.
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
Teil 23. Handelsklasse - Basisklasse des Handels, Verifikation der Parameter
Teil 24. Trading classes - Handelsklassen, automatische Korrektur ungültiger Parametern
Teil 25. Handelsklassen - Basisklasse des Handels, Behandlung der Fehlermeldungen vom Server
Teil 26. Arbeiten mit schwebenden Handelsanfragen - Erste Implementation (Öffnen von Positionen)
Teil 27. Arbeiten mit schwebenden Handelsanfragen - Platzieren von Pending-Orders
Teil 28. Arbeiten mit schwebenden Handelsanfragen - Schließen, Entfernen und Ändern
Teil 29. Arbeiten mit schwebenden Handelsanfragen - Die Klasse der Anfrageobjekte
Teil 30. Schwebende Handelsanfragen - Die Behandlung der Anfrageobjekte
Teil 31. Schwebende Handelsanfragen - Positionseröffnungen unter bestimmten Bedingungen
Teil 32. Schwebende Handelsanfragen - Platzieren von Pending-Orders unter bestimmten Bedingungen
Teil 33. Schwebende Handelsanfragen - Positionen unter bestimmten Bedingungen schließen (ganz, teilweise oder durch eine entgegengesetzte P.)