Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte IX): Compatibilidad con MQL4 - Preparando los datos
Contenido
En las anteriores partes de la descripción de la biblioteca multiplataforma para MetaTrader 5 y MetaTrader 4, preparamos el instrumental:
- para crear funciones de caso de usuario que ofrezcan acceso rápido desde sus programas a cualquier dato de cualquier orden y posición en las cuentas de cobertura y compensación,
- para monitorear los eventos que suceden con las órdenes y posiciones: establecimiento, eliminación y activación de órdenes pendientes, apertura y cierre de posiciones, y modificación de posiciones y órdenes.
Ahora, vamos a proceder a implementar la compatibilidad de la biblioteca con MQL4, puesto que, posteriormente, deberemos crear las clases comerciales, y la biblioteca tendrá que funcionar correctamente para ello tanto en MQL5 como en MQL4.
En el presente artículo, comenzaremos a mejorar la biblioteca para implementar su capacidad multiplataforma.
Diferencias entre MQL4 y MQL5
Vamos a copiar la carpeta completa de nuestra biblioteca en la carpeta correspondiente de MetaTrader 4: \MQL4\Include\DoEasy; asimismo, vamos tomar los asesores de prueba de las carpetas correspondientes a los artículos con los asesores MQL5 y a guardarlas con la extensión *.mq4 en el directorio de expertos \MQL4\Experts\TestDoEasy, en la carpeta correspondiente al número del artículo (en este caso, Part09) , donde se guardará el asesor de prueba para este artículo.
Encontramos en el navegador del editor la carpeta con la biblioteca \MQL4\Include\DoEasy, clicamos sobre ella con el botón derecho y seleccionamos "Compilar".
Esto hará que se compilen todos los archivos de la biblioteca, obteniendo como resultado más de dos mil errores de compilación:
Si analizamos los errores obtenidos, veremos que la mayoría están relacionados con las constantes y enumeraciones de MQL5, sobre las que MQL4 no sabe nada. Significa que debemos "presentar" a MQL4 con las constantes usadas por la biblioteca. También hay errores de otro tipo, como la ausencia en MQL4 de ciertas funciones de MQL5 que nosotros usamos: esto significa que crearemos su lógica de funcionamiento con la ayuda de las funciones MQL4 existentes.
Y, naturalmente, los sistemas de órdenes de MQL4 y MQL5 se diferencian sustancialmente. En este punto, deberemos crear para MQL4 un manejador de eventos aparte, diferente al ya implementado en MQL5, puesto que, por desgracia, en la lista de órdenes históricas en MQL4 se ofrece bastante menos información sobre las órdenes (sobre las transacciones no hay información en absoluto), y aquí no conseguiremos tomar los datos sobre las órdenes y transacciones directamente de la lista del terminal: nos veremos obligados a comparar lógicamente lo que sucede en las listas de órdenes de mercado activas e históricas y, basándonos en dicha comparación, determinar los eventos sucedidos.Mejorando la biblioteca
En la carpeta raíz de la biblioteca DoEasy, creamos el nuevo archivo de inclusión ToMQL4.mqh, en el que describiremos las constantes y enumeraciones necesarias para MQL4. A continuación, lo incluimos en el archivo Defines.mqh para la compilación en MQL4, al principio del todo del listado Defines.mqh:
//+------------------------------------------------------------------+ //| Defines.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/es/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/es/users/artmedia70" //+------------------------------------------------------------------+ //| Archivos de inclusión | //+------------------------------------------------------------------+ #ifdef __MQL4__ #include "ToMQL4.mqh" #endif //+------------------------------------------------------------------+
Después de dicha inclusión, la biblioteca al completo verá todo lo que esté escrito en el archivo ToMQL4.mqh al realizar la compilación para MQL4.
Vamos a pasar al principio de la lista de errores en el diario del editor "Errores", pulsando la tecla NumPad "Home", o simplemente desplazándonos con el ratón al comienzo, haciendo doble clic sobre el error a la derecha del todo:
El editor nos llevará al archivo Defines.mqh, a la línea donde se ha obtenido este error:
//+------------------------------------------------------------------+ //| Список возможных торговых событий на счёте | //+------------------------------------------------------------------+ enum ENUM_TRADE_EVENT { TRADE_EVENT_NO_EVENT = 0, // Нет торгового события TRADE_EVENT_PENDING_ORDER_PLASED, // Отложенный ордер установлен TRADE_EVENT_PENDING_ORDER_REMOVED, // Отложенный ордер удалён //--- члены перечисления, совпадающие с членами перечисления ENUM_DEAL_TYPE //--- (порядок следования констант ниже менять нельзя, удалять и добавлять новые - нельзя) TRADE_EVENT_ACCOUNT_CREDIT = DEAL_TYPE_CREDIT, // Начисление кредита (3) TRADE_EVENT_ACCOUNT_CHARGE, // Дополнительные сборы
Como es natural, MQL4 no sabe nada sobre las transacciones y sus tipos. Debemos informarle sobre ellos. Podemos simplemente abrir la guía del editor MQL5 y encontrar la información que necesitamos sobre las propiedades de las transacciones usando la búsqueda según DEAL_TYPE_CREDIT:
Identificador | Descripción | Tipo |
DEAL_TICKET | Ticket de la transacción. Número único asignado a cada transacción | long |
DEAL_ORDER | Orden que ha originado la transacción | long |
DEAL_TIME | Hora de ejecución de la transacción | datetime |
DEAL_TIME_MSC | Hora de ejecución de la transacción en milisegundos desde 01.01.1970 | long |
DEAL_TYPE | Tipo de transacción | |
DEAL_ENTRY | Dirección de la transacción: entrada en el mercado, salida del mercado o viraje | |
DEAL_MAGIC | Magic number для сделки (ver ORDER_MAGIC) | long |
DEAL_REASON | Causa u origen del comportamiento de la transacción | |
DEAL_POSITION_ID | Identificador de la posición en cuya apertura, modificación o cierre ha participado esta transacción. Cada posición tiene un identificador único, asignado a todas las transacciones realizadas en el símbolo durante la existencia completa de la posición. | long |
En este recuadro, nos interesa ENUM_DEAL_TYPE. Seguimos el enlace y obtenemos la lista con todos los tipos de transacciones:
Identificador | Descripción |
DEAL_TYPE_BUY | Compra |
DEAL_TYPE_SELL | Venta |
DEAL_TYPE_BALANCE | Balance |
DEAL_TYPE_CREDIT | Crédito |
DEAL_TYPE_CHARGE | Cargas adicionales |
DEAL_TYPE_CORRECTION | Corrección |
DEAL_TYPE_BONUS | Bonus |
DEAL_TYPE_COMMISSION | Comisiones adicionales |
DEAL_TYPE_COMMISSION_DAILY | Comisión calculada al final de la jornada de trading |
DEAL_TYPE_COMMISSION_MONTHLY | Comisión calculada al final del mes |
DEAL_TYPE_COMMISSION_AGENT_DAILY | Comisión de agente calculada al final de la jornada de trading |
DEAL_TYPE_COMMISSION_AGENT_MONTHLY | Comisión de agente calculada al final del mes |
DEAL_TYPE_INTEREST | Calculo de intereses sobre fondos libres |
DEAL_TYPE_BUY_CANCELED | Transacción de compra cancelada. Puede surgir la situación cuando una transacción de compra realizada anteriormente se cancele. En este caso el tipo de la operación realizada (DEAL_TYPE_BUY) se cambia a DEAL_TYPE_BUY_CANCELED, y su beneficio/pérdida se anula. El beneficio/pérdida producido/a anteriormente se carga/se quita de la cuenta por medio de una operación contable separada |
DEAL_TYPE_SELL_CANCELED | Transacción de venta cancelada. Puede surgir la situación cuando una transacción de venta realizada anteriormente se cancele. En este caso el tipo de la operación realizada (DEAL_TYPE_SELL) se cambia a DEAL_TYPE_SELL_CANCELED, y su beneficio/pérdida se anula. El beneficio/pérdida producido/a anteriormente se carga/se quita de la cuenta por medio de una operación contable separada |
DEAL_DIVIDEND | Operaciones con dividendos |
DEAL_DIVIDEND_FRANKED | Operaciones con dividendos franqueados (no tributables) |
DEAL_TAX | Carga impositiva |
Vamos a añadir los tipos de transacciones de la enumeración ENUM_DEAL_TYPE al archivo ToMQL4.mqh:
//+------------------------------------------------------------------+ //| ToMQL4.mqh | //| Copyright 2017, Artem A. Trishkin, Skype artmedia70 | //| https://www.mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2017, Artem A. Trishkin, Skype artmedia70" #property link "https://www.mql5.com/ru/users/artmedia70" #property strict #ifdef __MQL4__ //+------------------------------------------------------------------+ //| Типы сделок MQL5 | //+------------------------------------------------------------------+ enum ENUM_DEAL_TYPE { DEAL_TYPE_BUY, DEAL_TYPE_SELL, DEAL_TYPE_BALANCE, DEAL_TYPE_CREDIT, DEAL_TYPE_CHARGE, DEAL_TYPE_CORRECTION, DEAL_TYPE_BONUS, DEAL_TYPE_COMMISSION, DEAL_TYPE_COMMISSION_DAILY, DEAL_TYPE_COMMISSION_MONTHLY, DEAL_TYPE_COMMISSION_AGENT_DAILY, DEAL_TYPE_COMMISSION_AGENT_MONTHLY, DEAL_TYPE_INTEREST, DEAL_TYPE_BUY_CANCELED, DEAL_TYPE_SELL_CANCELED, DEAL_DIVIDEND, DEAL_DIVIDEND_FRANKED, DEAL_TAX }; //+------------------------------------------------------------------+ #endif
Guardamos el archivo y compilamos de nuevo todos los archivos de la biblioteca. Ahora hay menos errores:
Pasamos de nuevo al inicio de la lista de errores y clicamos sobre el primero. Ahora es ENUM_POSITION_TYPE, vamos a añadir:
//+------------------------------------------------------------------+ //| ToMQL4.mqh | //| Copyright 2017, Artem A. Trishkin, Skype artmedia70 | //| https://www.mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2017, Artem A. Trishkin, Skype artmedia70" #property link "https://www.mql5.com/ru/users/artmedia70" #property strict #ifdef __MQL4__ //+------------------------------------------------------------------+ //| Типы сделок MQL5 | //+------------------------------------------------------------------+ enum ENUM_DEAL_TYPE { DEAL_TYPE_BUY, DEAL_TYPE_SELL, DEAL_TYPE_BALANCE, DEAL_TYPE_CREDIT, DEAL_TYPE_CHARGE, DEAL_TYPE_CORRECTION, DEAL_TYPE_BONUS, DEAL_TYPE_COMMISSION, DEAL_TYPE_COMMISSION_DAILY, DEAL_TYPE_COMMISSION_MONTHLY, DEAL_TYPE_COMMISSION_AGENT_DAILY, DEAL_TYPE_COMMISSION_AGENT_MONTHLY, DEAL_TYPE_INTEREST, DEAL_TYPE_BUY_CANCELED, DEAL_TYPE_SELL_CANCELED, DEAL_DIVIDEND, DEAL_DIVIDEND_FRANKED, DEAL_TAX }; //+------------------------------------------------------------------+ //| Направление открытой позиции | //+------------------------------------------------------------------+ enum ENUM_POSITION_TYPE { POSITION_TYPE_BUY, POSITION_TYPE_SELL }; //+------------------------------------------------------------------+ #endif
Después de compilar, obtenemos aún menos errores. Pasamos al primer error, determinamos el motivo y añadimos la siguiente enumeración:
//+------------------------------------------------------------------+ //| ToMQL4.mqh | //| Copyright 2017, Artem A. Trishkin, Skype artmedia70 | //| https://www.mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2017, Artem A. Trishkin, Skype artmedia70" #property link "https://www.mql5.com/ru/users/artmedia70" #property strict #ifdef __MQL4__ //+------------------------------------------------------------------+ //| Типы сделок MQL5 | //+------------------------------------------------------------------+ enum ENUM_DEAL_TYPE { DEAL_TYPE_BUY, DEAL_TYPE_SELL, DEAL_TYPE_BALANCE, DEAL_TYPE_CREDIT, DEAL_TYPE_CHARGE, DEAL_TYPE_CORRECTION, DEAL_TYPE_BONUS, DEAL_TYPE_COMMISSION, DEAL_TYPE_COMMISSION_DAILY, DEAL_TYPE_COMMISSION_MONTHLY, DEAL_TYPE_COMMISSION_AGENT_DAILY, DEAL_TYPE_COMMISSION_AGENT_MONTHLY, DEAL_TYPE_INTEREST, DEAL_TYPE_BUY_CANCELED, DEAL_TYPE_SELL_CANCELED, DEAL_DIVIDEND, DEAL_DIVIDEND_FRANKED, DEAL_TAX }; //+------------------------------------------------------------------+ //| Направление открытой позиции | //+------------------------------------------------------------------+ enum ENUM_POSITION_TYPE { POSITION_TYPE_BUY, POSITION_TYPE_SELL }; //+------------------------------------------------------------------+ //| Состояние ордера | //+------------------------------------------------------------------+ enum ENUM_ORDER_STATE { ORDER_STATE_STARTED, ORDER_STATE_PLACED, ORDER_STATE_CANCELED, ORDER_STATE_PARTIAL, ORDER_STATE_FILLED, ORDER_STATE_REJECTED, ORDER_STATE_EXPIRED, ORDER_STATE_REQUEST_ADD, ORDER_STATE_REQUEST_MODIFY, ORDER_STATE_REQUEST_CANCEL }; //+------------------------------------------------------------------+ #endif
Durante la siguiente compilación, nos hemos encontrado el tipo erróneo de orden ORDER_TYPE_BUY_STOP_LIMIT.
En MQL4 ya existe la enumeración ENUM_ORDER_TYPE, a la que no podemos añadir nuevas constantes. Esto significa que deberemos añadirlas como macrosustituciones.
En MQL5, la constante ORDER_TYPE_BUY_STOP_LIMIT de la enumeración ENUM_ORDER_TYPE tiene el valor 6. Pero en MQL4, este tipo de orden existe. Esta operación contable, igual que ORDER_TYPE_SELL_STOP_LIMIT en MQL5, tiene el valor 7, y en MQL4, este tipo de orden es una operación de crédito.
Por eso, vamos a asignarles valores superiores a la constante de la orden de cierre ORDER_TYPE_CLOSE_BY en MQL5: ORDER_TYPE_CLOSE_BY+1 y ORDER_TYPE_CLOSE_BY+2, respectivamente:
//+------------------------------------------------------------------+ //| ToMQL4.mqh | //| Copyright 2017, Artem A. Trishkin, Skype artmedia70 | //| https://www.mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2017, Artem A. Trishkin, Skype artmedia70" #property link "https://www.mql5.com/ru/users/artmedia70" #property strict #ifdef __MQL4__ //+------------------------------------------------------------------+ //| Типы сделок MQL5 | //+------------------------------------------------------------------+ enum ENUM_DEAL_TYPE { DEAL_TYPE_BUY, DEAL_TYPE_SELL, DEAL_TYPE_BALANCE, DEAL_TYPE_CREDIT, DEAL_TYPE_CHARGE, DEAL_TYPE_CORRECTION, DEAL_TYPE_BONUS, DEAL_TYPE_COMMISSION, DEAL_TYPE_COMMISSION_DAILY, DEAL_TYPE_COMMISSION_MONTHLY, DEAL_TYPE_COMMISSION_AGENT_DAILY, DEAL_TYPE_COMMISSION_AGENT_MONTHLY, DEAL_TYPE_INTEREST, DEAL_TYPE_BUY_CANCELED, DEAL_TYPE_SELL_CANCELED, DEAL_DIVIDEND, DEAL_DIVIDEND_FRANKED, DEAL_TAX }; //+------------------------------------------------------------------+ //| Направление открытой позиции | //+------------------------------------------------------------------+ enum ENUM_POSITION_TYPE { POSITION_TYPE_BUY, POSITION_TYPE_SELL }; //+------------------------------------------------------------------+ //| Состояние ордера | //+------------------------------------------------------------------+ enum ENUM_ORDER_STATE { ORDER_STATE_STARTED, ORDER_STATE_PLACED, ORDER_STATE_CANCELED, ORDER_STATE_PARTIAL, ORDER_STATE_FILLED, ORDER_STATE_REJECTED, ORDER_STATE_EXPIRED, ORDER_STATE_REQUEST_ADD, ORDER_STATE_REQUEST_MODIFY, ORDER_STATE_REQUEST_CANCEL }; //+------------------------------------------------------------------+ //| Типы ордеров | //+------------------------------------------------------------------+ #define ORDER_TYPE_CLOSE_BY (8) #define ORDER_TYPE_BUY_STOP_LIMIT (9) #define ORDER_TYPE_SELL_STOP_LIMIT (10) //+------------------------------------------------------------------+ #endif
Compilamos la biblioteca al completo. Ahora, después de introducir las macrosustituciones de los tipos de órdenes StopLimit, aparece un error que señala al archivo DELib.mqh, a las funciones que retornan el precio correcto de colocación de una orden, concretamente, indica el hecho de que la enumeración ENUM_ORDER_TYPE no tiene los valores 9 y 10, dado que usamos en el operador switch el valor del tipo de orden, y este tiene el tipo de enumeración ENUM_ORDER_TYPE:
//+------------------------------------------------------------------+ //| Возвращает корректную цену постановки ордера | //| относительно StopLevel | //+------------------------------------------------------------------+ double CorrectPricePending(const string symbol_name,const ENUM_ORDER_TYPE order_type,const double price_set,const double price=0,const int spread_multiplier=2) { double pt=SymbolInfoDouble(symbol_name,SYMBOL_POINT),pp=0; int lv=StopLevel(symbol_name,spread_multiplier), dg=(int)SymbolInfoInteger(symbol_name,SYMBOL_DIGITS); switch(order_type) { case ORDER_TYPE_BUY_LIMIT : pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmin(pp-lv*pt,price_set),dg); case ORDER_TYPE_BUY_STOP : case ORDER_TYPE_BUY_STOP_LIMIT : pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmax(pp+lv*pt,price_set),dg); case ORDER_TYPE_SELL_LIMIT : pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmax(pp+lv*pt,price_set),dg); case ORDER_TYPE_SELL_STOP : case ORDER_TYPE_SELL_STOP_LIMIT : pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmin(pp-lv*pt,price_set),dg); default : Print(DFUN,TextByLanguage("Не правильный тип ордера: ","Invalid order type: "),EnumToString(order_type)); return 0; } } //+------------------------------------------------------------------+ //| Возвращает корректную цену постановки ордера | //| относительно StopLevel | //+------------------------------------------------------------------+ double CorrectPricePending(const string symbol_name,const ENUM_ORDER_TYPE order_type,const int distance_set,const double price=0,const int spread_multiplier=2) { double pt=SymbolInfoDouble(symbol_name,SYMBOL_POINT),pp=0; int lv=StopLevel(symbol_name,spread_multiplier), dg=(int)SymbolInfoInteger(symbol_name,SYMBOL_DIGITS); switch(order_type) { case ORDER_TYPE_BUY_LIMIT : pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmin(pp-lv*pt,pp-distance_set*pt),dg); case ORDER_TYPE_BUY_STOP : case ORDER_TYPE_BUY_STOP_LIMIT : pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmax(pp+lv*pt,pp+distance_set*pt),dg); case ORDER_TYPE_SELL_LIMIT : pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmax(pp+lv*pt,pp+distance_set*pt),dg); case ORDER_TYPE_SELL_STOP : case ORDER_TYPE_SELL_STOP_LIMIT : pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmin(pp-lv*pt,pp-distance_set*pt),dg); default : Print(DFUN,TextByLanguage("Не правильный тип ордера: ","Invalid order type: "),EnumToString(order_type)); return 0; } } //+------------------------------------------------------------------+
La solución es sencilla: convertimos order_type en switch en un tipo entero:
//+------------------------------------------------------------------+ //| Возвращает корректную цену постановки ордера | //| относительно StopLevel | //+------------------------------------------------------------------+ double CorrectPricePending(const string symbol_name,const ENUM_ORDER_TYPE order_type,const double price_set,const double price=0,const int spread_multiplier=2) { double pt=SymbolInfoDouble(symbol_name,SYMBOL_POINT),pp=0; int lv=StopLevel(symbol_name,spread_multiplier), dg=(int)SymbolInfoInteger(symbol_name,SYMBOL_DIGITS); switch((int)order_type) { case ORDER_TYPE_BUY_LIMIT : pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmin(pp-lv*pt,price_set),dg); case ORDER_TYPE_BUY_STOP : case ORDER_TYPE_BUY_STOP_LIMIT : pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmax(pp+lv*pt,price_set),dg); case ORDER_TYPE_SELL_LIMIT : pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmax(pp+lv*pt,price_set),dg); case ORDER_TYPE_SELL_STOP : case ORDER_TYPE_SELL_STOP_LIMIT : pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmin(pp-lv*pt,price_set),dg); default : Print(DFUN,TextByLanguage("Не правильный тип ордера: ","Invalid order type: "),EnumToString(order_type)); return 0; } } //+------------------------------------------------------------------+ //| Возвращает корректную цену постановки ордера | //| относительно StopLevel | //+------------------------------------------------------------------+ double CorrectPricePending(const string symbol_name,const ENUM_ORDER_TYPE order_type,const int distance_set,const double price=0,const int spread_multiplier=2) { double pt=SymbolInfoDouble(symbol_name,SYMBOL_POINT),pp=0; int lv=StopLevel(symbol_name,spread_multiplier), dg=(int)SymbolInfoInteger(symbol_name,SYMBOL_DIGITS); switch((int)order_type) { case ORDER_TYPE_BUY_LIMIT : pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmin(pp-lv*pt,pp-distance_set*pt),dg); case ORDER_TYPE_BUY_STOP : case ORDER_TYPE_BUY_STOP_LIMIT : pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmax(pp+lv*pt,pp+distance_set*pt),dg); case ORDER_TYPE_SELL_LIMIT : pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmax(pp+lv*pt,pp+distance_set*pt),dg); case ORDER_TYPE_SELL_STOP : case ORDER_TYPE_SELL_STOP_LIMIT : pp=(price==0 ? SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmin(pp-lv*pt,pp-distance_set*pt),dg); default : Print(DFUN,TextByLanguage("Не правильный тип ордера: ","Invalid order type: "),EnumToString(order_type)); return 0; } } //+------------------------------------------------------------------+
Compilamos una vez más. Ahora vemos un error en el archivo Order.mqh, MQL4 no conoce los valores de las constantes ORDER_FILLING_RETURN, ORDER_TIME_GTC, ORDER_REASON_SL, ORDER_REASON_TP y ORDER_REASON_EXPERT.
//+------------------------------------------------------------------+ //| Retorna el tipo de ejecución según el resto | //+------------------------------------------------------------------+ long COrder::OrderTypeFilling(void) const { #ifdef __MQL4__ return (long)ORDER_FILLING_RETURN; #else long res=0; switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS)) { case ORDER_STATUS_MARKET_ORDER : case ORDER_STATUS_MARKET_PENDING : res=::OrderGetInteger(ORDER_TYPE_FILLING); break; case ORDER_STATUS_HISTORY_PENDING : case ORDER_STATUS_HISTORY_ORDER : res=::HistoryOrderGetInteger(m_ticket,ORDER_TYPE_FILLING);break; default : res=0; break; } return res; #endif } //+------------------------------------------------------------------+ //| Retorna el tiempo de expiración de la orden | //+------------------------------------------------------------------+ long COrder::OrderTypeTime(void) const { #ifdef __MQL4__ return (long)ORDER_TIME_GTC; #else long res=0; switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS)) { case ORDER_STATUS_MARKET_ORDER : case ORDER_STATUS_MARKET_PENDING : res=::OrderGetInteger(ORDER_TYPE_TIME); break; case ORDER_STATUS_HISTORY_PENDING : case ORDER_STATUS_HISTORY_ORDER : res=::HistoryOrderGetInteger(m_ticket,ORDER_TYPE_TIME);break; default : res=0; break; } return res; #endif } //+------------------------------------------------------------------+ //| Motivo o razón de la colocación de la orden | //+------------------------------------------------------------------+ long COrder::OrderReason(void) const { #ifdef __MQL4__ return ( this.OrderCloseByStopLoss() ? ORDER_REASON_SL : this.OrderCloseByTakeProfit() ? ORDER_REASON_TP : this.OrderMagicNumber()!=0 ? ORDER_REASON_EXPERT : WRONG_VALUE ); #else long res=WRONG_VALUE; switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS)) { case ORDER_STATUS_MARKET_POSITION : res=::PositionGetInteger(POSITION_REASON); break; case ORDER_STATUS_MARKET_ORDER : case ORDER_STATUS_MARKET_PENDING : res=::OrderGetInteger(ORDER_REASON); break; case ORDER_STATUS_HISTORY_PENDING : case ORDER_STATUS_HISTORY_ORDER : res=::HistoryOrderGetInteger(m_ticket,ORDER_REASON);break; case ORDER_STATUS_DEAL : res=::HistoryDealGetInteger(m_ticket,DEAL_REASON); break; default : res=WRONG_VALUE; break; } return res; #endif } //+------------------------------------------------------------------+
Vamos a añadir macrosustituciones al archivo ToMQL4.mqh (las añadimos al final, no vamos a mostrar el listado completo, para ahorrar espacio):
//+------------------------------------------------------------------+ //| Типы ордеров, политика исполнения, срок действия, причины | //+------------------------------------------------------------------+ #define ORDER_TYPE_CLOSE_BY (8) #define ORDER_TYPE_BUY_STOP_LIMIT (9) #define ORDER_TYPE_SELL_STOP_LIMIT (10) #define ORDER_FILLING_RETURN (2) #define ORDER_TIME_GTC (0) #define ORDER_REASON_EXPERT (3) #define ORDER_REASON_SL (4) #define ORDER_REASON_TP (5) //+------------------------------------------------------------------+ #endif
La nueva compilación nos lleva a la ausencia de la función MQL5 HistoryOrderGetTicket() en el archivo HistoryCollection.mqh del método CHistoryCollection::OrderSearch(). El análisis del código nos indica que será más sencillo usar aquí directivas de compilación condicional. Completamos el método:
//+------------------------------------------------------------------+ //| Возвращает тип и тикет "потерянного" ордера | //+------------------------------------------------------------------+ ulong CHistoryCollection::OrderSearch(const int start,ENUM_ORDER_TYPE &order_type) { ulong order_ticket=0; #ifdef __MQL5__ for(int i=start-1;i>=0;i--) { ulong ticket=::HistoryOrderGetTicket(i); if(ticket==0) continue; ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)::HistoryOrderGetInteger(ticket,ORDER_TYPE); if(this.IsPresentOrderInList(ticket,type)) continue; order_ticket=ticket; order_type=type; } #else for(int i=start-1;i>=0;i--) { if(!::OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)) continue; ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)::OrderType(); ulong ticket=::OrderTicket(); if(ticket==0 || type<ORDER_TYPE_BUY_LIMIT || type>ORDER_TYPE_SELL_STOP) continue; if(this.IsPresentOrderInList(ticket,type)) continue; order_ticket=ticket; order_type=type; } #endif return order_ticket; } //+------------------------------------------------------------------+
Enmarcamos todo lo que hay en él para MQL5 con la directiva #ifdef __MQL5__ y completamos el código para MQL4 después de la directiva #else, hasta la directiva #endif.
El siguiente error aparece en el constructor de la clase CEvent. Vamos a completar el código usando las mismas directivas de compilación condicional:
//+------------------------------------------------------------------+ //| Конструктор | //+------------------------------------------------------------------+ CEvent::CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket) : m_event_code(event_code),m_digits(0) { this.m_long_prop[EVENT_PROP_STATUS_EVENT] = event_status; this.m_long_prop[EVENT_PROP_TICKET_ORDER_EVENT] = (long)ticket; this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif; this.m_digits_acc=#ifdef __MQL4__ 2 #else (int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS) #endif; this.m_chart_id=::ChartID(); } //+------------------------------------------------------------------+
Aquí, al comprobar si la cuenta es del tipo "cobertura", aparece el error de ausencia constante, por eso, simplemente retornamos true de inmediato: en MetaTrader 4, todas las cuentas son de tipo cobertura.
Al obtener el número de dígitos decimales en la divisa de la cuenta, también retornamos 2, ya que en MQL4 no hay posibilidad de obtener este valor.
La siguiente compilación nos lleva al método CEventsCollection::NewDealEventHedge(), la creación de un evento para una cuenta de cobertura en MetaTrader 5. Trabaja con transacciones que no existen en MQL4. Por el momento, desactivaremos totalmente este método, enmarcando el código completo del método dentro de la compilación condicional:
Pegamos al inicio del método la directiva
//+------------------------------------------------------------------+ //| Создаёт событие для хеджевого счёта | //+------------------------------------------------------------------+ void CEventsCollection::NewDealEventHedge(COrder* deal,CArrayObj* list_history,CArrayObj* list_market) { #ifdef __MQL5__ double ask=::SymbolInfoDouble(deal.Symbol(),SYMBOL_ASK); double bid=::SymbolInfoDouble(deal.Symbol(),SYMBOL_BID); //--- Вход в рынок
y al final del método
#endif } //+------------------------------------------------------------------+
A continuación, vemos un error en el método CEventsCollection::NewDealEventNetto(), la creación de un evento para una cuenta de compensación. La solución es la misma que en el caso anterior: enmarcamos el código completo del método NewDealEventNetto() con un directiva de compilación condicional.
Compilamos y topamos con el error de constante desconocida DEAL_ENTRY_IN en el método CEventsCollection::GetListAllDealsInByPosID(). Añadimos la enumeración necesaria al archivo ToMQL4.mqh (podríamos desactivar de nuevo el código de la compilación condicional, pero hemos pensado que esta enumeración podría sernos necesaria más adelante):
//+------------------------------------------------------------------+ //| Типы сделок MQL5 | //+------------------------------------------------------------------+ enum ENUM_DEAL_TYPE { DEAL_TYPE_BUY, DEAL_TYPE_SELL, DEAL_TYPE_BALANCE, DEAL_TYPE_CREDIT, DEAL_TYPE_CHARGE, DEAL_TYPE_CORRECTION, DEAL_TYPE_BONUS, DEAL_TYPE_COMMISSION, DEAL_TYPE_COMMISSION_DAILY, DEAL_TYPE_COMMISSION_MONTHLY, DEAL_TYPE_COMMISSION_AGENT_DAILY, DEAL_TYPE_COMMISSION_AGENT_MONTHLY, DEAL_TYPE_INTEREST, DEAL_TYPE_BUY_CANCELED, DEAL_TYPE_SELL_CANCELED, DEAL_DIVIDEND, DEAL_DIVIDEND_FRANKED, DEAL_TAX }; //+------------------------------------------------------------------+ //| Способ изменения позиции | //+------------------------------------------------------------------+ enum ENUM_DEAL_ENTRY { DEAL_ENTRY_IN, DEAL_ENTRY_OUT, DEAL_ENTRY_INOUT, DEAL_ENTRY_OUT_BY }; //+------------------------------------------------------------------+ //| Направление открытой позиции | //+------------------------------------------------------------------+ enum ENUM_POSITION_TYPE { POSITION_TYPE_BUY, POSITION_TYPE_SELL }; //+------------------------------------------------------------------+
Después, nos encontramos con el error de comprobación sobre el tipo "cobertura", que ya conocemos, pero ahora en el constructor de la clase de colección de eventos. Vamos a corregirlo:
//+------------------------------------------------------------------+ //| Конструктор | //+------------------------------------------------------------------+ CEventsCollection::CEventsCollection(void) : m_trade_event(TRADE_EVENT_NO_EVENT),m_trade_event_code(TRADE_EVENT_FLAG_NO_EVENT) { this.m_list_events.Clear(); this.m_list_events.Sort(SORT_BY_EVENT_TIME_EVENT); this.m_list_events.Type(COLLECTION_EVENTS_ID); this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif; this.m_chart_id=::ChartID(); ::ZeroMemory(this.m_tick); } //+------------------------------------------------------------------+
A continuación, implementamos la misma corrección en el constructor de la clase CEngine:
//+------------------------------------------------------------------+ //| CEngine конструктор | //+------------------------------------------------------------------+ CEngine::CEngine() : m_first_start(true),m_acc_trade_event(TRADE_EVENT_NO_EVENT) { ::ResetLastError(); if(!::EventSetMillisecondTimer(TIMER_FREQUENCY)) Print(DFUN,"Не удалось создать таймер. Ошибка: ","Could not create timer. Error: ",(string)::GetLastError()); this.m_list_counters.Sort(); this.m_list_counters.Clear(); this.CreateCounter(COLLECTION_COUNTER_ID,COLLECTION_COUNTER_STEP,COLLECTION_PAUSE); this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif; } //+------------------------------------------------------------------+
Ya está todo. Ahora, la biblioteca se compila sin errores. Pero esta es solo la primera etapa. A continuación, debemos iniciarla. Como hemos desactivado varios métodos con la compilación condicional, ahora tendremos que desarrollarlos para que funcionen en MetaTrader 4.
En MQL5, las operaciones contables son transacciones y se ubican en la lista de órdenes y transacciones históricas. En MQL4, en cambio, las operaciones contables son órdenes con el tipo ORDER_TYPE_BALANCE (6) y ORDER_TYPE_CREDIT (7). Por eso, hemos decidido hacer para MQL4 una clase de objeto de operación contable, que guardaremos igualmente en la lista de órdenes y posiciones históricas.
Creamos la nueva clase CHistoryBalance en la carpeta \MQL4\Include\DoEasy\Objects\Orders del archivo HistoryBalance.mqh. COrder deberá ser la clase básica:
//+------------------------------------------------------------------+ //| HistoryBalance.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/es/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/es/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Archivos de inclusión | //+------------------------------------------------------------------+ #include "Order.mqh" //+------------------------------------------------------------------+ //| Историческая балансовая операция | //+------------------------------------------------------------------+ class CHistoryBalance : public COrder { public: //--- Конструктор CHistoryBalance(const ulong ticket) : COrder(ORDER_STATUS_BALANCE,ticket) {} //--- Поддерживаемые свойства сделки (1) вещественные, (2) целочисленные virtual bool SupportProperty(ENUM_ORDER_PROP_INTEGER property); virtual bool SupportProperty(ENUM_ORDER_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_ORDER_PROP_STRING property); }; //+------------------------------------------------------------------+ //| Возвращает истину, если ордер поддерживает переданное | //| целочисленное свойство, возвращает ложь в противном случае | //+------------------------------------------------------------------+ bool CHistoryBalance::SupportProperty(ENUM_ORDER_PROP_INTEGER property) { if(property==ORDER_PROP_TICKET || property==ORDER_PROP_TIME_OPEN || property==ORDER_PROP_STATUS || property==ORDER_PROP_TYPE || property==ORDER_PROP_REASON ) return true; return false; } //+------------------------------------------------------------------+ //| Возвращает истину, если ордер поддерживает переданное | //| вещественное свойство, возвращает ложь в противном случае | //+------------------------------------------------------------------+ bool CHistoryBalance::SupportProperty(ENUM_ORDER_PROP_DOUBLE property) { return(property==ORDER_PROP_PROFIT ? true : false); } //+------------------------------------------------------------------+ //| Возвращает истину, если ордер поддерживает переданное | //| строковое свойство, возвращает ложь в противном случае | //+------------------------------------------------------------------+ bool CHistoryBalance::SupportProperty(ENUM_ORDER_PROP_STRING property) { if(property==ORDER_PROP_SYMBOL || property==ORDER_PROP_EXT_ID) return false; return true; } //+------------------------------------------------------------------+
Esta clase no tiene nada nuevo para nosotros, ya hemos analizado todas las clases de la órdenes históricas en la quinta parte de la descripció de la biblioteca.
Queremos destacar que hay dos tipos de operaciones contables: las operaciones de saldo y las de crédito. Sus tipos tienen los valores numéricos 6 y 7, respectivamente. Usaremos para ambos tipos una misma clase de operación contable, concretando el tipo en la propiedad de la orden "motivo".
Añadimos los dos "motivos" de aparición de la orden que faltan. Los incluimos en el archivo ToMQL4.mqh:
//+------------------------------------------------------------------+ //| Типы ордеров, политика исполнения, срок действия, причины | //+------------------------------------------------------------------+ #define ORDER_TYPE_CLOSE_BY (8) #define ORDER_TYPE_BUY_STOP_LIMIT (9) #define ORDER_TYPE_SELL_STOP_LIMIT (10) #define ORDER_FILLING_RETURN (2) #define ORDER_TIME_GTC (0) #define ORDER_REASON_EXPERT (3) #define ORDER_REASON_SL (4) #define ORDER_REASON_TP (5) #define ORDER_REASON_BALANCE (6) #define ORDER_REASON_CREDIT (7) //+------------------------------------------------------------------+
Y ya que tenemos una nueva clase heredera de la clase de orden abstracta, será necesario completar la funcionalidad restante en COrder.
En el método COrder::OrderPositionID(), sustituimos el retorno del número mágico para MQL4
//+------------------------------------------------------------------+ //| Retorna el identificador de la posición | //+------------------------------------------------------------------+ long COrder::OrderPositionID(void) const { #ifdef __MQL4__ return ::OrderMagicNumber(); #else
por el retorno del ticket (más adelante, implementaremos algo semejante a PositionID para las posiciones de MQL4):
//+------------------------------------------------------------------+ //| Retorna el identificador de la posición | //+------------------------------------------------------------------+ long COrder::OrderPositionID(void) const { #ifdef __MQL4__ return ::OrderTicket(); #else
En el método que retorna el estado de la orden en MQL4 siempre se ha retornado ORDER_STATE_FILLED de la enumeración ENUM_ORDER_STATE, lo cual no es cierto para las órdenes pendientes eliminadas. Vamos a implementar la comprobación del estado de la orden, y si se trata de una orden pendiente eliminada, retornaremos ORDER_STATE_CANCELED.
//+------------------------------------------------------------------+ //| Возвращает состояние ордера | //+------------------------------------------------------------------+ long COrder::OrderState(void) const { #ifdef __MQL4__ return(this.Status()==ORDER_STATUS_HISTORY_ORDER ? ORDER_STATE_FILLED : ORDER_STATE_CANCELED); #else long res=0; switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS)) { case ORDER_STATUS_HISTORY_PENDING : case ORDER_STATUS_HISTORY_ORDER : res=::HistoryOrderGetInteger(m_ticket,ORDER_STATE); break; case ORDER_STATUS_MARKET_ORDER : case ORDER_STATUS_MARKET_PENDING : res=::OrderGetInteger(ORDER_STATE); break; case ORDER_STATUS_MARKET_POSITION : case ORDER_STATUS_DEAL : default : res=0; break; } return res; #endif } //+------------------------------------------------------------------+
Añadimos los dos "motivos" nuevamente añadidos al método que retorna el motivo de la orden para MQL4:
//+------------------------------------------------------------------+ //| Motivo o razón de la colocación de la orden | //+------------------------------------------------------------------+ long COrder::OrderReason(void) const { #ifdef __MQL4__ return ( this.TypeOrder()==ORDER_TYPE_BALANCE ? ORDER_REASON_BALANCE : this.TypeOrder()==ORDER_TYPE_CREDIT ? ORDER_REASON_CREDIT : this.OrderCloseByStopLoss() ? ORDER_REASON_SL : this.OrderCloseByTakeProfit() ? ORDER_REASON_TP : this.OrderMagicNumber()!=0 ? ORDER_REASON_EXPERT : WRONG_VALUE ); #else
En nuestro caso, el método que retorna el volumen no ejecutado para MQL4, siempre ha retornado el lote de la orden, lo cual no es correcto para las posiciones. Para las órdenes pendientes eliminadas, retornaremos el lote de la orden, y para las posiciones, retornaremos cero:
//+------------------------------------------------------------------+ //| Retorna el volumen no ejecutado | //+------------------------------------------------------------------+ double COrder::OrderVolumeCurrent(void) const { #ifdef __MQL4__ return(this.Status()==ORDER_STATUS_HISTORY_PENDING ? ::OrderLots() : 0); #else
Añadimos la descripción de los dos nuevos "motivos" en el método que retorna la descripción del motivo de la orden. Comprobaremos el beneficio para las operaciones de balance y crédito, y si es superior a cero, significará que los fondos han sido depositados, de lo contrario, que han sido retirados:
//+------------------------------------------------------------------+ //| Descripción del motivo | //+------------------------------------------------------------------+ string COrder::GetReasonDescription(const long reason) const { #ifdef __MQL4__ return ( this.IsCloseByStopLoss() ? TextByLanguage("Срабатывание StopLoss","Due to StopLoss") : this.IsCloseByTakeProfit() ? TextByLanguage("Срабатывание TakeProfit","Due to TakeProfit") : this.Reason()==ORDER_REASON_EXPERT ? TextByLanguage("Выставлен из mql4-программы","Placed from mql4 program") : this.Comment()=="cancelled" ? TextByLanguage("Отменён","Cancelled") : this.Reason()==ORDER_REASON_BALANCE ? ( this.Profit()>0 ? TextByLanguage("Пополнение баланса","Deposit of funds on the account balance") : TextByLanguage("Снятие средств с баланса","Withdrawals from the balance") ) : this.Reason()==ORDER_REASON_CREDIT ? ( this.Profit()>0 ? TextByLanguage("Начисление кредитных средств","Received credit funds") : TextByLanguage("Изъятие кредитных средств","Withdrawal of credit") ) : TextByLanguage("Свойство не поддерживается в MQL4","Property is not supported in MQL4") ); #else
Asimismo, hemos realizado algunas pequeñas correcciones, cuya descripción carece de sentido práctico. En general, están relacionadas con el texto mostrado en el diario de MQL5 y MQL4. Todas las correcciones están disponibles en los archivos de la biblioteca adjuntos al artículo.
A continuación, vamos a mejorar la clase de la colección histórica en el archivo HistoryCollection.mqh.
En primer lugar, incluimos en ella el archivo de la nueva clase:
//+------------------------------------------------------------------+ //| Archivos de inclusión | //+------------------------------------------------------------------+ #include "ListObj.mqh" #include "..\Services\Select.mqh" #include "..\Objects\Orders\HistoryOrder.mqh" #include "..\Objects\Orders\HistoryPending.mqh" #include "..\Objects\Orders\HistoryDeal.mqh" #ifdef __MQL4__ #include "..\Objects\Orders\HistoryBalance.mqh" #endif //+------------------------------------------------------------------+
Ya que la clase CHistoryBalance es solo necesaria para la versión MQL4 de la biblioteca, la inclusión del archivo de esta clase también se encontrará enmarcada en una directiva de compilación condicional para MQL4.
Ahora tenemos una nueva clase de operaciones contables. Para crearla y ubicarla en la colección, vamos a necesitar añadir en el método Refresh() de la clase CHistoryCollection para MQL4 la comprobación de los tipos de órdenes sobre el tipo de operación de crédito y balance, así como su adición a la colección:
//+------------------------------------------------------------------+ //| Обновляет список ордеров и сделок | //+------------------------------------------------------------------+ void CHistoryCollection::Refresh(void) { #ifdef __MQL4__ int total=::OrdersHistoryTotal(),i=m_index_order; for(; i<total; i++) { if(!::OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)) continue; ENUM_ORDER_TYPE order_type=(ENUM_ORDER_TYPE)::OrderType(); //--- Закрытые позиции if(order_type<ORDER_TYPE_BUY_LIMIT) { CHistoryOrder *order=new CHistoryOrder(::OrderTicket()); if(order==NULL) continue; if(!this.m_list_all_orders.InsertSort(order)) { ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Could not add order to the list")); delete order; } } //--- Балансовые/кредитные операции else if(order_type>ORDER_TYPE_SELL_STOP) { CHistoryBalance *order=new CHistoryBalance(::OrderTicket()); if(order==NULL) continue; if(!this.m_list_all_orders.InsertSort(order)) { ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Could not add order to the list")); delete order; } } else { //--- Удалённые отложенные ордера CHistoryPending *order=new CHistoryPending(::OrderTicket()); if(order==NULL) continue; if(!this.m_list_all_orders.InsertSort(order)) { ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Could not add order to the list")); delete order; } } } //--- int delta_order=i-m_index_order; this.m_index_order=i; this.m_delta_order=delta_order; this.m_is_trade_event=(this.m_delta_order!=0 ? true : false); //--- __MQL5__ #elseVamos a hacer algunas correcciones en la clase de orden histórica:
//+------------------------------------------------------------------+ //| Возвращает истину, если ордер поддерживает переданное | //| вещественное свойство, возвращает ложь в противном случае | //+------------------------------------------------------------------+ bool CHistoryOrder::SupportProperty(ENUM_ORDER_PROP_DOUBLE property) { if( #ifdef __MQL5__ property==ORDER_PROP_PROFIT || property==ORDER_PROP_PROFIT_FULL || property==ORDER_PROP_SWAP || property==ORDER_PROP_COMMISSION || property==ORDER_PROP_PRICE_CLOSE || ( property==ORDER_PROP_PRICE_STOP_LIMIT && ( this.TypeOrder()<ORDER_TYPE_BUY_STOP_LIMIT || this.TypeOrder()>ORDER_TYPE_SELL_STOP_LIMIT ) ) #else property==ORDER_PROP_PRICE_STOP_LIMIT && this.Status()==ORDER_STATUS_HISTORY_ORDER #endif ) return false; return true; } //+------------------------------------------------------------------+
Anteriormente, el precio de colocación de una orden StopLimit en MQL5 no se mostraba en el diario, por eso hemos implementado la siguiente comprobación: si la propiedad comprobadaes el precio de una orden StopLimit, y además el tipo de orden no es StopLimit, entonces, solo en este caso, la propiedad no será utilizada. En caso contrario, se tratará de una orden StopLimit, y esta propiedad será necesaria.
Para MQL4, el precio de colocación de una orden StopLimit para las posiciones no se usa.
Bien, con esto, podemos dar por finalizadas las mejoras de la primera etapa de compatibilidad con MQL4.
Simulación
Para realizar la simulación, tomaremos el asesor TestDoEasyPart03_1.mq5 de la carpeta de asesores MQL5 \MQL5\Experts\TestDoEasy\Part03 y lo guardaremos con el nombre TestDoEasyPart09.mq4 en la carpeta de asesores MQL4 \MQL4\Experts\TestDoEasy\Part09.
El asesor se compila sin realizar cambio alguno, pero si echamos un vistazo al código, veremos que usa una lista de transacciones que no se encuentra en MQL4:
//--- enums enum ENUM_TYPE_ORDERS { TYPE_ORDER_MARKET, // Market-orders TYPE_ORDER_PENDING, // Pending orders TYPE_ORDER_DEAL // Deals }; //--- input parameters //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- обновляем историю history.Refresh(); //--- получаем список-коллекцию в диапазоне дат CArrayObj* list=history.GetListByTime(InpTimeBegin,InpTimeEnd,SELECT_BY_TIME_CLOSE); if(list==NULL) { Print("Could not get collection list"); return INIT_FAILED; } int total=list.Total(); for(int i=0;i<total;i++) { //--- получаем ордер из списка COrder* order=list.At(i); if(order==NULL) continue; //--- если это сделка if(order.Status()==ORDER_STATUS_DEAL && InpOrderType==TYPE_ORDER_DEAL) order.Print(); //--- если это исторический маркет-ордер if(order.Status()==ORDER_STATUS_HISTORY_ORDER && InpOrderType==TYPE_ORDER_MARKET) order.Print(); //--- если это удалённый отложенный ордер if(order.Status()==ORDER_STATUS_HISTORY_PENDING && InpOrderType==TYPE_ORDER_PENDING) order.Print(); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
Solo tenemos que sustituir las transacciones por operaciones contables. En este caso, usaremos la compilación condicional directamente en el asesor, lo cual no es muy adecuado para el producto final, donde todas las acciones destinadas a la limitación de las versiones del lenguaje deben estar ocultas para el usuario. En este caso, simplemente vamos a poner prueba las mejoras de la biblioteca, así que no es para tanto.
Vamos a añadir algunos pequeños cambios al código del asesor, sustituyendo las transacciones de MQL5 por operaciones contables de MQL4:
//+------------------------------------------------------------------+ //| TestDoEasyPart03_1.mq4 | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/es/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/es/users/artmedia70" #property version "1.00" //--- includes #include <DoEasy\Collections\HistoryCollection.mqh> //--- enums enum ENUM_TYPE_ORDERS { TYPE_ORDER_MARKET, // Market-orders TYPE_ORDER_PENDING, // Pending orders #ifdef __MQL5__ TYPE_ORDER_DEAL // Deals #else TYPE_ORDER_BALANCE // Balance/Credit #endif }; //--- input parameters input ENUM_TYPE_ORDERS InpOrderType = TYPE_ORDER_MARKET; // Show type: input datetime InpTimeBegin = 0; // Start date of required range input datetime InpTimeEnd = END_TIME; // End date of required range //--- global variables CHistoryCollection history; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- обновляем историю history.Refresh(); //--- получаем список-коллекцию в диапазоне дат CArrayObj* list=history.GetListByTime(InpTimeBegin,InpTimeEnd,SELECT_BY_TIME_CLOSE); if(list==NULL) { Print("Could not get collection list"); return INIT_FAILED; } int total=list.Total(); for(int i=0;i<total;i++) { //--- получаем ордер из списка COrder* order=list.At(i); if(order==NULL) continue; //--- если это сделка #ifdef __MQL5__ if(order.Status()==ORDER_STATUS_DEAL && InpOrderType==TYPE_ORDER_DEAL) order.Print(); #else //--- если это балансовая/кредитная операция if(order.Status()==ORDER_STATUS_BALANCE && InpOrderType==TYPE_ORDER_BALANCE) order.Print(); #endif //--- если это исторический маркет-ордер if(order.Status()==ORDER_STATUS_HISTORY_ORDER && InpOrderType==TYPE_ORDER_MARKET) order.Print(); //--- если это удалённый отложенный ордер if(order.Status()==ORDER_STATUS_HISTORY_PENDING && InpOrderType==TYPE_ORDER_PENDING) order.Print(); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- } //+------------------------------------------------------------------+
Compilamos e iniciamos el asesor en el terminal (este asesor, perteneciente al tercer artículo, funciona solo en el manejador OnInit(), por eso, mostrará una sola vez la lista necesaria de la colección histórica después del inicio, o bien tras modificar la listas en los ajustes).
Antes de iniciar el asesor, es recomendable seleccionar con el botón derecho el punto "Historia completa" en la pestaña "Historia de la cuenta" del menú contextual en el terminal, ya que en MetaTrader 4 la cantidad de historia disponible para los programas depende del tamaño de la historia seleccionado en esta pestaña.
En los ajustes se encuentra seleccionado "Balance/Credit", y el primer depósito de saldo se mostrará en el diario:
Ahora, debemos comprobar si la búsqueda y la representación de las posiciones cerradas son correctas. Como hemos abierto la cuenta en MetaTrader 4 hace poco, no se han realizado transacciones en la misma. Hemos abierto una posición Sell, le hemos colocado un StopLoss y un TakeProfit, y hemos esperado un rato tomando café. Al regresar, la posición había sido cerrada por un stop, después de lo cual, el mercado comenzó a moverse en dirección a la posición abierta de venta, como siempre :)
Sin embargo, ahora tenemos una posición cerrada para realizar pruebas.
En los ajustes se encuentra seleccionado "Market-orders":
Ahora, comprobamos la lista de órdenes pendientes eliminadas. Hemos colocado un par de órdenes, eliminándolas después.
En los ajustes se encuentra seleccionado "Pending orders":
La lista de órdenes pendientes eliminadas también se representa.
¿Qué es lo próximo?
En el siguiente artículo, implementaremos el trabajo con posiciones de mercado y órdenes pendientes activas en MQL4.
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.
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/6651
- 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