Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte XXI): Clases comerciales - El objeto comercial multiplataforma básico
Contenido
Con este artículo, comenzamos un tema nuevo y bastante amplio: las clases comerciales.Concepto
Disponer de multitud de datos diferentes y lograr un acceso sencillo a los mismos en cualquier momento es algo magnífico. Pero estos datos
carecerán prácticamente de sentido si no podemos reaccionar a ellos según su sentido directo, es decir, comerciando. Naturalmente,
aparte de la funcionalidad que ya tenemos, vamos a necesitar una funcionalidad comercial.
Este apartado será bastante voluminoso, por lo que lo construiremos paso a paso.
- Necesitaremos tener la posibilidad de enviar cualquier orden comercial desde cualquier plataforma, MetaTrader 5 o MetaTrader 4. En este caso, además, sin tener que pensar desde qué plataforma precisamente estamos enviando la orden comercial, todo será igual.
- Por ende, necesitaremos comprobar preliminarmente si las solicitudes comerciales son correctas, para no sobrecargar el servidor
con solicitudes que ya sabemos erróneas.
- De esta forma, tendremos que tener en cuenta y procesar correctamente los códigos de retorno del servidor comercial. Porque, ¿qué es lo que hace en definitiva el asesor al enviar una orden al servidor? Mantiene un diálogo con el servidor, en forma de solicitud y respuesta. Y para que el asesor pueda comunicarse con el servidor, nuestra tarea consistirá en organizar este "canal de comunicación", es decir, crear los métodos de procesamiento de las respuestas del servidor comercial.
- Por este motivo, deberemos crear varias opciones de procesamiento de las respuestas del servidor, y es que a veces tenemos que abrir una
posición "preferiblemente a cualquier precio". Para ello, deberemos organizar la repetición del envío de las órdenes al servidor, en
el caso de que se nos deniegue la colocación de una orden: podemos, o bien corregir los parámetros de la solicitud comercial y enviarla de
nuevo, o bien dejar los parámetros como están, pero esperar el momento adecuado en el que la orden con dichos parámetros será aceptada,
enviando esta de inmediato. Además, también deberemos tener en cuenta el nivel de precio, para no enviar repetidamente una orden al
peor precio, conocido de antemano.
Sin embargo, a veces necesitamos simplemente enviar una orden comercial, e independientemente del resultado, continuar trabajando.
- Asimismo, deberemos implementar el trabajo con las clases comerciales de tal forma que, al ubicar un programa creado sobre la base de la
biblioteca
en el Mercado de mql5, no surja ningún problema: dicho programa tendrá que superar todas las comprobaciones sin complicación alguna.
Estos son los planeas mínimos e inmediatos en lo que respecta a las clases comerciales.
En cuanto a hoy, vamos a
analizar la creación del objeto comercial básico que envía una solicitud comercial al servidor desde cualquier plataforma de la misma
manera. Dicho objeto comercial, al enviar una solicitud al servidor, presupondrá que los parámetros de la solicitud comercial que se le han
transmitido han sido verificados y son correctos. Es decir, no se comprobará la corrección de los parámetros de este objeto, los parámetros
se comprobarán en otra clase comercial que se creará más tarde
Para ser honestos, debemos decir que la selección de una orden o posición según el ticket siempre se realizará en este objeto comercial, y
ya después, al crear la nueva clase comercial, trasladaremos a la misma dicha comprobación.
Dado que todo el trading se relaciona con un símbolo, el objeto comercial básico se contará entre los componentes del objeto de símbolo que
analizamos en el
artículo 14. El acceso a los objetos comerciales también lo organizaremos
posteriormente, en la clase comercial principal. En el presente artículo, implementaremos el acceso a los objetos comerciales de los
símbolos de la clase básica de la biblioteca
CEngine, que ya analizamos en el artículo 3; y es que
precisamente en la clase básica se acumulan todos los datos del entorno. Precisamente allí tendremos a nuestra disposición cualquier
propiedad de la cuenta y el símbolo necesaria para trabajar con las clases comerciales.
Creando el objeto comercial único
Para registrar el funcionamiento de las clases comerciales, necesitaremos crear una enumeración con los niveles de registro en el archivo de
la biblioteca
Defines.mqh.
Vamos a añadir al final del listado la enumeración
necesaria:
//+------------------------------------------------------------------+ //| Data for working with trading classes | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Logging level | //+------------------------------------------------------------------+ enum ENUM_LOG_LEVEL { LOG_LEVEL_NO_MSG, // Trading logging disabled LOG_LEVEL_ERROR_MSG, // Only trading errors LOG_LEVEL_ALL_MSG // Full logging }; //+------------------------------------------------------------------+
Para mostrar los mensajes en el diario, necesitaremos los textos de los mensajes y sus índices en la lista de mensajes de la biblioteca.
Añadimos
los índices necesarios en el archivo Datas.mqh:
MSG_LIB_SYS_NOT_SYMBOL_ON_SERVER, // Error. No such symbol on server MSG_LIB_SYS_NOT_SYMBOL_ON_LIST, // Error. No such symbol in the list of used symbols: MSG_LIB_SYS_FAILED_PUT_SYMBOL, // Failed to place to market watch. Error: MSG_LIB_SYS_ERROR_NOT_POSITION, // Error. Not a position: MSG_LIB_SYS_ERROR_NO_OPEN_POSITION_WITH_TICKET, // Error. No open position with ticket # MSG_LIB_SYS_ERROR_NO_PLACED_ORDER_WITH_TICKET, // Error. No placed order with ticket # MSG_LIB_SYS_ERROR_FAILED_CLOSE_POS, // Failed to closed position. Error MSG_LIB_SYS_ERROR_FAILED_MODIFY_ORD, // Failed to modify order. Error MSG_LIB_SYS_ERROR_UNABLE_PLACE_WITHOUT_TIME_SPEC, // Error: Cannot place order without explicitly specified expiration time MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ, // Error. Failed to get trading object MSG_LIB_SYS_ERROR_FAILED_GET_POS_OBJ, // Error. Failed to get position object MSG_LIB_SYS_ERROR_FAILED_GET_ORD_OBJ, // Error. Failed to get order object MSG_LIB_SYS_ERROR_FAILED_GET_SYM_OBJ, // Error. Failed to get symbol object MSG_LIB_SYS_ERROR_CODE_OUT_OF_RANGE, // Return code out of range of error codes MSG_LIB_TEXT_FAILED_ADD_TO_LIST, // failed to add to list MSG_LIB_TEXT_TIME_UNTIL_THE_END_DAY, // Order lifetime till the end of the current day to be used MSG_LIB_TEXT_SUNDAY, // Sunday MSG_ACC_MARGIN_MODE_RETAIL_EXCHANGE, // Exchange markets mode MSG_ACC_UNABLE_CLOSE_BY, // Close by is available only on hedging accounts MSG_ACC_SAME_TYPE_CLOSE_BY, // Error. Positions for close by are of the same type //--- CEngine MSG_ENG_NO_TRADE_EVENTS, // There have been no trade events since the last launch of EA MSG_ENG_FAILED_GET_LAST_TRADE_EVENT_DESCR, // Failed to get description of the last trading event MSG_ENG_FAILED_GET_MARKET_POS_LIST, // Failed to get the list of open positions MSG_ENG_FAILED_GET_PENDING_ORD_LIST, // Failed to get the list of placed orders MSG_ENG_NO_OPEN_POSITIONS, // No open positions MSG_ENG_NO_PLACED_ORDERS, // No placed orders };
Aquí se muestran solo las partes del archivo con "vinculación a la ubicación" donde debemos añadir las constantes de las enumeraciones de los índices.
Ahora, vamos a añadir las matrices de los mensajes de texto los mensajes necesarios, cuyos índices acabamos de determinar:
{"Ошибка. Такого символа нет на сервере","Error. There is no such symbol on the server"}, {"Ошибка. Такого символа нет в списке используемых символов: ","Error. This symbol is not in the list of the symbols used: "}, {"Не удалось поместить в обзор рынка. Ошибка: ","Failed to put in the market watch. Error: "}, {"Ошибка. Не позиция: ","Error. Not position: "}, {"Ошибка. Нет открытой позиции с тикетом #","Error. No open position with ticket #"}, {"Ошибка. Нет установленного ордера с тикетом #","Error. No placed order with ticket #"}, {"Не удалось закрыть позицию. Ошибка ","Could not close position. Error "}, {"Не удалось модифицировать ордер. Ошибка ","Failed to order modify. Error "}, {"Ошибка: невозможно разместить ордер без явно заданного его времени истечения","Error: Unable to place order without explicitly specified expiration time"}, {"Ошибка. Не удалось получить торговый объект","Error. Failed to get a trade object"}, {"Ошибка. Не удалось получить объект-позицию","Error. Failed to get position object"}, {"Ошибка. Не удалось получить объект-ордер","Error. Failed to get order object"}, {"Ошибка. Не удалось получить объект-символ","Error. Failed to get symbol object"}, {"Код возврата вне заданного диапазона кодов ошибок","Return code out of range of error codes"}, {"не удалось добавить в список","failed to add to the list"}, {"Будет использоваться время действия ордера до конца текущего дня","The order validity time until the end of the current day will be used"}, {"Воскресение","Sunday"}, {"Биржевой рынок","Exchange market mode"}, {"Закрытие встречным доступно только на счетах с типом \"Хеджинг\"","Close by opposite position is available only on accounts with the type \"Hedging\""}, {"Ошибка. Позиции для встречного закрытия имеют один и тот же тип","Error. Positions of the same type in a counterclosure request"}, //--- CEngine {"С момента последнего запуска ЕА торговых событий не было","There have been no trade events since the last launch of EA"}, {"Не удалось получить описание последнего торгового события","Failed to get the description of the last trading event"}, {"Не удалось получить список открытых позиций","Failed to get open positions list"}, {"Не удалось получить список установленных ордеров","Failed to get pending orders list"}, {"Нет открытых позиций","No open positions"}, {"Нет установленных ордеров","No placed orders"}, };
Aquí todo se realiza exactamente igual que al determinar las constantes de los índices mostrados, con la excepción de los lugares de inserción de los textos de los mensajes necesarios. En los archivos adjuntos al final del artículo se encuentra la versión completa de Datas.mqh mejorado, que se puede abrir y analizar.
Al enviar órdenes comerciales de apertura de posición, necesitaremos conocer el tipo de orden opuesto a la dirección de la posición cerrada
(en MQL5, el cierre de posición se realiza precisamente con la apertura de una posición opuesta, mientras que a la orden comercial se envía el
tipo de orden, y no el tipo de posición).
En el archivo de funciones de servicio de la biblioteca DELib.mqh, vamos a escribir dos funciones para
obtener el tipo de orden según la dirección de la posición, y el tipo de
orden opuesto a la dirección de la posición:
//+------------------------------------------------------------------+ //| Return an order type by a position type | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE OrderTypeByPositionType(ENUM_POSITION_TYPE type_position) { return(type_position==POSITION_TYPE_BUY ? ORDER_TYPE_BUY : ORDER_TYPE_SELL); } //+------------------------------------------------------------------+ //| Return a reverse order type by a position type | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE OrderTypeOppositeByPositionType(ENUM_POSITION_TYPE type_position) { return(type_position==POSITION_TYPE_BUY ? ORDER_TYPE_SELL : ORDER_TYPE_BUY); } //+------------------------------------------------------------------+
Ya hemos preparado todos los datos, ahora nos ocuparemos propiamente de la clase del objeto comercial.
Creamos en la carpeta de objetos de la biblioteca \MQL5\Include\DoEasy\Objects\ la subcarpeta Trade\, y
en ella, la nueva clase
CTradeObj en el archivo TradeObj.mqh.
De inmediato, incluimos
en el archivo creado nuevamente el archivo de funciones de servicio:
//+------------------------------------------------------------------+ //| TradeObj.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/es/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/es/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Archivos de inclusión | //+------------------------------------------------------------------+ #include "..\..\Services\DELib.mqh" //+------------------------------------------------------------------+
Añadimos al archivo de la clase todas las variables de miembro de clase y los métodos necesarios:
//+------------------------------------------------------------------+ //| TradeObj.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\..\Services\DELib.mqh" //+------------------------------------------------------------------+ //| Trading object class | //+------------------------------------------------------------------+ class CTradeObj { private: MqlTick m_tick; // Tick structure for receiving prices MqlTradeRequest m_request; // Trade request structure MqlTradeResult m_result; // trade request execution result ENUM_ACCOUNT_MARGIN_MODE m_margin_mode; // Margin calculation mode ENUM_ORDER_TYPE_FILLING m_type_filling; // Filling policy ENUM_ORDER_TYPE_TIME m_type_expiration; // Order expiration type int m_symbol_expiration_flags; // Flags of order expiration modes for a trading object symbol ulong m_magic; // Magic number string m_symbol; // Symbol string m_comment; // Comment ulong m_deviation; // Slippage in points double m_volume; // Volume datetime m_expiration; // Order expiration time (for ORDER_TIME_SPECIFIED type order) bool m_async_mode; // Flag of asynchronous sending of a trade request ENUM_LOG_LEVEL m_log_level; // Logging level int m_stop_limit; // Distance of placing a StopLimit order in points public: //--- Constructor CTradeObj();; //--- Set default values void Init(const string symbol, const ulong magic, const double volume, const ulong deviation, const int stoplimit, const datetime expiration, const bool async_mode, const ENUM_ORDER_TYPE_FILLING type_filling, const ENUM_ORDER_TYPE_TIME type_expiration, ENUM_LOG_LEVEL log_level); //--- (1) Return the margin calculation mode, (2) hedge account flag ENUM_ACCOUNT_MARGIN_MODE GetMarginMode(void) const { return this.m_margin_mode; } bool IsHedge(void) const { return this.GetMarginMode()==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING; } //--- (1) Set, (2) return the error logging level void SetLogLevel(const ENUM_LOG_LEVEL level) { this.m_log_level=level; } ENUM_LOG_LEVEL GetLogLevel(void) const { return this.m_log_level; } //--- (1) Set, (2) return the filling policy void SetTypeFilling(const ENUM_ORDER_TYPE_FILLING type) { this.m_type_filling=type; } ENUM_ORDER_TYPE_FILLING GetTypeFilling(void) const { return this.m_type_filling; } //--- (1) Set, (2) return order expiration type void SetTypeExpiration(const ENUM_ORDER_TYPE_TIME type) { this.m_type_expiration=type; } ENUM_ORDER_TYPE_TIME GetTypeExpiration(void) const { return this.m_type_expiration; } //--- (1) Set, (2) return the magic number void SetMagic(const ulong magic) { this.m_magic=magic; } ulong GetMagic(void) const { return this.m_magic; } //--- (1) Set, (2) return a symbol void SetSymbol(const string symbol) { this.m_symbol=symbol; } string GetSymbol(void) const { return this.m_symbol; } //--- (1) Set, (2) return a comment void SetComment(const string comment) { this.m_comment=comment; } string GetComment(void) const { return this.m_comment; } //--- (1) Set, (2) return slippage void SetDeviation(const ulong deviation) { this.m_deviation=deviation; } ulong GetDeviation(void) const { return this.m_deviation; } //--- (1) Set, (2) return volume void SetVolume(const double volume) { this.m_volume=volume; } double GetVolume(void) const { return this.m_volume; } //--- (1) Set, (2) return order expiration date void SetExpiration(const datetime time) { this.m_expiration=time; } datetime GetExpiration(void) const { return this.m_expiration; } //--- (1) Set, (2) return the flag of the asynchronous sending of a trading request void SetAsyncMode(const bool async) { this.m_async_mode=async; } bool GetAsyncMode(void) const { return this.m_async_mode; } //--- Last request data: //--- Return (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 ENUM_TRADE_REQUEST_ACTIONS GetLastRequestAction(void) const { return this.m_request.action; } ulong GetLastRequestMagic(void) const { return this.m_request.magic; } ulong GetLastRequestOrder(void) const { return this.m_request.order; } double GetLastRequestVolume(void) const { return this.m_request.volume; } double GetLastRequestPrice(void) const { return this.m_request.price; } double GetLastRequestStopLimit(void) const { return this.m_request.stoplimit; } double GetLastRequestStopLoss(void) const { return this.m_request.sl; } double GetLastRequestTakeProfit(void) const { return this.m_request.tp; } ulong GetLastRequestDeviation(void) const { return this.m_request.deviation; } ENUM_ORDER_TYPE GetLastRequestType(void) const { return this.m_request.type; } ENUM_ORDER_TYPE_FILLING GetLastRequestTypeFilling(void) const { return this.m_request.type_filling; } ENUM_ORDER_TYPE_TIME GetLastRequestTypeTime(void) const { return this.m_request.type_time; } datetime GetLastRequestExpiration(void) const { return this.m_request.expiration; } string GetLastRequestComment(void) const { return this.m_request.comment; } ulong GetLastRequestPosition(void) const { return this.m_request.position; } ulong GetLastRequestPositionBy(void) const { return this.m_request.position_by; } //--- Data on the last request result: //--- Return (1) operation result code, (2) performed deal ticket, (3) placed order ticket, //--- (4) deal volume confirmed by a broker, (5) deal price confirmed by a broker, //--- (6) current market Bid (requote) price, (7) current market Ask (requote) price //--- (8) broker comment to operation (by default, it is filled by the trade server return code description), //--- (9) request ID set by the terminal when sending, (10) external trading system return code uint GetResultRetcode(void) const { return this.m_result.retcode; } ulong GetResultDeal(void) const { return this.m_result.deal; } ulong GetResultOrder(void) const { return this.m_result.order; } double GetResultVolume(void) const { return this.m_result.volume; } double GetResultPrice(void) const { return this.m_result.price; } double GetResultBid(void) const { return this.m_result.bid; } double GetResultAsk(void) const { return this.m_result.ask; } string GetResultComment(void) const { return this.m_result.comment; } uint GetResultRequestID(void) const { return this.m_result.request_id; } uint GetResultRetcodeEXT(void) const { return this.m_result.retcode_external;} //--- 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 ulong deviation=ULONG_MAX, const string comment=NULL); //--- Close a position bool ClosePosition(const ulong ticket, const ulong deviation=ULONG_MAX, const string comment=NULL); //--- Close a position partially bool ClosePositionPartially(const ulong ticket, const double volume, const ulong deviation=ULONG_MAX, const string comment=NULL); //--- Close a position by an opposite one bool ClosePositionBy(const ulong ticket,const ulong ticket_by); //--- Modify a position bool ModifyPosition(const ulong ticket,const double sl=WRONG_VALUE,const double tp=WRONG_VALUE); //--- Place an order bool SetOrder(const ENUM_ORDER_TYPE type, const double volume, const double price, const double sl=0, const double tp=0, const double price_stoplimit=0, const ulong magic=ULONG_MAX, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC, const string comment=NULL); //--- Remove an order bool DeleteOrder(const ulong ticket); //--- Modify an order bool ModifyOrder(const ulong ticket, const double price=WRONG_VALUE, const double sl=WRONG_VALUE, const double tp=WRONG_VALUE, const double price_stoplimit=WRONG_VALUE, const datetime expiration=WRONG_VALUE, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE); }; //+------------------------------------------------------------------+
Echemos un vistazo a lo que hemos escrito.
Para obtener los precios actuales, deberemos recurrir a las propiedades del símbolo en el que se enviará la solicitud comercial. Dado que debemos disponer de los precios actuales, pero también tenemos que recibir estos antes de mandar la solicitud comercial, vamos a ubicar la variable m_tick con el tipo de estructura MqlTick directamente en el objeto comercial básico (podríamos transmitir desde el objeto de símbolo, pero será mejor evitar gastos innecesarios en la transmisión de una propiedad al objeto comercial)
La variable m_request con el tipo de estructura de solicitud comercial MqlTradeRequest es necesaria para rellenar todas las propiedades de la solicitud comercial y enviarlas a la función OrderSend(). Transmitimos a esta misma función la variable m_result con el tipo de estructura del resultado de la solicitud comercial MqlTradeResult : esta será rellenada por el servidor al obtener la respuesta del servidor comercial, y si se da un resultado erróneo en el envío de la orden al servidor, siempre podremos leer los campos de la estructura del resultado de la solicitud comercial para comprender qué ha sucedido.
A nuestro parecer, el resto de las variables de miembros de clase no requieren explicación.
Vamos a echar un vistazo a la implementación de los métodos.
Los métodos para establecer y obtener las propiedades (métodos Set y Get) de la orden comercial se han escrito en el cuerpo de la clase. Lo único que hacen es escribir en la variable correspondiente el valor transmitido al método, o bien retornar el valor de la variable correspondiente. Estos métodos trabajan solo con las variables que guardan los valores por defecto, es decir, con la ayuda de estos métodos, podemos establecer la propiedad necesaria de la solicitud comercial, que después tendrá por defecto el valor establecido. Si necesitamos usar una única vez otro valor para la orden comercial (distinto al establecido por defecto), en los métodos de envío de las órdenes comerciales se ha diseñado la transmisión de los valores necesarios para el uso único de un valor transmitido al método.
Los métodos que retornan los parámetros de la última solicitud comercial
son necesarios para que exista la posibilidad de ver qué valor precisamente ha sido transmitido a esta o aquella propiedad de la última
solicitud comercial, y realizar alguna acción destinada ha eliminar los errores o bien usar estos valores para la siguiente solicitud al
servidor.
Los métodos retornan el contenido de los campos de estructura de la solicitud comercial correspondientes al método. Antes de enviar la
solicitud, algunos de los campos de esta estructura (correspondientes a la solicitud comercial) son rellenados y enviados a la función de
envío de solicitudes al servidor. Precisamente de esta estructura obtenemos los valores que han sido rellenados la última vez.
Los métodos que retornan el resultado de la solicitud comercial
sirven para obtener información sobre el resultado del procesamiento de la solicitud comercial. Si la solicitud ha resultado errónea, en retcode podremos
ver información concreta sobre el código de error. O bien la estructura será rellenada con los datos sobre la posición abierta o la orden
pendiente establecida, mientras que en
request_id se registra el código de la solicitud que se puede analizar como consecuencia en el manejador OnTradeTransaction(),
para que sea posible vincular la solicitud comercial enviada al servidor mediante
OrderSendAsync() con el resultado de esta solicitud.
En esta biblioteca no utilizamos OnTradeTransaction() porque no existe en MQL4, así que el análisis del envío asincrónico de solicitudes y
sus resultados se realizará por cuenta propia.
Constructor de la clase:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CTradeObj::CTradeObj(void) : m_magic(0), m_deviation(5), m_stop_limit(0), m_expiration(0), m_async_mode(false), m_type_filling(ORDER_FILLING_FOK), m_type_expiration(ORDER_TIME_GTC), m_comment(::MQLInfoString(MQL_PROGRAM_NAME)+" by DoEasy"), m_log_level(LOG_LEVEL_ERROR_MSG) { //--- Margin calculation mode this.m_margin_mode= ( #ifdef __MQL5__ (ENUM_ACCOUNT_MARGIN_MODE)::AccountInfoInteger(ACCOUNT_MARGIN_MODE) #else /* MQL4 */ ACCOUNT_MARGIN_MODE_RETAIL_HEDGING #endif ); } //+------------------------------------------------------------------+
Establecemos los valores de inicialización en su lista de inicialización:
- el número mágico es igual a cero,
- establecemos un deslizamiento de cinco puntos,
- indicamos un precio para la orden StopLimit igual a cero (no hay precio),
- como valor de caducidad, también indicamos cero (tiempo ilimitado),
- el modo de envío asincrónico de solicitudes comerciales
está desactivado,
- en la política de ejecución de órdenes, elegimos "Todo o nada",
- el tiempo de ejecución de órdenes es ilimitado
- en los comentarios de la orden, escribimos el nombre del programa + " by DoEasy",
- en el modo de registro del funcionamiento de la clase comercial, seleccionamos solo errores.
En el cuerpo de la clase, añadimos a la variable m_margin_mode, el modo de cálculo de margen establecido para la
cuenta.
Para MQL5, obtenemos el valor necesario con la ayuda de la función AccountInfoInteger(),
con el identificador de función
ACCOUNT_MARGIN_MODE.
Para
MQL4, en cambio, añadimos de inmediato el modo de cobertura de cálculo de margen (
ACCOUNT_MARGIN_MODE_RETAIL_HEDGING).
Tendremos la posibilidad de transmitir a los métodos comerciales los valores necesarios de las propiedades deseadas para rellenar la solicitud comercial. Pero, con frecuencia, no deberemos rellenar todas las propiedades necesarias: estas se deben modificar normalmente para cada orden comercial. Por eso, resulta imprescindible tener la posibilidad de inicializar los valores de variable por defecto, y ya en los métodos comerciales seleccionar qué valores vamos a usar precisamente en la orden comercial: o bien el valor transmitido al método de envío de solicitudes al servidor, o bien el valor establecido por defecto.
Vamos a escribir el método de inicialización de los parámetros por defecto de la solicitud comercial:
//+------------------------------------------------------------------+ //| Set default values | //+------------------------------------------------------------------+ void CTradeObj::Init(const string symbol, const ulong magic, const double volume, const ulong deviation, const int stoplimit, const datetime expiration, const bool async_mode, const ENUM_ORDER_TYPE_FILLING type_filling, const ENUM_ORDER_TYPE_TIME type_expiration, ENUM_LOG_LEVEL log_level) { this.SetSymbol(symbol); this.SetMagic(magic); this.SetDeviation(deviation); this.SetVolume(volume); this.SetExpiration(expiration); this.SetTypeFilling(type_filling); this.SetTypeExpiration(type_expiration); this.SetAsyncMode(async_mode); this.SetLogLevel(log_level); this.m_symbol_expiration_flags=(int)::SymbolInfoInteger(this.m_symbol,SYMBOL_EXPIRATION_MODE); this.m_volume=::SymbolInfoDouble(this.m_symbol,SYMBOL_VOLUME_MIN); } //+------------------------------------------------------------------+
Transmitimos al método los valores necesarios de los parámetros de la solicitud comercial, y a continuación, en el cuerpo del método, asignamos
los valores transmitidos a las variables correspondientes con la ayuda de sus métodos de establecimiento, que hemos visto
anteriormente.
Asimismo,
establecemos las banderas de los modos de caducidad de la orden permitidos con la ayuda de la función SymbolInfoInteger(),
con el identificador de propiedad
SYMBOL_EXPIRATION_MODE. En
cuanto al volumen, estableceremos el mínimo permitido para el símbolo con la ayuda de
SymbolInfoDouble(), con el identificador de
propiedad
SYMBOL_VOLUME_MIN.
Método de apertura de una posición:
//+------------------------------------------------------------------+ //| 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 ulong deviation=ULONG_MAX, const string comment=NULL) { //--- If failed to get the current prices, write the error code and description, send the message to the journal and return 'false' if(!::SymbolInfoTick(this.m_symbol,this.m_tick)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_GET_PRICE),CMessage::Text(this.m_result.retcode)); return false; } //--- Clear the structures ::ZeroMemory(this.m_request); ::ZeroMemory(this.m_result); //--- Fill in the request structure this.m_request.action = TRADE_ACTION_DEAL; this.m_request.symbol = this.m_symbol; this.m_request.magic = (magic==ULONG_MAX ? this.m_magic : magic); this.m_request.type = OrderTypeByPositionType(type); this.m_request.price = (type==POSITION_TYPE_BUY ? this.m_tick.ask : this.m_tick.bid); this.m_request.volume = volume; this.m_request.sl = sl; this.m_request.tp = tp; this.m_request.deviation= (deviation==ULONG_MAX ? this.m_deviation : deviation); this.m_request.comment = (comment==NULL ? this.m_comment : comment); //--- Return the result of sending a request to the server return ( #ifdef __MQL5__ !this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result) #else (::OrderSend(m_request.symbol,m_request.type,m_request.volume,m_request.price,(int)m_request.deviation,m_request.sl,m_request.tp,m_request.comment,(int)m_request.magic,m_request.expiration,clrNONE)!=WRONG_VALUE) #endif ); } //+------------------------------------------------------------------+
Transmitimos al método el tipo de posición, su volumen, los precios de StopLoss y TakeProfit, el número mágico de la posición, la magnitud del
deslizamiento y los comentarios.
Por defecto, para el StopLoss, el TakeProfit, el número mágico y el comentario, se han establecido los valores. Si dejamos estos valores
sin cambios al llamar al método, para estos valores se usarán los establecidos por defecto en el método Init(), o bien los establecidos
directamente en el programa con los métodos de indicación de valores por defecto, que hemos analizado más arriba. La lógica completa del
método está escrita en los comentarios al código.
Lo único que podemos destacar aquí es que al campo de la estructura de la
solicitud comercial (encargada de guardar el tipo de orden), se envía el resultado retornado por la función OrderTypeByPositionType(),
que hemos escrito en DELib.mqh para obtener el tipo de orden según el tipo de posición. Otro punto a tener en cuenta es que el método no
comprueba de forma alguna la corrección de los parámetros que se le han transmitido, considera que estos se han comprobado y son correctos.
Para MQL4, no vamos a comprobar nada por ahora al retornar el
resultado del envío de la solicitud al servidor, ni tampoco rellenaremos la estructura del resultado de la solicitud comercial: en este
momento, necesitamos montar rápidamente los métodos para la simulación. En artículos posteriores, lo pondremos todo en orden.
Método de cierre de una posición:
//+------------------------------------------------------------------+ //| Close a position | //+------------------------------------------------------------------+ bool CTradeObj::ClosePosition(const ulong ticket, const ulong deviation=ULONG_MAX, const string comment=NULL) { //--- If failed to select a position. Write the error code and description, send the message to the journal and return 'false' if(!::PositionSelectByTicket(ticket)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) Print(DFUN,"#",(string)ticket,": ",CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_SELECT_POS),CMessage::Text(this.m_result.retcode)); return false; } //--- Get a position symbol string symbol=::PositionGetString(POSITION_SYMBOL); //--- If failed to get the current prices, write the error code and description, send the message to the journal and return 'false' if(!::SymbolInfoTick(symbol,this.m_tick)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_GET_PRICE),CMessage::Text(this.m_result.retcode)); return false; } //--- Get a position type and an order type inverse of the position type ENUM_POSITION_TYPE position_type=(ENUM_POSITION_TYPE)::PositionGetInteger(POSITION_TYPE); ENUM_ORDER_TYPE type=OrderTypeOppositeByPositionType(position_type); //--- Get a position volume and magic number double position_volume=::PositionGetDouble(POSITION_VOLUME); ulong magic=::PositionGetInteger(POSITION_MAGIC); //--- Clear the structures ::ZeroMemory(this.m_request); ::ZeroMemory(this.m_result); //--- Fill in the request structure this.m_request.action = TRADE_ACTION_DEAL; this.m_request.symbol = symbol; this.m_request.magic = magic; this.m_request.type = type; this.m_request.price = (position_type==POSITION_TYPE_SELL ? this.m_tick.ask : this.m_tick.bid); this.m_request.volume = position_volume; this.m_request.deviation= (deviation==ULONG_MAX ? this.m_deviation : deviation); this.m_request.comment = (comment==NULL ? this.m_comment : comment); //--- In case of a hedging account, write the ticket of a closed position to the structure if(this.IsHedge()) this.m_request.position=::PositionGetInteger(POSITION_TICKET); //--- Return the result of sending a request to the server return ( #ifdef __MQL5__ !this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result) #else ::OrderClose((int)m_request.position,m_request.volume,m_request.price,(int)m_request.deviation,clrNONE) #endif ); } //+------------------------------------------------------------------+
Transmitimos al método el ticket de la posición cerrada, el deslizamiento y los comentarios.
Aquí, como sucede en los
demás métodos comerciales, todo se realiza igual que en el método de apertura posiciones analizado más arriba.
Método de cierre parcial de una posición:
//+------------------------------------------------------------------+ //| Close a position partially | //+------------------------------------------------------------------+ bool CTradeObj::ClosePositionPartially(const ulong ticket, const double volume, const ulong deviation=ULONG_MAX, const string comment=NULL) { //--- If failed to select a position. Write the error code and description, send the message to the journal and return 'false' if(!::PositionSelectByTicket(ticket)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) Print(DFUN,"#",(string)ticket,": ",CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_SELECT_POS),CMessage::Text(this.m_result.retcode)); return false; } //--- Get a position symbol string symbol=::PositionGetString(POSITION_SYMBOL); //--- If failed to get the current prices, write the error code and description, send the message to the journal and return 'false' if(!::SymbolInfoTick(symbol,this.m_tick)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_GET_PRICE),CMessage::Text(this.m_result.retcode)); return false; } //--- Get a position type and an order type inverse of the position type ENUM_POSITION_TYPE position_type=(ENUM_POSITION_TYPE)::PositionGetInteger(POSITION_TYPE); ENUM_ORDER_TYPE type=OrderTypeOppositeByPositionType(position_type); //--- Get a position volume and magic number double position_volume=::PositionGetDouble(POSITION_VOLUME); ulong magic=::PositionGetInteger(POSITION_MAGIC); //--- Clear the structures ::ZeroMemory(this.m_request); ::ZeroMemory(this.m_result); //--- Fill in the request structure this.m_request.action = TRADE_ACTION_DEAL; this.m_request.position = ticket; this.m_request.symbol = symbol; this.m_request.magic = magic; this.m_request.type = type; this.m_request.price = (position_type==POSITION_TYPE_SELL ? this.m_tick.ask : this.m_tick.bid); this.m_request.volume = (volume<position_volume ? volume : position_volume); this.m_request.deviation= (deviation==ULONG_MAX ? this.m_deviation : deviation); this.m_request.comment = (comment==NULL ? this.m_comment : comment); //--- In case of a hedging account, write the ticket of a closed position to the structure if(this.IsHedge()) this.m_request.position=::PositionGetInteger(POSITION_TICKET); //--- Return the result of sending a request to the server return ( #ifdef __MQL5__ !this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result) #else ::OrderClose((int)m_request.position,m_request.volume,m_request.price,(int)m_request.deviation,clrNONE) #endif ); } //+------------------------------------------------------------------+
Transmitimos al método el ticket de la posición cerrada, el volumen que se debe cerrar, el deslizamiento y los comentarios.
Aquí, debemos tener en
cuenta que,
si se ha transmitido al método un volumen superior al disponible en la posición, la
posición se cerrará por completo.
Método de cierre de una posición con otra opuesta:
//+------------------------------------------------------------------+ //| Close a position by an opposite one | //+------------------------------------------------------------------+ bool CTradeObj::ClosePositionBy(const ulong ticket,const ulong ticket_by) { #ifdef __MQL5__ //--- If this is not a hedging account. if(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)!=ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) { //--- Close by is available only on hedging accounts. //---Write the error code and description, send the message to the journal and return 'false' this.m_result.retcode=MSG_ACC_UNABLE_CLOSE_BY; this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_ACC_UNABLE_CLOSE_BY)); return false; } #endif //--- Closed position //--- If failed to select a position, write the error code and description, send the message to the journal and return 'false' if(!::PositionSelectByTicket(ticket)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) Print(DFUN,"#",(string)ticket,": ",CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_SELECT_POS),CMessage::Text(this.m_result.retcode)); return false; } //--- Get a type and magic of a closed position ENUM_POSITION_TYPE position_type=(ENUM_POSITION_TYPE)::PositionGetInteger(POSITION_TYPE); ulong magic=::PositionGetInteger(POSITION_MAGIC); //--- Opposite position //--- If failed to select a position, write the error code and description, send the message to the journal and return 'false' if(!::PositionSelectByTicket(ticket_by)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) Print(DFUN,"#",(string)ticket_by,": ",CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_SELECT_POS),CMessage::Text(this.m_result.retcode)); return false; } //--- Get an opposite position type ENUM_POSITION_TYPE position_type_by=(ENUM_POSITION_TYPE)::PositionGetInteger(POSITION_TYPE); //--- If types of a closed and an opposite position match, write the error code and description, send the message to the journal and return 'false' if(position_type==position_type_by) { this.m_result.retcode=MSG_ACC_SAME_TYPE_CLOSE_BY; this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) Print(DFUN,CMessage::Text(MSG_ACC_SAME_TYPE_CLOSE_BY)); return false; } //--- Clear the structures ::ZeroMemory(this.m_request); ::ZeroMemory(this.m_result); //--- Fill in the request structure this.m_request.action = TRADE_ACTION_CLOSE_BY; this.m_request.position = ticket; this.m_request.position_by = ticket_by; this.m_request.magic = magic; //--- Return the result of sending a request to the server return ( #ifdef __MQL5__ !this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result) #else ::OrderCloseBy((int)m_request.position,(int)m_request.position_by,clrNONE) #endif ); } //+------------------------------------------------------------------+
Transmitimos al método el ticket de la posición cerrada y el ticket de la opuesta.
Método de modificación de los niveles stop de una posición:
//+------------------------------------------------------------------+ //| Modify a position | //+------------------------------------------------------------------+ bool CTradeObj::ModifyPosition(const ulong ticket,const double sl=WRONG_VALUE,const double tp=WRONG_VALUE) { //--- If all default values are passed, there is nothing to be modified if(sl==WRONG_VALUE && tp==WRONG_VALUE) { //--- There are no changes in the request - write the error code and description, send the message to the journal and return 'false' this.m_result.retcode= #ifdef __MQL5__ TRADE_RETCODE_NO_CHANGES #else 10025 #endif ; this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) Print(DFUN,CMessage::Text(this.m_result.retcode),CMessage::Retcode(this.m_result.retcode)); return false; } //--- If failed to select a position, write the error code and description, send the message to the journal and return 'false' if(!::PositionSelectByTicket(ticket)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) Print(DFUN,"#",(string)ticket,": ",CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_SELECT_POS),CMessage::Text(this.m_result.retcode)); return false; } //--- Clear the structures ::ZeroMemory(this.m_request); ::ZeroMemory(this.m_result); //--- Fill in the request structure m_request.action = TRADE_ACTION_SLTP; m_request.position= ticket; m_request.symbol = ::PositionGetString(POSITION_SYMBOL); m_request.magic = ::PositionGetInteger(POSITION_MAGIC); m_request.sl = (sl==WRONG_VALUE ? ::PositionGetDouble(POSITION_SL) : sl); m_request.tp = (tp==WRONG_VALUE ? ::PositionGetDouble(POSITION_TP) : tp); //--- Return the result of sending a request to the server return ( #ifdef __MQL5__ !this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result) #else ::OrderModify((int)m_request.position,::OrderOpenPrice(),m_request.sl,m_request.tp,::OrderExpiration(),clrNONE) #endif ); } //+------------------------------------------------------------------+
Transmitimos al método el ticket de la posición modificada y los niveles de StopLoss y TakeProfit.
Método de colocación de una orden pendiente:
//+------------------------------------------------------------------+ //| Set an order | //+------------------------------------------------------------------+ bool CTradeObj::SetOrder(const ENUM_ORDER_TYPE type, const double volume, const double price, const double sl=0, const double tp=0, const double price_stoplimit=0, const ulong magic=ULONG_MAX, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC, const string comment=NULL) { //--- If an invalid order type has been passed, write the error code and description, send the message to the journal and return 'false' if(type==ORDER_TYPE_BUY || type==ORDER_TYPE_SELL || type==ORDER_TYPE_CLOSE_BY #ifdef __MQL4__ || type==ORDER_TYPE_BUY_STOP_LIMIT || type==ORDER_TYPE_SELL_STOP_LIMIT #endif ) { this.m_result.retcode=MSG_LIB_SYS_INVALID_ORDER_TYPE; this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_INVALID_ORDER_TYPE),OrderTypeDescription(type)); return false; } //--- Clear the structures ::ZeroMemory(this.m_request); ::ZeroMemory(this.m_result); //--- Fill in the request structure m_request.action = TRADE_ACTION_PENDING; m_request.symbol = this.m_symbol; m_request.magic = (magic==ULONG_MAX ? this.m_magic : magic); m_request.volume = volume; m_request.type = type; m_request.stoplimit = price_stoplimit; m_request.price = price; m_request.sl = sl; m_request.tp = tp; m_request.type_time = type_time; m_request.expiration = expiration; m_request.comment = (comment==NULL ? this.m_comment : comment); //--- Return the result of sending a request to the server return ( #ifdef __MQL5__ !this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result) #else (::OrderSend(m_request.symbol,m_request.type,m_request.volume,m_request.price,(int)m_request.deviation,m_request.sl,m_request.tp,m_request.comment,(int)m_request.magic,m_request.expiration,clrNONE)!=WRONG_VALUE) #endif ); } //+------------------------------------------------------------------+
Transmitimos al método el tipo de orden pendiente, su volumen, el precio de colocación, los precios de las órdenes StopLoss, TakeProfit y StopLimit, el
número mágico, la vida útil de la orden, el tipo de caducidad de la misma y los comentarios.
Método de eliminación de una orden pendiente:
//+------------------------------------------------------------------+ //| Remove an order | //+------------------------------------------------------------------+ bool CTradeObj::DeleteOrder(const ulong ticket) { //--- Clear the structures ::ZeroMemory(this.m_request); ::ZeroMemory(this.m_result); //--- Fill in the request structure m_request.action = TRADE_ACTION_REMOVE; m_request.order = ticket; //--- Return the result of sending a request to the server return ( #ifdef __MQL5__ !this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result) #else ::OrderDelete((int)m_request.order,clrNONE) #endif ); } //+------------------------------------------------------------------+
Transmitimos al método el ticket de la orden eliminada.
Método de modificación de una orden pendiente:
//+------------------------------------------------------------------+ //| Modify an order | //+------------------------------------------------------------------+ bool CTradeObj::ModifyOrder(const ulong ticket, const double price=WRONG_VALUE, const double sl=WRONG_VALUE, const double tp=WRONG_VALUE, const double price_stoplimit=WRONG_VALUE, const datetime expiration=WRONG_VALUE, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE) { //--- If failed to select an order, write the error code and description, send the message to the journal and return 'false' #ifdef __MQL5__ if(!::OrderSelect(ticket)) #else if(!::OrderSelect((int)ticket,SELECT_BY_TICKET)) #endif { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) Print(DFUN,"#",(string)ticket,": ",CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_SELECT_ORD),CMessage::Text(this.m_result.retcode)); return false; } double order_price=::OrderGetDouble(ORDER_PRICE_OPEN); double order_sl=::OrderGetDouble(ORDER_SL); double order_tp=::OrderGetDouble(ORDER_TP); double order_stoplimit=::OrderGetDouble(ORDER_PRICE_STOPLIMIT); ENUM_ORDER_TYPE_TIME order_type_time=(ENUM_ORDER_TYPE_TIME)::OrderGetInteger(ORDER_TYPE_TIME); datetime order_expiration=(datetime)::OrderGetInteger(ORDER_TIME_EXPIRATION); //--- If the default values are passed and the price is equal to the price set in the order, the request is unchanged //---Write the error code and description, send the message to the journal and return 'false' if(price==order_price && sl==WRONG_VALUE && tp==WRONG_VALUE && price_stoplimit==WRONG_VALUE && type_time==WRONG_VALUE && expiration==WRONG_VALUE) { this.m_result.retcode = #ifdef __MQL5__ TRADE_RETCODE_NO_CHANGES #else 10025 #endif ; this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) Print(DFUN,CMessage::Text(this.m_result.retcode),CMessage::Retcode(this.m_result.retcode)); return false; } //--- Clear the structures ::ZeroMemory(this.m_request); ::ZeroMemory(this.m_result); //--- Fill in the request structure m_request.action = TRADE_ACTION_MODIFY; m_request.order = ticket; m_request.price = (price==WRONG_VALUE ? order_price : price); m_request.sl = (sl==WRONG_VALUE ? order_sl : sl); m_request.tp = (tp==WRONG_VALUE ? order_tp : tp); m_request.stoplimit = (price_stoplimit==WRONG_VALUE ? order_stoplimit : price_stoplimit); m_request.type_time = (type_time==WRONG_VALUE ? order_type_time : type_time); m_request.expiration = (expiration==WRONG_VALUE ? order_expiration : expiration); //--- Return an order modification result return ( #ifdef __MQL5__ !this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result) #else ::OrderModify((int)m_request.order,m_request.price,m_request.sl,m_request.tp,m_request.expiration,clrNONE) #endif ); Print(DFUN); } //+------------------------------------------------------------------+
Transmitimos al método el ticket de la orden modificada, los nuevos valores de precio, los niveles de las órdenes StopLoss, TakeProfit y StopLimit, la
vida útil de la orden y el tipo de caducidad de la misma.
En todos los métodos se han organizado comprobaciones idénticas de los valores por defecto transmitidos al método; además, todas las acciones han sido comentadas, por lo que lector podrá analizar su descripción de manera independiente.
Con esto, podemos dar por finalizada la creación de la funcionalidad mínima de la clase comercial básica.
Dado que cualquier orden comercial enviada se relaciona de una u otra forma con algún símbolo, ubicaremos el objeto comercial básico en el objeto de símbolo, implementando el acceso al mismo desde el exterior.
Abrimos el archivo del objeto de símbolo \MQL5\Include\DoEasy\Objects\Symbols\Symbol.mqh e
incluimos
en el mismo el archivo del objeto comercial TradeObj.mqh:
//+------------------------------------------------------------------+ //| Symbol.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\BaseObj.mqh" #include "..\Trade\TradeObj.mqh" //+------------------------------------------------------------------+
Declaramos en la sección privada la variable de objeto de la clase comercial:
//+------------------------------------------------------------------+ //| Abstract symbol class | //+------------------------------------------------------------------+ class CSymbol : public CBaseObj { private: struct MqlMarginRate { double Initial; // initial margin rate double Maintenance; // maintenance margin rate }; struct MqlMarginRateMode { MqlMarginRate Long; // MarginRate of long positions MqlMarginRate Short; // MarginRate of short positions MqlMarginRate BuyStop; // MarginRate of BuyStop orders MqlMarginRate BuyLimit; // MarginRate of BuyLimit orders MqlMarginRate BuyStopLimit; // MarginRate of BuyStopLimit orders MqlMarginRate SellStop; // MarginRate of SellStop orders MqlMarginRate SellLimit; // MarginRate of SellLimit orders MqlMarginRate SellStopLimit; // MarginRate of SellStopLimit orders }; MqlMarginRateMode m_margin_rate; // Margin ratio structure MqlBookInfo m_book_info_array[]; // Array of the market depth data structures long m_long_prop[SYMBOL_PROP_INTEGER_TOTAL]; // Integer properties double m_double_prop[SYMBOL_PROP_DOUBLE_TOTAL]; // Real properties string m_string_prop[SYMBOL_PROP_STRING_TOTAL]; // String properties bool m_is_change_trade_mode; // Flag of changing trading mode for a symbol CTradeObj m_trade; // Trading class object //--- Return the index of the array the symbol's (1) double and (2) string properties are located at int IndexProp(ENUM_SYMBOL_PROP_DOUBLE property) const { return(int)property-SYMBOL_PROP_INTEGER_TOTAL; } int IndexProp(ENUM_SYMBOL_PROP_STRING property) const { return(int)property-SYMBOL_PROP_INTEGER_TOTAL-SYMBOL_PROP_DOUBLE_TOTAL; } //--- (1) Fill in all the "margin ratio" symbol properties, (2) initialize the ratios bool MarginRates(void); void InitMarginRates(void); //--- Reset all symbol object data void Reset(void); //--- Return the current day of the week ENUM_DAY_OF_WEEK CurrentDayOfWeek(void) const; public: //--- Default constructor
En la sección pública de la clase, declaramos dos métodos:
el
método que retorna la política de ejecución correcta, y el método que
retorna el tipo correcto de caducidad de las órdenes:
public: //--- Set (1) integer, (2) real and (3) string symbol properties void SetProperty(ENUM_SYMBOL_PROP_INTEGER property,long value) { this.m_long_prop[property]=value; } void SetProperty(ENUM_SYMBOL_PROP_DOUBLE property,double value) { this.m_double_prop[this.IndexProp(property)]=value; } void SetProperty(ENUM_SYMBOL_PROP_STRING property,string value) { this.m_string_prop[this.IndexProp(property)]=value; } //--- Return (1) integer, (2) real and (3) string symbol properties from the properties array long GetProperty(ENUM_SYMBOL_PROP_INTEGER property) const { return this.m_long_prop[property]; } double GetProperty(ENUM_SYMBOL_PROP_DOUBLE property) const { return this.m_double_prop[this.IndexProp(property)]; } string GetProperty(ENUM_SYMBOL_PROP_STRING property) const { return this.m_string_prop[this.IndexProp(property)]; } //--- Return the flag of a symbol supporting the property virtual bool SupportProperty(ENUM_SYMBOL_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_SYMBOL_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_SYMBOL_PROP_STRING property) { return true; } //--- Return the flag of allowing (1) market, (2) limit, (3) stop (4) and stop limit orders, //--- the flag of allowing setting (5) StopLoss and (6) TakeProfit orders, (7) as well as closing by an opposite order bool IsMarketOrdersAllowed(void) const { return((this.OrderModeFlags() & SYMBOL_ORDER_MARKET)==SYMBOL_ORDER_MARKET); } bool IsLimitOrdersAllowed(void) const { return((this.OrderModeFlags() & SYMBOL_ORDER_LIMIT)==SYMBOL_ORDER_LIMIT); } bool IsStopOrdersAllowed(void) const { return((this.OrderModeFlags() & SYMBOL_ORDER_STOP)==SYMBOL_ORDER_STOP); } bool IsStopLimitOrdersAllowed(void) const { return((this.OrderModeFlags() & SYMBOL_ORDER_STOP_LIMIT)==SYMBOL_ORDER_STOP_LIMIT); } bool IsStopLossOrdersAllowed(void) const { return((this.OrderModeFlags() & SYMBOL_ORDER_SL)==SYMBOL_ORDER_SL); } bool IsTakeProfitOrdersAllowed(void) const { return((this.OrderModeFlags() & SYMBOL_ORDER_TP)==SYMBOL_ORDER_TP); } bool IsCloseByOrdersAllowed(void) const; //--- Return the (1) FOK and (2) IOC filling flag bool IsFillingModeFOK(void) const { return((this.FillingModeFlags() & SYMBOL_FILLING_FOK)==SYMBOL_FILLING_FOK); } bool IsFillingModeIOC(void) const { return((this.FillingModeFlags() & SYMBOL_FILLING_IOC)==SYMBOL_FILLING_IOC); } //--- Return the flag of order expiration: (1) GTC, (2) DAY, (3) Specified and (4) Specified Day bool IsExpirationModeGTC(void) const { return((this.ExpirationModeFlags() & SYMBOL_EXPIRATION_GTC)==SYMBOL_EXPIRATION_GTC); } bool IsExpirationModeDAY(void) const { return((this.ExpirationModeFlags() & SYMBOL_EXPIRATION_DAY)==SYMBOL_EXPIRATION_DAY); } bool IsExpirationModeSpecified(void) const { return((this.ExpirationModeFlags() & SYMBOL_EXPIRATION_SPECIFIED)==SYMBOL_EXPIRATION_SPECIFIED); } bool IsExpirationModeSpecifiedDay(void) const { return((this.ExpirationModeFlags() & SYMBOL_EXPIRATION_SPECIFIED_DAY)==SYMBOL_EXPIRATION_SPECIFIED_DAY); } //--- Return the description of allowing (1) market, (2) limit, (3) stop and (4) stop limit orders, //--- the description of allowing (5) StopLoss and (6) TakeProfit orders, (7) as well as closing by an opposite order string GetMarketOrdersAllowedDescription(void) const; string GetLimitOrdersAllowedDescription(void) const; string GetStopOrdersAllowedDescription(void) const; string GetStopLimitOrdersAllowedDescription(void) const; string GetStopLossOrdersAllowedDescription(void) const; string GetTakeProfitOrdersAllowedDescription(void) const; string GetCloseByOrdersAllowedDescription(void) const; //--- Return the description of allowing the filling type (1) FOK and (2) IOC, (3) as well as allowed order expiration modes string GetFillingModeFOKAllowedDescrioption(void) const; string GetFillingModeIOCAllowedDescrioption(void) const; //--- Return the description of order expiration: (1) GTC, (2) DAY, (3) Specified and (4) Specified Day string GetExpirationModeGTCDescription(void) const; string GetExpirationModeDAYDescription(void) const; string GetExpirationModeSpecifiedDescription(void) const; string GetExpirationModeSpecDayDescription(void) const; //--- Return the description of the (1) status, (2) price type for constructing bars, //--- (3) method of calculating margin, (4) instrument trading mode, //--- (5) deal execution mode for a symbol, (6) swap calculation mode, //--- (7) StopLoss and TakeProfit lifetime, (8) option type, (9) option rights //--- flags of (10) allowed order types, (11) allowed filling types, //--- (12) allowed order expiration modes string GetStatusDescription(void) const; string GetChartModeDescription(void) const; string GetCalcModeDescription(void) const; string GetTradeModeDescription(void) const; string GetTradeExecDescription(void) const; string GetSwapModeDescription(void) const; string GetOrderGTCModeDescription(void) const; string GetOptionTypeDescription(void) const; string GetOptionRightDescription(void) const; string GetOrderModeFlagsDescription(void) const; string GetFillingModeFlagsDescription(void) const; string GetExpirationModeFlagsDescription(void) const; //--- Return (1) execution type, (2) order expiration type equal to 'type' if it is available on a symbol, otherwise - the correct option ENUM_ORDER_TYPE_FILLING GetCorrectTypeFilling(const uint type=ORDER_FILLING_RETURN); ENUM_ORDER_TYPE_TIME GetCorrectTypeExpiration(uint expiration=ORDER_TIME_GTC); //+------------------------------------------------------------------+
Los implementamos fuera del cuerpo de la clase:
//+------------------------------------------------------------------+ //| Return an order expiration type equal to 'type', | //| if it is available on a symbol, otherwise, the correct option | //| https://www.mql5.com/ru/forum/170952/page4#comment_4128864 | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE_FILLING CSymbol::GetCorrectTypeFilling(const uint type=ORDER_FILLING_RETURN) { const ENUM_SYMBOL_TRADE_EXECUTION exe_mode=this.TradeExecutionMode(); const int filling_mode=this.FillingModeFlags(); return( (filling_mode == 0 || (type >= ORDER_FILLING_RETURN) || ((filling_mode & (type + 1)) != type + 1)) ? (((exe_mode == SYMBOL_TRADE_EXECUTION_EXCHANGE) || (exe_mode == SYMBOL_TRADE_EXECUTION_INSTANT)) ? ORDER_FILLING_RETURN : ((filling_mode == SYMBOL_FILLING_IOC) ? ORDER_FILLING_IOC : ORDER_FILLING_FOK)) : (ENUM_ORDER_TYPE_FILLING)type ); } //+------------------------------------------------------------------+ //| Return order expiration type equal to 'expiration' | //| if it is available on Symb symbol, otherwise - the correct option| //| https://www.mql5.com/en/forum/170952/page4#comment_4128871 | //| Application: | //| Request.type_time = GetExpirationType((uint)Expiration); | //| 'Expiration' can be datetime | //| if(Expiration > ORDER_TIME_DAY) Request.expiration = Expiration; | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE_TIME CSymbol::GetCorrectTypeExpiration(uint expiration=ORDER_TIME_GTC) { #ifdef __MQL5__ const int expiration_mode=this.ExpirationModeFlags(); if((expiration > ORDER_TIME_SPECIFIED_DAY) || (((expiration_mode >> expiration) & 1) == 0)) { if((expiration < ORDER_TIME_SPECIFIED) || (expiration_mode < SYMBOL_EXPIRATION_SPECIFIED)) expiration=ORDER_TIME_GTC; else if(expiration > ORDER_TIME_DAY) expiration=ORDER_TIME_SPECIFIED; uint i=1 << expiration; while((expiration <= ORDER_TIME_SPECIFIED_DAY) && ((expiration_mode & i) != i)) { i <<= 1; expiration++; } } #endif return (ENUM_ORDER_TYPE_TIME)expiration; } //+------------------------------------------------------------------+
Para no volver a inventar la rueda, hemos tomado la lógica de estos métodos de los posts del forero fxsaber,
ofreciendo en los encabezados del código los enlaces a los posts con los códigos.
Para ser sinceros, no resulta muy agradable profundizar en una lógica tan enrevesada, por lo que, conociendo al autor que ha publicado las
funciones, y siendo conscientes de sus grandes capacidades como desarrollador, hemos decidido que podemos confiar en su
profesionalidad. En principio, podríamos desmenuzar la lógica completa en componentes, obtener los métodos más amplios en cuanto a
contenido, y describir la lógica de los mismos. Pero lo más sencillo es crear solo las descripciones de los métodos:
Transmitimos a los métodos la política de ejecución y el tipo de caducidad de órdenes deseados. Si el símbolo soporta este tipo o
política, este será retornado; en cambio, si los modos deseados no tienen soporte en el símbolo, se retornarán los modos permitidos. De esta
manera, los métodos siempre retornarán modos de política de ejecución o caducidad de órdenes que tengan soporte o sean correctos.
Añadimos en el bloque de acceso simplificado a las propiedades de tipo entero del objeto de símbolo en la sección pública
la
declaración del método que retorna el lote normalizado:
//+------------------------------------------------------------------+ //| 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); } datetime 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; //--- String properties
Añadimos al final del cuerpo de la clase el método que retorna el objeto comercial que pertenece al objeto de símbolo:
//--- The average weighted session price //--- setting the controlled session average weighted price (1) increase, (2) decrease and (3) control value //--- getting (4) the change value of the average weighted session price, //--- getting the flag of the average weighted session price change exceeding the (5) increase, (6) decrease value void SetControlSessionPriceAWInc(const double value) { this.SetControlledValueINC(SYMBOL_PROP_SESSION_AW,::fabs(value)); } void SetControlSessionPriceAWDec(const double value) { this.SetControlledValueDEC(SYMBOL_PROP_SESSION_AW,::fabs(value)); } void SetControlSessionPriceAWLevel(const double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_SESSION_AW,::fabs(value)); } double GetValueChangedSessionPriceAW(void) const { return this.GetPropDoubleChangedValue(SYMBOL_PROP_SESSION_AW); } bool IsIncreasedSessionPriceAW(void) const { return (bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_SESSION_AW); } bool IsDecreasedSessionPriceAW(void) const { return (bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_SESSION_AW); } //--- Return a trading object CTradeObj *GetTradeObj(void) { return &this.m_trade; } }; //+------------------------------------------------------------------+
Dado que el objeto comercial se crea inmediatamente al crear el objeto de símbolo, y que al crearse el objeto comercial, este tiene los valores
iniciales de inicialización en todos sus campos, deberemos inicializar con los valores predeterminados que necesitamos. Para hacerlo
así, llamamos al final del constructor de la clase CSymbol
el método Init() del objeto comercial con los valores por defecto
necesarios indicados:
//--- Fill in the symbol current data for(int i=0;i<SYMBOL_PROP_INTEGER_TOTAL;i++) this.m_long_prop_event[i][3]=this.m_long_prop[i]; for(int i=0;i<SYMBOL_PROP_DOUBLE_TOTAL;i++) this.m_double_prop_event[i][3]=this.m_double_prop[i]; //--- Update the base object data and search for changes CBaseObj::Refresh(); //--- if(!select) this.RemoveFromMarketWatch(); //--- Initializing default values of a trading object this.m_trade.Init(this.Name(),0,this.LotsMin(),5,0,0,false,this.GetCorrectTypeFilling(),this.GetCorrectTypeExpiration(),LOG_LEVEL_ERROR_MSG); } //+------------------------------------------------------------------+
Al llamar el método, transmitimos al objeto comercial:
- el nombre del símbolo,
- el lote mínimo permitido de dicho símbolo,
- una magnitud de deslizamiento igual a cinco puntos,
- un precio de StopLoss igual a cero: no hay StopLoss,
- un precio de TakeProfit igual a cero: no hay TakeProfit,
- una bandera de envío asincrónico de órdenes comerciales igual a false: envío sincrónico,
- obtenemos de inmediato la política correcta de ejecución de órdenes y la establecemos para el objeto comercial,
- obtenemos de inmediato el método correcto de caducidad de las órdenes y lo establecemos para el objeto comercial,
- y establecemos el nivel de registro de métodos comerciales como "solo errores"
Estos valores se asignan de inmediato al objeto comercial por defecto, pero siempre se pueden cambiar usando los métodos Set analizados anteriormente, para cada propiedad por separado, o bien se pueden dejar los valores por defecto, pero al llamar al método comercial, se transmitirá a este otro parámetro que se utilizará una sola vez al enviar la solicitud al servidor.
Implementamos el método de normalización del lote fuera del cuerpo de la clase:
//+------------------------------------------------------------------+ //| Return a normalized lot considering symbol properties | //+------------------------------------------------------------------+ double CSymbol::NormalizedLot(const double volume) const { double ml=this.LotsMin(); double mx=this.LotsMax(); double ln=::NormalizeDouble(volume,this.DigitsLot()); return(ln<ml ? ml : ln>mx ? mx : ln); } //+------------------------------------------------------------------+
Transmitimos al método el valor de lote necesario para la normalización. Transmitimos al método los lotes máximo y mínimo permitidos para el símbolo, normalizamos el valor del lote transmitido al método, y después, usando la comparación simple del valor normalizado con el lote máximo y mínimo, determinamos el valor que debemos retornar: si el lote transmitido al método es menor o mayor que lote mín./máx. del símbolo, retornamos el lote mín./máx. respectivamente, de lo contrario, retornamos el lote normalizado teniendo en cuenta el número de decimales tras la coma en el valor del lote (método DigitsLot()).
Ya hemos finalizado las mejoras de la clase CSymbol.
Ahora necesitaremos poner a prueba los métodos comerciales, y dado que por el momento no disponemos de la clase comercial principal, vamos a escribir los métodos temporales en la clase del objeto básico de la biblioteca CEngine para acceder al objeto comercial del símbolo necesario. Puesto que precisamente en este objeto tenemos acceso completo a todas las colecciones importantes de la biblioteca, ubicaremos precisamente en él los métodos para poner a prueba el objeto comercial.
Preste atención: los métodos en la clase serán temporales, más tarde crearemos una clase comercial completa, donde se encontrarán todos los valores y métodos comerciales necesarios para la comprobación.
Añadimos a la sección pública de la clase CEngine todos los métodos que necesitamos ahora para poner a prueba el objeto comercial:
//--- Set the following for the trading classes: //--- (1) correct filling policy, (2) filling policy, //--- (3) correct order expiration type, (4) order expiration type, //--- (5) magic number, (6) comment, (7) slippage, (8) volume, (9) order expiration date, //--- (10) the flag of asynchronous sending of a trading request, (11) logging level void SetTradeCorrectTypeFilling(const ENUM_ORDER_TYPE_FILLING type=ORDER_FILLING_FOK,const string symbol_name=NULL); void SetTradeTypeFilling(const ENUM_ORDER_TYPE_FILLING type=ORDER_FILLING_FOK,const string symbol_name=NULL); void SetTradeCorrectTypeExpiration(const ENUM_ORDER_TYPE_TIME type=ORDER_TIME_GTC,const string symbol_name=NULL); void SetTradeTypeExpiration(const ENUM_ORDER_TYPE_TIME type=ORDER_TIME_GTC,const string symbol_name=NULL); void SetTradeMagic(const ulong magic,const string symbol_name=NULL); void SetTradeComment(const string comment,const string symbol_name=NULL); void SetTradeDeviation(const ulong deviation,const string symbol_name=NULL); void SetTradeVolume(const double volume=0,const string symbol_name=NULL); void SetTradeExpiration(const datetime expiration=0,const string symbol_name=NULL); void SetTradeAsyncMode(const bool mode=false,const string symbol_name=NULL); void SetTradeLogLevel(const ENUM_LOG_LEVEL log_level=LOG_LEVEL_ERROR_MSG,const string symbol_name=NULL); //--- Return a symbol trading object by (1) position, (2) order ticket CTradeObj *GetTradeObjByPosition(const ulong ticket); CTradeObj *GetTradeObjByOrder(const ulong ticket); //--- Open (1) Buy, (2) Sell position bool OpenBuy(const double volume,const string symbol,const ulong magic=ULONG_MAX,double sl=0,double tp=0,const string comment=NULL); bool OpenSell(const double volume,const string symbol,const ulong magic=ULONG_MAX,double sl=0,double tp=0,const string comment=NULL); //--- Modify a position bool ModifyPosition(const ulong ticket,const double sl=WRONG_VALUE,const double tp=WRONG_VALUE); //--- Close a position (1) fully, (2) partially, (3) by an opposite one bool ClosePosition(const ulong ticket); bool ClosePositionPartially(const ulong ticket,const double volume); bool ClosePositionBy(const ulong ticket,const ulong ticket_by); //--- Set (1) BuyStop, (2) BuyLimit, (3) BuyStopLimit pending order bool PlaceBuyStop(const double volume, const string symbol, const double price, const double sl=0, const double tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC); bool PlaceBuyLimit(const double volume, const string symbol, const double price, const double sl=0, const double tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC); bool PlaceBuyStopLimit(const double volume, const string symbol, const double price_stop, const double price_limit, const double sl=0, const double tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC); //--- Set (1) SellStop, (2) SellLimit, (3) SellStopLimit pending order bool PlaceSellStop(const double volume, const string symbol, const double price, const double sl=0, const double tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC); bool PlaceSellLimit(const double volume, const string symbol, const double price, const double sl=0, const double tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC); bool PlaceSellStopLimit(const double volume, const string symbol, const double price_stop, const double price_limit, const double sl=0, const double tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC); //--- Modify a pending order bool ModifyOrder(const ulong ticket, const double price=WRONG_VALUE, const double sl=WRONG_VALUE, const double tp=WRONG_VALUE, const double stoplimit=WRONG_VALUE, datetime expiration=WRONG_VALUE, ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE); //--- Remove a pending order bool DeleteOrder(const ulong ticket); //--- Return event (1) milliseconds, (2) reason and (3) source from its 'long' value ushort EventMSC(const long lparam) const { return this.LongToUshortFromByte(lparam,0); } ushort EventReason(const long lparam) const { return this.LongToUshortFromByte(lparam,1); } ushort EventSource(const long lparam) const { return this.LongToUshortFromByte(lparam,2); } //--- Constructor/destructor CEngine(); ~CEngine(); }; //+------------------------------------------------------------------+
Implementamos fuera del cuerpo de la clase los métodos declarados:
Método de apertura de una posición Buy:
//+------------------------------------------------------------------+ //| Open Buy position | //+------------------------------------------------------------------+ bool CEngine::OpenBuy(const double volume,const string symbol,const ulong magic=ULONG_MAX,double sl=0,double tp=0,const string comment=NULL) { CSymbol *symbol_obj=this.GetSymbolObjByName(symbol); if(symbol_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_SYMBOL_ON_LIST)); return false; } CTradeObj *trade_obj=symbol_obj.GetTradeObj(); if(trade_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.OpenPosition(POSITION_TYPE_BUY,volume,sl,tp,magic,trade_obj.GetDeviation(),comment); } //+------------------------------------------------------------------+
Transmitimos al método:
- el volumen de la posición abierta (se indica necesariamente),
- el símbolo en el que debemos abrir la posición (se indica necesariamente),
- el número mágico que se asignará a la posición abierta (por defecto, será 0),
- el precio de StopLoss de la posición (por defecto, no lo hay),
- el precio de TakeProfit de la posición (por defecto, no lo hay),
- los comentarios a la posición (por defecto, el nombre del programa+" by DoEasy")
Obtenemos el objeto de símbolo según el nombre del símbolo. Si
no hemos logrado obtener el objeto, mostramos un mensaje sobre ello y retornamos false.
Obtenemos el objeto comercial desde el objeto de símbolo. Si
no hemos logrado obtener el objeto, mostramos un mensaje sobre ello y retornamos false.
Retornamos el resultado del funcionamiento del método de apertura de posiciones del objeto
comercial que hemos analizado anteriormente.
Método de apertura de una posición Sell:
//+------------------------------------------------------------------+ //| Open a Sell position | //+------------------------------------------------------------------+ bool CEngine::OpenSell(const double volume,const string symbol,const ulong magic=ULONG_MAX,double sl=0,double tp=0,const string comment=NULL) { CSymbol *symbol_obj=this.GetSymbolObjByName(symbol); if(symbol_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_SYMBOL_ON_LIST)); return false; } CTradeObj *trade_obj=symbol_obj.GetTradeObj(); if(trade_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.OpenPosition(POSITION_TYPE_SELL,volume,sl,tp,magic,trade_obj.GetDeviation(),comment); } //+------------------------------------------------------------------+
Transmitimos al método:
- el volumen de la posición abierta (se indica necesariamente),
- el símbolo en el que debemos abrir la posición (se indica necesariamente),
- el número mágico que se asignará a la posición abierta (por defecto, será 0),
- el precio de StopLoss de la posición (por defecto, no lo hay),
- el precio de TakeProfit de la posición (por defecto, no lo hay),
- los comentarios a la posición (por defecto, el nombre del programa+" by DoEasy")
Obtenemos el objeto de símbolo según el nombre del símbolo. Si
no hemos logrado obtener el objeto, mostramos un mensaje sobre ello y retornamos false.
Obtenemos el objeto comercial desde el objeto de símbolo. Si
no hemos logrado obtener el objeto, mostramos un mensaje sobre ello y retornamos false.
Retornamos el resultado del funcionamiento del método de apertura de posiciones del objeto
comercial que hemos analizado anteriormente.
Método para modificar los precios de StopLoss y TakeProfit de una posición:
//+------------------------------------------------------------------+ //| Modify a position | //+------------------------------------------------------------------+ bool CEngine::ModifyPosition(const ulong ticket,const double sl=WRONG_VALUE,const double tp=WRONG_VALUE) { CTradeObj *trade_obj=this.GetTradeObjByPosition(ticket); if(trade_obj==NULL) { //--- Error. Failed to get trading object ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.ModifyPosition(ticket,sl,tp); } //+------------------------------------------------------------------+
Transmitimos al método:
- el ticket de la posición modificada (se indica necesariamente),
- el nuevo precio de StopLoss de la posición (por defecto, sin cambios),
- el nuevo precio de TakeProfit de la posición (por defecto, sin cambios),
Obtenemos el objeto comercial según el ticket de la posición usando el método
GetTradeObjByPosition() que veremos más abajo.
Si no hemos
logrado obtener el objeto, mostramos un mensaje sobre ello y retornamos false.
Retornamos el resultado del funcionamiento del método de modificación de posiciones del
objeto comercial que hemos analizado anteriormente.
Método de cierre total de una posición:
//+------------------------------------------------------------------+ //| Close a position in full | //+------------------------------------------------------------------+ bool CEngine::ClosePosition(const ulong ticket) { CTradeObj *trade_obj=this.GetTradeObjByPosition(ticket); if(trade_obj==NULL) { //--- Error. Failed to get trading object ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.ClosePosition(ticket); } //+------------------------------------------------------------------+
Transmitimos al método el ticket de la posición cerrada.
Obtenemos el objeto comercial según el ticket de la posición usando el método
GetTradeObjByPosition() que veremos más abajo.
Si no hemos
logrado obtener el objeto, mostramos un mensaje sobre ello y retornamos false.
Retornamos el resultado del funcionamiento del método de cierre de posiciones del objeto
comercial que hemos analizado anteriormente.
Método de cierre parcial de una posición:
//+------------------------------------------------------------------+ //| Close a position partially | //+------------------------------------------------------------------+ bool CEngine::ClosePositionPartially(const ulong ticket,const double volume) { CTradeObj *trade_obj=this.GetTradeObjByPosition(ticket); if(trade_obj==NULL) { //--- Error. Failed to get trading object ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } CSymbol *symbol=this.GetSymbolObjByName(trade_obj.GetSymbol()); return trade_obj.ClosePositionPartially(ticket,symbol.NormalizedLot(volume)); } //+------------------------------------------------------------------+
Transmitimos al método el ticket de la posición y el volumen cerrado.
Obtenemos el objeto comercial según el ticket de la posición usando el método
GetTradeObjByPosition() que veremos más abajo.
Si no hemos
logrado obtener el objeto, mostramos un mensaje sobre ello y retornamos false.
Obtenemos el objeto de símbolo según
el nombre del símbolo comercial.
Retornamos el resultado del
funcionamiento del método de cierre parcial de posiciones del objeto comercial que hemos analizado anteriormente. Transmitimos
al método el volumen cerrado normalizado.
Método de cierre de una posición con otra opuesta:
//+------------------------------------------------------------------+ //| Close a position by an opposite one | //+------------------------------------------------------------------+ bool CEngine::ClosePositionBy(const ulong ticket,const ulong ticket_by) { CTradeObj *trade_obj_pos=this.GetTradeObjByPosition(ticket); if(trade_obj_pos==NULL) { //--- Error. Failed to get trading object ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } CTradeObj *trade_obj_by=this.GetTradeObjByPosition(ticket_by); if(trade_obj_by==NULL) { //--- Error. Failed to get trading object ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj_pos.ClosePositionBy(ticket,ticket_by); } //+------------------------------------------------------------------+
Transmitimos al método:
- el ticket de la posición cerrada,
- el ticket de la posición opuesta,
Obtenemos el objeto comercial según el ticket de la posición cerrada usando el
método GetTradeObjByPosition() que veremos más abajo.
Si
no hemos logrado obtener el objeto, mostramos un mensaje sobre ello y retornamos false.
Obtenemos el objeto comercial según el ticket de la posición opuesta usando el método
GetTradeObjByPosition() que veremos más abajo.
Si no
hemos logrado obtener el objeto, mostramos un mensaje sobre ello y retornamos false.
Retornamos el resultado del funcionamiento del método de cierre de posiciones
mediante otra opuesta del objeto comercial que hemos analizado anteriormente.
Método de colocación de una orden pendiente BuyStop:
//+------------------------------------------------------------------+ //| Place BuyStop pending order | //+------------------------------------------------------------------+ bool CEngine::PlaceBuyStop(const double volume, const string symbol, const double price, const double sl=0, const double tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC) { CSymbol *symbol_obj=this.GetSymbolObjByName(symbol); if(symbol_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_SYMBOL_ON_LIST)); return false; } CTradeObj *trade_obj=symbol_obj.GetTradeObj(); if(trade_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.SetOrder(ORDER_TYPE_BUY_STOP,volume,price,sl,tp,0,magic,expiration,type_time,comment); } //+------------------------------------------------------------------+
Transmitimos al método:
- el volumen de la orden colocada (se indica necesariamente),
- el símbolo en el que debemos colocar la orden (se indica necesariamente),
- el precio al que debemos colocar la orden (se indica necesariamente),
- el precio de StopLoss de la orden colocada (por defecto, no lo hay),
- el precio de TakeProfit de la orden colocada (por defecto, no lo hay),
- el número mágico de la orden colocada (por defecto, 0),
- los comentarios a la orden colocada (por defecto, el nombre del programa+" by DoEasy")
- el tiempo de vida útil de la orden colocada (por defecto, ilimitado),
- el tipo de tiempo de vida útil de la orden colocada (por defecto, hasta su cambio explícito),
Obtenemos el objeto de símbolo según el nombre del símbolo.
Si no hemos logrado obtener el objeto, mostramos un mensaje
sobre ello y retornamos
false.
Obtenemos el objeto
comercial desde el objeto de símbolo. Si no hemos logrado obtener el
objeto, mostramos un mensaje sobre ello y retornamos false.
Retornamos
el resultado del funcionamiento del método de colocación de órdenes del objeto comercial que hemos analizado anteriormente.
Método de colocación de una orden pendiente BuyLimit:
//+------------------------------------------------------------------+ //| Place BuyLimit pending order | //+------------------------------------------------------------------+ bool CEngine::PlaceBuyLimit(const double volume, const string symbol, const double price, const double sl=0, const double tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC) { CSymbol *symbol_obj=this.GetSymbolObjByName(symbol); if(symbol_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_SYMBOL_ON_LIST)); return false; } CTradeObj *trade_obj=symbol_obj.GetTradeObj(); if(trade_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.SetOrder(ORDER_TYPE_BUY_LIMIT,volume,price,sl,tp,0,magic,expiration,type_time,comment); } //+------------------------------------------------------------------+
Transmitimos al método:
- el volumen de la orden colocada (se indica necesariamente),
- el símbolo en el que debemos colocar la orden (se indica necesariamente),
- el precio al que debemos colocar la orden (se indica necesariamente),
- el precio de StopLoss de la orden colocada (por defecto, no lo hay),
- el precio de TakeProfit de la orden colocada (por defecto, no lo hay),
- el número mágico de la orden colocada (por defecto, 0),
- los comentarios a la orden colocada (por defecto, el nombre del programa+" by DoEasy")
- el tiempo de vida útil de la orden colocada (por defecto, ilimitado),
- el tipo de tiempo de vida útil de la orden colocada (por defecto, hasta su cambio explícito),
Obtenemos el objeto de símbolo según el nombre del símbolo.
Si no hemos logrado obtener el objeto, mostramos un mensaje
sobre ello y retornamos
false.
Obtenemos el objeto
comercial desde el objeto de símbolo. Si no hemos logrado obtener el
objeto, mostramos un mensaje sobre ello y retornamos false.
Retornamos
el resultado del funcionamiento del método de colocación de órdenes del objeto comercial que hemos analizado anteriormente.
Método de colocación de una orden pendiente BuyStopLimit:
//+------------------------------------------------------------------+ //| Place BuyStopLimit pending order | //+------------------------------------------------------------------+ bool CEngine::PlaceBuyStopLimit(const double volume, const string symbol, const double price_stop, const double price_limit, const double sl=0, const double tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC) { #ifdef __MQL5__ CSymbol *symbol_obj=this.GetSymbolObjByName(symbol); if(symbol_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_SYMBOL_ON_LIST)); return false; } CTradeObj *trade_obj=symbol_obj.GetTradeObj(); if(trade_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.SetOrder(ORDER_TYPE_BUY_STOP_LIMIT,volume,price_stop,sl,tp,price_limit,magic,expiration,type_time,comment); //--- MQL4 #else return true; #endif } //+------------------------------------------------------------------+
Transmitimos al método:
- el volumen de la orden colocada (se indica necesariamente),
- el símbolo en el que debemos colocar la orden (se indica necesariamente),
- el precio al que debemos colocar la orden BuyStop (se indica necesariamente),
- el precio al que debemos colocar la orden BuyLimit al activarse la orden BuyStop (se indica necesariamente),
- el precio de StopLoss de la orden colocada (por defecto, no lo hay),
- el precio de TakeProfit de la orden colocada (por defecto, no lo hay),
- el número mágico de la orden colocada (por defecto, 0),
- los comentarios a la orden colocada (por defecto, el nombre del programa+" by DoEasy")
- el tiempo de vida útil de la orden colocada (por defecto, ilimitado),
- el tipo de tiempo de vida útil de la orden colocada (por defecto, hasta su cambio explícito),
Para MQL5:
Obtenemos el objeto de símbolo según el nombre del símbolo.
Si no hemos logrado obtener el objeto, mostramos un mensaje
sobre ello y retornamos
false.
Obtenemos el objeto
comercial desde el objeto de símbolo. Si no hemos logrado obtener el
objeto, mostramos un mensaje sobre ello y retornamos false.
Retornamos
el resultado del funcionamiento del método de colocación de órdenes del objeto comercial que hemos analizado anteriormente.
Para MQL4:
No hacemos nada, retornamos true.
Métodos para colocar las órdenes pendientes SellStop, SellLimit y SellStopLimit:
//+------------------------------------------------------------------+ //| Place SellStop pending order | //+------------------------------------------------------------------+ bool CEngine::PlaceSellStop(const double volume, const string symbol, const double price, const double sl=0, const double tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC) { CSymbol *symbol_obj=this.GetSymbolObjByName(symbol); if(symbol_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_SYMBOL_ON_LIST)); return false; } CTradeObj *trade_obj=symbol_obj.GetTradeObj(); if(trade_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.SetOrder(ORDER_TYPE_SELL_STOP,volume,price,sl,tp,0,magic,expiration,type_time,comment); } //+------------------------------------------------------------------+ //| Place SellLimit pending order | //+------------------------------------------------------------------+ bool CEngine::PlaceSellLimit(const double volume, const string symbol, const double price, const double sl=0, const double tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC) { CSymbol *symbol_obj=this.GetSymbolObjByName(symbol); if(symbol_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_SYMBOL_ON_LIST)); return false; } CTradeObj *trade_obj=symbol_obj.GetTradeObj(); if(trade_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.SetOrder(ORDER_TYPE_SELL_LIMIT,volume,price,sl,tp,0,magic,expiration,type_time,comment); } //+------------------------------------------------------------------+ //| Place SellStopLimit pending order | //+------------------------------------------------------------------+ bool CEngine::PlaceSellStopLimit(const double volume, const string symbol, const double price_stop, const double price_limit, const double sl=0, const double tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC) { #ifdef __MQL5__ CSymbol *symbol_obj=this.GetSymbolObjByName(symbol); if(symbol_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_SYMBOL_ON_LIST)); return false; } CTradeObj *trade_obj=symbol_obj.GetTradeObj(); if(trade_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.SetOrder(ORDER_TYPE_SELL_STOP_LIMIT,volume,price_stop,sl,tp,price_limit,magic,expiration,type_time,comment); //--- MQL4 #else return true; #endif } //+------------------------------------------------------------------+
En estos métodos, todo se realiza como en los métodos de colocación de órdenes pendientes de compra.
Método de modificación de una orden pendiente:
//+------------------------------------------------------------------+ //| Modify a pending order | //+------------------------------------------------------------------+ bool CEngine::ModifyOrder(const ulong ticket, const double price=WRONG_VALUE, const double sl=WRONG_VALUE, const double tp=WRONG_VALUE, const double stoplimit=WRONG_VALUE, datetime expiration=WRONG_VALUE, ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE) { CTradeObj *trade_obj=this.GetTradeObjByOrder(ticket); if(trade_obj==NULL) { //--- Error. Failed to get trading object ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.ModifyOrder(ticket,price,sl,tp,stoplimit,expiration,type_time); } //+------------------------------------------------------------------+
Transmitimos al método:
- el ticket de la orden modificada (se indica necesariamente),
- el nuevo precio de colocación de la orden pendiente (por defecto, sin cambios),
- el nuevo precio de StopLoss de la orden pendiente (por defecto, sin cambios),
- el nuevo precio de TakeProfit de la orden pendiente (por defecto, sin cambios),
- el nuevo precio de StopLimit de la orden pendiente (por defecto, sin cambios),
- el nuevo tiempo de expiración de la orden pendiente (por defecto, sin cambios),
- el nuevo modo de vida útil de la orden pendiente (por defecto, sin cambios),
Obtenemos el objeto comercial según el ticket de la orden modificada usando el
método GetTradeObjByOrder() que veremos más abajo.
Si no
hemos logrado obtener el objeto, mostramos un mensaje sobre ello y retornamos false.
Retornamos el resultado del funcionamiento del método de modificación de órdenes
pendientes del objeto comercial que hemos analizado anteriormente.
Método de eliminación de una orden pendiente:
//+------------------------------------------------------------------+ //| Remove a pending order | //+------------------------------------------------------------------+ bool CEngine::DeleteOrder(const ulong ticket) { CTradeObj *trade_obj=this.GetTradeObjByOrder(ticket); if(trade_obj==NULL) { //--- Error. Failed to get trading object ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.DeleteOrder(ticket); } //+------------------------------------------------------------------+
Transmitimos al método el ticket de la orden eliminada.
Obtenemos el objeto comercial según el ticket de la orden usando el método
GetTradeObjByOrder() que veremos más abajo.
Si
no hemos logrado obtener el objeto, mostramos un mensaje sobre ello y retornamos false.
Retornamos el resultado del funcionamiento del método de eliminación de órdenes
pendientes del objeto comercial que hemos analizado anteriormente.
Métodos que retornan el objeto comercial del símbolo según el ticket de la posicióny la orden:
//+------------------------------------------------------------------+ //| Return a symbol trading object by a position ticket | //+------------------------------------------------------------------+ CTradeObj *CEngine::GetTradeObjByPosition(const ulong ticket) { //--- Get the list of open positions CArrayObj *list=this.GetListMarketPosition(); //--- If failed to get the list of open positions, display the message and return NULL if(list==NULL) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_ENG_FAILED_GET_MARKET_POS_LIST)); return NULL; } //--- If the list is empty (no open positions), display the message and return NULL if(list.Total()==0) { ::Print(DFUN,CMessage::Text(MSG_ENG_NO_OPEN_POSITIONS)); return NULL; } //--- Sort the list by a ticket list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,ticket,EQUAL); //--- If failed to get the list of open positions, display the message and return NULL if(list==NULL) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_ENG_FAILED_GET_MARKET_POS_LIST)); return NULL; } //--- If the list is empty (no required ticket), display the message and return NULL if(list.Total()==0) { //--- Error. No open position with #ticket ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_NO_OPEN_POSITION_WITH_TICKET),(string)ticket); return NULL; } //--- Get a position with #ticket from the obtained list COrder *pos=list.At(0); //--- If failed to get the position object, display the message and return NULL if(pos==NULL) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_POS_OBJ)); return NULL; } //--- Get a symbol object by name CSymbol * symbol_obj=this.GetSymbolObjByName(pos.Symbol()); //--- If failed to get the symbol object, display the message and return NULL if(symbol_obj==NULL) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_SYM_OBJ)); return NULL; } //--- Get and return the trading object from the symbol object CTradeObj *obj=symbol_obj.GetTradeObj(); return obj; } //+------------------------------------------------------------------+ //| Return a symbol trading object by an order ticket | //+------------------------------------------------------------------+ CTradeObj *CEngine::GetTradeObjByOrder(const ulong ticket) { //--- Get the list of placed orders CArrayObj *list=this.GetListMarketPendings(); //--- If failed to get the list of placed orders, display the message and return NULL if(list==NULL) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_ENG_FAILED_GET_PENDING_ORD_LIST)); return NULL; } //--- If the list is empty (no placed orders), display the message and return NULL if(list.Total()==0) { ::Print(DFUN,CMessage::Text(MSG_ENG_NO_PLACED_ORDERS)); return NULL; } //--- Sort the list by a ticket list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,ticket,EQUAL); //--- If failed to get the list of placed orders, display the message and return NULL if(list==NULL) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_ENG_FAILED_GET_PENDING_ORD_LIST)); return NULL; } //--- If the list is empty (no required ticket), display the message and return NULL if(list.Total()==0) { //--- Error. No placed order with #ticket ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_NO_PLACED_ORDER_WITH_TICKET),(string)ticket); return NULL; } //--- Get an order with #ticket from the obtained list COrder *ord=list.At(0); //--- If failed to get an object order, display the message and return NULL if(ord==NULL) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_ORD_OBJ)); return NULL; } //--- Get a symbol object by name CSymbol *symbol_obj=this.GetSymbolObjByName(ord.Symbol()); //--- If failed to get the symbol object, display the message and return NULL if(symbol_obj==NULL) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_SYM_OBJ)); return NULL; } //--- Get and return the trading object from the symbol object CTradeObj *obj=symbol_obj.GetTradeObj(); return obj; } //+--------------------------------------------------------------------+
Ambos métodos son prácticamente iguales, salvo que en el primer
método obtenemos la lista con todas las posiciones abiertas, y en
el segundo, la lista con todas las órdenes pendientes colocadas. Después, la lógica es absolutamente idéntica en ambos
métodos, y está toda escrita en los comentarios al código, así que dejaremos al lector que la estudie por sí mismo.
Métodos para establecerla política de ejecución y la política correcta de ejecución en los objetos comerciales de todos los símbolos que se encuentran en la lista de colección de símbolos o para un símbolo establecido:
//+------------------------------------------------------------------+ //| Set the valid filling policy | //| for trading objects of all symbols | //+------------------------------------------------------------------+ void CEngine::SetTradeCorrectTypeFilling(const ENUM_ORDER_TYPE_FILLING type=ORDER_FILLING_FOK,const string symbol_name=NULL) { //--- Declare the empty pointer to a symbol object CSymbol *symbol=NULL; //--- If a symbol name passed in the method inputs is not set, specify a filling policy for all symbols if(symbol_name==NULL) { //--- get the list of all used symbols CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); //--- In a loop by the list of symbol objects for(int i=0;i<total;i++) { //--- get the next symbol object symbol=list.At(i); if(symbol==NULL) continue; //--- get a trading object from a symbol object CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; //--- set correct filling policy to the trading object (the default is "fill or kill") obj.SetTypeFilling(symbol.GetCorrectTypeFilling(type)); } } //--- If a symbol name is specified in the method inputs, set the filling policy only for the specified symbol else { //--- Get a symbol object by a symbol name symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; //--- get a trading object from a symbol object CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; //--- set correct filling policy to the trading object (the default is "fill or kill") obj.SetTypeFilling(symbol.GetCorrectTypeFilling(type)); } } //+------------------------------------------------------------------+ //| Set the filling policy | //| for trading objects of all symbols | //+------------------------------------------------------------------+ void CEngine::SetTradeTypeFilling(const ENUM_ORDER_TYPE_FILLING type=ORDER_FILLING_FOK,const string symbol_name=NULL) { //--- Declare the empty pointer to a symbol object CSymbol *symbol=NULL; //--- If a symbol name passed in the method inputs is not set, specify a filling policy for all symbols if(symbol_name==NULL) { //--- get the list of all used symbols CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); //--- In a loop by the list of symbol objects for(int i=0;i<total;i++) { //--- get the next symbol object symbol=list.At(i); if(symbol==NULL) continue; //--- get a trading object from a symbol object CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; //--- for the trading object, set a filling policy passed to the method in the inputs (the default is "fill or kill") obj.SetTypeFilling(type); } } //--- If a symbol name is specified in the method inputs, set the filling policy only for the specified symbol else { //--- Get a symbol object by a symbol name symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; //--- get a trading object from a symbol object CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; //--- for the trading object, set a filling policy passed to the method in the inputs (the default is "fill or kill") obj.SetTypeFilling(type); } } //+------------------------------------------------------------------+
Transmitimos a los métodos la política de ejecución (por defecto, "todo o nada") y el símbolo (por defecto, todos los símbolos de la colección de símbolos).
La lógica de los métodos se describe en los comentarios, por lo que resultará comprensible al lector. En cualquier caso, podrán escribir cualquier duda en los comentarios al artículo.
Los demás métodos para establecer los valores por defecto para los objetos comerciales de los símbolos poseen exactamente la misma lógica, de ahí que no tengan comentarios. Sea como fuere, el lector podrá estudiar la lógica de estos dos métodos.
Aquí tenemos el resto de métodos para asignar los valores por defecto a los objetos comerciales de los símbolos:
//+------------------------------------------------------------------+ //| Set a correct order expiration type | //| for trading objects of all symbols | //+------------------------------------------------------------------+ void CEngine::SetTradeCorrectTypeExpiration(const ENUM_ORDER_TYPE_TIME type=ORDER_TIME_GTC,const string symbol_name=NULL) { CSymbol *symbol=NULL; if(symbol_name==NULL) { CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); for(int i=0;i<total;i++) { symbol=list.At(i); if(symbol==NULL) continue; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; obj.SetTypeExpiration(symbol.GetCorrectTypeExpiration(type)); } } else { symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; obj.SetTypeExpiration(symbol.GetCorrectTypeExpiration(type)); } } //+------------------------------------------------------------------+ //| Set an order expiration type | //| for trading objects of all symbols | //+------------------------------------------------------------------+ void CEngine::SetTradeTypeExpiration(const ENUM_ORDER_TYPE_TIME type=ORDER_TIME_GTC,const string symbol_name=NULL) { CSymbol *symbol=NULL; if(symbol_name==NULL) { CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); for(int i=0;i<total;i++) { symbol=list.At(i); if(symbol==NULL) continue; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; obj.SetTypeExpiration(type); } } else { symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; obj.SetTypeExpiration(type); } } //+------------------------------------------------------------------+ //| Set a magic number for trading objects of all symbols | //+------------------------------------------------------------------+ void CEngine::SetTradeMagic(const ulong magic,const string symbol_name=NULL) { CSymbol *symbol=NULL; if(symbol_name==NULL) { CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); for(int i=0;i<total;i++) { symbol=list.At(i); if(symbol==NULL) continue; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; obj.SetMagic(magic); } } else { symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; obj.SetMagic(magic); } } //+------------------------------------------------------------------+ //| Set a comment for trading objects of all symbols | //+------------------------------------------------------------------+ void CEngine::SetTradeComment(const string comment,const string symbol_name=NULL) { CSymbol *symbol=NULL; if(symbol_name==NULL) { CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); for(int i=0;i<total;i++) { symbol=list.At(i); if(symbol==NULL) continue; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; obj.SetComment(comment); } } else { symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; obj.SetComment(comment); } } //+------------------------------------------------------------------+ //| Set a slippage | //| for trading objects of all symbols | //+------------------------------------------------------------------+ void CEngine::SetTradeDeviation(const ulong deviation,const string symbol_name=NULL) { CSymbol *symbol=NULL; if(symbol_name==NULL) { CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); for(int i=0;i<total;i++) { symbol=list.At(i); if(symbol==NULL) continue; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; obj.SetDeviation(deviation); } } else { symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; obj.SetDeviation(deviation); } } //+------------------------------------------------------------------+ //| Set a volume for trading objects of all symbols | //+------------------------------------------------------------------+ void CEngine::SetTradeVolume(const double volume=0,const string symbol_name=NULL) { CSymbol *symbol=NULL; if(symbol_name==NULL) { CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); for(int i=0;i<total;i++) { symbol=list.At(i); if(symbol==NULL) continue; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; obj.SetVolume(volume!=0 ? symbol.NormalizedLot(volume) : symbol.LotsMin()); } } else { symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; obj.SetVolume(volume!=0 ? symbol.NormalizedLot(volume) : symbol.LotsMin()); } } //+------------------------------------------------------------------+ //| Set an order expiration date | //| for trading objects of all symbols | //+------------------------------------------------------------------+ void CEngine::SetTradeExpiration(const datetime expiration=0,const string symbol_name=NULL) { CSymbol *symbol=NULL; if(symbol_name==NULL) { CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); for(int i=0;i<total;i++) { symbol=list.At(i); if(symbol==NULL) continue; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; obj.SetExpiration(expiration); } } else { symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; obj.SetExpiration(expiration); } } //+------------------------------------------------------------------+ //| Set the flag of asynchronous sending of trading requests | //| for trading objects of all symbols | //+------------------------------------------------------------------+ void CEngine::SetTradeAsyncMode(const bool mode=false,const string symbol_name=NULL) { CSymbol *symbol=NULL; if(symbol_name==NULL) { CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); for(int i=0;i<total;i++) { symbol=list.At(i); if(symbol==NULL) continue; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; obj.SetAsyncMode(mode); } } else { symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; obj.SetAsyncMode(mode); } } //+------------------------------------------------------------------+ //| Set a logging level of trading requests | //| for trading objects of all symbols | //+------------------------------------------------------------------+ void CEngine::SetTradeLogLevel(const ENUM_LOG_LEVEL log_level=LOG_LEVEL_ERROR_MSG,const string symbol_name=NULL) { CSymbol *symbol=NULL; if(symbol_name==NULL) { CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); for(int i=0;i<total;i++) { symbol=list.At(i); if(symbol==NULL) continue; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; obj.SetLogLevel(log_level); } } else { symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; obj.SetLogLevel(log_level); } } //+------------------------------------------------------------------+
Hemos escrito todos los métodos auxiliares temporales en la clase CEngine, para poner a prueba los objetos comerciales de los símbolos.
Gracias a los métodos comerciales multipltaforma disponibles (aunque en estado embrionario), tenemos la posibilidad de evitar en el
asesor de prueba la compilación condicional para MQL5 o MQL4; ahora, todas las funciones comerciales del asesor de prueba serán
iguales para todas las plataformas. Y en lo sucesivo, dividiremos el trabajo con las clases comerciales de la biblioteca de tal forma
que podamos obtener la funcionalidad completa necesaria para trabajar sin problemas con nuestros programas.
Simulando el objeto comercial único
Para poner a prueba los objetos comerciales de los símbolos, tomaremos el
asesor de prueba del artículo anterior y corregiremos sus funciones comerciales para trabajar con los objetos comerciales de los
símbolos. No debemos olvidar que, por el momento, no disponemos de ninguna comprobación en cuanto a la corrección de los valores de las
solicitudes comerciales, pero esto nos da la posibilidad de simular la reacción ante parámetros erróneos que implementaremos más tarde.
Guardamos el asesor en la nueva carpeta \MQL5\Experts\TestDoEasy\Part21\ con el nuevo nombre TestDoEasyPart21.mq5.
En primer lugar, eliminamos la inclusión de la clase comercial CTrade en la biblioteca estándar y la declaración del objeto comercial con el tipo de clase CTrade:
//+------------------------------------------------------------------+ //| TestDoEasyPart20.mq5 | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/es/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/es/users/artmedia70" #property version "1.00" //--- includes #include <DoEasy\Engine.mqh> #ifdef __MQL5__ #include <Trade\Trade.mqh> #endif //--- enums enum ENUM_BUTTONS { BUTT_BUY, BUTT_BUY_LIMIT, BUTT_BUY_STOP, BUTT_BUY_STOP_LIMIT, BUTT_CLOSE_BUY, BUTT_CLOSE_BUY2, BUTT_CLOSE_BUY_BY_SELL, BUTT_SELL, BUTT_SELL_LIMIT, BUTT_SELL_STOP, BUTT_SELL_STOP_LIMIT, BUTT_CLOSE_SELL, BUTT_CLOSE_SELL2, BUTT_CLOSE_SELL_BY_BUY, BUTT_DELETE_PENDING, BUTT_CLOSE_ALL, BUTT_PROFIT_WITHDRAWAL, BUTT_SET_STOP_LOSS, BUTT_SET_TAKE_PROFIT, BUTT_TRAILING_ALL }; #define TOTAL_BUTT (20) //--- structures struct SDataButt { string name; string text; }; //--- input variables input ulong InpMagic = 123; // Magic number input double InpLots = 0.1; // Lots input uint InpStopLoss = 50; // StopLoss in points input uint InpTakeProfit = 50; // TakeProfit in points input uint InpDistance = 50; // Pending orders distance (points) input uint InpDistanceSL = 50; // StopLimit orders distance (points) input uint InpSlippage = 0; // Slippage in points input double InpWithdrawal = 10; // Withdrawal funds (in tester) input uint InpButtShiftX = 40; // Buttons X shift input uint InpButtShiftY = 10; // Buttons Y shift input uint InpTrailingStop = 50; // Trailing Stop (points) input uint InpTrailingStep = 20; // Trailing Step (points) input uint InpTrailingStart = 0; // Trailing Start (points) input uint InpStopLossModify = 20; // StopLoss for modification (points) input uint InpTakeProfitModify = 60; // TakeProfit for modification (points) input ENUM_SYMBOLS_MODE InpModeUsedSymbols = SYMBOLS_MODE_CURRENT; // Mode of used symbols list input string InpUsedSymbols = "EURUSD,AUDUSD,EURAUD,EURCAD,EURGBP,EURJPY,EURUSD,GBPUSD,NZDUSD,USDCAD,USDJPY"; // List of used symbols (comma - separator) //--- global variables CEngine engine; #ifdef __MQL5__ CTrade trade; #endif SDataButt butt_data[TOTAL_BUTT]; string prefix; double lot; double withdrawal=(InpWithdrawal<0.1 ? 0.1 : InpWithdrawal); ulong magic_number; uint stoploss; uint takeprofit; uint distance_pending; uint distance_stoplimit; uint slippage; bool trailing_on; double trailing_stop; double trailing_step; uint trailing_start; uint stoploss_to_modify; uint takeprofit_to_modify; int used_symbols_mode; string used_symbols; string array_used_symbols[]; //+------------------------------------------------------------------+
Eliminamos en el manejador OnInit() la asignación de los parámetros al objeto trade de la clase comercial CTrade:
//--- Set trailing activation button status ButtonState(butt_data[TOTAL_BUTT-1].name,trailing_on); //--- Set CTrade trading class parameters #ifdef __MQL5__ trade.SetDeviationInPoints(slippage); trade.SetExpertMagicNumber(magic_number); trade.SetTypeFillingBySymbol(Symbol()); trade.SetMarginMode(); trade.LogLevel(LOG_LEVEL_NO); #endif //--- Create and check the resource files
Después, todo resulta sencillo: con la ayuda de la búsqueda(Ctrl+F), encontramos las entradas de la línea "trade" y sustituimos la llamada de los métodos comerciales de la biblioteca por las nuestras.
Por ejemplo, esta:
COrder* position=list_positions.At(index); if(position!=NULL) { //--- Get a ticket of a position with the highest profit and close the position by a ticket #ifdef __MQL5__ trade.PositionClose(position.Ticket()); #else PositionClose(position.Ticket(),position.Volume()); #endif }
la cambiamos por esta:
COrder* position=list_positions.At(index); if(position!=NULL) { //--- Get a ticket of a position with the highest profit and close the position by a ticket engine.ClosePosition(position.Ticket()); }
Y después, a medida que vayamos encontrando las llamadas de los métodos comerciales de la biblioteca estándar, simplemente las cambiamos por las nuestras.
Vamos a analizar el manejador resultante para pulsar los botones del panel. Todas las llamadas de los nuevos métodos comerciales han sido destacadas con color amarillo:
//+------------------------------------------------------------------+ //| Handle pressing the buttons | //+------------------------------------------------------------------+ void PressButtonEvents(const string button_name) { string comment=""; //--- Convert button name into its string ID string button=StringSubstr(button_name,StringLen(prefix)); //--- If the button is pressed if(ButtonState(button_name)) { //--- If the BUTT_BUY button is pressed: Open Buy position if(button==EnumToString(BUTT_BUY)) { //--- Get the correct StopLoss and TakeProfit prices relative to StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY,0,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY,0,takeprofit); //--- Open Buy position engine.OpenBuy(lot,Symbol(),magic_number,sl,tp); // No comment - the default comment is to be set } //--- If the BUTT_BUY_LIMIT button is pressed: Place BuyLimit else if(button==EnumToString(BUTT_BUY_LIMIT)) { //--- Get correct order placement relative to StopLevel double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_LIMIT,distance_pending); //--- Get correct StopLoss and TakeProfit prices relative to the order placement level considering StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_LIMIT,price_set,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_LIMIT,price_set,takeprofit); //--- Set BuyLimit order engine.PlaceBuyLimit(lot,Symbol(),price_set,sl,tp,magic_number,TextByLanguage("Отложенный BuyLimit","Pending BuyLimit order")); } //--- If the BUTT_BUY_STOP button is pressed: Set BuyStop else if(button==EnumToString(BUTT_BUY_STOP)) { //--- Get correct order placement relative to StopLevel double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_STOP,distance_pending); //--- Get correct StopLoss and TakeProfit prices relative to the order placement level considering StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_STOP,price_set,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_STOP,price_set,takeprofit); //--- Set BuyStop order engine.PlaceBuyStop(lot,Symbol(),price_set,sl,tp,magic_number,TextByLanguage("Отложенный BuyStop","Pending BuyStop order")); } //--- If the BUTT_BUY_STOP_LIMIT button is pressed: Set BuyStopLimit else if(button==EnumToString(BUTT_BUY_STOP_LIMIT)) { //--- Get the correct BuyStop order placement price relative to StopLevel double price_set_stop=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_STOP,distance_pending); //--- Calculate BuyLimit order price relative to BuyStop level considering StopLevel double price_set_limit=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_LIMIT,distance_stoplimit,price_set_stop); //--- Get correct StopLoss and TakeProfit prices relative to the order placement level considering StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_STOP,price_set_limit,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_STOP,price_set_limit,takeprofit); //--- Set BuyStopLimit order engine.PlaceBuyStopLimit(lot,Symbol(),price_set_stop,price_set_limit,sl,tp,magic_number,TextByLanguage("Отложенный BuyStopLimit","Pending BuyStopLimit order")); } //--- If the BUTT_SELL button is pressed: Open Sell position else if(button==EnumToString(BUTT_SELL)) { //--- Get the correct StopLoss and TakeProfit prices relative to StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL,0,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL,0,takeprofit); //--- Open Sell position engine.OpenSell(lot,Symbol(),magic_number,sl,tp); // No comment - the default comment is to be set } //--- If the BUTT_SELL_LIMIT button is pressed: Set SellLimit else if(button==EnumToString(BUTT_SELL_LIMIT)) { //--- Get correct order placement relative to StopLevel double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_LIMIT,distance_pending); //--- Get correct StopLoss and TakeProfit prices relative to the order placement level considering StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_LIMIT,price_set,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_LIMIT,price_set,takeprofit); //--- Set SellLimit order engine.PlaceSellLimit(lot,Symbol(),price_set,sl,tp,magic_number,TextByLanguage("Отложенный SellLimit","Pending SellLimit order")); } //--- If the BUTT_SELL_STOP button is pressed: Set SellStop else if(button==EnumToString(BUTT_SELL_STOP)) { //--- Get correct order placement relative to StopLevel double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_STOP,distance_pending); //--- Get correct StopLoss and TakeProfit prices relative to the order placement level considering StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_STOP,price_set,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_STOP,price_set,takeprofit); //--- Set SellStop order engine.PlaceSellStop(lot,Symbol(),price_set,sl,tp,magic_number,TextByLanguage("Отложенный SellStop","Pending SellStop order")); } //--- If the BUTT_SELL_STOP_LIMIT button is pressed: Set SellStopLimit else if(button==EnumToString(BUTT_SELL_STOP_LIMIT)) { //--- Get the correct SellStop order price relative to StopLevel double price_set_stop=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_STOP,distance_pending); //--- Calculate SellLimit order price relative to SellStop level considering StopLevel double price_set_limit=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_LIMIT,distance_stoplimit,price_set_stop); //--- Get correct StopLoss and TakeProfit prices relative to the order placement level considering StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_STOP,price_set_limit,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_STOP,price_set_limit,takeprofit); //--- Set SellStopLimit order engine.PlaceSellStopLimit(lot,Symbol(),price_set_stop,price_set_limit,sl,tp,magic_number,TextByLanguage("Отложенный SellStopLimit","Pending SellStopLimit order")); } //--- If the BUTT_CLOSE_BUY button is pressed: Close Buy with the maximum profit else if(button==EnumToString(BUTT_CLOSE_BUY)) { //--- Get the list of all open positions CArrayObj* list=engine.GetListMarketPosition(); //--- Select only Buy positions from the list list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- Sort the list by profit considering commission and swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Buy position with the maximum profit int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { //--- Get the Buy position object and close a position by ticket COrder* position=list.At(index); if(position!=NULL) engine.ClosePosition((ulong)position.Ticket()); } } //--- If the BUTT_CLOSE_BUY2 button is pressed: Close the half of the Buy with the maximum profit else if(button==EnumToString(BUTT_CLOSE_BUY2)) { //--- Get the list of all open positions CArrayObj* list=engine.GetListMarketPosition(); //--- Select only Buy positions from the list list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- Sort the list by profit considering commission and swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Buy position with the maximum profit int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { COrder* position=list.At(index); if(position!=NULL) { //--- If this is a hedge account, close the half of the Buy position by the ticket if(engine.IsHedge()) engine.ClosePositionPartially((ulong)position.Ticket(),position.Volume()/2.0); //--- If this is a netting account, open a Sell position with the half of the Buy position volume else engine.OpenSell(NormalizeLot(position.Symbol(),position.Volume()/2.0),Symbol(),magic_number,position.StopLoss(),position.TakeProfit(),"Частичное закрытие Buy #"+(string)position.Ticket()); } } } //--- If the BUTT_CLOSE_BUY_BY_SELL button is pressed: Close Buy with the maximum profit by the opposite Sell with the maximum profit else if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)) { //--- In case of a hedging account if(engine.IsHedge()) { //--- Get the list of all open positions CArrayObj* list_buy=engine.GetListMarketPosition(); //--- Select only Buy positions from the list list_buy=CSelect::ByOrderProperty(list_buy,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- Sort the list by profit considering commission and swap list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Buy position with the maximum profit int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL); //--- Get the list of all open positions CArrayObj* list_sell=engine.GetListMarketPosition(); //--- Select only Sell positions from the list list_sell=CSelect::ByOrderProperty(list_sell,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- Sort the list by profit considering commission and swap list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Sell position with the maximum profit int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL); if(index_buy>WRONG_VALUE && index_sell>WRONG_VALUE) { //--- Select the Buy position with the maximum profit COrder* position_buy=list_buy.At(index_buy); //--- Select the Sell position with the maximum profit COrder* position_sell=list_sell.At(index_sell); //--- Close the Buy position by the opposite Sell one if(position_buy!=NULL && position_sell!=NULL) engine.ClosePositionBy((ulong)position_buy.Ticket(),(ulong)position_sell.Ticket()); } } } //--- If the BUTT_CLOSE_SELL button is pressed: Close Sell with the maximum profit else if(button==EnumToString(BUTT_CLOSE_SELL)) { //--- Get the list of all open positions CArrayObj* list=engine.GetListMarketPosition(); //--- Select only Sell positions from the list list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- Sort the list by profit considering commission and swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Sell position with the maximum profit int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { //--- Get the Sell position object and close a position by ticket COrder* position=list.At(index); if(position!=NULL) engine.ClosePosition((ulong)position.Ticket()); } } //--- If the BUTT_CLOSE_SELL2 button is pressed: Close the half of the Sell with the maximum profit else if(button==EnumToString(BUTT_CLOSE_SELL2)) { //--- Get the list of all open positions CArrayObj* list=engine.GetListMarketPosition(); //--- Select only Sell positions from the list list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- Sort the list by profit considering commission and swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Sell position with the maximum profit int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { COrder* position=list.At(index); if(position!=NULL) { //--- If this is a hedge account, close the half of the Sell position by the ticket if(engine.IsHedge()) engine.ClosePositionPartially((ulong)position.Ticket(),position.Volume()/2.0); //--- If this is a netting account, open a Buy position with the half of the Sell position volume else engine.OpenBuy(NormalizeLot(position.Symbol(),position.Volume()/2.0),Symbol(),position.Magic(),position.StopLoss(),position.TakeProfit(),"Partial closure Buy #"+(string)position.Ticket()); } } } //--- If the BUTT_CLOSE_SELL_BY_BUY button is pressed: Close Sell with the maximum profit by the opposite Buy with the maximum profit else if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)) { //--- Get the list of all open positions CArrayObj* list_sell=engine.GetListMarketPosition(); //--- Select only Sell positions from the list list_sell=CSelect::ByOrderProperty(list_sell,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- Sort the list by profit considering commission and swap list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Sell position with the maximum profit int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL); //--- Get the list of all open positions CArrayObj* list_buy=engine.GetListMarketPosition(); //--- Select only Buy positions from the list list_buy=CSelect::ByOrderProperty(list_buy,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- Sort the list by profit considering commission and swap list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Buy position with the maximum profit int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL); if(index_sell>WRONG_VALUE && index_buy>WRONG_VALUE) { //--- Select the Sell position with the maximum profit COrder* position_sell=list_sell.At(index_sell); //--- Select the Buy position with the maximum profit COrder* position_buy=list_buy.At(index_buy); if(position_sell!=NULL && position_buy!=NULL) { //--- Close the Sell position by the opposite Buy one engine.ClosePositionBy((ulong)position_sell.Ticket(),(ulong)position_buy.Ticket()); } } } //--- If the BUTT_CLOSE_ALL is pressed: Close all positions starting with the one with the least profit else if(button==EnumToString(BUTT_CLOSE_ALL)) { //--- Get the list of all open positions CArrayObj* list=engine.GetListMarketPosition(); if(list!=NULL) { //--- Sort the list by profit considering commission and swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); int total=list.Total(); //--- In the loop from the position with the least profit for(int i=0;i<total;i++) { COrder* position=list.At(i); if(position==NULL) continue; //--- close each position by its ticket engine.ClosePosition((ulong)position.Ticket()); } } } //--- If the BUTT_DELETE_PENDING button is pressed: Remove the first pending order else if(button==EnumToString(BUTT_DELETE_PENDING)) { //--- Get the list of all orders CArrayObj* list=engine.GetListMarketPendings(); if(list!=NULL) { //--- Sort the list by placement time list.Sort(SORT_BY_ORDER_TIME_OPEN); int total=list.Total(); //--- In the loop from the position with the most amount of time for(int i=total-1;i>=0;i--) { COrder* order=list.At(i); if(order==NULL) continue; //--- delete the order by its ticket engine.DeleteOrder((ulong)order.Ticket()); } } } //--- If the BUTT_PROFIT_WITHDRAWAL button is pressed: Withdraw funds from the account if(button==EnumToString(BUTT_PROFIT_WITHDRAWAL)) { //--- If the program is launched in the tester if(MQLInfoInteger(MQL_TESTER)) { //--- Emulate funds withdrawal TesterWithdrawal(withdrawal); } } //--- If the BUTT_SET_STOP_LOSS button is pressed: Place StopLoss to all orders and positions where it is not present if(button==EnumToString(BUTT_SET_STOP_LOSS)) { SetStopLoss(); } //--- If the BUTT_SET_TAKE_PROFIT button is pressed: Place TakeProfit to all orders and positions where it is not present if(button==EnumToString(BUTT_SET_TAKE_PROFIT)) { SetTakeProfit(); } //--- Wait for 1/10 of a second Sleep(100); //--- "Unpress" the button (if this is not a trailing button) if(button!=EnumToString(BUTT_TRAILING_ALL)) ButtonState(button_name,false); //--- If the BUTT_TRAILING_ALL button is pressed else { //--- Set the color of the active button ButtonState(button_name,true); trailing_on=true; } //--- re-draw the chart ChartRedraw(); } //--- Return the inactive button color (if this is a trailing button) else if(button==EnumToString(BUTT_TRAILING_ALL)) { ButtonState(button_name,false); trailing_on=false; //--- re-draw the chart ChartRedraw(); } } //+------------------------------------------------------------------+
El resto de funciones del asesor que hemos mejorado de esta forma con la llamada de los métodos de la clase comercial CTrade de la biblioteca estándar no las vamos a ver aquí, se encuentran en los anexos al artículo.
Ahora, nos limitaremos a compilar el asesor e iniciarlo en el simulador.
Pulsamos varios botones del panel y comprobamos
que los objetos comerciales funcionen:
Nuestros primeros objetos comerciales de los símbolos funcionan como teníamos previsto.
Aún nos queda mucho por hacer
para que el trabajo con ellos resulte cómodo y completo.
¿Qué es lo próximo?
En un futuro inmediato, planeamos crear una clase completa con la que podremos recurrir a los objetos comerciales de los símbolos.
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
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/7229
- 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