Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte XXXII): Solicitudes comerciales pendientes - Colocación de órdenes según condiciones
Contenido
En el artículo anterior, implementamos la posibibilidad de enviar solicitudes comerciales según condiciones establecidas de antemano. Al cumplirse la condición (o condiciones) establecidas, se envía una orden comercial de apertura de posición. Las condiciones de ejecución de una transacción pueden abarcar un gran número de combinaciones distintas de las listas de condiciones de los estados de la cuenta, el símbolo y los eventos que tienen lugar en la cuenta.
Las órdenes comerciales se envían con la ayuda de solicitudes comerciales pendientes en el momento en que se cumplen todas las condiciones escritas en el objeto de solicitud pendiente.
En el presentente artículo, vamos a continuar desarrollando este concepto; para ello, crearemos una funcionalidad que permita colocar órdenes pendientes con la ayuda de solicitudes comerciales pendientes en las que se indicarán todas las condiciones necesarias para colocar una orden pendiente.
Concepto
En el objeto de solicitud pendiente, hemos creado una matriz en la que se guardan todas las condiciones para su activación. En el temporizador de la clase de control comercial se visualiza constantemente la lista de solicitudes comerciales pendientes, y al darse el momento de ejecución de la solicitud comercial pendiente (todas las condiciones de activación escritas ella se han cumplido), se envía al servidor la orden comercial cuyos parámetros han sido indicados en la solicitud pendiente activada.
Para abrir posisiciones, basta con controlar el momento en el que se cumplen las condiciones establecidas, y en cuanto se registre este hecho, la orden comercial de apertura de posición será enviada al servidor.
No obstante, existe una complicación a la hora de colocar órdenes pendientes con la ayuda de objetos de solicitudes pendientes: la orden se coloca a una distancia determinada del precio, mientras que la posición se establece al precio actual correspondiente.
Por eso, para trabajar con la colocación de órdenes pendientes según una condición, deberemos también tener en cuenta la distancia de colocación de la orden pendiente. Y aquí nos surge un dilema: al crear una solicitud pendiente, nosotros indicamos la distancia de colocación de la futura orden pendiente, pero... ¿respecto a qué precio? ¿Respecto al precio que hay en el momento en que se crea la solicitud pendiente? ¿O respecto al precio que habrá cuando se cumplan todas las condiciones para su activación indicadas en el objeto de solicitud? Y es que en el momento en que se cumplan todas las condiciones, el precio puede haberse ido muy lejos del lugar en el que se creó la solicitud pendiente. Y el precio futuro solo lo podemos saber en un caso: cuando la única condición de activación de la solicitud pendiente es precisamente el valor de precio indicado. En los demás casos, desconocemos el precio futuro respecto al que debemos colocar la orden.
Haremos lo siguiente: al crear una solicitud pendiente, indicaremos la distancia de colocación de la orden pendiente. Esta distancia siempre se puede saber con la ayuda de la diferencia entre el precio actual en el momento de creación de la solicitud pendiente (en esta propiedad se escribe el precio Ask o Bid actual, dependiendo de la dirección de la futura orden) y el precio de colocación de la orden pendiente (que también se escribe en las propiedades del objeto de solicitud pendiente). es decir, sea cual sea el valor de precio en el momento de activación de la solicitud pendiente, tendremos la posibilidad de calcular el nuevo precio de colocación de la orden pendiente, o dejarlo como se indicaba al crear la solicitud pendiente.
En el primer caso, en el momento que se activa la solicitud pendiente, el precio de colocación de la orden se recalcula con respecto al precio actual en el momento de la activación de la solicitud pendiente; en el segundo caso, al servidor se enviará una orden comercial para establecer una orden pendiente con respecto al precio al que se creó la solicitud comercial. En esta variante, el precio será corregido por el permisible en el caso de que se haya vuelto erróneo durante el tiempo de espera hasta la activación de la solicitud pendiente.
Implementación
En el archivo PendRequest.mqh de la clase del objeto de la solicitud pendiente abstracta CPendRequest, en su sección privada, añadimos la variable de miembro de clase para guardar la bandera de recálculo del nivel para la colocación de una orden pendiente con respecto al precio actual:
+//+------------------------------------------------------------------+ //| Abstract pending trading request class | +//+------------------------------------------------------------------+ class CPendRequest : public CBaseObj { private: MqlTradeRequest m_request; // Trade request structure CPause m_pause; // Pause class object bool m_follow; // The flag of the pending order distance reference point following the price /* Data on a pending request activation in the array:
Si la variable tiene el valor true, al activar el objeto de solicitud pendiente, el precio de colocación de la orden se recalculará con respecto al precio actual en el momento de activación de la solicitud pendiente. En caso contrario, la orden pendiente se colocará al precio establecido en las propiedades del objeto de solicitud pendiente, y se corregirá en el caso de que el precio de colocación de la orden se vuelva erróneo debido al cambio del precio actual respecto al precio de creación de la solicitud pendiente.
Declaramos en la sección protegida de la clase el método para establecer los precios de la orden pendiente de acuerdo con el desplazamiento:
//--- Return the number of decimal places of a controlled property int DigitsControlledValue(const uint index) const; //--- Set a new value changed by the shift (+/-) for all order prices void SetAllMqlPrices(const double shift); public:
En la sección pública de la clase, en el bloque de métodos de acceso simplificado a las propiedades del objeto de solicitud, declaramos el método para corregir los precios de la orden pendiente con respecto al precio actual, y escribimos los métodos para esteblcer los nuevos precios de la orden en las propiedades del objeto de solicitud pendiente, así como los métodos para establecer/obtener la bandera de seguimiento tras el precio del punto de cálculo del precio de colocación de la orden:
+//+------------------------------------------------------------------+ //| Methods of a simplified access to the request object properties | +//+------------------------------------------------------------------+ //--- Return (1) request structure, (2) status, (3) type, (4) price at the moment of the request generation, //--- (5) request generation time, (6) next attempt activation time, //--- (7) waiting time between requests, (8) current attempt index, //--- (9) number of attempts, (10) request ID //--- (11) result a request is based on, //--- (12) order ticket, (13) position ticket, (14) trading operation type MqlTradeRequest MqlRequest(void) const { return this.m_request; } ENUM_PEND_REQ_STATUS Status(void) const { return (ENUM_PEND_REQ_STATUS)this.GetProperty(PEND_REQ_PROP_STATUS); } ENUM_PEND_REQ_TYPE TypeRequest(void) const { return (ENUM_PEND_REQ_TYPE)this.GetProperty(PEND_REQ_PROP_TYPE); } double PriceCreate(void) const { return this.GetProperty(PEND_REQ_PROP_PRICE_CREATE); } ulong TimeCreate(void) const { return this.GetProperty(PEND_REQ_PROP_TIME_CREATE); } ulong TimeActivate(void) const { return this.GetProperty(PEND_REQ_PROP_TIME_ACTIVATE); } ulong WaitingMSC(void) const { return this.GetProperty(PEND_REQ_PROP_WAITING); } uchar CurrentAttempt(void) const { return (uchar)this.GetProperty(PEND_REQ_PROP_CURRENT_ATTEMPT); } uchar TotalAttempts(void) const { return (uchar)this.GetProperty(PEND_REQ_PROP_TOTAL); } uchar ID(void) const { return (uchar)this.GetProperty(PEND_REQ_PROP_ID); } int Retcode(void) const { return (int)this.GetProperty(PEND_REQ_PROP_RETCODE); } ulong Order(void) const { return this.GetProperty(PEND_REQ_PROP_MQL_REQ_ORDER); } ulong Position(void) const { return this.GetProperty(PEND_REQ_PROP_MQL_REQ_POSITION); } ENUM_TRADE_REQUEST_ACTIONS Action(void) const { return (ENUM_TRADE_REQUEST_ACTIONS)this.GetProperty(PEND_REQ_PROP_MQL_REQ_ACTION); } //--- Return the actual (1) volume, (2) order, (3) limit order, //--- (4) stoploss order and (5) takeprofit order prices, (6) order filling type, //--- (7) order expiration type and (8) order lifetime double ActualVolume(void) const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_VOLUME); } double ActualPrice(void) const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_PRICE); } double ActualStopLimit(void) const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_STOPLIMIT); } double ActualSL(void) const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_SL); } double ActualTP(void) const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_TP); } ENUM_ORDER_TYPE_FILLING ActualTypeFilling(void) const { return (ENUM_ORDER_TYPE_FILLING)this.GetProperty(PEND_REQ_PROP_ACTUAL_TYPE_FILLING); } ENUM_ORDER_TYPE_TIME ActualTypeTime(void) const { return (ENUM_ORDER_TYPE_TIME)this.GetProperty(PEND_REQ_PROP_ACTUAL_TYPE_TIME); } datetime ActualExpiration(void) const { return (datetime)this.GetProperty(PEND_REQ_PROP_ACTUAL_EXPIRATION); } //--- Modify order prices by the current price void CorrectMqlPricesByCurrentPrice(const double price); //--- Set (1) the price when creating a request, (2) setting, (3) StopLoss, (4) TakeProfit, (5) stoplimit, //--- (6) request creation time, (7) current attempt time, (8) waiting time between requests, (9) current attempt index, //--- (10) number of attempts,(11) id, (12) order ticket, (13) position ticket, (14) pending request type void SetPriceCreate(const double price) { this.SetProperty(PEND_REQ_PROP_PRICE_CREATE,price); } void SetMqlPrice(const double price) { this.SetProperty(PEND_REQ_PROP_MQL_REQ_PRICE,price); this.m_request.price=price; } void SetMqlSL(const double sl) { this.SetProperty(PEND_REQ_PROP_MQL_REQ_SL,sl); this.m_request.sl=sl; } void SetMqlTP(const double tp) { this.SetProperty(PEND_REQ_PROP_MQL_REQ_TP,tp); this.m_request.tp=tp; } void SetMqlStopLimit(const double stoplimit) { this.SetProperty(PEND_REQ_PROP_MQL_REQ_STOPLIMIT,stoplimit); this.m_request.stoplimit=stoplimit; } void SetTimeCreate(const ulong time) { this.SetProperty(PEND_REQ_PROP_TIME_CREATE,time); this.m_pause.SetTimeBegin(time); } void SetTimeActivate(const ulong time) { this.SetProperty(PEND_REQ_PROP_TIME_ACTIVATE,time); } void SetWaitingMSC(const ulong miliseconds) { this.SetProperty(PEND_REQ_PROP_WAITING,miliseconds); this.m_pause.SetWaitingMSC(miliseconds); } void SetCurrentAttempt(const uchar number) { this.SetProperty(PEND_REQ_PROP_CURRENT_ATTEMPT,number); } void SetTotalAttempts(const uchar number) { this.SetProperty(PEND_REQ_PROP_TOTAL,number); } void SetID(const uchar id) { this.SetProperty(PEND_REQ_PROP_ID,id); } void SetOrder(const ulong ticket) { this.SetProperty(PEND_REQ_PROP_MQL_REQ_ORDER,ticket); } void SetPosition(const ulong ticket) { this.SetProperty(PEND_REQ_PROP_MQL_REQ_POSITION,ticket); } void SetTypeRequest(const ENUM_PEND_REQ_TYPE type) { this.SetProperty(PEND_REQ_PROP_TYPE,type); } //--- Set the actual (1) volume, (2) order, (3) limit order, //--- (4) stoploss order and (5) takeprofit order prices, (6) order filling type, //--- (7) order expiration type and (8) order lifetime void SetActualVolume(const double volume) { this.SetProperty(PEND_REQ_PROP_ACTUAL_VOLUME,volume); } void SetActualPrice(const double price) { this.SetProperty(PEND_REQ_PROP_ACTUAL_PRICE,price); } void SetActualStopLimit(const double price) { this.SetProperty(PEND_REQ_PROP_ACTUAL_STOPLIMIT,price); } void SetActualSL(const double price) { this.SetProperty(PEND_REQ_PROP_ACTUAL_SL,price); } void SetActualTP(const double price) { this.SetProperty(PEND_REQ_PROP_ACTUAL_TP,price); } void SetActualTypeFilling(const ENUM_ORDER_TYPE_FILLING type) { this.SetProperty(PEND_REQ_PROP_ACTUAL_TYPE_FILLING,type); } void SetActualTypeTime(const ENUM_ORDER_TYPE_TIME type) { this.SetProperty(PEND_REQ_PROP_ACTUAL_TYPE_TIME,type); } void SetActualExpiration(const datetime expiration) { this.SetProperty(PEND_REQ_PROP_ACTUAL_EXPIRATION,expiration); } //--- Set a controlled property and a comparison method for a request activation criteria data by its index - both the actual one and the one in the object of //--- account, symbol or trading event property value (depends on 'source' value) for activating a pending request void SetNewActivationProperties(const ENUM_PEND_REQ_ACTIVATION_SOURCE source, const int property, const double control_value, const ENUM_COMPARER_TYPE comparer_type, const double actual_value); //--- Set a (1) controlled property, (2) comparison type, (3) object value and //--- (4) actual controlled property value for activating a pending request bool SetActivationProperty(const uint index,const ENUM_PEND_REQ_ACTIVATION_SOURCE source,const int property); bool SetActivationComparerType(const uint index,const ENUM_COMPARER_TYPE comparer_type); bool SetActivationControlValue(const uint index,const double value); bool SetActivationActualValue(const uint index,const double value); //--- Return (1) a pending request activation source, (2) controlled property, (3) comparison type, //--- (4) object value,(5) actual controlled property value for activating a pending request ENUM_PEND_REQ_ACTIVATION_SOURCE GetActivationSource(const uint index) const; int GetActivationProperty(const uint index) const; ENUM_COMPARER_TYPE GetActivationComparerType(const uint index) const; double GetActivationControlValue(const uint index) const; double GetActivationActualValue(const uint index) const; //--- Return the flag of a successful check of all controlled object properties and the appropriate actual properties bool IsAllComparisonCompleted(void) const; //--- Return/set the flag of the pending order distance reference point following the price bool IsFollowThePrice(void) const { return this.m_follow; } void SetFollowThePrice(const bool flag) { this.m_follow=flag; } +//+------------------------------------------------------------------+ //| Descriptions of request object properties | +//+------------------------------------------------------------------+
En el constructor de la clase, establecemos la bandera de segumiento tras el precio del punto de cálculo del precio de colocación de la orden:
+//+------------------------------------------------------------------+ //| Constructor | +//+------------------------------------------------------------------+ CPendRequest::CPendRequest(const ENUM_PEND_REQ_STATUS status, const uchar id, const double price, const ulong time, const MqlTradeRequest &request, const int retcode) { this.CopyRequest(request); this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif; this.m_digits=(int)::SymbolInfoInteger(this.GetProperty(PEND_REQ_PROP_MQL_REQ_SYMBOL),SYMBOL_DIGITS); int dg=(int)DigitsLots(this.GetProperty(PEND_REQ_PROP_MQL_REQ_SYMBOL)); this.m_digits_lot=(dg==0 ? 1 : dg); this.SetProperty(PEND_REQ_PROP_STATUS,status); this.SetProperty(PEND_REQ_PROP_ID,id); this.SetProperty(PEND_REQ_PROP_RETCODE,retcode); this.SetProperty(PEND_REQ_PROP_TYPE,this.GetProperty(PEND_REQ_PROP_RETCODE)>0 ? PEND_REQ_TYPE_ERROR : PEND_REQ_TYPE_REQUEST); this.SetProperty(PEND_REQ_PROP_TIME_CREATE,time); this.SetProperty(PEND_REQ_PROP_PRICE_CREATE,price); this.m_pause.SetTimeBegin(this.GetProperty(PEND_REQ_PROP_TIME_CREATE)); this.m_pause.SetWaitingMSC(this.GetProperty(PEND_REQ_PROP_WAITING)); ::ArrayResize(this.m_activated_control,0,10); this.m_follow=true; } +//+------------------------------------------------------------------+
Implementamos fuera del cuerpo de la clase el método para asignar los nuevos valores a todos los precios de la orden:
+//+------------------------------------------------------------------+ //| Set a new value changed by the shift (+/-), | //| for all order prices (+/-) | +//+------------------------------------------------------------------+ void CPendRequest::SetAllMqlPrices(const double shift) { this.SetMqlPrice(this.GetProperty(PEND_REQ_PROP_MQL_REQ_PRICE)-shift); if(this.GetProperty(PEND_REQ_PROP_MQL_REQ_SL)!=0) this.SetMqlSL(this.GetProperty(PEND_REQ_PROP_MQL_REQ_SL)-shift); if(this.GetProperty(PEND_REQ_PROP_MQL_REQ_TP)!=0) this.SetMqlTP(this.GetProperty(PEND_REQ_PROP_MQL_REQ_TP)-shift); if(this.GetProperty(PEND_REQ_PROP_MQL_REQ_STOPLIMIT)!=0) this.SetMqlStopLimit(this.GetProperty(PEND_REQ_PROP_MQL_REQ_STOPLIMIT)-shift); } +//+------------------------------------------------------------------+
Transmitimos al método el desplazamiento en valor de precio, y a continuación, con la ayuda de los métodos descritos anteriormente, establecemos los nuevos precios en cada propiedad del objeto de solicitud pendiente que se corresponde con los tipos de los precios de la orden pendiente, calculados como (valor actual de la propiedad menos magnitud del desplazamiento).
Comprobamos preliminarmente la existencia de este precio para los precios StopLoss de la orden, TakeProfit de la orden y StopLimit de la orden, y solo establecemos el desplazamiento si el precio anotado en las propiedades del objeto de solicitud pendiente tiene un valor cero.
Implementación del método para corregir los precios de una orden pendiente colocada según el valor de precio actual en el momento de la activación de la solicitud pendiente:
+//+------------------------------------------------------------------+ //| Adjust order prices by the current price | +//+------------------------------------------------------------------+ void CPendRequest::CorrectMqlPricesByCurrentPrice(const double price) { ENUM_ORDER_TYPE type=this.m_request.type; if(!this.m_follow || (type<ORDER_TYPE_BUY_LIMIT && type>ORDER_TYPE_SELL_STOP_LIMIT)) return; this.SetAllMqlPrices(this.PriceCreate()-price); } +//+------------------------------------------------------------------+
Transmitimos al método el precio actual respecto al cual debemos colocar la orden pendiente. Si no se ha establecido la bandera de recálculo del nivel para la colocación de una orden pendiente respecto al precio actual, o en la estructura de la solicitud comercial del objeto de solicitud pendiente no ha sido añadida una orden pendiente, salimos del método.
A continuación, llamamos al método de cambio de todos los precios de la orden pendiente (analizado anteriormente), al que transmitimos la magnitud del desplazamiento calculada como el precio al crear el objeto de solicitud pendiente menos el precio actual transmitido al método.
Ahora, introducimos las adiciones y correcciones en el archivo PendReqControl.mqh de la clase de control del comercio CPendReqControl.
Asimismo, renombramos los métodos públicos de creación de solicitudes pendientes OpenPositionPending() y PlaceOrderPending() como CreatePReqPosition() y CreatePReqOrder(), respectivamente. A nuestro parecer, estos nombres reflejan mejor la esencia de dichos métodos, es decir, la creación de una solicitud pendiente.
En los parámetros de entrada del método CreatePReqOrder(), añadimos la transmisión de los identificadores de los grupos:
//--- (1) Create a pending request (1) to open a position, (2) to place a pending order template<typename SL,typename TP> int CreatePReqPosition(const ENUM_POSITION_TYPE type, const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); template<typename PS,typename PL,typename SL,typename TP> int CreatePReqOrder(const ENUM_ORDER_TYPE order_type, const double volume, const string symbol, const PS price_set, const PL price_limit=0, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); //--- Set pending request activation criteria
Introducimos las adiciones en el manejador de solicitudes pendientes creadas según la solicitud:
+//+------------------------------------------------------------------+ //| The handler of pending requests created by request | +//+------------------------------------------------------------------+ void CTradingControl::OnPReqByRequestHandler(CPendRequest *req_obj,const int index) { //--- get the request structure and the symbol object a trading operation should be performed for MqlTradeRequest request=req_obj.MqlRequest(); CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(request.symbol); if(symbol_obj==NULL || !symbol_obj.RefreshRates()) return; //--- Check the relevance of a pending request and exit to the external loop if the request is handled or an error occurs if(!this.CheckPReqRelevance(req_obj,request,index)) return; //--- Update relevant data on request activation conditions this.RefreshControlActualDatas(req_obj,symbol_obj); //--- If all pending request activation conditions are met if(req_obj.IsAllComparisonCompleted()) { //--- Set the attempt number in the request object req_obj.SetCurrentAttempt(uchar(req_obj.CurrentAttempt()+1)); //--- Adjust prices for a pending order relative to the current price and get the request again if(request.action==TRADE_ACTION_PENDING) { req_obj.CorrectMqlPricesByCurrentPrice(PositionTypeByOrderType(request.type)==POSITION_TYPE_BUY ? symbol_obj.AskLast() : symbol_obj.BidLast()); request=req_obj.MqlRequest(); } //--- Display the request activation message in the journal if(this.m_log_level>LOG_LEVEL_NO_MSG) { ::Print(CMessage::Text(MSG_LIB_TEXT_REQUEST_ACTIVATED)+(string)req_obj.ID()+":"); req_obj.PrintShort(); } //--- Depending on the type of action performed in the trading request switch(request.action) { //--- Opening/closing a position case TRADE_ACTION_DEAL : //--- If no ticket is present in the request structure - this is opening a position if(request.position==0) this.OpenPosition((ENUM_POSITION_TYPE)request.type,request.volume,request.symbol,request.magic,request.sl,request.tp,request.comment,request.deviation,request.type_filling); //--- If the ticket is present in the request structure - this is a position closure else this.ClosePosition(request.position,request.volume,request.comment,request.deviation); break; //--- Modify StopLoss/TakeProfit position case TRADE_ACTION_SLTP : this.ModifyPosition(request.position,request.sl,request.tp); break; //--- Close by an opposite one case TRADE_ACTION_CLOSE_BY : this.ClosePositionBy(request.position,request.position_by); break; //--- //--- Place a pending order case TRADE_ACTION_PENDING : this.PlaceOrder(request.type,request.volume,request.symbol,request.price,request.stoplimit,request.sl,request.tp,request.magic,request.comment,request.expiration,request.type_time,request.type_filling); break; //--- Modify a pending order case TRADE_ACTION_MODIFY : this.ModifyOrder(request.order,request.price,request.sl,request.tp,request.stoplimit,request.expiration,request.type_time,request.type_filling); break; //--- Remove a pending order case TRADE_ACTION_REMOVE : this.DeleteOrder(request.order); break; //--- default: break; } } } +//+------------------------------------------------------------------+
Aquí: si el tipo de operación comercial escrito en la estructura de la solicitud comercial del objeto de solicitud pendiente es "colocar una orden pendiente", llamamos al método de corrección de los precios de la orden pondiente escritos en las propiedades del objeto de solicitud pendiente. Como resultado, los precios de la orden pendiente en el objeto de solicitud serán o bien corregidos respecto al precio actual, o bien no serán corregidos en absoluto: todo dependerá del estado de la bandera de recálculo del nivel para colocar una orden pendiente respecto al precio actual en el objeto de solicitud pendiente. Ya hemos analizado este comportamiento más arriba.
Vamos a corregir ligeramente el método de creación de una solicitud pendiente para la apertura de una posición. Al crear dicho método mediante copia-pega, hemos cometido un leve error: el método debe retornar el valor de tipo entero del identificador de la solicitud pendiente, y en este momento, retorna false en las situaciones erróneas. Vamos a corregirlo por WRONG_VALUE:
+//+------------------------------------------------------------------+ //| Create a pending request for opening a position | +//+------------------------------------------------------------------+ template<typename SL,typename TP> int CTradingControl::CreatePReqPosition(const ENUM_POSITION_TYPE type, const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const ulong deviation=ULONG_MAX, 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_ORDER_TYPE order_type=(ENUM_ORDER_TYPE)type; ENUM_ACTION_TYPE action=(ENUM_ACTION_TYPE)order_type; //--- Get a symbol object by a symbol name. CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(symbol); //--- If failed to get - write the "internal error" flag, display the message in the journal and return WRONG_VALUE 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 failed to get - write the "internal error" flag, display the message in the journal and return WRONG_VALUE 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 WRONG_VALUE if(!this.SetPrices(order_type,0,sl,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 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; } //--- Write the volume, deviation, comment and filling type to the request structure this.m_request.volume=volume; this.m_request.deviation=(deviation==ULONG_MAX ? trade_obj.GetDeviation() : deviation); this.m_request.comment=(comment==NULL ? trade_obj.GetComment() : comment); this.m_request.type_filling=(type_filling>WRONG_VALUE ? type_filling : trade_obj.GetTypeFilling()); //--- Write pending request object ID to the magic number, add group IDs to the magic number value //--- and fill in the remaining unfilled trading request structure fields uint mn=(magic==ULONG_MAX ? (uint)trade_obj.GetMagic() : (uint)magic); this.SetPendReqID((uchar)id,mn); if(group_id1>0) this.SetGroupID1(group_id1,mn); if(group_id2>0) this.SetGroupID2(group_id2,mn); this.m_request.magic=mn; this.m_request.action=TRADE_ACTION_DEAL; this.m_request.symbol=symbol_obj.Name(); 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_OPEN,(uchar)id,1,ulong(END_TIME-(ulong)::TimeCurrent()),this.m_request,0,symbol_obj,NULL)) return id; return WRONG_VALUE; } +//+------------------------------------------------------------------+
Vamos a escribir la implementación del método de creación de una solicitud pendiente para la colocación de una orden pendiente:
+//+------------------------------------------------------------------+ //| Create a pending request to place a pending order | +//+------------------------------------------------------------------+ template<typename PS,typename PL,typename SL,typename TP> int CTradingControl::CreatePReqOrder(const ENUM_ORDER_TYPE order_type, const double volume, const string symbol, const PS price_set, const PL price_limit=0, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const datetime expiration=0, 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=(ENUM_ACTION_TYPE)order_type; //--- Get a symbol object by a symbol name CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(symbol); 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 WRONG_VALUE if(!this.SetPrices(order_type,price_set,sl,tp,price_limit,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 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; } //--- Write the volume, comment, as well as expiration and filling types to the request structure this.m_request.volume=volume; this.m_request.comment=(comment==NULL ? trade_obj.GetComment() : comment); this.m_request.type_time=(type_time>WRONG_VALUE ? type_time : trade_obj.GetTypeExpiration()); this.m_request.type_filling=(type_filling>WRONG_VALUE ? type_filling : trade_obj.GetTypeFilling()); //--- Write the request ID to the magic number, while a symbol name is set in the request structure, //--- trading operation and order types uint mn=(magic==ULONG_MAX ? (uint)trade_obj.GetMagic() : (uint)magic); this.SetPendReqID((uchar)id,mn); if(group_id1>0) this.SetGroupID1(group_id1,mn); if(group_id2>0) this.SetGroupID2(group_id2,mn); this.m_request.magic=mn; this.m_request.symbol=symbol_obj.Name(); this.m_request.action=TRADE_ACTION_PENDING; 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_PLACE,(uchar)id,1,ulong(END_TIME-(ulong)::TimeCurrent()),this.m_request,0,symbol_obj,NULL)) return id; return WRONG_VALUE; } +//+------------------------------------------------------------------+
El método se describe detalladamente en los comentarios; ya hemos visto antes un método similar de creación de una solicitud pendiente para la apertura de una posición, por eso, dejaremos que el lector estudie el código del método por sí mismo. En cualquier caso, el lector podrá escribir cualquier duda en los comentarios al artículo.
Al crear una solicitud pendiente, necesitamos anotar en el objeto de solicitud pendiente el precio en el momento de su creación. Para los diferentes tipos de órdenes, necesitaremos escribir precios distintos: para las órdenes de compra, el precio Ask actual, y para la órdenes de venta, el precio Bid actual.
Para ello, introduciremos las correcciones pertinentes en el método de creación de solicitudes pendientes CreatePendingRequest(), en el archivo Trading.mqh de la clase del objeto comercial principal CTrading:
+//+------------------------------------------------------------------+ //| Create a pending request | +//+------------------------------------------------------------------+ bool CTrading::CreatePendingRequest(const ENUM_PEND_REQ_STATUS status, const uchar id, const uchar attempts, const ulong wait, const MqlTradeRequest &request, const int retcode, CSymbol *symbol_obj, COrder *order) { //--- Create a new pending request object depending on a request status CPendRequest *req_obj=NULL; double price=(PositionTypeByOrderType(request.type)==POSITION_TYPE_BUY ? symbol_obj.AskLast() : symbol_obj.BidLast()); switch(status) { case PEND_REQ_STATUS_OPEN : req_obj=new CPendReqOpen(id,price,symbol_obj.Time(),request,retcode); break; case PEND_REQ_STATUS_CLOSE : req_obj=new CPendReqClose(id,price,symbol_obj.Time(),request,retcode); break; case PEND_REQ_STATUS_SLTP : req_obj=new CPendReqSLTP(id,price,symbol_obj.Time(),request,retcode); break; case PEND_REQ_STATUS_PLACE : req_obj=new CPendReqPlace(id,price,symbol_obj.Time(),request,retcode); break; case PEND_REQ_STATUS_REMOVE : req_obj=new CPendReqRemove(id,price,symbol_obj.Time(),request,retcode); break; case PEND_REQ_STATUS_MODIFY : req_obj=new CPendReqModify(id,price,symbol_obj.Time(),request,retcode); break; default: req_obj=NULL; break; } if(req_obj==NULL) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_FAILING_CREATE_PENDING_REQ)); return false; } //--- If failed to add the request to the list, display the appropriate message, //--- remove the created object and return 'false' if(!this.m_list_request.Add(req_obj)) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_FAILING_CREATE_PENDING_REQ)); delete req_obj; return false; } //--- Fill in the properties of a successfully created object by the values passed to the method req_obj.SetTimeActivate(symbol_obj.Time()+wait); req_obj.SetWaitingMSC(wait); req_obj.SetCurrentAttempt(0); req_obj.SetTotalAttempts(attempts); if(order!=NULL) { req_obj.SetActualVolume(order.Volume()); req_obj.SetActualPrice(order.PriceOpen()); req_obj.SetActualStopLimit(order.PriceStopLimit()); req_obj.SetActualSL(order.StopLoss()); req_obj.SetActualTP(order.TakeProfit()); req_obj.SetActualTypeFilling(order.TypeFilling()); req_obj.SetActualTypeTime(order.TypeTime()); req_obj.SetActualExpiration(order.TimeExpiration()); } else { req_obj.SetActualVolume(request.volume); req_obj.SetActualPrice(request.price); req_obj.SetActualStopLimit(request.stoplimit); req_obj.SetActualSL(request.sl); req_obj.SetActualTP(request.tp); req_obj.SetActualTypeFilling(request.type_filling); req_obj.SetActualTypeTime(request.type_time); req_obj.SetActualExpiration(request.expiration); } //--- Display a brief description of a created pending request if(this.m_log_level>LOG_LEVEL_NO_MSG) { ::Print(CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_CREATED)," #",req_obj.ID(),":"); req_obj.PrintShort(); } //--- successful return true; } +//+------------------------------------------------------------------+
Aquí, con la ayuda de la función de determinación del tipo de posición según el tipo de orden PositionTypeByOrderType(), averiguamos la dirección de la orden, y si es de compra, usamos el precio Ask, mientras que si es de venta, usamos el precio Bid. A continuación, al crear una solicitud pendiente, transmitimos a su método de creación el precio obtenido.
Ahora, solo nos queda implementar el acceso a la funcionalidad creada. En el objeto principal de la biblioteca CEngine, en su sección pública, declaramos los métodos para crear solicitudes pendientes de colocación de todos los tipos de órdenes:
//--- Create a pending request (1) to open Buy and (2) Sell positions template<typename SL,typename TP> int OpenBuyPending(const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); template<typename SL,typename TP> int OpenSellPending(const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); //--- Create a pending request to place a (1) BuyLimit, (2) BuyStop and (3) BuyStopLimit order template<typename PS,typename SL,typename TP> int PlaceBuyLimitPending(const double volume, const string symbol, const PS price_set, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); template<typename PS,typename SL,typename TP> int PlaceBuyStopPending( const double volume, const string symbol, const PS price_set, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); template<typename PS,typename PL,typename SL,typename TP> int PlaceBuyStopLimitPending(const double volume, const string symbol, const PS price_stop, const PL price_limit, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); //--- Create a pending request to place a (1) SellLimit, (2) SellStop, (3) SellStopLimit order template<typename PS,typename SL,typename TP> int PlaceSellLimitPending(const double volume, const string symbol, const PS price_set, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); template<typename PS,typename SL,typename TP> int PlaceSellStopPending(const double volume, const string symbol, const PS price_set, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); template<typename PS,typename PL,typename SL,typename TP> int PlaceSellStopLimitPending(const double volume, const string symbol, const PS price_stop, const PL price_limit, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const datetime expiration=0, 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);
Asimismo, fuera del cuerpo de clase, escribimos la implementación de todos estos métodos, cambiando a la vez a la nueva denominación dentro de los métodos de creación de solicitudes pendientes de apertura de posiciones (ya los habíamos renombrado antes con nombres más adecuados):
+//+------------------------------------------------------------------+ //| Create a pending request for opening a Buy position | +//+------------------------------------------------------------------+ template<typename SL,typename TP> int CEngine::OpenBuyPending(const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.CreatePReqPosition(POSITION_TYPE_BUY,volume,symbol,magic,sl,tp,group_id1,group_id2,comment,deviation,type_filling); } +//+------------------------------------------------------------------+ //| Create a pending request for opening a Sell position | +//+------------------------------------------------------------------+ template<typename SL,typename TP> int CEngine::OpenSellPending(const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.CreatePReqPosition(POSITION_TYPE_SELL,volume,symbol,magic,sl,tp,group_id1,group_id2,comment,deviation,type_filling); } +//+------------------------------------------------------------------+ //| Create a pending request to place a BuyLimit order | +//+------------------------------------------------------------------+ template<typename PS,typename SL,typename TP> int CEngine::PlaceBuyLimitPending(const double volume, const string symbol, const PS price_set, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.CreatePReqOrder(ORDER_TYPE_BUY_LIMIT,volume,symbol,price_set,0,sl,tp,magic,group_id1,group_id2,comment,expiration,type_time,type_filling); } +//+------------------------------------------------------------------+ //| Create a pending request to place a BuyStop order | +//+------------------------------------------------------------------+ template<typename PS,typename SL,typename TP> int CEngine::PlaceBuyStopPending(const double volume, const string symbol, const PS price_set, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.CreatePReqOrder(ORDER_TYPE_BUY_STOP,volume,symbol,price_set,0,sl,tp,magic,group_id1,group_id2,comment,expiration,type_time,type_filling); } +//+------------------------------------------------------------------+ //| Create a pending request to place a BuyStopLimit order | +//+------------------------------------------------------------------+ template<typename PS,typename PL,typename SL,typename TP> int CEngine::PlaceBuyStopLimitPending(const double volume, const string symbol, const PS price_stop, const PL price_limit, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return ( #ifdef __MQL4__ WRONG_VALUE #else this.m_trading.CreatePReqOrder(ORDER_TYPE_BUY_STOP_LIMIT,volume,symbol,price_stop,price_limit,sl,tp,magic,group_id1,group_id2,comment,expiration,type_time,type_filling); #endif ); } +//+------------------------------------------------------------------+ //| Create a pending request to place a SellLimit order | +//+------------------------------------------------------------------+ template<typename PS,typename SL,typename TP> int CEngine::PlaceSellLimitPending(const double volume, const string symbol, const PS price_set, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.CreatePReqOrder(ORDER_TYPE_SELL_LIMIT,volume,symbol,price_set,0,sl,tp,magic,group_id1,group_id2,comment,expiration,type_time,type_filling); } +//+------------------------------------------------------------------+ //| Create a pending request to place a SellStop order | +//+------------------------------------------------------------------+ template<typename PS,typename SL,typename TP> int CEngine::PlaceSellStopPending(const double volume, const string symbol, const PS price_set, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.CreatePReqOrder(ORDER_TYPE_SELL_STOP,volume,symbol,price_set,0,sl,tp,magic,group_id1,group_id2,comment,expiration,type_time,type_filling); } +//+------------------------------------------------------------------+ //| Create a pending request to place a SellStopLimit order | +//+------------------------------------------------------------------+ template<typename PS,typename PL,typename SL,typename TP> int CEngine::PlaceSellStopLimitPending(const double volume, const string symbol, const PS price_stop, const PL price_limit, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return ( #ifdef __MQL4__ WRONG_VALUE #else this.m_trading.CreatePReqOrder(ORDER_TYPE_SELL_STOP_LIMIT,volume,symbol,price_stop,price_limit,sl,tp,magic,group_id1,group_id2,comment,expiration,type_time,type_filling) #endif ); } +//+------------------------------------------------------------------+
Aquí, los métodos de creación de solicitudes pendientes para la colocación de órdenes pendientes retornan el resultado del funcionamiento del método de creación de solicitudes pendientes de la clase de control del comercio CTradingControl, al que se transmiten los tipos requeridos de orden pendiente correspondientes al método desde el que se realiza la creación de la solicitud pendiente. Para MQL4, retornamos WRONG_VALUE, por el momento, no hemos implementado la clase del objeto de orden StopLimit pendiente para MQL4.
Estos son todos los cambios necesarios para colocar órdenes pendientes según condiciones usando solicitudes comerciales pendientes.
Simulación
Para la simulación, vamos a tomar el asesor del artículo anterior y guardarlo en la nueva carpeta \MQL5\Experts\TestDoEasy\ Part32\ con el nuevo nombre TestDoEasyPart32.mq5.
Todos lo que necesitaremos añadirle es el control de los estados de los botones encargados de activar el trabajo con solicitudes pendientes para los botones correspondientes de colocación de órdenes pendientes. Si uno de los botones " P" o "T" (condición según precio o según tiempo) ubicados junto al botón de colocación de una orden pendiente está pulsado, esta orden no será colocada de inmediato, sino que se creará una solicitud pendiente cuya activación según la condición indicada provocará la colocación de una orden pendiente. La orden se colocará con respecto al precio al que se ha activado de la solicitud pendiente.
Añadimos a la función de procesamiento de la pulsación de los botones del panel comercial del asesor de prueba dos variables para guardar los valores Point() y Digits() del símbolo actual, y también añadimos el procesamiento de la pulsación de los botones del panel comercial de creación de solicitudes pendientes para colocar todos los tipos de órdenes pendientes:
+//+------------------------------------------------------------------+ //| 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=""; double point=SymbolInfoDouble(NULL,SYMBOL_POINT); int digits=(int)SymbolInfoInteger(NULL,SYMBOL_DIGITS); //--- 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(!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) { //--- If the price criterion is selected if(ButtonState(prefix+EnumToString(BUTT_BUY)+"_PRICE")) { double ask=SymbolInfoDouble(NULL,SYMBOL_ASK); double control_value=NormalizeDouble(ask-distance_pending_request*SymbolInfoDouble(NULL,SYMBOL_POINT),(int)SymbolInfoInteger(NULL,SYMBOL_DIGITS)); engine.SetNewActivationProperties((uchar)id,PEND_REQ_ACTIVATION_SOURCE_SYMBOL,PEND_REQ_ACTIVATE_BY_SYMBOL_ASK,control_value,EQUAL_OR_LESS,ask); } //--- If the time criterion is selected if(ButtonState(prefix+EnumToString(BUTT_BUY)+"_TIME")) { ulong control_time=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); engine.SetNewActivationProperties((uchar)id,PEND_REQ_ACTIVATION_SOURCE_SYMBOL,PEND_REQ_ACTIVATE_BY_SYMBOL_TIME,control_time,EQUAL_OR_MORE,TimeCurrent()); } } CPendRequest *req_obj=engine.GetPendRequestByID((uchar)id); if(req_obj==NULL) return; if(engine.TradingGetLogLevel(Symbol())>LOG_LEVEL_NO_MSG) { ::Print(CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ADD_CRITERIONS)," #",req_obj.ID(),":"); req_obj.PrintActivations(); } } } //--- 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(!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 { double ask=SymbolInfoDouble(NULL,SYMBOL_ASK); int id=engine.PlaceBuyLimitPending(lot,Symbol(),distance_pending,stoploss,takeprofit,magic); if(id>0) { //--- If the price criterion is selected if(ButtonState(prefix+EnumToString(BUTT_BUY_LIMIT)+"_PRICE")) { //--- set the pending request activation price double price_act=NormalizeDouble(ask-distance_pending_request*point,digits); engine.SetNewActivationProperties((uchar)id,PEND_REQ_ACTIVATION_SOURCE_SYMBOL,PEND_REQ_ACTIVATE_BY_SYMBOL_ASK,price_act,EQUAL_OR_LESS,ask); } //--- If the time criterion is selected if(ButtonState(prefix+EnumToString(BUTT_BUY_LIMIT)+"_TIME")) { //--- set the pending request activation time ulong control_time=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); engine.SetNewActivationProperties((uchar)id,PEND_REQ_ACTIVATION_SOURCE_SYMBOL,PEND_REQ_ACTIVATE_BY_SYMBOL_TIME,control_time,EQUAL_OR_MORE,TimeCurrent()); } //--- Get a newly created pending request by ID and display the message about adding the conditions to the journal CPendRequest *req_obj=engine.GetPendRequestByID((uchar)id); if(req_obj==NULL) return; if(engine.TradingGetLogLevel(Symbol())>LOG_LEVEL_NO_MSG) { ::Print(CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ADD_CRITERIONS)," #",req_obj.ID(),":"); req_obj.PrintActivations(); } } } } //--- 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(!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 { double ask=SymbolInfoDouble(NULL,SYMBOL_ASK); int id=engine.PlaceBuyStopPending(lot,Symbol(),distance_pending,stoploss,takeprofit,magic); if(id>0) { //--- If the price criterion is selected if(ButtonState(prefix+EnumToString(BUTT_BUY_STOP)+"_PRICE")) { //--- set the pending request activation price double price_act=NormalizeDouble(ask-distance_pending_request*point,digits); engine.SetNewActivationProperties((uchar)id,PEND_REQ_ACTIVATION_SOURCE_SYMBOL,PEND_REQ_ACTIVATE_BY_SYMBOL_ASK,price_act,EQUAL_OR_LESS,ask); } //--- If the time criterion is selected if(ButtonState(prefix+EnumToString(BUTT_BUY_STOP)+"_TIME")) { //--- set the pending request activation time ulong control_time=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); engine.SetNewActivationProperties((uchar)id,PEND_REQ_ACTIVATION_SOURCE_SYMBOL,PEND_REQ_ACTIVATE_BY_SYMBOL_TIME,control_time,EQUAL_OR_MORE,TimeCurrent()); } //--- Get a newly created pending request by ID and display the message about adding the conditions to the journal CPendRequest *req_obj=engine.GetPendRequestByID((uchar)id); if(req_obj==NULL) return; if(engine.TradingGetLogLevel(Symbol())>LOG_LEVEL_NO_MSG) { ::Print(CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ADD_CRITERIONS)," #",req_obj.ID(),":"); req_obj.PrintActivations(); } } } } //--- 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(!pending_buy_stoplimit) engine.PlaceBuyStopLimit(lot,Symbol(),distance_pending,distance_stoplimit,stoploss,takeprofit,magic,TextByLanguage("Отложенный BuyStopLimit","Pending order BuyStopLimit")); //--- Otherwise, create a pending request to place a BuyStopLimit order with the placement distances //--- and set the conditions depending on active buttons else { double ask=SymbolInfoDouble(NULL,SYMBOL_ASK); int id=engine.PlaceBuyStopLimitPending(lot,Symbol(),distance_pending,distance_stoplimit,stoploss,takeprofit,magic); if(id>0) { //--- If the price criterion is selected if(ButtonState(prefix+EnumToString(BUTT_BUY_STOP_LIMIT)+"_PRICE")) { //--- set the pending request activation price double price_act=NormalizeDouble(ask-distance_pending_request*point,digits); engine.SetNewActivationProperties((uchar)id,PEND_REQ_ACTIVATION_SOURCE_SYMBOL,PEND_REQ_ACTIVATE_BY_SYMBOL_ASK,price_act,EQUAL_OR_LESS,ask); } //--- If the time criterion is selected if(ButtonState(prefix+EnumToString(BUTT_BUY_STOP_LIMIT)+"_TIME")) { //--- set the pending request activation time ulong control_time=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); engine.SetNewActivationProperties((uchar)id,PEND_REQ_ACTIVATION_SOURCE_SYMBOL,PEND_REQ_ACTIVATE_BY_SYMBOL_TIME,control_time,EQUAL_OR_MORE,TimeCurrent()); } //--- Get a newly created pending request by ID and display the message about adding the conditions to the journal CPendRequest *req_obj=engine.GetPendRequestByID((uchar)id); if(req_obj==NULL) return; if(engine.TradingGetLogLevel(Symbol())>LOG_LEVEL_NO_MSG) { ::Print(CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ADD_CRITERIONS)," #",req_obj.ID(),":"); req_obj.PrintActivations(); } } } } //--- 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(!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) { //--- If the price criterion is selected if(ButtonState(prefix+EnumToString(BUTT_SELL)+"_PRICE")) { double bid=SymbolInfoDouble(NULL,SYMBOL_BID); double control_value=NormalizeDouble(bid+distance_pending_request*SymbolInfoDouble(NULL,SYMBOL_POINT),(int)SymbolInfoInteger(NULL,SYMBOL_DIGITS)); engine.SetNewActivationProperties((uchar)id,PEND_REQ_ACTIVATION_SOURCE_SYMBOL,PEND_REQ_ACTIVATE_BY_SYMBOL_BID,control_value,EQUAL_OR_MORE,bid); } //--- If the time criterion is selected if(ButtonState(prefix+EnumToString(BUTT_SELL)+"_TIME")) { ulong control_time=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); engine.SetNewActivationProperties((uchar)id,PEND_REQ_ACTIVATION_SOURCE_SYMBOL,PEND_REQ_ACTIVATE_BY_SYMBOL_TIME,control_time,EQUAL_OR_MORE,TimeCurrent()); } } CPendRequest *req_obj=engine.GetPendRequestByID((uchar)id); if(req_obj==NULL) return; if(engine.TradingGetLogLevel(Symbol())>LOG_LEVEL_NO_MSG) { ::Print(CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ADD_CRITERIONS)," #",req_obj.ID(),":"); req_obj.PrintActivations(); } } } //--- 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(!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 { double bid=SymbolInfoDouble(NULL,SYMBOL_BID); int id=engine.PlaceSellLimitPending(lot,Symbol(),distance_pending,stoploss,takeprofit,magic); if(id>0) { //--- If the price criterion is selected if(ButtonState(prefix+EnumToString(BUTT_SELL_LIMIT)+"_PRICE")) { //--- set the pending request activation price double price_act=NormalizeDouble(bid+distance_pending_request*point,digits); engine.SetNewActivationProperties((uchar)id,PEND_REQ_ACTIVATION_SOURCE_SYMBOL,PEND_REQ_ACTIVATE_BY_SYMBOL_BID,price_act,EQUAL_OR_MORE,bid); } //--- If the time criterion is selected if(ButtonState(prefix+EnumToString(BUTT_SELL_LIMIT)+"_TIME")) { //--- set the pending request activation time ulong control_time=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); engine.SetNewActivationProperties((uchar)id,PEND_REQ_ACTIVATION_SOURCE_SYMBOL,PEND_REQ_ACTIVATE_BY_SYMBOL_TIME,control_time,EQUAL_OR_MORE,TimeCurrent()); } //--- Get a newly created pending request by ID and display the message about adding the conditions to the journal CPendRequest *req_obj=engine.GetPendRequestByID((uchar)id); if(req_obj==NULL) return; if(engine.TradingGetLogLevel(Symbol())>LOG_LEVEL_NO_MSG) { ::Print(CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ADD_CRITERIONS)," #",req_obj.ID(),":"); req_obj.PrintActivations(); } } } } //--- 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(!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 { double bid=SymbolInfoDouble(NULL,SYMBOL_BID); int id=engine.PlaceSellStopPending(lot,Symbol(),distance_pending,stoploss,takeprofit,magic); if(id>0) { //--- If the price criterion is selected if(ButtonState(prefix+EnumToString(BUTT_SELL_STOP)+"_PRICE")) { //--- set the pending request activation price double price_act=NormalizeDouble(bid+distance_pending_request*point,digits); engine.SetNewActivationProperties((uchar)id,PEND_REQ_ACTIVATION_SOURCE_SYMBOL,PEND_REQ_ACTIVATE_BY_SYMBOL_BID,price_act,EQUAL_OR_MORE,bid); } //--- If the time criterion is selected if(ButtonState(prefix+EnumToString(BUTT_SELL_STOP)+"_TIME")) { //--- set the pending request activation time ulong control_time=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); engine.SetNewActivationProperties((uchar)id,PEND_REQ_ACTIVATION_SOURCE_SYMBOL,PEND_REQ_ACTIVATE_BY_SYMBOL_TIME,control_time,EQUAL_OR_MORE,TimeCurrent()); } //--- Get a newly created pending request by ID and display the message about adding the conditions to the journal CPendRequest *req_obj=engine.GetPendRequestByID((uchar)id); if(req_obj==NULL) return; if(engine.TradingGetLogLevel(Symbol())>LOG_LEVEL_NO_MSG) { ::Print(CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ADD_CRITERIONS)," #",req_obj.ID(),":"); req_obj.PrintActivations(); } } } } //--- 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(!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 { double bid=SymbolInfoDouble(NULL,SYMBOL_BID); int id=engine.PlaceSellStopLimitPending(lot,Symbol(),distance_pending,distance_stoplimit,stoploss,takeprofit,magic); if(id>0) { //--- If the price criterion is selected if(ButtonState(prefix+EnumToString(BUTT_SELL_STOP_LIMIT)+"_PRICE")) { //--- set the pending request activation price double price_act=NormalizeDouble(bid+distance_pending_request*point,digits); engine.SetNewActivationProperties((uchar)id,PEND_REQ_ACTIVATION_SOURCE_SYMBOL,PEND_REQ_ACTIVATE_BY_SYMBOL_BID,price_act,EQUAL_OR_MORE,bid); } //--- If the time criterion is selected if(ButtonState(prefix+EnumToString(BUTT_SELL_STOP_LIMIT)+"_TIME")) { //--- set the pending request activation time ulong control_time=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); engine.SetNewActivationProperties((uchar)id,PEND_REQ_ACTIVATION_SOURCE_SYMBOL,PEND_REQ_ACTIVATE_BY_SYMBOL_TIME,control_time,EQUAL_OR_MORE,TimeCurrent()); } //--- Get a newly created pending request by ID and display the message about adding the conditions to the journal CPendRequest *req_obj=engine.GetPendRequestByID((uchar)id); if(req_obj==NULL) return; if(engine.TradingGetLogLevel(Symbol())>LOG_LEVEL_NO_MSG) { ::Print(CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ADD_CRITERIONS)," #",req_obj.ID(),":"); req_obj.PrintActivations(); } } } } //--- 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) engine.ClosePosition((ulong)position.Ticket()); } } //--- 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) { COrder* position=list.At(index); //--- Close the Buy position partially if(position!=NULL) engine.ClosePositionPartially((ulong)position.Ticket(),position.Volume()/2.0); } } //--- 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); //--- Close the Buy position by the opposite Sell one if(position_buy!=NULL && position_sell!=NULL) engine.ClosePositionBy((ulong)position_buy.Ticket(),(ulong)position_sell.Ticket()); } } } //--- 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) engine.ClosePosition((ulong)position.Ticket()); } } //--- 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) { COrder* position=list.At(index); //--- Close the Sell position partially if(position!=NULL) engine.ClosePositionPartially((ulong)position.Ticket(),position.Volume()/2.0); } } //--- 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); //--- Close the Sell position by the opposite Buy one if(position_sell!=NULL && position_buy!=NULL) engine.ClosePositionBy((ulong)position_sell.Ticket(),(ulong)position_buy.Ticket()); } } } //--- 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; //--- close each position by its ticket engine.ClosePosition((ulong)position.Ticket()); } } } //--- 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; //--- delete the order by its ticket engine.DeleteOrder((ulong)order.Ticket()); } } } //--- 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); } } //--- 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(); } //--- 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); pending_close_sell_by_buy=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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); pending_close_sell_by_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_PRICE")); } //--- re-draw the chart ChartRedraw(); } } +//+------------------------------------------------------------------+
Ya hemos comentado todo lo necesario en los códigos de procesamiento de la pulsación de los botones: el lector podrá estudiar el material por sí mismo. En cualquier caso, no dude en escribir cualquier duda en los comentarios al artículo.
Estos son todos los cambios necesarios en el asesor de prueba.
Compilamos e iniciamos el asesor en el simulador en el modo de visualización.
Solo tenemos que activar los botones de creación de solicitudes pendientes para la colocación de órdenes pendientes, y echar un vistazo a cómo se ejecutan las solicitudes pendientes:
Primero se ha creado una solicitud comercial para la colocación de una orden pendiente según las condiciones de precio y tiempo, mientras que las demás solicitudes pendientes se han creado solo según las condiciones de tiempo. Y, como podemos ver, todas las solicitudes pendientes se han activado al cumplirse sus condiciones de activación: la primera, al darse la condición de precio y tiempo, y las siguientes, al llegar su momento de activación. De esta forma, todo funciona de la forma que habíamos planeado.
¿Qué es lo próximo?
En el próximo artículo, continuaremos desarrollando el concepto de comercio con solicitudes pendientes, y también implementaremos el cierre de posiciones (completo, parcial o usando una opuesta) según una condición.
Más abajo se adjuntan todos los archivos de la versión actual de la biblioteca y los archivos del asesor de prueba. Puede descargarlo todo y ponerlo a prueba por sí mismo.
Si tiene preguntas, observaciones o sugerencias, podrá concretarlas en los comentarios al artículo.
Artículos de esta serie:
Parte 1: Concepto y organización de datosParte 2: Colecciones de las órdenes y transacciones históricas
Parte 3: Colección de órdenes y posiciones de mercado, organización de la búsqueda
Parte 4: Eventos comerciales. Concepto
Parte 5: Clases y colección de eventos comerciales. Envío de eventos al programa.
Parte 6. Eventos en la cuenta con compensación
Parte 7. Eventos de activación de órdenes StopLimit, preparación de la funcionalidad para el registro de los eventos de modificación de órdenes y posiciones
Parte 8. Eventos de modificación de órdenes y posiciones
Parte 9. Compatibilidad con MQL4 - Preparando los datos
Parte 10. Compatibilidad con MQL4 - Eventos de apertura de posición y activación de órdenes pendientes
Parte 11. Compatibilidad con MQL4 - Eventos de cierre de posiciones
Parte 12. Implementando la clase de objeto "cuenta" y la colección de objetos de cuenta
Parte 13. Eventos del objeto "cuenta"
Parte 14. El objeto "Símbolo"
Parte 15. Colección de objetos de símbolo
Parte 16. Eventos de la colección de símbolos
Parte 17. Interactividad de los objetos de la biblioteca
Parte 18. Interactividad del objeto de cuenta y cualquier otro objeto de la biblioteca
Parte 19. Clase de mensajes de la biblioteca
Parte 20. Creación y guardado de los recursos del programa
Parte 21. Clases comerciales - El objeto comercial multiplataforma básico
Parte 22. Clases comerciales - Clase comercial principal, control de limitaciones
Parte 23. Clase comercial principal - Control de parámetros permitidos
Parte 24. Clase comercial principal - corrección automática de parámetros erróneos
Parte 25. Procesando los errores retornados por el servidor comercial
Parte 26. Trabajando con las solicitudes comerciales pendientes - primera implementación (apertura de posiciones)
Parte 27. Trabajando con las solicitudes comerciales pendientes - Colocación de órdenes pendientes
Parte 28. Trabajando con las solicitudes comerciales pendientes - Cierre, eliminación y modificación
Parte 29. Solicitudes comerciales pendientes - Clases de objetos de solicitudes
Parte 30. Solicitudes comerciales pendientes - Control de los objetos de solicitudes
Parte 31. Solicitudes comerciales pendientes - Apertura de posiciones según condiciones
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/7536
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso