
Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte XXVII): Trabajando con las solicitudes comerciales - Colocación de órdenes pendientes
Contenido
- Preparando los datos
- Corrigiendo los errores de la clase comercial y creando una solicitud pendiente para colocar órdenes
- Simulación
- ¿Qué es lo próximo?
En el artículo anterior, comenzamos a implementar solicitudes comerciales pendientes y creamos la primera solicitud pendiente para abrir una posición en el caso de que obtuviésemos un error en la clase comercial después de enviar una solicitud al servidor. Hoy, continuaremos el desarrollo de la clase comercial, centrándonos en el trabajo con las solicitudes pendientes, y también implementaremos la creación de una solicitud pendiente cuando se dé un error al colocar órdenes pendientes.
Durante la simulación de la clase comercial, hemos detectado algunos errores cometidos en su diseño. En particular, a los objetos comerciales de los símbolos, al inicializarlos en el constructor de la clase, se les asignaban valores por defecto rigurosamente establecidos, que podían no tener soporte en las especificaciones del símbolo. Esto provocaba errores obtenidos desde el servidor al intentar colocar órdenes pendientes: el servidor generaba el error de tipo soportado de expiración de la orden, y este error no se corregía en ninguna parte, lo que finalmente provocaba que no fuese posible colocar la orden. Al enviar una solicitud comercial con los valores por defecto, a la solicitud comercial también se le enviaban ciertos datos que carecían de soporte, y que posteriormente no eran corregidos en ninguna parte. Para encontrar una salida a esta situación, debíamos indicar directamente en la solicitud comercial los datos correctos, que se correspondiesen con las especificaciones del símbolo en el que se tendría que realizar la operación comercial.
Y esto resulta muy incómodo, dado que requiere conocimientos obligatorios sobre las especificaciones del símbolo y la introducción de los valores exactos manualmente, en lugar de permitir al usuario confiar la corrección automática de los valores a la propia biblioteca. Por eso, vamos a introducir ciertas correcciones en la lógica del trabajo con la clase comercial, de forma que todos los objetos comerciales de los símbolos se inicialicen seleccionando automáticamente los valores correctos en el asesor, en el manejador OnInit(). A los métodos comerciales de la clase comercial se les transmitirán por defecto valores -1 para los tipos de rellenado y expiración de órdenes, lo que será una señal para utilizar los valores por defecto establecidos correctamente con anterioridad. Si se transmite otro valor desde el programa al método comercial, se utilizará este; si el valor resulta erróneo, será corregido al procesar los errores en la clase comercial.Preparando los datos
Aparte de realizar las correcciones pertinentes en la clase comercial, vamos a mejorar la clase del objeto de solicitud pendiente, en concreto,
añadiremos la muestra de todos sus parámetros en el diario. Esto nos facilitará la posterior simulación del trabajo con los objetos de
solicitudes pendientes.
En primer lugar, vamos a añadir todos los mensajes necesarios a la matriz de mensajes de la biblioteca.
Abrimos el archivo Datas.mqh y le añadimos los índices de los nuevos mensajes:
//+------------------------------------------------------------------+ //| List of the library's text message indices | //+------------------------------------------------------------------+ enum ENUM_MESSAGES_LIB { MSG_LIB_PARAMS_LIST_BEG=ERR_USER_ERROR_FIRST, // Beginning of the parameter list MSG_LIB_PARAMS_LIST_END, // End of the parameter list MSG_LIB_PROP_NOT_SUPPORTED, // Property not supported MSG_LIB_PROP_NOT_SUPPORTED_MQL4, // Property not supported in MQL4 MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_2155, // Property not supported in MetaTrader 5 versions lower than 2155 MSG_LIB_PROP_NOT_SUPPORTED_POSITION, // Property not supported for position MSG_LIB_PROP_NOT_SUPPORTED_PENDING, // Property not supported for pending order MSG_LIB_PROP_NOT_SUPPORTED_MARKET, // Property not supported for market order MSG_LIB_PROP_NOT_SUPPORTED_MARKET_HIST, // Property not supported for historical market order MSG_LIB_PROP_NOT_SET, // Value not set MSG_LIB_PROP_EMPTY, // Not set MSG_LIB_PROP_AS_IN_ORDER, // According to the order expiration mode MSG_LIB_SYS_ERROR, // Error
...
MSG_LIB_TEXT_FAILING_CREATE_PENDING_REQ, // Failed to create a pending request MSG_LIB_TEXT_TRY_N, // Trading attempt # MSG_LIB_TEXT_RE_TRY_N, // Repeated trading attempt # MSG_LIB_TEXT_REQUEST_ACTION, // Type of a performed action MSG_LIB_TEXT_REQUEST_MAGIC, // EA stamp (magic number) MSG_LIB_TEXT_REQUEST_ORDER, // Order ticket MSG_LIB_TEXT_REQUEST_SYMBOL, // Name of a trading instrument MSG_LIB_TEXT_REQUEST_VOLUME, // Requested volume of a deal in lots MSG_LIB_TEXT_REQUEST_PRICE, // Price MSG_LIB_TEXT_REQUEST_STOPLIMIT, // StopLimit MSG_LIB_TEXT_REQUEST_SL, // Stop Loss MSG_LIB_TEXT_REQUEST_TP, // Take Profit MSG_LIB_TEXT_REQUEST_DEVIATION, // Maximum price deviation MSG_LIB_TEXT_REQUEST_TYPE, // Order type MSG_LIB_TEXT_REQUEST_TYPE_FILLING, // Order filling type MSG_LIB_TEXT_REQUEST_TYPE_TIME, // Order lifetime type MSG_LIB_TEXT_REQUEST_EXPIRATION, // Order expiration date MSG_LIB_TEXT_REQUEST_COMMENT, // Order comment MSG_LIB_TEXT_REQUEST_POSITION, // Position ticket MSG_LIB_TEXT_REQUEST_POSITION_BY, // Opposite position ticket MSG_LIB_TEXT_REQUEST_ACTION_DEAL, // Place a market order MSG_LIB_TEXT_REQUEST_ACTION_PENDING, // Place a pending order MSG_LIB_TEXT_REQUEST_ACTION_SLTP, // Change open position Stop Loss and Take Profit MSG_LIB_TEXT_REQUEST_ACTION_MODIFY, // Change parameters of the previously placed trading order MSG_LIB_TEXT_REQUEST_ACTION_REMOVE, // Remove previously placed pending order MSG_LIB_TEXT_REQUEST_ACTION_CLOSE_BY, // Close a position by an opposite one MSG_LIB_TEXT_REQUEST_ACTION_UNCNOWN, // Unknown trading operation type MSG_LIB_TEXT_REQUEST_ORDER_FILLING_FOK, // Order is executed in the specified volume only, otherwise it is canceled MSG_LIB_TEXT_REQUEST_ORDER_FILLING_IOK, // Order is filled within an available volume, while the unfilled one is canceled MSG_LIB_TEXT_REQUEST_ORDER_FILLING_RETURN, // Order is filled within an available volume, while the unfilled one remains MSG_LIB_TEXT_REQUEST_ORDER_TIME_GTC, // Order is valid till explicitly canceled MSG_LIB_TEXT_REQUEST_ORDER_TIME_DAY, // Order is valid only during the current trading day MSG_LIB_TEXT_REQUEST_ORDER_TIME_SPECIFIED, // Order is valid till the expiration date MSG_LIB_TEXT_REQUEST_ORDER_TIME_SPECIFIED_DAY, // Order is valid till 23:59:59 of a specified day MSG_LIB_TEXT_REQUEST_DATAS, // Trading request parameters MSG_LIB_TEXT_PEND_REQUEST_DATAS, // Pending trading request parameters MSG_LIB_TEXT_PEND_REQUEST_CREATED, // Pending request created MSG_LIB_TEXT_PEND_REQUEST_DELETED, // Pending request is removed due to its expiration MSG_LIB_TEXT_PEND_REQUEST_PRICE_CREATE, // Price at the moment of request generation MSG_LIB_TEXT_PEND_REQUEST_TIME_CREATE, // Request creation time MSG_LIB_TEXT_PEND_REQUEST_TIME_ACTIVATE, // Request activation time MSG_LIB_TEXT_PEND_REQUEST_WAITING, // Waiting time between trading attempts MSG_LIB_TEXT_PEND_REQUEST_CURRENT_ATTEMPT, // Current trading attempt MSG_LIB_TEXT_PEND_REQUEST_TOTAL_ATTEMPTS, // Total number of trading attempts MSG_LIB_TEXT_PEND_REQUEST_ID, // Trading request ID MSG_LIB_TEXT_PEND_REQUEST_RETCODE, // Return code a request is based on MSG_LIB_TEXT_PEND_REQUEST_TYPE, // Pending request type MSG_LIB_TEXT_PEND_REQUEST_BY_ERROR, // Pending request generated based on the server return code MSG_LIB_TEXT_PEND_REQUEST_BY_REQUEST, // Pending request created by request MSG_LIB_TEXT_PEND_REQUEST_WAITING_ONSET, // Wait for the first trading attempt };
y los textos de los mensajes correspondientes a los índices:
//+------------------------------------------------------------------+ string messages_library[][TOTAL_LANG]= { {"Начало списка параметров","The beginning of the event parameter list"}, {"Конец списка параметров","End of the parameter list"}, {"Свойство не поддерживается","Property is not support"}, {"Свойство не поддерживается в MQL4","Property is not supported in MQL4"}, {"Свойство не поддерживается в MetaTrader5 версии ниже 2155","The property is not supported in MetaTrader5, build lower than 2155"}, {"Свойство не поддерживается у позиции","Property not supported for position"}, {"Свойство не поддерживается у отложенного ордера","The property is not supported for a pending order"}, {"Свойство не поддерживается у маркет-ордера","The property is not supported for a market-order"}, {"Свойство не поддерживается у исторического маркет-ордера","The property is not supported for a history market-order"}, {"Значение не задано","Value not set"}, {"Отсутствует","Not set"}, {"В соответствии с режимом истечения ордера","In accordance with the order expiration mode"}, {"Ошибка ","Error"},
...
{"Не удалось создать отложенный запрос","Failed to create pending request"}, {"Торговая попытка #","Trading attempt #"}, {"Повторная торговая попытка #","Retry trading attempt #"}, {"Тип выполняемого действия","Trade operation type"}, {"Штамп эксперта (magic number)","Expert Advisor ID (magic number)"}, {"Тикет ордера","Order ticket"}, {"Имя торгового инструмента","Trade symbol"}, {"Запрашиваемый объем сделки в лотах","Requested volume for a deal in lots"}, {"Цена","Price"}, {"Уровень StopLimit ордера","StopLimit level of the order"}, {"Уровень Stop Loss ордера","Stop Loss level of the order"}, {"Уровень Take Profit ордера","Take Profit level of the order"}, {"Максимальное отклонение от цены","Maximal deviation from the price"}, {"Тип ордера","Order type"}, {"Тип ордера по исполнению","Order execution type"}, {"Тип ордера по времени действия","Order expiration type"}, {"Срок истечения ордера","Order expiration time"}, {"Комментарий к ордеру","Order comment"}, {"Тикет позиции","Position ticket"}, {"Тикет встречной позиции","Opposite position ticket"}, {"Поставить рыночный ордер","Place market order"}, {"Установить отложенный ордер","Place pending order"}, {"Изменить значения Stop Loss и Take Profit у открытой позиции","Modify Stop Loss and Take Profit values of an opened position"}, {"Изменить параметры ранее установленного торгового ордера","Modify the parameters of the order placed previously"}, {"Удалить ранее выставленный отложенный ордер","Delete the pending order placed previously"}, {"Закрыть позицию встречной","Close a position by an opposite one"}, {"Неизвестный тип торговой операции","Unknown trade action type"}, {"Ордер исполняется исключительно в указанном объеме, иначе отменяется (FOK)","The order is executed exclusively in the specified volume, otherwise it is canceled (FOK)"}, {"Ордер исполняется на доступный объем, неисполненный отменяется (IOK)","The order is executed on the available volume, the unfulfilled is canceled (IOK)"}, {"Ордер исполняется на доступный объем, неисполненный остаётся (Return)","The order is executed at an available volume, unfulfilled remains in the market (Return)"}, {"Ордер действителен до явной отмены","Good till cancel order"}, {"Ордер действителен только в течение текущего торгового дня","Good till current trade day order"}, {"Ордер действителен до даты истечения","Good till expired order"}, {"Ордер действителен до 23:59:59 указанного дня","The order will be effective till 23:59:59 of the specified day"}, {"Параметры торгового запроса","Trade request's parameters"}, {"Параметры отложенного торгового запроса","Pending trade request's parameters"}, {"Создан отложенный запрос","Pending request created"}, {"Отложенный запрос удалён в связи с окончанием времени его действия","Pending request deleted due to expiration"}, {"Цена в момент создания запроса: ","Price at time of request create: "}, {"Время создания запроса: ","Request creation time: "}, {"Время активации запроса: ","Request activation time: "}, {"Время ожидания между торговыми попытками: ","Waiting time between trading attempts: "}, {"Текущая торговая попытка: ","Current trading attempt: "}, {"Общее количество торговых попыток: ","Total trade attempts: "}, {"Идентификатор торгового запроса: ","Trade request ID: "}, {"Код возврата, на основании которого создан запрос: ","Return code based on which the request was created: "}, {"Тип отложенного запроса: ","Pending request type: "}, {"Отложенный запрос, созданный по коду возврата сервера","Pending request that was created as a result of the server code"}, {"Отложенный запрос, созданный по запросу","Pending request created by request"}, {"Ожидание наступления времени первой торговой попытки","Waiting for the onset time of the first trading attempt"}, }; //+---------------------------------------------------------------------+
Aparte de los errores descritos arriba, hemos notado que los valores de los indicadores de las colecciones se cruzan con los identificadores de los tipos de objetos en la biblioteca estándar. En concreto, el identificador de la colección de órdenes y transacciones históricas COLLECTION_HISTORY_ID tiene el valor 0x7779, que se corresponde con el valor del tipo de lista de la clase de lista dinámica de ejemplares de la clase CObject y sus herederos CList de la biblioteca estándar. Y el hecho de que los identificadores de los objetos tengan el mismo valor no es correcto.
Aquí tenemos la lista (probablemente) incompleta con los identificadores de los objetos de la biblioteca estándar y los valores hexadecimales que les corresponden:
Clase básica CObject |
Type
= 0 |
Colección de datos CArrayChar |
Type
= 0x77 |
Colección de datos CArrayShort |
Type
= 0x82 |
Colección de datos CArrayInt |
Type =
0x82 |
Colección de datos CArrayLong |
Type = 0x84 |
Colección de datos CArrayFloat |
Type
= 0x87 |
Colección de datos CArrayDouble |
Type
= 0x87 |
Colección de datos CArrayString |
Type
= 0x89 |
Colección de datos CArrayObj |
Type
= 0x7778 |
Colección de datos CList |
Type
= 0x7779 |
Objetos gráficos de la clase básica CChartObject |
Type
= 0x8888 |
Gráficos de precio CChart |
Type
= 0x1111 |
Como podemos ver, el tipo de objeto de lista coincide con el identificador de
la colección de órdenes y transacciones históricas establecido para la bilbioteca.
Vamos a corregir esta colisión de datos en
el archivo Defines.mqh, incrementando en 1 el valor de todos los identificadores de las
colecciones:
//--- Collection list IDs #define COLLECTION_HISTORY_ID (0x777A) // Historical collection list ID #define COLLECTION_MARKET_ID (0x777B) // Market collection list ID #define COLLECTION_EVENTS_ID (0x777C) // Event collection list ID #define COLLECTION_ACCOUNT_ID (0x777D) // Account collection list ID #define COLLECTION_SYMBOLS_ID (0x777E) // Symbol collection list ID //--- Data parameters for file operations
Dado que posteriormente vamos a implementar la posibilidad de comerciar con la ayuda de solicitudes pendientes, entonces tendremos dos tipos de solicitudes pendientes:
- una solicitud creada según el código de error y obtenida del servidor comercial (nos ocupamos de la implementación de estas
solicitudes en esta etapa);
- una solicitud pendiente creada según la solicitud de un programa (el comercio con solicitudes pendientes, que implementaremos
más tarde).
Por eso, para separar los tipos de solicitudes, introduciremos el concepto "tipo de solicitud" y los identificadores que se corresponden con el tipo:
//--- Symbol parameters #define CLR_DEFAULT (0xFF000000) // Default color #define SYMBOLS_COMMON_TOTAL (1000) // Total number of working symbols //--- Pending request type IDs #define PENDING_REQUEST_ID_TYPE_ERR (1) // Type of a pending request created based on the server return code #define PENDING_REQUEST_ID_TYPE_REQ (2) // Type of a pending request created by request //+------------------------------------------------------------------+
Y añadimos al final del archivo Defines.mqh una enumeración con los tipos de solicitudes pendientes:
//+------------------------------------------------------------------+ //| Pending request type | //+------------------------------------------------------------------+ enum ENUM_PENDING_REQUEST_TYPE { PENDING_REQUEST_TYPE_ERROR=PENDING_REQUEST_ID_TYPE_ERR, // Pending request created based on the return code or error PENDING_REQUEST_TYPE_REQUEST=PENDING_REQUEST_ID_TYPE_REQ,// Pending request created by request }; //+------------------------------------------------------------------+
Para mostrar la descripción de las solicitudes comerciales en el diario, necesitaremos preparar funciones que se encarguen de ello.
En el archivo de funciones de servicio DELib.mqh, escribimos todas las funciones necesarias para crear un mensaje a partir de
los textos predeterminados que se encuentran en el archivo Datas.mqh y los valores de las propiedades mostrados por las funciones.
Funciones para mostrar la descripción del modo de rellenado de la
orden y el tipo de expiración:
//+------------------------------------------------------------------+ //| Return the order filling mode description | //+------------------------------------------------------------------+ string OrderTypeFillingDescription(const ENUM_ORDER_TYPE_FILLING type) { return ( type==ORDER_FILLING_FOK ? CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_FILLING_FOK) : type==ORDER_FILLING_IOC ? CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_FILLING_IOK) : type==ORDER_FILLING_RETURN ? CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_FILLING_RETURN): type==WRONG_VALUE ? "WRONG_VALUE" : EnumToString(type) ); } //+------------------------------------------------------------------+ //| Return the order expiration type description | //+------------------------------------------------------------------+ string OrderTypeTimeDescription(const ENUM_ORDER_TYPE_TIME type) { return ( type==ORDER_TIME_GTC ? CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_TIME_GTC) : type==ORDER_TIME_DAY ? CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_TIME_DAY) : type==ORDER_TIME_SPECIFIED ? CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_TIME_SPECIFIED) : type==ORDER_TIME_SPECIFIED_DAY ? CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_TIME_SPECIFIED_DAY) : type==WRONG_VALUE ? "WRONG_VALUE" : EnumToString(type) ); } //+------------------------------------------------------------------+
Funciones para mostrar la descripción de
la estructura de la solicitud comercial MqlTradeRequest:
//+------------------------------------------------------------------+ //| Display the trading request description in the journal | //+------------------------------------------------------------------+ void PrintRequestDescription(const MqlTradeRequest &request) { string datas= ( " - "+RequestActionDescription(request)+"\n"+ " - "+RequestMagicDescription(request)+"\n"+ " - "+RequestOrderDescription(request)+"\n"+ " - "+RequestSymbolDescription(request)+"\n"+ " - "+RequestVolumeDescription(request)+"\n"+ " - "+RequestPriceDescription(request)+"\n"+ " - "+RequestStopLimitDescription(request)+"\n"+ " - "+RequestStopLossDescription(request)+"\n"+ " - "+RequestTakeProfitDescription(request)+"\n"+ " - "+RequestDeviationDescription(request)+"\n"+ " - "+RequestTypeDescription(request)+"\n"+ " - "+RequestTypeFillingDescription(request)+"\n"+ " - "+RequestTypeTimeDescription(request)+"\n"+ " - "+RequestExpirationDescription(request)+"\n"+ " - "+RequestCommentDescription(request)+"\n"+ " - "+RequestPositionDescription(request)+"\n"+ " - "+RequestPositionByDescription(request) ); Print("================== ",CMessage::Text(MSG_LIB_TEXT_REQUEST_DATAS)," ==================\n",datas,"\n"); } //+------------------------------------------------------------------+ //| Return the executed action type description | //+------------------------------------------------------------------+ string RequestActionDescription(const MqlTradeRequest &request) { int code_descr= ( request.action==TRADE_ACTION_DEAL ? MSG_LIB_TEXT_REQUEST_ACTION_DEAL : request.action==TRADE_ACTION_PENDING ? MSG_LIB_TEXT_REQUEST_ACTION_PENDING : request.action==TRADE_ACTION_SLTP ? MSG_LIB_TEXT_REQUEST_ACTION_SLTP : request.action==TRADE_ACTION_MODIFY ? MSG_LIB_TEXT_REQUEST_ACTION_MODIFY : request.action==TRADE_ACTION_REMOVE ? MSG_LIB_TEXT_REQUEST_ACTION_REMOVE : request.action==TRADE_ACTION_CLOSE_BY ? MSG_LIB_TEXT_REQUEST_ACTION_CLOSE_BY : MSG_LIB_TEXT_REQUEST_ACTION_UNCNOWN ); return CMessage::Text(MSG_LIB_TEXT_REQUEST_ACTION)+": "+CMessage::Text(code_descr); } //+------------------------------------------------------------------+ //| Return the magic number value description | //+------------------------------------------------------------------+ string RequestMagicDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_ORD_MAGIC)+": "+(string)request.magic; } //+------------------------------------------------------------------+ //| Return the order ticket value description | //+------------------------------------------------------------------+ string RequestOrderDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER)+": "+(request.order>0 ? (string)request.order : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+ //| Return the trading instrument name description | //+------------------------------------------------------------------+ string RequestSymbolDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_SYMBOL)+": "+request.symbol; } //+------------------------------------------------------------------+ //| Return the request volume description | //+------------------------------------------------------------------+ string RequestVolumeDescription(const MqlTradeRequest &request) { int dg=(int)DigitsLots(request.symbol); int dgl=(dg==0 ? 1 : dg); return CMessage::Text(MSG_LIB_TEXT_REQUEST_VOLUME)+": "+(request.volume>0 ? DoubleToString(request.volume,dgl) : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+ //| Return the request price value description | //+------------------------------------------------------------------+ string RequestPriceDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_PRICE)+": "+(request.price>0 ? DoubleToString(request.price,(int)SymbolInfoInteger(request.symbol,SYMBOL_DIGITS)) : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+ //| Return the request StopLimit order price description | //+------------------------------------------------------------------+ string RequestStopLimitDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_STOPLIMIT)+": "+(request.stoplimit>0 ? DoubleToString(request.stoplimit,(int)SymbolInfoInteger(request.symbol,SYMBOL_DIGITS)) : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+ //| Return the request StopLoss order price description | //+------------------------------------------------------------------+ string RequestStopLossDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_SL)+": "+(request.sl>0 ? DoubleToString(request.sl,(int)SymbolInfoInteger(request.symbol,SYMBOL_DIGITS)) : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+ //| Return the request TakeProfit order price description | //+------------------------------------------------------------------+ string RequestTakeProfitDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_TP)+": "+(request.tp>0 ? DoubleToString(request.tp,(int)SymbolInfoInteger(request.symbol,SYMBOL_DIGITS)) : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+ //| Return the request deviation size description | //+------------------------------------------------------------------+ string RequestDeviationDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_DEVIATION)+": "+(string)request.deviation; } //+------------------------------------------------------------------+ //| Return the request order type description | //+------------------------------------------------------------------+ string RequestTypeDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_TYPE)+": "+OrderTypeDescription(request.type); } //+------------------------------------------------------------------+ //| Return the request order filling mode description | //+------------------------------------------------------------------+ string RequestTypeFillingDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_TYPE_FILLING)+": "+OrderTypeFillingDescription(request.type_filling); } //+------------------------------------------------------------------+ //| Return the request order lifetime type description | //+------------------------------------------------------------------+ string RequestTypeTimeDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_TYPE_TIME)+": "+OrderTypeTimeDescription(request.type_time); } //+------------------------------------------------------------------+ //| Return the request order expiration time description | //+------------------------------------------------------------------+ string RequestExpirationDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_EXPIRATION)+": "+(request.expiration>0 ? TimeToString(request.expiration) : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+ //| Return the request order comment description | //+------------------------------------------------------------------+ string RequestCommentDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_COMMENT)+": "+(request.comment!="" && request.comment!=NULL ? "\""+request.comment+"\"" : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+ //| Return the request position ticket description | //+------------------------------------------------------------------+ string RequestPositionDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_POSITION)+": "+(request.position>0 ? (string)request.position : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+ //| Return the request opposite position ticket description | //+------------------------------------------------------------------+ string RequestPositionByDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_POSITION_BY)+": "+(request.position_by>0 ? (string)request.position_by : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+
Vamos a mejorar la clase comercial, corrigiendo los errores encontrados y escribiendo al mismo tiempo todo lo necesario para crear un
objeto de solicitud pendiente para colocar órdenes pendientes.
Corrigiendo los errores de la clase comercial y creando una solicitud pendiente para colocar órdenes
Hemos notado una peculiaridad interesante de algunos símbolos cuyo gráfico se construye según los precios Last. En ocasiones, no tienen los precios Ask y Bid, o uno de los dos. Para obtener el precio en cualquier caso, hemos tenido que crear métodos adicionales (y modificar los ya existentes) en la clase del objeto de símbolo abstracto CSymbol, encargados de comprobar el tipo de construcción del gráfico. Si este se construye según los precios Last, se comprueba adicionalmente la presencia de los precios Ask y Bid, y si estos existen, se usan precisamente ellos, de lo contrario, se usa el precio Last.
En el archivo Symbol.mqh, en el bloque de acceso simplificado a las propiedades del objeto de símbolo, cambiamos
el tipo de método que retorna la hora. Como la hora se retorna en milisegundos, el tipo también deberá ser ulong, en lugar de datetime.
Asimismo, vamos a declarar los tres métodos adicionales cuya
misión hemos mencionado anteriormente:
//+------------------------------------------------------------------+ //| Methods of a simplified access to the order object properties | //+------------------------------------------------------------------+ //--- Integer properties long Status(void) const { return this.GetProperty(SYMBOL_PROP_STATUS); } int IndexInMarketWatch(void) const { return (int)this.GetProperty(SYMBOL_PROP_INDEX_MW); } bool IsCustom(void) const { return (bool)this.GetProperty(SYMBOL_PROP_CUSTOM); } color ColorBackground(void) const { return (color)this.GetProperty(SYMBOL_PROP_BACKGROUND_COLOR); } ENUM_SYMBOL_CHART_MODE ChartMode(void) const { return (ENUM_SYMBOL_CHART_MODE)this.GetProperty(SYMBOL_PROP_CHART_MODE); } bool IsExist(void) const { return (bool)this.GetProperty(SYMBOL_PROP_EXIST); } bool IsExist(const string name) const { return this.SymbolExists(name); } bool IsSelect(void) const { return (bool)this.GetProperty(SYMBOL_PROP_SELECT); } bool IsVisible(void) const { return (bool)this.GetProperty(SYMBOL_PROP_VISIBLE); } long SessionDeals(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_DEALS); } long SessionBuyOrders(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_BUY_ORDERS); } long SessionSellOrders(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_SELL_ORDERS); } long Volume(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME); } long VolumeHigh(void) const { return this.GetProperty(SYMBOL_PROP_VOLUMEHIGH); } long VolumeLow(void) const { return this.GetProperty(SYMBOL_PROP_VOLUMELOW); } long Time(void) const { return (datetime)this.GetProperty(SYMBOL_PROP_TIME); } int Digits(void) const { return (int)this.GetProperty(SYMBOL_PROP_DIGITS); } int DigitsLot(void) const { return (int)this.GetProperty(SYMBOL_PROP_DIGITS_LOTS); } int Spread(void) const { return (int)this.GetProperty(SYMBOL_PROP_SPREAD); } bool IsSpreadFloat(void) const { return (bool)this.GetProperty(SYMBOL_PROP_SPREAD_FLOAT); } int TicksBookdepth(void) const { return (int)this.GetProperty(SYMBOL_PROP_TICKS_BOOKDEPTH); } ENUM_SYMBOL_CALC_MODE TradeCalcMode(void) const { return (ENUM_SYMBOL_CALC_MODE)this.GetProperty(SYMBOL_PROP_TRADE_CALC_MODE); } ENUM_SYMBOL_TRADE_MODE TradeMode(void) const { return (ENUM_SYMBOL_TRADE_MODE)this.GetProperty(SYMBOL_PROP_TRADE_MODE); } datetime StartTime(void) const { return (datetime)this.GetProperty(SYMBOL_PROP_START_TIME); } datetime ExpirationTime(void) const { return (datetime)this.GetProperty(SYMBOL_PROP_EXPIRATION_TIME); } int TradeStopLevel(void) const { return (int)this.GetProperty(SYMBOL_PROP_TRADE_STOPS_LEVEL); } int TradeFreezeLevel(void) const { return (int)this.GetProperty(SYMBOL_PROP_TRADE_FREEZE_LEVEL); } ENUM_SYMBOL_TRADE_EXECUTION TradeExecutionMode(void) const { return (ENUM_SYMBOL_TRADE_EXECUTION)this.GetProperty(SYMBOL_PROP_TRADE_EXEMODE); } ENUM_SYMBOL_SWAP_MODE SwapMode(void) const { return (ENUM_SYMBOL_SWAP_MODE)this.GetProperty(SYMBOL_PROP_SWAP_MODE); } ENUM_DAY_OF_WEEK SwapRollover3Days(void) const { return (ENUM_DAY_OF_WEEK)this.GetProperty(SYMBOL_PROP_SWAP_ROLLOVER3DAYS); } bool IsMarginHedgedUseLeg(void) const { return (bool)this.GetProperty(SYMBOL_PROP_MARGIN_HEDGED_USE_LEG); } int ExpirationModeFlags(void) const { return (int)this.GetProperty(SYMBOL_PROP_EXPIRATION_MODE); } int FillingModeFlags(void) const { return (int)this.GetProperty(SYMBOL_PROP_FILLING_MODE); } int OrderModeFlags(void) const { return (int)this.GetProperty(SYMBOL_PROP_ORDER_MODE); } ENUM_SYMBOL_ORDER_GTC_MODE OrderModeGTC(void) const { return (ENUM_SYMBOL_ORDER_GTC_MODE)this.GetProperty(SYMBOL_PROP_ORDER_GTC_MODE); } ENUM_SYMBOL_OPTION_MODE OptionMode(void) const { return (ENUM_SYMBOL_OPTION_MODE)this.GetProperty(SYMBOL_PROP_OPTION_MODE); } ENUM_SYMBOL_OPTION_RIGHT OptionRight(void) const { return (ENUM_SYMBOL_OPTION_RIGHT)this.GetProperty(SYMBOL_PROP_OPTION_RIGHT); } //--- Real properties double Bid(void) const { return this.GetProperty(SYMBOL_PROP_BID); } double BidHigh(void) const { return this.GetProperty(SYMBOL_PROP_BIDHIGH); } double BidLow(void) const { return this.GetProperty(SYMBOL_PROP_BIDLOW); } double Ask(void) const { return this.GetProperty(SYMBOL_PROP_ASK); } double AskHigh(void) const { return this.GetProperty(SYMBOL_PROP_ASKHIGH); } double AskLow(void) const { return this.GetProperty(SYMBOL_PROP_ASKLOW); } double Last(void) const { return this.GetProperty(SYMBOL_PROP_LAST); } double LastHigh(void) const { return this.GetProperty(SYMBOL_PROP_LASTHIGH); } double LastLow(void) const { return this.GetProperty(SYMBOL_PROP_LASTLOW); } double VolumeReal(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_REAL); } double VolumeHighReal(void) const { return this.GetProperty(SYMBOL_PROP_VOLUMEHIGH_REAL); } double VolumeLowReal(void) const { return this.GetProperty(SYMBOL_PROP_VOLUMELOW_REAL); } double OptionStrike(void) const { return this.GetProperty(SYMBOL_PROP_OPTION_STRIKE); } double Point(void) const { return this.GetProperty(SYMBOL_PROP_POINT); } double TradeTickValue(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE); } double TradeTickValueProfit(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT); } double TradeTickValueLoss(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE_LOSS); } double TradeTickSize(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_SIZE); } double TradeContractSize(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_CONTRACT_SIZE); } double TradeAccuredInterest(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_ACCRUED_INTEREST); } double TradeFaceValue(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_FACE_VALUE); } double TradeLiquidityRate(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_LIQUIDITY_RATE); } double LotsMin(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_MIN); } double LotsMax(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_MAX); } double LotsStep(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_STEP); } double VolumeLimit(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_LIMIT); } double SwapLong(void) const { return this.GetProperty(SYMBOL_PROP_SWAP_LONG); } double SwapShort(void) const { return this.GetProperty(SYMBOL_PROP_SWAP_SHORT); } double MarginInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_INITIAL); } double MarginMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_MAINTENANCE); } double MarginLongInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_LONG_INITIAL); } double MarginBuyStopInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOP_INITIAL); } double MarginBuyLimitInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_LIMIT_INITIAL); } double MarginBuyStopLimitInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_INITIAL); } double MarginLongMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_LONG_MAINTENANCE); } double MarginBuyStopMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOP_MAINTENANCE); } double MarginBuyLimitMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_LIMIT_MAINTENANCE); } double MarginBuyStopLimitMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_MAINTENANCE); } double MarginShortInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SHORT_INITIAL); } double MarginSellStopInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOP_INITIAL); } double MarginSellLimitInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_LIMIT_INITIAL); } double MarginSellStopLimitInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_INITIAL); } double MarginShortMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SHORT_MAINTENANCE); } double MarginSellStopMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOP_MAINTENANCE); } double MarginSellLimitMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_LIMIT_MAINTENANCE); } double MarginSellStopLimitMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_MAINTENANCE); } double SessionVolume(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_VOLUME); } double SessionTurnover(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_TURNOVER); } double SessionInterest(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_INTEREST); } double SessionBuyOrdersVolume(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME); } double SessionSellOrdersVolume(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME); } double SessionOpen(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_OPEN); } double SessionClose(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_CLOSE); } double SessionAW(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_AW); } double SessionPriceSettlement(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_SETTLEMENT); } double SessionPriceLimitMin(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_LIMIT_MIN); } double SessionPriceLimitMax(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_LIMIT_MAX); } double MarginHedged(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_HEDGED); } double NormalizedPrice(const double price) const; double NormalizedLot(const double volume) const; double BidLast(void) const; double BidLastHigh(void) const; double BidLastLow(void) const; double AskLast(void) const; double AskLastHigh(void) const; double AskLastLow(void) const; //--- String properties string Name(void) const { return this.GetProperty(SYMBOL_PROP_NAME); } string Basis(void) const { return this.GetProperty(SYMBOL_PROP_BASIS); } string CurrencyBase(void) const { return this.GetProperty(SYMBOL_PROP_CURRENCY_BASE); } string CurrencyProfit(void) const { return this.GetProperty(SYMBOL_PROP_CURRENCY_PROFIT); } string CurrencyMargin(void) const { return this.GetProperty(SYMBOL_PROP_CURRENCY_MARGIN); } string Bank(void) const { return this.GetProperty(SYMBOL_PROP_BANK); } string Description(void) const { return this.GetProperty(SYMBOL_PROP_DESCRIPTION); } string Formula(void) const { return this.GetProperty(SYMBOL_PROP_FORMULA); } string ISIN(void) const { return this.GetProperty(SYMBOL_PROP_ISIN); } string Page(void) const { return this.GetProperty(SYMBOL_PROP_PAGE); } string Path(void) const { return this.GetProperty(SYMBOL_PROP_PATH); } string Category(void) const { return this.GetProperty(SYMBOL_PROP_CATEGORY); } string Exchange(void) const { return this.GetProperty(SYMBOL_PROP_EXCHANGE); } //+------------------------------------------------------------------+
Ya habíamos hecho antes tres métodos así para el precio Bid, pero en ellos no existía la comprobación de la presencia de precios. Vamos a introducir los cambios en ellos:
//+------------------------------------------------------------------+ //| Return Bid or Last price | //| depending on the chart construction method and price availability| //+------------------------------------------------------------------+ double CSymbol::BidLast(void) const { return ( this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetProperty(SYMBOL_PROP_BID) : (this.GetProperty(SYMBOL_PROP_BID)==0 ? this.GetProperty(SYMBOL_PROP_LAST) : this.GetProperty(SYMBOL_PROP_BID)) ); } //+------------------------------------------------------------------+ //| Return maximum Bid or Last price for a day | //| depending on the chart construction method and price availability| //+------------------------------------------------------------------+ double CSymbol::BidLastHigh(void) const { return ( this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetProperty(SYMBOL_PROP_BIDHIGH) : (this.GetProperty(SYMBOL_PROP_BIDHIGH)==0 ? this.GetProperty(SYMBOL_PROP_LASTHIGH) : this.GetProperty(SYMBOL_PROP_BIDHIGH)) ); } //+------------------------------------------------------------------+ //| Return minimum Bid or Last price for a day | //| depending on the chart construction method and price availability| //+------------------------------------------------------------------+ double CSymbol::BidLastLow(void) const { return ( this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetProperty(SYMBOL_PROP_BIDLOW) : (this.GetProperty(SYMBOL_PROP_BIDLOW)==0 ? this.GetProperty(SYMBOL_PROP_LASTLOW) : this.GetProperty(SYMBOL_PROP_BIDLOW)) ); } //+------------------------------------------------------------------+
Como hemos mencionado anteriormente, comprobamos el tipo de construcción del gráfico, y si el gráfico se construye según los precios Bid, retornamos los precios Bid correspondientes; si el gráfico se construye según los precios Last, comprobamos adicionalmente si la propiedad del símbolo retornada tiene un precio Bid igual a cero. Si es igual a cero, retornamos el precio Last correspondiente, de lo contrario, el precio Bid correspondiente.
Vamos a realizar exactamente la misma implementación para los métodos que retornan los precios Ask:
//+------------------------------------------------------------------+ //| Return Ask or Last price | //| depending on the chart construction method and price availability| //+------------------------------------------------------------------+ double CSymbol::AskLast(void) const { return ( this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetProperty(SYMBOL_PROP_ASK) : (this.GetProperty(SYMBOL_PROP_ASK)==0 ? this.GetProperty(SYMBOL_PROP_LAST) : this.GetProperty(SYMBOL_PROP_ASK)) ); } //+------------------------------------------------------------------+ //| Return maximum Ask or Last price for a day | //| depending on the chart construction method and price availability| //+------------------------------------------------------------------+ double CSymbol::AskLastHigh(void) const { return ( this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetProperty(SYMBOL_PROP_ASKHIGH) : (this.GetProperty(SYMBOL_PROP_ASKHIGH)==0 ? this.GetProperty(SYMBOL_PROP_LASTHIGH) : this.GetProperty(SYMBOL_PROP_ASKHIGH)) ); } //+------------------------------------------------------------------+ //| Return minimum Ask or Last price for a day | //| depending on the chart construction method and price availability| //+------------------------------------------------------------------+ double CSymbol::AskLastLow(void) const { return ( this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetProperty(SYMBOL_PROP_ASKLOW) : (this.GetProperty(SYMBOL_PROP_ASKLOW)==0 ? this.GetProperty(SYMBOL_PROP_LASTLOW) : this.GetProperty(SYMBOL_PROP_ASKLOW)) ); } //+------------------------------------------------------------------+
Ahora, usaremos precisamente estos métodos en la biblioteca al calcular los niveles de precio para obtener los precios Ask o Last y Bid o Last.
Al mostrar los mensajes sobre los eventos comerciales en el diario, ahora se muestran las designaciones adicionales para los
identificadores del número mágico y los dos grupos, con la condición de que en el valor del número mágico se guarden datos adicionales
(implementamos esto en el artículo anterior). Pero, si el número mágico
contiene además el identificador de la solicitud pendiente, esto significará que no lo hemos mostrado en el diario. Vamos a corregir este
detalle. Solo tenemos que añadir un par de líneas a cada una de las clases de evento en los archivos EventModify.mqh, EventOrderPlaced.mqh,
EventOrderRemoved.mqh, EventPositionClose.mqh y EventPositionOpen.mqh, en sus métodos de descripción
breve del evento.
Reemplazamos la siguiente línea en cada uno de lo archivos:
string magic=(this.Magic()!=0 ? ", "+CMessage::Text(MSG_ORD_MAGIC)+" "+(string)this.Magic()+magic_id+group_id1+group_id2 : "");
con las dos siguientes:
string pend_req_id=(this.GetPendReqID()>0 ? ", ID: "+(string)this.GetPendReqID() : ""); string magic=(this.Magic()!=0 ? ", "+CMessage::Text(MSG_ORD_MAGIC)+" "+(string)this.Magic()+magic_id+group_id1+group_id2+pend_req_id : "");
Así, hemos añadido la descripción del identificador del evento pendiente (si lo hay) a la descripción del número mágico.
Añadimos los métodos que retornan la descripción de los campos de la estructura de la solicitud comercial MqlTradeRequest a la sección pública de la clase del objeto comercial del símbolo CTradeObj, en el archivo TradeObj.mqh:
//--- Return the description of the (1) executed action type, (2) magic number, (3) order ticket, (4) volume, //--- (5) open, (6) StopLimit order, (7) StopLoss, (8) TakeProfit price, (9) deviation, //--- type of (10) order, (11) execution, (12) lifetime, (13) order expiration date, //--- (14) comment, (15) position ticket, (16) opposite position ticket string GetRequestActionDescription(void) const { return RequestActionDescription(this.m_request); } string GetRequestMagicDescription(void) const { return RequestMagicDescription(this.m_request); } string GetRequestOrderDescription(void) const { return RequestOrderDescription(this.m_request); } string GetRequestSymbolDescription(void) const { return RequestSymbolDescription(this.m_request); } string GetRequestVolumeDescription(void) const { return RequestVolumeDescription(this.m_request); } string GetRequestPriceDescription(void) const { return RequestPriceDescription(this.m_request); } string GetRequestStopLimitDescription(void) const { return RequestStopLimitDescription(this.m_request); } string GetRequestStopLossDescription(void) const { return RequestStopLossDescription(this.m_request); } string GetRequestTakeProfitDescription(void) const { return RequestTakeProfitDescription(this.m_request); } string GetRequestDeviationDescription(void) const { return RequestDeviationDescription(this.m_request); } string GetRequestTypeDescription(void) const { return RequestTypeDescription(this.m_request); } string GetRequestTypeFillingDescription(void) const { return RequestTypeFillingDescription(this.m_request); } string GetRequestTypeTimeDescription(void) const { return RequestTypeTimeDescription(this.m_request); } string GetRequestExpirationDescription(void) const { return RequestExpirationDescription(this.m_request); } string GetRequestCommentDescription(void) const { return RequestCommentDescription(this.m_request); } string GetRequestPositionDescription(void) const { return RequestPositionDescription(this.m_request); } string GetRequestPositionByDescription(void) const { return RequestPositionByDescription(this.m_request); } //--- Open a position
Los métodos simplemente llaman a las funciones correspondientes, creadas por nosotros anteriormente en el archivo de las funciones de
servicio de la biblioteca.
Por algún motivo, antes pasamos por alto la transmisión del tipo de rellenado de la orden a los métodos de apertura de posición.
Añadimos este
parámetro en la declaración del método de apertura de las posiciones de la clase:
//--- Open a position bool OpenPosition(const ENUM_POSITION_TYPE type, const double volume, const double sl=0, const double tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
y a la implementación del método:
//+------------------------------------------------------------------+ //| Open a position | //+------------------------------------------------------------------+ bool CTradeObj::OpenPosition(const ENUM_POSITION_TYPE type, const double volume, const double sl=0, const double tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) {
Dado que en el futuro organizaremos la posibilidad de comerciar con solicitudes pendientes, hemos introducido el concepto del tipo de solicitud pendiente.
En el archivo de la clase comercial Trading.mqh, en la clase de la solicitud pendiente CPendingReq
, añadimos
a la sección privada la variable de miembro de clase para guardar el tipo de solicitud pendiente:
//+------------------------------------------------------------------+ //| Pending request object class | //+------------------------------------------------------------------+ class CPendingReq : public CObject { private: MqlTradeRequest m_request; // Trade request structure uchar m_id; // Trading request ID int m_type; // Pending request type int m_retcode; // Result a request is based on double m_price_create; // Price at the moment of a request generation ulong m_time_create; // Request generation time ulong m_time_activate; // Next attempt activation time ulong m_waiting_msc; // Waiting time between requests uchar m_current_attempt; // Current attempt index uchar m_total_attempts; // Number of attempts
Añadimos a la sección pública el método que devuelve el código de retorno
del servidor que ha sido usado como base para crear la solicitud pendiente, los
métodos que retornan las descripciones de las propiedades de la solicitud pendiente, y el
tipo de solicitud, así como el método que muestra en el diario todos los
datos de la solicitud comercial:
public: //--- Return (1) the request structure, (2) the price at the moment of the request generation, //--- (3) request generation time, (4) current attempt time, //--- (5) waiting time between requests, (6) current attempt index, //--- (7) number of attempts, (8) request ID MqlTradeRequest MqlRequest(void) const { return this.m_request; } double PriceCreate(void) const { return this.m_price_create; } ulong TimeCreate(void) const { return this.m_time_create; } ulong TimeActivate(void) const { return this.m_time_activate; } ulong WaitingMSC(void) const { return this.m_waiting_msc; } uchar CurrentAttempt(void) const { return this.m_current_attempt; } uchar TotalAttempts(void) const { return this.m_total_attempts; } uchar ID(void) const { return this.m_id; } int Retcode(void) const { return this.m_retcode; } //--- Set (1) the price when creating a request, (2) request creation time, //--- (3) current attempt time, (4) waiting time between requests, //--- (5) current attempt index, (6) number of attempts, (7) request ID void SetPriceCreate(const double price) { this.m_price_create=price; } void SetTimeCreate(const ulong time) { this.m_time_create=time; } void SetTimeActivate(const ulong time) { this.m_time_activate=time; } void SetWaitingMSC(const ulong miliseconds) { this.m_waiting_msc=miliseconds;} void SetCurrentAttempt(const uchar number) { this.m_current_attempt=number; } void SetTotalAttempts(const uchar number) { this.m_total_attempts=number; } void SetID(const uchar id) { this.m_id=id; } //--- Return the description of the (1) request structure, (2) the price at the moment of the request generation, //--- (3) request generation time, (4) current attempt time, //--- (5) waiting time between requests, (6) current attempt index, //--- (7) number of attempts, (8) request ID string MqlRequestDescription(void) const { return RequestActionDescription(this.m_request); } string TypeDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_TYPE) + (this.Type()==PENDING_REQUEST_ID_TYPE_ERR ? CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_BY_ERROR) : CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_BY_REQUEST) ); } string PriceCreateDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_PRICE_CREATE)+": "+ ::DoubleToString(this.PriceCreate(),(int)::SymbolInfoInteger(this.m_request.symbol,SYMBOL_DIGITS)); } string TimeCreateDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_TIME_CREATE)+TimeMSCtoString(this.TimeCreate()); } string TimeActivateDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_TIME_ACTIVATE)+TimeMSCtoString(this.TimeActivate());} string WaitingMSCDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_WAITING)+(string)this.WaitingMSC(); } string CurrentAttemptDescription(void) const { return (CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_CURRENT_ATTEMPT)+ (this.CurrentAttempt()==0 ? CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_WAITING_ONSET) : (string)this.CurrentAttempt()) ); } string TotalAttemptsDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_TOTAL_ATTEMPTS)+(string)this.TotalAttempts(); } string IdDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ID)+(string)this.ID(); } string RetcodeDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_RETCODE)+(string)this.Retcode(); } string ReasonDescription(void) const { return CMessage::Text(this.m_retcode); } //--- Return a request type virtual int Type(void) const { return this.m_type; } //--- Display request data in the journal void Print(void); //--- Constructors CPendingReq(void){;} CPendingReq(const uchar id,const double price,const ulong time,const MqlTradeRequest &request,const int retcode); }; //+------------------------------------------------------------------+
Los métodos que retornan las descripciones de las propiedades del objeto de solicitud pendiente simplemente crean una descripción
compuesta por un encabezado que describe la propiedad y su valor. El método Type(), que retorna el tipo de solicitud pendiente, se ha hecho
virtual, dado que en el objeto básico CObject, cuyo heredero es el objeto de solicitud pendiente, ya
se ha definido el mismo método virtual. Para implementar el retorno del tipo del objeto heredero, debemos redefinir el método en el
heredero. Hemos hecho exactamente eso, y ahora este método retorna el valor de la variable m_type definida en la clase de solicitud
pendiente.
Establecemos en el constructor de la clase el valor para el tipo de solicitud pendiente:
//+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+ CPendingReq::CPendingReq(const uchar id,const double price,const ulong time,const MqlTradeRequest &request,const int retcode) : m_price_create(price), m_time_create(time), m_id(id), m_retcode(retcode) { this.CopyRequest(request); this.m_type=(retcode>0 ? PENDING_REQUEST_ID_TYPE_ERR : PENDING_REQUEST_ID_TYPE_REQ); } //+------------------------------------------------------------------+
Dado que nuestras solicitudes pendientes se crearán por dos motivos: según el código de retorno del servidor y según la solicitud del
programa, para definir el tipo de solicitud pendiente bastará con saber el código de respuesta del servidor. Precisamente eso hacemos
aquí: si el valor del código de retorno es superior a cero (el
servidor ha retornado error), significará que la solicitud se ha creado según el código de
retorno del servidor, de lo contrario, si el código es igual a cero,
significará que el objeto de solicitud pendiente ha sido creado según la solicitud del programa.
Vamos a completar el método virtual Compare() del objeto de solicitud pendiente.
Antes, siempre comparaba los
objetos solo según una propiedad, el identificador de la solicitud:
//+------------------------------------------------------------------+ //| Compare CPendingReq objects by IDs | //+------------------------------------------------------------------+ int CPendingReq::Compare(const CObject *node,const int mode=0) const { const CPendingReq *compared_req=node; return(this.ID()>compared_req.ID() ? 1 : this.ID()<compared_req.ID() ? -1 : 0); return 0; } //+------------------------------------------------------------------+
Ahora, después de introducir los tipos de solicitudes pendientes, deberemos comparar los objetos según dos propiedades: según el
identificador y según el tipo.
Para ello, cambiamos el método de comparación de los dos objetos:
//+------------------------------------------------------------------+ //| Compare CPendingReq objects by properties | //+------------------------------------------------------------------+ int CPendingReq::Compare(const CObject *node,const int mode=0) const { const CPendingReq *compared_req=node; return ( //--- Compare by ID mode==0 ? (this.ID()>compared_req.ID() ? 1 : this.ID()<compared_req.ID() ? -1 : 0) : //--- Compare by type (this.Type()>compared_req.Type() ? 1 : this.Type()<compared_req.Type() ? -1 : 0) ); } //+------------------------------------------------------------------+
Aquí, seleccionamos las propiedades a comparar del valor de la variable mode. Si se da un valor cero, los objetos se compararán según los identificadores, si el valor es distinto a cero, según los tipos de los objetos.
También fuera del cuerpo de la clase, escribimos el método que muestra en el diario la descripción completa de todas las propiedades del objeto de solicitud pendiente:
//+------------------------------------------------------------------+ //| Display request data in the journal | //+------------------------------------------------------------------+ void CPendingReq::Print(void) { string action=" - "+RequestActionDescription(this.m_request)+"\n"; string symbol="",order="",volume="",price="",stoplimit="",sl="",tp="",deviation="",type="",type_filling=""; string type_time="",expiration="",position="",position_by="",magic="",comment="",request_data=""; string type_req=" - "+this.TypeDescription()+"\n"; if(this.m_request.action==TRADE_ACTION_DEAL) { symbol=" - "+RequestSymbolDescription(this.m_request)+"\n"; volume=" - "+RequestVolumeDescription(this.m_request)+"\n"; price=" - "+RequestPriceDescription(this.m_request)+"\n"; sl=" - "+RequestStopLossDescription(this.m_request)+"\n"; tp=" - "+RequestTakeProfitDescription(this.m_request)+"\n"; deviation=" - "+RequestDeviationDescription(this.m_request)+"\n"; type=" - "+RequestTypeDescription(this.m_request)+"\n"; type_filling=" - "+RequestTypeFillingDescription(this.m_request)+"\n"; magic=" - "+RequestMagicDescription(this.m_request)+"\n"; comment=" - "+RequestCommentDescription(this.m_request)+"\n"; request_data= ("================== "+ CMessage::Text(MSG_LIB_TEXT_REQUEST_DATAS)+" ==================\n"+ action+symbol+volume+price+sl+tp+deviation+type+type_filling+magic+comment+ " ==================\n" ); } else if(this.m_request.action==TRADE_ACTION_SLTP) { symbol=" - "+RequestSymbolDescription(this.m_request)+"\n"; sl=" - "+RequestStopLossDescription(this.m_request)+"\n"; tp=" - "+RequestTakeProfitDescription(this.m_request)+"\n"; position=" - "+RequestPositionDescription(this.m_request)+"\n"; request_data= ("================== "+ CMessage::Text(MSG_LIB_TEXT_REQUEST_DATAS)+" ==================\n"+ action+symbol+sl+tp+position+ " ==================\n" ); } else if(this.m_request.action==TRADE_ACTION_PENDING) { symbol=" - "+RequestSymbolDescription(this.m_request)+"\n"; volume=" - "+RequestVolumeDescription(this.m_request)+"\n"; price=" - "+RequestPriceDescription(this.m_request)+"\n"; stoplimit=" - "+RequestStopLimitDescription(this.m_request)+"\n"; sl=" - "+RequestStopLossDescription(this.m_request)+"\n"; tp=" - "+RequestTakeProfitDescription(this.m_request)+"\n"; type=" - "+RequestTypeDescription(this.m_request)+"\n"; type_filling=" - "+RequestTypeFillingDescription(this.m_request)+"\n"; type_time=" - "+RequestTypeTimeDescription(this.m_request)+"\n"; expiration=" - "+RequestExpirationDescription(this.m_request)+"\n"; magic=" - "+RequestMagicDescription(this.m_request)+"\n"; comment=" - "+RequestCommentDescription(this.m_request)+"\n"; request_data= ("================== "+ CMessage::Text(MSG_LIB_TEXT_REQUEST_DATAS)+" ==================\n"+ action+symbol+volume+price+stoplimit+sl+tp+type+type_filling+type_time+expiration+magic+comment+ " ==================\n" ); } else if(this.m_request.action==TRADE_ACTION_MODIFY) { order=" - "+RequestOrderDescription(this.m_request)+"\n"; price=" - "+RequestPriceDescription(this.m_request)+"\n"; sl=" - "+RequestStopLossDescription(this.m_request)+"\n"; tp=" - "+RequestTakeProfitDescription(this.m_request)+"\n"; type_time=" - "+RequestTypeTimeDescription(this.m_request)+"\n"; expiration=" - "+RequestExpirationDescription(this.m_request)+"\n"; request_data= ("================== "+ CMessage::Text(MSG_LIB_TEXT_REQUEST_DATAS)+" ==================\n"+ action+order+price+sl+tp+type_time+expiration+ " ==================\n" ); } else if(this.m_request.action==TRADE_ACTION_REMOVE) { order=" - "+RequestOrderDescription(this.m_request)+"\n"; request_data= ("================== "+ CMessage::Text(MSG_LIB_TEXT_REQUEST_DATAS)+" ==================\n"+ action+order+ " ==================\n" ); } else if(this.m_request.action==TRADE_ACTION_CLOSE_BY) { position=" - "+RequestPositionDescription(this.m_request)+"\n"; position_by=" - "+RequestPositionByDescription(this.m_request)+"\n"; magic=" - "+RequestMagicDescription(this.m_request)+"\n"; request_data= ("================== "+ CMessage::Text(MSG_LIB_TEXT_REQUEST_DATAS)+" ==================\n"+ action+position+position_by+magic+ " ==================\n" ); } string datas= ( " - "+this.TypeDescription()+"\n"+ " - "+this.IdDescription()+"\n"+ " - "+this.RetcodeDescription()+" \""+this.ReasonDescription()+"\"\n"+ " - "+this.TimeCreateDescription()+"\n"+ " - "+this.PriceCreateDescription()+"\n"+ " - "+this.TimeActivateDescription()+"\n"+ " - "+this.WaitingMSCDescription()+" ("+TimeToString(this.WaitingMSC()/1000,TIME_MINUTES|TIME_SECONDS)+")"+"\n"+ " - "+this.CurrentAttemptDescription()+"\n"+ " - "+this.TotalAttemptsDescription()+"\n" ); ::Print("================== ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_DATAS)," ==================\n",datas,request_data); } //+------------------------------------------------------------------+
En las variables de línea del método se reúnen las descripciones de todas las propiedades del objeto, incluyendo la descripción de los campos de la estructura de la solicitud comercial incluida en el objeto. El número de datos mostrados depende del tipo de acción ejecutada en la solicitud comercial, puesto que al darse acciones diferentes se usa un número distinto de campos de estructura de la solicitud comercial. Por eso, se comprueba el campo action y se muestran solo los campos que le corresponden. Primero se muestra la descripción de las variables del objeto, y a continuación, la descripción de los campos de la estructura de la solicitud. De esta forma, se muestran al completo en el diario todas las propiedades del objeto de solicitud pendiente, de acuerdo con el tipo de acción comercial de la solicitud (action).
Antes, hemos añadido al método de apertura de posiciones en la clase CTradeObj la propiedad adicional para el tipo de
rellenado de la orden.
Ahora, vamos a añadir esta misma propiedad a la definición del método privado de apertura de posiciones en la
clase CTrading:
//--- (1) Open a position, (2) place a pending order template<typename SL,typename TP> bool OpenPosition(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 string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
Y también añadimos las mismas propiedades a la definición de los métodos privados de apertura de posiciones Buy y Sell:
//--- Open (1) Buy, (2) Sell position template<typename SL,typename TP> bool OpenBuy(const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); template<typename SL,typename TP> bool OpenSell(const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
Por consiguiente, también añadimos estos parámetros en la implementación de estos métodos fuera del cuerpo de la clase:
//+------------------------------------------------------------------+ //| Open a position | //+------------------------------------------------------------------+ template<typename SL,typename TP> bool CTrading::OpenPosition(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 string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { //+------------------------------------------------------------------+ //| Open Buy position | //+------------------------------------------------------------------+ template<typename SL,typename TP> bool CTrading::OpenBuy(const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { //--- Return the result of sending a trading request from the OpenPosition() method return this.OpenPosition(POSITION_TYPE_BUY,volume,symbol,magic,sl,tp,comment,deviation,type_filling); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Open a Sell position | //+------------------------------------------------------------------+ template<typename SL,typename TP> bool CTrading::OpenSell(const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { //--- Return the result of sending a trading request from the OpenPosition() method return this.OpenPosition(POSITION_TYPE_SELL,volume,symbol,magic,sl,tp,comment,deviation,type_filling); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+
En la sección privada de la clase, declaramos el método que retorna el índice
del objeto de solicitud en la lista según el identificador:
//--- Look for the first free pending request ID int GetFreeID(void); //--- Return the request object index in the list by ID int GetIndexPendingRequestByID(const uchar id); public:
La implementación del temporizador de la clase ha experimentado algunos cambios.
Aquí tenemos el código completo con
la implementación del temporizador y la descripción de la lógica en los comentarios:
//+------------------------------------------------------------------+ //| Timer | //+------------------------------------------------------------------+ void CTrading::OnTimer(void) { //--- In a loop by the list of pending requests int total=this.m_list_request.Total(); for(int i=total-1;i>WRONG_VALUE;i--) { //--- receive the next request object CPendingReq *req_obj=this.m_list_request.At(i); if(req_obj==NULL) continue; //--- 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()) continue; //--- if the current attempt exceeds the defined number of trading attempts, //--- or the current time exceeds the waiting time of all attempts //--- remove the current request object and move on to the next one if(req_obj.CurrentAttempt()>req_obj.TotalAttempts() || req_obj.CurrentAttempt()>=UCHAR_MAX || (long)symbol_obj.Time()>long(req_obj.TimeCreate()+req_obj.WaitingMSC()*req_obj.TotalAttempts())) { if(this.m_log_level>LOG_LEVEL_NO_MSG) { ::Print(CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_DELETED)); req_obj.Print(); } this.m_list_request.Delete(i); continue; } //--- Get the pending request ID uchar id=this.GetPendReqID((uint)request.magic); //--- Get the list of orders/positions containing the order/position with the pending request ID CArrayObj *list=this.m_market.GetList(ORDER_PROP_PEND_REQ_ID,id,EQUAL); if(::CheckPointer(list)==POINTER_INVALID) continue; //--- If the order/position is present, the request is handled: remove it and proceed to the next if(list.Total()>0) { this.m_list_request.Delete(i); continue; } //--- Set the request activation time in the request object req_obj.SetTimeActivate(req_obj.TimeCreate()+req_obj.WaitingMSC()*(req_obj.CurrentAttempt()+1)); //--- If the current time is less than the request activation time, //--- this is not the request time - move on to the next request in the list if((long)symbol_obj.Time()<(long)req_obj.TimeActivate()) continue; //--- Set the attempt number in the request object req_obj.SetCurrentAttempt(uchar(req_obj.CurrentAttempt()+1)); //--- Display the number of the trading attempt in the journal if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(CMessage::Text(MSG_LIB_TEXT_RE_TRY_N)+(string)req_obj.CurrentAttempt()); //--- Depending on the type of action performed in the trading request switch(request.action) { //--- Open a position case TRADE_ACTION_DEAL : this.OpenPosition((ENUM_POSITION_TYPE)request.type,request.volume,request.symbol,request.magic,request.sl,request.tp,request.comment,request.deviation,request.type_filling); 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; //--- default: break; } } } //+------------------------------------------------------------------+
Esperamos que todo esté lo suficientemente claro para el lector.
Vamos a añadir en el método de corrección de errores RequestErrorsCorrecting() la corrección del tipo de expiración al obtener el error "la fecha de expiración de la orden es incorrecta" (solo la parte del código con las correcciones):
//--- The specified type of order execution by balance is not supported if(this.IsPresentErorCode(10030)) request.type_filling=symbol_obj.GetCorrectTypeFilling(); //--- Invalid order expiration in a request - if(this.IsPresentErorCode(10022)) { //--- Set a correct order expiration type request.type_time=symbol_obj.GetCorrectTypeExpiration(); //--- if the expiration type is not supported as set by the expiration date and the expiration data is defined, reset the expiration date if(!symbol_obj.IsExpirationModeSpecified() && request.expiration>0) request.expiration=0; } //--- View the list of remaining errors and correct trading request parameters
Antes, añadimos al objeto de símbolo los nuevos métodos para obtener los precios Ask y corregimos los métodos para obtener los precios Bid. Ahora, necesitaremos sustituir todas las entradas de las líneas "Ask()" y "Bid()" en todo el listado por "AskLast()" y "BidLast()", respectivamente. Para ello, resulta muy cómodo usar la función de búsqueda con reemplazo (Ctrl+H) y, pasando por todo el código, sustituir todas las líneas necesarias. De esta forma, usaremos la selección automática de los precios adecuados en todos los lugares donde se requiera el uso de los precios Ask y Bid del objeto de símbolo.
Por ejemplo, el método de establecimiento del precio ahora será el siguiente con los precios reemplazados:
//+------------------------------------------------------------------+ //| Set trading request prices | //+------------------------------------------------------------------+ template <typename PS,typename SL,typename TP,typename PL> bool CTrading::SetPrices(const ENUM_ORDER_TYPE action,const PS price,const SL sl,const TP tp,const PL limit,const string source_method,CSymbol *symbol_obj) { //--- Reset prices ::ZeroMemory(this.m_request); //--- Update all data by symbol if(!symbol_obj.RefreshRates()) { this.AddErrorCodeToList(10021); // No quotes to handle the request return false; } //--- Open/close price if(price>0) { //--- price parameter type (double) - normalize the price up to Digits(), since the price has been passed if(typename(price)=="double") this.m_request.price=::NormalizeDouble(price,symbol_obj.Digits()); //--- price parameter type (int) - the distance has been passed else if(typename(price)=="int" || typename(price)=="uint" || typename(price)=="long" || typename(price)=="ulong") { //--- Calculate the order price switch((int)action) { //--- Pending order case ORDER_TYPE_BUY_LIMIT : this.m_request.price=::NormalizeDouble(symbol_obj.AskLast()-price*symbol_obj.Point(),symbol_obj.Digits()); break; case ORDER_TYPE_BUY_STOP : case ORDER_TYPE_BUY_STOP_LIMIT : this.m_request.price=::NormalizeDouble(symbol_obj.AskLast()+price*symbol_obj.Point(),symbol_obj.Digits()); break; case ORDER_TYPE_SELL_LIMIT : this.m_request.price=::NormalizeDouble(symbol_obj.BidLast()+price*symbol_obj.Point(),symbol_obj.Digits()); break; case ORDER_TYPE_SELL_STOP : case ORDER_TYPE_SELL_STOP_LIMIT : this.m_request.price=::NormalizeDouble(symbol_obj.BidLast()-price*symbol_obj.Point(),symbol_obj.Digits()); break; //--- Default - current position open prices default : this.m_request.price= ( this.DirectionByActionType((ENUM_ACTION_TYPE)action)==ORDER_TYPE_BUY ? ::NormalizeDouble(symbol_obj.AskLast(),symbol_obj.Digits()) : ::NormalizeDouble(symbol_obj.BidLast(),symbol_obj.Digits()) ); break; } } //--- unsupported price types - display the message and return 'false' else { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(source_method,CMessage::Text(MSG_LIB_TEXT_UNSUPPORTED_PR_TYPE)); return false; } } //--- If no price is specified, use the current prices else { this.m_request.price= ( this.DirectionByActionType((ENUM_ACTION_TYPE)action)==ORDER_TYPE_BUY ? ::NormalizeDouble(symbol_obj.AskLast(),symbol_obj.Digits()) : ::NormalizeDouble(symbol_obj.BidLast(),symbol_obj.Digits()) ); } //--- StopLimit order price or distance if(limit>0) { //--- limit order price parameter type (double) - normalize the price up to Digits(), since the price has been passed if(typename(limit)=="double") this.m_request.stoplimit=::NormalizeDouble(limit,symbol_obj.Digits()); //--- limit order price parameter type (int) - the distance has been passed else if(typename(limit)=="int" || typename(limit)=="uint" || typename(limit)=="long" || typename(limit)=="ulong") { //--- Calculate a limit order price if(this.DirectionByActionType((ENUM_ACTION_TYPE)action)==ORDER_TYPE_BUY) this.m_request.stoplimit=::NormalizeDouble(this.m_request.price-limit*symbol_obj.Point(),symbol_obj.Digits()); else this.m_request.stoplimit=::NormalizeDouble(this.m_request.price+limit*symbol_obj.Point(),symbol_obj.Digits()); } //--- unsupported limit order price types - display the message and return 'false' else { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(source_method,CMessage::Text(MSG_LIB_TEXT_UNSUPPORTED_PL_TYPE)); return false; } } //--- Order price stop order prices are calculated from double price_open= ( (action==ORDER_TYPE_BUY_STOP_LIMIT || action==ORDER_TYPE_SELL_STOP_LIMIT) && limit>0 ? this.m_request.stoplimit : this.m_request.price ); //--- StopLoss if(sl>0) { //--- StopLoss parameter type (double) - normalize the price up to Digits(), since the price has been passed if(typename(sl)=="double") this.m_request.sl=::NormalizeDouble(sl,symbol_obj.Digits()); //--- StopLoss parameter type (int) - calculate the placement distance else if(typename(sl)=="int" || typename(sl)=="uint" || typename(sl)=="long" || typename(sl)=="ulong") { //--- Calculate the StopLoss price if(this.DirectionByActionType((ENUM_ACTION_TYPE)action)==ORDER_TYPE_BUY) this.m_request.sl=::NormalizeDouble(price_open-sl*symbol_obj.Point(),symbol_obj.Digits()); else this.m_request.sl=::NormalizeDouble(price_open+sl*symbol_obj.Point(),symbol_obj.Digits()); } //--- unsupported StopLoss types - display the message and return 'false' else { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(source_method,CMessage::Text(MSG_LIB_TEXT_UNSUPPORTED_SL_TYPE)); return false; } } //--- TakeProfit if(tp>0) { //--- TakeProfit parameter type (double) - normalize the price up to Digits(), since the price has been passed if(typename(tp)=="double") this.m_request.tp=::NormalizeDouble(tp,symbol_obj.Digits()); //--- TakeProfit parameter type (int) - calculate the placement distance else if(typename(tp)=="int" || typename(tp)=="uint" || typename(tp)=="long" || typename(tp)=="ulong") { if(this.DirectionByActionType((ENUM_ACTION_TYPE)action)==ORDER_TYPE_BUY) this.m_request.tp=::NormalizeDouble(price_open+tp*symbol_obj.Point(),symbol_obj.Digits()); else this.m_request.tp=::NormalizeDouble(price_open-tp*symbol_obj.Point(),symbol_obj.Digits()); } //--- unsupported TakeProfit types - display the message and return 'false' else { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(source_method,CMessage::Text(MSG_LIB_TEXT_UNSUPPORTED_TP_TYPE)); return false; } } //--- All prices are recorded return true; } //+------------------------------------------------------------------+
No vamos a mostrar aquí todos los códigos con las sustituiciones realizadas, pues ya han sido todos corregidos y se adjuntan en los archivos al final del artículo.
En la implementación del método privado de colocación de una orden pendiente, añadimos el bloque de creación de la solicitud comercial pendiente al darse el error recibido del servidor:
//+------------------------------------------------------------------+ //| Place a pending order | //+------------------------------------------------------------------+ template<typename PS,typename PL,typename SL,typename TP> bool CTrading::PlaceOrder(const ENUM_ORDER_TYPE order_type, const double volume, const string symbol, const PS price_stop, const PL price_limit=0, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { bool res=true; 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 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_stop,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 false; } //--- In case of trading limitations, funds insufficiency, //--- there are limitations on StopLevel - play the error sound and exit this.m_request.volume=volume; this.m_request.type_filling=type_filling; this.m_request.type_time=type_time; this.m_request.expiration=expiration; ENUM_ERROR_CODE_PROCESSING_METHOD method=this.CheckErrors(this.m_request.volume, this.m_request.price, action, order_type, symbol_obj, trade_obj, DFUN, this.m_request.stoplimit, this.m_request.sl, this.m_request.tp); if(method!=ERROR_CODE_PROCESSING_METHOD_OK) { //--- If trading is completely disabled if(method==ERROR_CODE_PROCESSING_METHOD_DISABLE) { trade_obj.SetResultRetcode(MSG_LIB_TEXT_TRADING_DISABLE); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(CMessage::Text(MSG_LIB_TEXT_TRADING_DISABLE)); if(this.IsUseSounds()) trade_obj.PlaySoundError(action,order_type); return false; } //--- If the check result is "abort trading operation" - set the last error code to the return structure, //--- display a journal message, play the error sound and exit if(method==ERROR_CODE_PROCESSING_METHOD_EXIT) { int code=this.m_list_errors.At(this.m_list_errors.Total()-1); if(code!=NULL) { trade_obj.SetResultRetcode(code); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); } if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(CMessage::Text(MSG_LIB_TEXT_TRADING_OPERATION_ABORTED)); if(this.IsUseSounds()) trade_obj.PlaySoundError(action,order_type); return false; } //--- If the check result is "waiting" - set the last error code to the return structure and display the message in the journal if(method==ERROR_CODE_PROCESSING_METHOD_WAIT) { int code=this.m_list_errors.At(this.m_list_errors.Total()-1); if(code!=NULL) { trade_obj.SetResultRetcode(code); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); } if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(CMessage::Text(MSG_LIB_TEXT_CREATE_PENDING_REQUEST)); //--- Instead of creating a pending request, we temporarily wait the required time period (the CheckErrors() method result is returned) ::Sleep(method); symbol_obj.Refresh(); } //--- If the check result is "create a pending request", do nothing temporarily if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_PENDING_REQUEST) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(CMessage::Text(MSG_LIB_TEXT_CREATE_PENDING_REQUEST)); } } //--- In the loop by the number of attempts for(int i=0;i<this.m_total_try;i++) { //--- Send the request res=trade_obj.SetOrder(order_type, this.m_request.volume, this.m_request.price, this.m_request.sl, this.m_request.tp, this.m_request.stoplimit, magic, comment, this.m_request.expiration, this.m_request.type_time, this.m_request.type_filling); //--- If the request is executed successfully or the asynchronous order sending mode is set, play the success sound //--- set for a symbol trading object for this type of trading operation and return 'true' if(res || trade_obj.IsAsyncMode()) { if(this.IsUseSounds()) trade_obj.PlaySoundSuccess(action,order_type); return true; } //--- If the request is not successful, play the error sound set for a symbol trading object for this type of trading operation else { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(CMessage::Text(MSG_LIB_TEXT_TRY_N),string(i+1),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(trade_obj.GetResultRetcode())); if(this.IsUseSounds()) trade_obj.PlaySoundError(action,order_type); method=this.ResultProccessingMethod(trade_obj.GetResultRetcode()); //--- If "Disable trading for the EA" is received as a result of sending a request, enable the disabling flag and end the attempt loop if(method==ERROR_CODE_PROCESSING_METHOD_DISABLE) { this.SetTradingDisableFlag(true); break; } //--- If "Exit the trading method" is received as a result of sending a request, end the attempt loop if(method==ERROR_CODE_PROCESSING_METHOD_EXIT) { break; } //--- If "Correct the parameters and repeat" is received as a result of sending a request - //--- correct the parameters and start the next iteration if(method==ERROR_CODE_PROCESSING_METHOD_CORRECT) { this.RequestErrorsCorrecting(this.m_request,order_type,trade_obj.SpreadMultiplier(),symbol_obj,trade_obj); continue; } //--- If "Update data and repeat" is received as a result of sending a request - //--- update data and start the next iteration if(method==ERROR_CODE_PROCESSING_METHOD_REFRESH) { symbol_obj.Refresh(); continue; } //--- If "Wait and repeat" is received as a result of sending a request //--- create a pending request and end the loop if(method>ERROR_CODE_PROCESSING_METHOD_REFRESH) { //--- If the trading request magic number, has no pending request ID if(this.GetPendReqID((uint)magic)==0) { //--- Waiting time in milliseconds: //--- for the "Wait and repeat" handling method, the waiting value corresponds to the 'method' value, //--- for the "Create a pending request" handling method - till there is a zero waiting time ulong wait=(method>ERROR_CODE_PROCESSING_METHOD_PENDING ? method : 0); //--- Look for the least of the possible IDs. If failed to find //--- or in case of an error while updating the current symbol data, return 'false' int id=this.GetFreeID(); if(id<1 || !symbol_obj.RefreshRates()) return false; //--- Write the request ID to the magic number, while a symbol name is set in the request structure, //--- set the trading operation and order types, comment and position type, filling and expiration type uint mn=(magic==ULONG_MAX ? (uint)trade_obj.GetMagic() : (uint)magic); this.SetPendReqID((uchar)id,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; 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()); //--- Pass the number of trading attempts to the pending request uchar attempts=(this.m_total_try < 1 ? 1 : this.m_total_try); this.CreatePendingRequest((uchar)id,attempts,wait,this.m_request,trade_obj.GetResultRetcode(),symbol_obj); break; } } } } //--- Return the result of sending a trading request in a symbol trading object return res; } //+------------------------------------------------------------------+
Todas las acciones relacionadas con la creación de una solicitud pendiente están descritas en los comentarios al código, y creemos que son bastante comprensibles. En cualquier caso, el lector podrá plantear cualquier duda en los comentarios al artículo.
Para simplificar la depuración y hacer visible el resultado de la creación de la solicitud pendiente, añadimos al método de creación del objeto de solicitud pendiente la muestra en el diario de las propiedades de la solicitud nuevamente creada:
//+------------------------------------------------------------------+ //| Create a pending request | //+------------------------------------------------------------------+ bool CTrading::CreatePendingRequest(const uchar id,const uchar attempts,const ulong wait,const MqlTradeRequest &request,const int retcode,CSymbol *symbol_obj) { //--- Create a new pending request object CPendingReq *req_obj=new CPendingReq(id,symbol_obj.BidLast(),symbol_obj.Time(),request,retcode); 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; } //--- Filled in the fields 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(this.m_log_level>LOG_LEVEL_NO_MSG) { ::Print(CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_CREATED)," #",req_obj.ID(),":"); req_obj.Print(); } return true; } //+------------------------------------------------------------------+
Al final del listado de la clase comercial, escribimos la implementación del método que retorna el índice de un objeto de solicitud en la lista según su identificador:
//+------------------------------------------------------------------+ //| Return the request object index in the list by ID | //+------------------------------------------------------------------+ int CTrading::GetIndexPendingRequestByID(const uchar id) { CPendingReq *req=new CPendingReq(); if(req==NULL) return WRONG_VALUE; req.SetID(id); this.m_list_request.Sort(); int index=this.m_list_request.Search(req); delete req; return index; } //+------------------------------------------------------------------+
Transmitimos al método el identificador buscado, creamos un objeto de solicitud temporal y establecemos para este el identificador transmitido al método.
A continuación, establecemos para la lista que contiene los objetos de solicitud la bandera de lista clasificada. Por defecto, el modo de clasificación es igual a cero, y precisamente en este modo, en el método virtual Compare() de la clase CPendingReq, tenemos organizada la comparación de los objetos según el identificador. Por eso, podemos usar tranquilamente el método de búsqueda de objetos Search() en la matriz dinámica de punteros a los objetos. El método retorna el índice del objeto encontrado en la lista, o bien -1, si el objeto no ha sido encontrado. Antes de salir del método, eliminamos el objeto de solicitud temporal, y luego retornamos el índice obtenido del objeto encontrado, o bien -1.
Lo único que queda por hacer es añadir a la clase del objeto básico de la biblioteca CEngine (a las definiciones de sus métodos de envío de solicitudes comerciales) el parámetro que indica el tipo de rellenado de las órdenes.
//--- Open (1) Buy, (2) Sell position template<typename SL,typename TP> bool OpenBuy(const double volume, const string symbol, const ulong magic=ULONG_MAX, SL sl=0, TP tp=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); template<typename SL,typename TP> bool OpenSell(const double volume, const string symbol, const ulong magic=ULONG_MAX, SL sl=0, TP tp=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); //--- Устанавливает отложенный ордер (1) BuyStop, (2) BuyLimit, (3) BuyStopLimit template<typename PR,typename SL,typename TP> bool PlaceBuyStop(const double volume, const string symbol, const PR price, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); template<typename PR,typename SL,typename TP> bool PlaceBuyLimit(const double volume, const string symbol, const PR price, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); template<typename PR,typename PL,typename SL,typename TP> bool PlaceBuyStopLimit(const double volume, const string symbol, const PR price_stop, const PL price_limit, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); //--- Устанавливает отложенный ордер (1) SellStop, (2) SellLimit, (3) SellStopLimit template<typename PR,typename SL,typename TP> bool PlaceSellStop(const double volume, const string symbol, const PR price, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); template<typename PR,typename SL,typename TP> bool PlaceSellLimit(const double volume, const string symbol, const PR price, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); template<typename PR,typename PL,typename SL,typename TP> bool PlaceSellStopLimit(const double volume, const string symbol, const PR price_stop, const PL price_limit, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
Si el valor por defecto es igual a -1, los valores correctos de los tipos de rellenado de órdenes se tomarán de los objetos comerciales de los símbolos en los que se realizará la operación comercial.
Vamos a añadir los mismos parámetros a los códigos de implementación de estos métodos comerciales:
//+------------------------------------------------------------------+ //| Open Buy position | //+------------------------------------------------------------------+ template<typename SL,typename TP> bool CEngine::OpenBuy(const double volume, const string symbol, const ulong magic=ULONG_MAX, SL sl=0,TP tp=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.OpenBuy(volume,symbol,magic,sl,tp,comment,deviation,type_filling); } //+------------------------------------------------------------------+ //| Open a Sell position | //+------------------------------------------------------------------+ template<typename SL,typename TP> bool CEngine::OpenSell(const double volume, const string symbol, const ulong magic=ULONG_MAX, SL sl=0,TP tp=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.OpenSell(volume,symbol,magic,sl,tp,comment,deviation,type_filling); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Place BuyStop pending order | //+------------------------------------------------------------------+ template<typename PR,typename SL,typename TP> bool CEngine::PlaceBuyStop(const double volume, const string symbol, const PR price, const SL sl=0, const TP tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.PlaceBuyStop(volume,symbol,price,sl,tp,magic,comment,expiration,type_time,type_filling); } //+------------------------------------------------------------------+ //| Place BuyLimit pending order | //+------------------------------------------------------------------+ template<typename PR,typename SL,typename TP> bool CEngine::PlaceBuyLimit(const double volume, const string symbol, const PR price, const SL sl=0, const TP tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.PlaceBuyLimit(volume,symbol,price,sl,tp,magic,comment,expiration,type_time,type_filling); } //+------------------------------------------------------------------+ //| Place BuyStopLimit pending order | //+------------------------------------------------------------------+ template<typename PR,typename PL,typename SL,typename TP> bool CEngine::PlaceBuyStopLimit(const double volume, const string symbol, const PR price_stop, const PL price_limit, const SL sl=0, const TP tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.PlaceBuyStopLimit(volume,symbol,price_stop,price_limit,sl,tp,magic,comment,expiration,type_time,type_filling); } //+------------------------------------------------------------------+ //| Place SellStop pending order | //+------------------------------------------------------------------+ template<typename PR,typename SL,typename TP> bool CEngine::PlaceSellStop(const double volume, const string symbol, const PR price, const SL sl=0, const TP tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.PlaceSellStop(volume,symbol,price,sl,tp,magic,comment,expiration,type_time,type_filling); } //+------------------------------------------------------------------+ //| Place SellLimit pending order | //+------------------------------------------------------------------+ template<typename PR,typename SL,typename TP> bool CEngine::PlaceSellLimit(const double volume, const string symbol, const PR price, const SL sl=0, const TP tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.PlaceSellLimit(volume,symbol,price,sl,tp,magic,comment,expiration,type_time,type_filling); } //+------------------------------------------------------------------+ //| Place SellStopLimit pending order | //+------------------------------------------------------------------+ template<typename PR,typename PL,typename SL,typename TP> bool CEngine::PlaceSellStopLimit(const double volume, const string symbol, const PR price_stop, const PL price_limit, const SL sl=0, const TP tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.PlaceSellStopLimit(volume,symbol,price_stop,price_limit,sl,tp,magic,comment,expiration,type_time,type_filling); } //+------------------------------------------------------------------+
Estos son todos los cambios y ajustes por ahora.
Simulación
Para poner a prueba las solicitudes pendientes en cuanto a la colocación de órdenes pendientes, vamos a tomar el asesor del artículo anterior y guardarlo en la nueva carpeta \MQL5\Experts\TestDoEasy\Part27\ con el nuevo nombre TestDoEasyPart27.mq5.
En la función de inicialización de la biblioteca del asesor, añadimos el establecimiento de los valores correctos de los tipos de rellenado de las órdenes y sus tipos de expiración para todos los objetos comerciales de todos los símbolos utilizados en el asesor:
//+------------------------------------------------------------------+ //| Initializing DoEasy library | //+------------------------------------------------------------------+ void OnInitDoEasy() { //--- Check if working with the full list is selected used_symbols_mode=InpModeUsedSymbols; if((ENUM_SYMBOLS_MODE)used_symbols_mode==SYMBOLS_MODE_ALL) { int total=SymbolsTotal(false); string ru_n="\nКоличество символов на сервере "+(string)total+".\nМаксимальное количество: "+(string)SYMBOLS_COMMON_TOTAL+" символов."; string en_n="\nNumber of symbols on server "+(string)total+".\nMaximum number: "+(string)SYMBOLS_COMMON_TOTAL+" symbols."; string caption=TextByLanguage("Внимание!","Attention!"); string ru="Выбран режим работы с полным списком.\nВ этом режиме первичная подготовка списка коллекции символов может занять длительное время."+ru_n+"\nПродолжить?\n\"Нет\" - работа с текущим символом \""+Symbol()+"\""; string en="Full list mode selected.\nIn this mode, the initial preparation of the collection symbols list may take a long time."+en_n+"\nContinue?\n\"No\" - working with the current symbol \""+Symbol()+"\""; string message=TextByLanguage(ru,en); int flags=(MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2); int mb_res=MessageBox(message,caption,flags); switch(mb_res) { case IDNO : used_symbols_mode=SYMBOLS_MODE_CURRENT; break; default: break; } } //--- Fill in the array of used symbols used_symbols=InpUsedSymbols; CreateUsedSymbolsArray((ENUM_SYMBOLS_MODE)used_symbols_mode,used_symbols,array_used_symbols); //--- Set the type of the used symbol list in the symbol collection engine.SetUsedSymbols(array_used_symbols); //--- Displaying the selected mode of working with the symbol object collection Print(engine.ModeSymbolsListDescription(),TextByLanguage(". Number of used symbols: ",". Number of symbols used: "),engine.GetSymbolsCollectionTotal()); //--- Create resource text files engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_01",TextByLanguage("Звук упавшей монетки 1","Falling coin 1"),sound_array_coin_01); engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_02",TextByLanguage("Звук упавших монеток","Falling coins"),sound_array_coin_02); engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_03",TextByLanguage("Звук монеток","Coins"),sound_array_coin_03); engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_04",TextByLanguage("Звук упавшей монетки 2","Falling coin 2"),sound_array_coin_04); engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_01",TextByLanguage("Звук щелчка по кнопке 1","Button click 1"),sound_array_click_01); engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_02",TextByLanguage("Звук щелчка по кнопке 2","Button click 2"),sound_array_click_02); engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_03",TextByLanguage("Звук щелчка по кнопке 3","Button click 3"),sound_array_click_03); engine.CreateFile(FILE_TYPE_WAV,"sound_array_cash_machine_01",TextByLanguage("Звук кассового аппарата","Cash machine"),sound_array_cash_machine_01); engine.CreateFile(FILE_TYPE_BMP,"img_array_spot_green",TextByLanguage("Изображение \"Зелёный светодиод\"","Image \"Green Spot lamp\""),img_array_spot_green); engine.CreateFile(FILE_TYPE_BMP,"img_array_spot_red",TextByLanguage("Изображение \"Красный светодиод\"","Image \"Red Spot lamp\""),img_array_spot_red); //--- Pass all existing collections to the trading class engine.TradingOnInit(); //--- Set the default magic number for all used symbols engine.TradingSetMagic(engine.SetCompositeMagicNumber(magic_number)); //--- Set synchronous passing of orders for all used symbols engine.TradingSetAsyncMode(false); //--- Set the number of trading attempts in case of an error engine.TradingSetTotalTry(InpTotalAttempts); //--- Set correct order expiration and filling types to all trading objects engine.TradingSetCorrectTypeExpiration(); engine.TradingSetCorrectTypeFilling(); //--- Set standard sounds for trading objects of all used symbols engine.SetSoundsStandart(); //--- Set the general flag of using sounds engine.SetUseSounds(InpUseSounds); //--- Set the spread multiplier for symbol trading objects in the symbol collection engine.SetSpreadMultiplier(InpSpreadMultiplier); //--- Set controlled values for symbols //--- Get the list of all collection symbols CArrayObj *list=engine.GetListAllUsedSymbols(); if(list!=NULL && list.Total()!=0) { //--- In a loop by the list, set the necessary values for tracked symbol properties //--- By default, the LONG_MAX value is set to all properties, which means "Do not track this property" //--- It can be enabled or disabled (by setting the value less than LONG_MAX or vice versa - set the LONG_MAX value) at any time and anywhere in the program /* for(int i=0;i<list.Total();i++) { CSymbol* symbol=list.At(i); if(symbol==NULL) continue; //--- Set control of the symbol price increase by 100 points symbol.SetControlBidInc(100000*symbol.Point()); //--- Set control of the symbol price decrease by 100 points symbol.SetControlBidDec(100000*symbol.Point()); //--- Set control of the symbol spread increase by 40 points symbol.SetControlSpreadInc(400); //--- Set control of the symbol spread decrease by 40 points symbol.SetControlSpreadDec(400); //--- Set control of the current spread by the value of 40 points symbol.SetControlSpreadLevel(400); } */ } //--- Set controlled values for the current account CAccount* account=engine.GetAccountCurrent(); if(account!=NULL) { //--- Set control of the profit increase to 10 account.SetControlledValueINC(ACCOUNT_PROP_PROFIT,10.0); //--- Set control of the funds increase to 15 account.SetControlledValueINC(ACCOUNT_PROP_EQUITY,15.0); //--- Set profit control level to 20 account.SetControlledValueLEVEL(ACCOUNT_PROP_PROFIT,20.0); } } //+------------------------------------------------------------------+
Y, por raro que parezca, estos son todos los cambios en el asesor. El resto ya lo hemos hecho en los códigos de la biblioteca.
Para poner a prueba el funcionamiento de las solicitudes comerciales pendientes en cuanto a la colocación de órdenes pendientes, vamos a hacer exactamente los mismo que la otra vez: desconectamos internet, intentamos colocar una orden pendiente, obtenemos el error de ausencia de conexión con el servidor comercial, recibimos un mensaje en el diario sobre la creación de una solicitud pendiente e imprimimos sus parámetros. A continuación, conectamos de nuevo internet. Como resultado de ello, debe activarse la solicitud pendiente y después colocarse la orden pendiente solicitada.
Comprobémoslo.
Compilamos e iniciamos el asesor. Desconectamos internet y esperamos a que aparezca este icono en la esquina inferior derecha:
Después de desconectar internet y pulsar el botón Sell, el servidor comercial retornará error, y en el diario se mostrarán las entradas sobre el
error y la creación de una solicitud pendiente.
A continuación, conectamos internet, reestableciendo con ello la conexión con el servidor comercial:
En cuanto se reestablezca, la biblioteca comenzará a procesar la solicitud pendiente, enviando esta al servidor.
Como resultado, tendremos una orden pendiente colocada y las siguientes entradas en el diario:
2019.12.05 16:38:32.591 CTrading::PlaceOrder<uint,int,uint,uint>: Invalid request: 2019.12.05 16:38:32.591 No connection with the trade server 2019.12.05 16:38:32.591 Correction of trade request parameters ... 2019.12.05 16:38:32.610 Trading attempt #1. Error: No connection with the trade server 2019.12.05 16:38:32.610 Pending request created #1: 2019.12.05 16:38:32.610 ================== Pending trade request's parameters ================== 2019.12.05 16:38:32.610 - Pending request type: Pending request that was created as a result of the server code 2019.12.05 16:38:32.610 - Trade request ID: 1 2019.12.05 16:38:32.610 - Return code based on which the request was created: 10031 "No connection with the trade server" 2019.12.05 16:38:32.610 - Request creation time: 2019.12.05 11:37:39.054 2019.12.05 16:38:32.610 - Price at time of request create: : 1.10913 2019.12.05 16:38:32.610 - Request activation time: 2019.12.05 11:37:59.054 2019.12.05 16:38:32.610 - Waiting time between trading attempts: 20000 (00:00:20) 2019.12.05 16:38:32.610 - Current trading attempt: Waiting for the onset time of the first trading attempt 2019.12.05 16:38:32.610 - Total trade attempts: 5 2019.12.05 16:38:32.610 ================== Trade request's parameters ================== 2019.12.05 16:38:32.610 - Trade operation type: Place pending order 2019.12.05 16:38:32.610 - Trade symbol: EURUSD 2019.12.05 16:38:32.610 - Requested volume for a deal in lots: 0.10 2019.12.05 16:38:32.610 - Price: 1.10963 2019.12.05 16:38:32.610 - StopLimit level of the order: Value not set 2019.12.05 16:38:32.610 - Stop Loss level of the order: 1.11113 2019.12.05 16:38:32.610 - Take Profit level of the order: 1.10813 2019.12.05 16:38:32.610 - Order type: Pending order Sell Limit 2019.12.05 16:38:32.610 - Order execution type: The order is executed exclusively in the specified volume, otherwise it is canceled (FOK) 2019.12.05 16:38:32.610 - Order expiration type: Good till cancel order 2019.12.05 16:38:32.610 - Order expiration time: Value not set 2019.12.05 16:38:32.610 - Magic number: 24379515 2019.12.05 16:38:32.610 - Order comment: "Pending order SellLimit" 2019.12.05 16:38:32.610 ================== 2019.12.05 16:38:32.610 2019.12.05 16:38:45.185 Retry trading attempt #1 2019.12.05 16:38:45.185 CTrading::PlaceOrder<double,double,double,double>: Invalid request: 2019.12.05 16:38:45.185 Trading is prohibited for the current account 2019.12.05 16:38:45.185 Correction of trade request parameters ... 2019.12.05 16:38:45.185 Trading operation aborted 2019.12.05 16:38:45.512 Retry trading attempt #2 2019.12.05 16:38:45.512 CTrading::PlaceOrder<double,double,double,double>: Invalid request: 2019.12.05 16:38:45.512 Trading is prohibited for the current account 2019.12.05 16:38:45.512 Correction of trade request parameters ... 2019.12.05 16:38:45.512 Trading operation aborted 2019.12.05 16:38:45.852 Retry trading attempt #3 2019.12.05 16:38:46.405 - Pending order placed: 2019.12.05 11:38:45.933 - 2019.12.05 16:38:46.405 EURUSD Placed 0.10 Pending order Sell Limit #491179168 at price 1.10963, sl 1.11113, tp 1.10813, Magic number 24379515 (123), G1: 4, G2: 7, ID: 1 2019.12.05 16:38:46.472 OnDoEasyEvent: Pending order placed
Qué podemos ver aquí:
Primero, obtenemos el error del servidor comercial "No hay conexión con el servidor
comercial".
A continuación, obtenemos un mensaje sobre la creación de
una solicitud comercial pendiente con el identificador #1; en el mensaje, además, se encuentran todos los
parámetros del objeto y los parámetros de la estructura de la solicitud
en este objeto.
Después de ello, se ejecutan los intentos
repetidos #1 y #2, enviados desde la solicitud comercial pendiente. Como resultado de los mismos, se retorna el error de ausencia de permiso
de comercio en la cuenta (después de reestablecer la conexión, no ha tenido tiempo de activarse el permiso para comerciar en la
cuenta).
Y, finalmente, al tercer intento enviado desde el objeto de
solicitud comercial pendiente, logramos colocar la orden pendiente.
En la descripción del número mágico de la orden pendiente colocada, tenemos el número mágico 24379515, después el identificador del número mágico establecido en los parámetros del asesor (123), el indentificador del primer grupo "G1: 4", el indentificador del segundo grupo "G2: 7" y el indentificador de la solicitud pendiente "ID: 1"
Por favor, tenga en cuenta:
¡Insistimos en que los resultados del trabajo con la clase comercial con solicitudes pendientes que hemos descrito en el presente artículo no se deben utilizar en ningún caso en proyectos propios para el comercio real!Este artículo, así como sus materiales y resultados, constituyen solo una continuación del concepto de las solicitudes pendientes y, en el presente estadio, no suponen de forma alguna un producto definitivo a usar en cuentas reales, sino exclusivamente en el modo demo o el simulador.
¿Qué es lo próximo?
En el próximo artículo, continuaremos trabajando con las posibilidades básicas de las solicitudes pendientes: la modificación, la eliminación y el cierre de órdenes/posiciones.
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 datos
Parte
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
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/7418





- 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