Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte I): Concepto, organización de datos y primeros resultados

Artyom Trishkin | 15 mayo, 2019

Contenido

Introducción

Tras analizar una ingente cantidad de estrategias comerciales, ejecutar multitud de encargos de preparación de programas para los terminales MT5 y MT4, y visitar distintos sitios web dedicados a scripts, indicadores y robots para MetaTrader, hemos llegado a la conclusión de que una mayoría aplastante de esta diversidad se construye en la práctica sobre un número fijo de funciones elementales, acciones y valores que se repiten de un programa a otro.

La lógica de casi cualquier programa se puede dividir en multitud de acciones prácticamente idénticas, cuyo resultado se usa para construir la lógica de las aplicaciones. Este hecho ha sido confirmado más de una vez por la semejanza de las preguntas sobre el lenguaje MQL4/MQL5 en los foros: diferentes personas formulan en momentos distintos aproximadamente las mismas preguntas sobre los algoritmos y tareas que resuelven.

Partiendo de estas conclusiones, tuvimos la idea de crear una gran biblioteca con funciones integradas para consultar y recuperar los datos necesarios. Para usar el conjunto de datos de la biblioteca propuesta, el usuario solo tiene que valerse del método de pregunta-respuesta para obtener un gran número de datos completamente distintos, en diferentes combinaciones y cualquier clasificación, que la biblioteca recopila y almacena de forma independiente en su base de datos.

Concepto

Cualquier tipo de datos se puede representar como un conjunto de objetos con las mismas propiedades.
Por ejemplo, una serie temporal se puede representar como una lista larga y ordenada, con celdas subsiguiente que almacenen un objeto, y cuyo conjunto de propiedades sea del mismo tipo para todos y cada uno de los mismos objetos posteriores de esta serie temporal. Los datos de este objeto se muestran con las estructura MqlRates:

Estructura para guardar la información sobre los precios, los volúmenes y el spread.

struct MqlRates 
  { 
   datetime time;         // hora de comienzo del periodo 
   double   open;         // precio de apertura 
   double   high;         // mayor precio en el periodo 
   double   low;          // menor precio en el periodo 
   double   close;        // precio de cierre 
   long     tick_volume;  // volumen de ticks 
   int      spread;       // spread 
   long     real_volume;  // volumen bursátil 
  };

Al mismo tiempo, podemos representar de la misma forma un conjunto de datos con una lista ordenada, donde el objeto con el conjunto modificado de propiedades será el tick, representado por la estructura MqlTick:

Estructura para el guardado de las últimas cotizaciones del símbolo. Ha sido pensada para obtener rápidamente la información más demandada sobre los precios actuales.

struct MqlTick 
  { 
   datetime     time;          // Hora de la última actualización de precios 
   double       bid;           // Precio Bid actual 
   double       ask;           // Precio Ask actual 
   double       last;          // Precio actual de la última transacción (Last) 
   ulong        volume;        // Volumen para el precio Last actual 
   long         time_msc;      // Hora de la última actualización de precios en milisegundos 
   uint         flags;         // banderas de los ticks 
   double       volume_real;   // Volumen para el precio Last actual con precisión aumentada 
  };

Y el resto — cualquier dato necesario para analizar y componer la lógica del programa — se organizará igualmente como listas sencillas de objetos.

Todos los objetos en una lista tendrá datos de un tipo, propios precisamente a ese tipo de objeto, ya sea una lista de órdenes, transacciones u órdenes pendientes. Para cada objeto concreto, crearemos una clase dotada de una funcionalidad mínima para guardar, clasificar y generar datos.

Estructura de datos de la biblioteca

Como ya hemos descrito anteriormente, la biblioteca constará de listas de objetos y nos dará la posibilidad de elegir cualquiera de los elementos de la lista, de acuerdo con cualquiera de los criterios o propiedades solicitados por el usuario, y soportados por este objeto. La biblioteca recopilará por sí misma toda la información necesaria para el almacenamiento y el procesamiento posterior independiente, es decir, el usuario no deberá preocuparse en absoluto sobre ello, solo usará los resultados de sus solicitudes a la biblioteca.

Se ha planeado paso a paso, comenzando por lo más sencillo, añadiendo gradualmente nuevas funcionalidades y nuevos datos a los existentes, para describir todos los pasos para crear esta biblioteca. La biblioteca se desarrollará "en vivo", es decir, en cada artículo, de una versión a la siguiente, se realizarán las ediciones y adiciones necesarias. En nuestra opinión, este estilo de presentación resulta es más útil, ya que da la sensación de que el lector toma parte en el desarrollo.

La estructura mínima de datos de la biblioteca es una colección de diferentes objetos destinada a describir las propiedades de los datos necesarios; las colecciones de datos son listas que almacenan los objetos correspondientes.

Para organizar las listas, usaremos la clase de matriz dinámica de punteros a los ejemplares de la clase CObject y sus herederos de la colección de datos de la biblioteca estándar. Y puesto que el almacenamiento en dicha lista necesita exactamente los objetos de la clase base de la biblioteca estándar de CObject, sencillamente heredaremos cada clase de nuestros objetos de la clase base de CObject.

Primera implementación

Primero, crearemos la biblioteca para las cuentas de cobertura del terminal MetaTrader 5, luego, después de preparar la funcionalidad mínima, la corregiremos para trabajar con MetaTrader 4, y después, tras implementar el trabajo con la historia de cuentas, las posiciones actuales del mercado y las órdenes pendientes, implementaremos el trabajo para las cuentas de compensación de MetaTrader 5. A continuación, comenzaremos a rellenar el resto de la funcionalidad.

Vamos a empezar por la historia de la cuenta. En muchas estrategias, de una manera u otra, se usan los resultados de las transacciones anteriores, por ejemplo: el resultado del cierre de una posición pasada, cómo se cerró esta, por stop-loss, por take-profit, a qué precio, etc. El resultado del trabajo del último día se puede usar como punto de partida para el plan de trabajo de hoy. En general, no es posible enumerar al completo la ingente diversidad de estrategias comerciales, pero sí que podemos proporcionar un acceso rápido a dicha diversidad: eso es lo que comenzaremos a hacer ahora.

En primer lugar, vamos a definir la terminología para trabajar con las colecciones de solicitudes y transacciones históricas, las órdenes de mercado y las posiciones. La cosa es que el sistema de órdenes de MetaTrader 4 difiere del sistema de MetaTrader 5. Mientras que en MetaTrader 4 solo hay órdenes (de mercado y pendientes), en MetaTrader 5, si hablamos de forma abstracta y breve, una orden es un pedido comercial (orden de mercado) que genera una transacción, y la transacción genera a su vez una posición. Además, existen también las órdenes pendientes. Es decir, tenemos como mínimo tres objetos de estudio: la orden, la transacción y la posición. Para no perdernos entre tantas definiciones diferentes, la clase para almacenar datos de las órdenes, transacciones y posiciones la llamaremos simplemente clase abstracta de la orden; más adelante en la lista (en la colección de órdenes históricas de la parte inicial de la narración), tendremos todo clasificado por tipos: órdenes, transacciones, etc.

Esta clase abstracta de la orden contendrá el ticket del pedido o transacción, además de todos los datos referentes a los parámetros de la orden o transacción y su estado. El estado nos indicará exactamente qué es este objeto: una orden, una transacción o una posición.

Cree en el directorio <catálogo de datos del terminal>\MQL5\Include la carpeta DoEasy, en ella ubicaremos los archivos de la biblioteca. En esta etapa, necesitaremos crear otros dos catálogos más en la carpeta DoEasy: la carpeta Objects (en ella ubicaremos las clases de los objetos), y la carpeta Collections (en ella se ubicarán las colecciones de datos, las listas de objetos).
Para encontrar el catálogo de datos del terminal, podemos usar el punto del menú del editor -> Abrir catálogo de datos, o bien pulsar la combinación de teclas Ctrl + Shift + D

Ahora podemos crear la primera clase: la clase de orden abstracta. Cree una nueva clase en la carpeta Objects y llámela COrder (1). No olvide indicar como clase básica la clase de la biblioteca estándar CObject (2), en este caso, nuestra nueva clase se heredará de la clase CObject, y se podrá ubicar en la lista de objetos CArrayObj de la biblioteca estándar.


Después de pulsar el botón "Listo" en la carpeta Objects del catálogo de la biblioteca, aparecerá un nuevo archivo, Order.mqh (3). Por el momento, solo se trata de una plantilla de clase:

//+------------------------------------------------------------------+
//|                                                        Order.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"
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:

public:
                     COrder();
                    ~COrder();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::COrder()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::~COrder()
  {
  }
//+------------------------------------------------------------------+

Si intentamos compilarla, obtendremos cinco errores. Todos ellos indican que no tenemos la clase CObject, de la cual hicimos heredera la clase COrder. Incluimos en la lista el archivo de la clase CObject y compilamos de nuevo, ahora todo irá bien.

//+------------------------------------------------------------------+
//|                                                        Order.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"
#include <Object.mqh>
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:

public:
                     COrder();
                    ~COrder();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::COrder()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::~COrder()
  {
  }
//+------------------------------------------------------------------+

Un objeto de esta clase se creará en el ciclo de órdenes, transacciones o posiciones en el momento en que se seleccione la siguiente orden, transacción o posición. Para inicializar sus campos justo al crear un objeto, vamos a crear un constructor privado, al que, al crearlo, transmitiremos el tipo (estado) de este objeto y su ticket para su posterior identificación. Pero antes, vamos a crear en la carpeta raíz del proyecto un nuevo archivo de inclusión, Defines.mqh, en el que guardaremos todas las enumeraciones, macrosustituciones, constantes y estructuras necesarias para la biblioteca.

En este momento, necesitamos enumeraciones que describan el estado de la orden, y enumeraciones que describan todos los parámetros de la orden, transacción o posición. Las enumeraciones con los parámetros de las órdenes serán tres: enteras, reales y de línea.

//+------------------------------------------------------------------+
//|                                                      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"
//+------------------------------------------------------------------+
//| Tipo (estado) de la orden abstracta                              |
//+------------------------------------------------------------------+
enum ENUM_ORDER_STATUS
  {
   ORDER_STATUS_MARKET_PENDING,                             // Orden pendiente activa
   ORDER_STATUS_MARKET_ACTIVE,                              // Orden de mercado activa
   ORDER_STATUS_HISTORY_ORDER,                              // Orden de mercado histórica
   ORDER_STATUS_HISTORY_PENDING,                            // Orden pendiente eliminada
   ORDER_STATUS_BALANCE,                                    // Operación de balance
   ORDER_STATUS_CREDIT,                                     // Operación de crédito
   ORDER_STATUS_DEAL,                                       // Transacción
   ORDER_STATUS_UNKNOWN                                     // Estado desconocido
  };
//+------------------------------------------------------------------+
//| Propiedades de tipo entero de la orden, transacción, posición    |
//+------------------------------------------------------------------+
enum ENUM_ORDER_PROP_INTEGER
  {
   ORDER_PROP_TICKET = 0,                                   // Ticket de la orden
   ORDER_PROP_MAGIC,                                        // Número mágico de la orden
   ORDER_PROP_TIME_OPEN,                                    // Hora de apertura
   ORDER_PROP_TIME_CLOSE,                                   // Hora de cierre
   ORDER_PROP_TIME_EXP,                                     // Fecha de expiración de la orden (para las órdenes pendientes)
   ORDER_PROP_TYPE,                                         // Tipo de orden, transacción
   ORDER_PROP_STATUS,                                       // Estado de la orden (de la enumeración ENUM_ORDER_STATUS)
   ORDER_PROP_REASON,                                       // Motivo o razón de la transacción/orden/posición
   ORDER_PROP_POSITION_ID,                                  // Indicador de la posición
   ORDER_PROP_POSITION_BY_ID,                               // Indicador de la posición opuesta
   ORDER_PROP_DEAL_ORDER,                                   // Orden sobre cuya base se ha ejecutado la transacción
   ORDER_PROP_DEAL_ENTRY,                                   // Dirección de la transacción – IN, OUT o IN/OUT
   ORDER_PROP_TIME_OPEN_MSC,                                // Hora de apertura en milisegundos
   ORDER_PROP_TIME_CLOSE_MSC,                               // Hora de cierre en milisegundos (Hora de ejecución o eliminación - ORDER_TIME_DONE_MSC)
   ORDER_PROP_TIME_UPDATE,                                  // Hora de cambio de la posición en el segundos
   ORDER_PROP_TIME_UPDATE_MSC,                              // Hora de cambio de la posición en milisegundos
   ORDER_PROP_TICKET_FROM,                                  // Ticket de la orden padre
   ORDER_PROP_TICKET_TO,                                    // Ticket de la orden derivada
   ORDER_PROP_PROFIT_PT,                                    // Beneficio en puntos
   ORDER_PROP_CLOSE_BY_SL,                                  // Señal de cierre por StopLoss
   ORDER_PROP_CLOSE_BY_TP,                                  // Señal de cierre por TakeProfit
   ORDER_PROP_DIRECTION,                                    // Tipo según la dirección (Buy, Sell)
  }; 
#define ORDER_PROP_INTEGER_TOTAL    (22)                    // Número total de propiedades de tipo entero
//+------------------------------------------------------------------+
//| Propiedades de tipo real de la orden, transacción, posición      |
//+------------------------------------------------------------------+
enum ENUM_ORDER_PROP_DOUBLE
  {
   ORDER_PROP_PRICE_OPEN = ORDER_PROP_INTEGER_TOTAL,        // Precio de apertura (precio de transacción MQL5)
   ORDER_PROP_PRICE_CLOSE,                                  // Precio de cierre
   ORDER_PROP_PROFIT,                                       // Beneficio
   ORDER_PROP_COMMISSION,                                   // Comisión
   ORDER_PROP_SWAP,                                         // Swap
   ORDER_PROP_VOLUME,                                       // Volumen
   ORDER_PROP_VOLUME_CURRENT,                               // Volumen no ejecutado
   ORDER_PROP_SL,                                           // Precio de StopLoss
   ORDER_PROP_TP,                                           // Precio de TakeProfit
   ORDER_PROP_PROFIT_FULL,                                  // Beneficio+comisión+swap
   ORDER_PROP_PRICE_STOP_LIMIT,                             // Precio de colocación de una orden Limit al activarse una orden StopLimit
  };
#define ORDER_PROP_DOUBLE_TOTAL     (11)                    // Número total de propiedades reales
//+------------------------------------------------------------------+
//| Propiedades de línea de la orden, transacción, posición          |
//+------------------------------------------------------------------+
enum ENUM_ORDER_PROP_STRING
  {
   ORDER_PROP_SYMBOL = (ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_DOUBLE_TOTAL), // Símbolo de la orden
   ORDER_PROP_COMMENT,                                      // Comentario a la orden
   ORDER_PROP_EXT_ID                                        // Identificador de la orden en el sistema comercial externo
  };
#define ORDER_PROP_STRING_TOTAL     (3)                     // Número general de las propiedades de línea
//+------------------------------------------------------------------+

Además, se han añadido algunas propiedades a las enumeraciones: el ticket de la orden padre, el ticket de la orden derivada, el beneficio en puntos, las señales de cierre por stop-loss y take-profit, el tipo de dirección y el beneficio total teniendo en cuenta la comisión y el swap. Asimismo, los datos se usan con frecuencia para la lógica de la estrategia comercial, por lo que es mejor guardarlos directamente en los campos de la orden abstracta, actualizarlos y obtenerlos en los programas personalizados.

Para unificar todas las propiedades de la orden: de tipo entero, real y string, vamos a crear para cada uno de los tipos de propiedades macrosustituciones que contengan el número de parámetros en cada una de las tres enumeraciones.
La numeración de las enumeraciones de las propiedades de tipo entero comenzará desde cero, mientras que la numeración de las enumeraciones del resto de propiedades comenzará a partir del número total de propiedades anteriores. De esta manera, siempre podremos obtener el índice de la propiedad que necesitamos según la diferencia entre el número de la propiedad solicitada y el número de la propiedad inicial de esta enumeración.

Bien. Después de crear el archivo Defines.mqh, lo incluimos en el archivo actual de la clase COrder y creamos en la sección privada una variable miembro de clase para guardar el ticket de la orden y tres matrices para guardar las propiedades de la orden, a saber, de tipo entero, real y string:

//+------------------------------------------------------------------+
//|                                                        Order.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"
#include <Object.mqh>
#include "..\Defines.mqh"
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:
   ulong             m_ticket;                                 // Ticket de la orden/transacción seleccionada (MQL5)
   long              m_long_prop[ORDER_PROP_INTEGER_TOTAL];    // Propiedades de tipo entero
   double            m_double_prop[ORDER_PROP_DOUBLE_TOTAL];   // Propiedades de tipo real
   string            m_string_prop[ORDER_PROP_STRING_TOTAL];   // Propiedades de tipo string
public:
                     COrder();
                    ~COrder();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::COrder()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::~COrder()
  {
  }
//+------------------------------------------------------------------+

Hay que destacar que, a la hora de incluir el archivo Defines.mqh, no hemos usado cuñas (<>), como hicimos al incluir CObject, sino que hemos establecido entre paréntesis la ruta relativa al archivo. Lo hemos hecho de esa forma para que, al trasladar la biblioteca a cualquier otro catálogo, la conexión entre archivos no se pierda y siempre indique la ubicación del archivo Defines.mqh con respecto al catálogo actual.

Ahora, vamos a crear en la misma sección privada dos métodos, que nos retornarán la ubicación exacta de la propiedad necesaria en las matrices y un constructor protegido en la sección protected correspondiente. Vamos a dejar el constructor por defecto para crear una orden-objeto vacía sin inicializar sus propiedades; también eliminaremos el destructor (no lo necesitamos, además, se creará automáticamente durante la compilación):

//+------------------------------------------------------------------+
//|                                                        Order.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"
#include <Object.mqh>
#include "..\Defines.mqh"
//+------------------------------------------------------------------+
//| Clase de orden abstracta                                         |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:
   ulong             m_ticket;                                    // Ticket de la orden/transacción seleccionada (MQL5)
   long              m_long_prop[ORDER_PROP_INTEGER_TOTAL];       // Propiedades de tipo entero
   double            m_double_prop[ORDER_PROP_DOUBLE_TOTAL];      // Propiedades de tipo real
   string            m_string_prop[ORDER_PROP_STRING_TOTAL];      // Propiedades de tipo string
   
   //--- Retorna el índice de la matriz según el cual está realmente ubicada la propiedad double
   int               IndexProp(ENUM_ORDER_PROP_DOUBLE property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL;                  }
   //--- Retorna el índice de la matriz según el cual está realmente ubicada la propiedad string
   int               IndexProp(ENUM_ORDER_PROP_STRING property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL-ORDER_PROP_DOUBLE_TOTAL;}
public:
   //--- Constructor por defecto
                     COrder(void){;}
protected:                                                                     
   //--- Constructor paramétrico protegido                                
                     COrder(ENUM_ORDER_STATUS order_status,const ulong ticket);
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket)
  {
  }
//+------------------------------------------------------------------+

Por el momento, el constructor protegido no hace nada, lo necesitaremos para que, a la hora de crear un objeto, se inicien de inmediato todas las propiedades de la orden cuyo ticket se transmite al constructor de la clase. Ya que la orden no ha sido elegida, no podremos obtener directamente las propiedades necesarias de la orden seleccionada y grabarlas en las matrices de propiedades del objeto. Esto lo haremos más tarde, después de crear los métodos de obtención y retorno de los datos de la orden.

Puesto que estamos haciendo una biblioteca multiplataforma, resultará más cómodo crear métodos aparte para obtener las propiedades de la orden.
En la sección protegida, añadimos las descripciones de los métodos para obtener las propiedades de tipo entero, real y string de la orden seleccionada.

//+------------------------------------------------------------------+
//|                                                        Order.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"
#include <Object.mqh>
#include "..\Defines.mqh"
//+------------------------------------------------------------------+
//| Clase de orden abstracta                                         |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:
   ulong             m_ticket;                                    // Ticket de la orden/transacción seleccionada (MQL5)
   long              m_long_prop[ORDER_PROP_INTEGER_TOTAL];       // Propiedades de tipo entero
   double            m_double_prop[ORDER_PROP_DOUBLE_TOTAL];      // Propiedades de tipo real
   string            m_string_prop[ORDER_PROP_STRING_TOTAL];      // Propiedades de tipo string
   
   //--- Retorna el índice de la matriz según el cual está realmente ubicada la propiedad double
   int               IndexProp(ENUM_ORDER_PROP_DOUBLE property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL;                        }
   //--- Retorna el índice de la matriz según el cual está realmente ubicada la propiedad string
   int               IndexProp(ENUM_ORDER_PROP_STRING property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL-ORDER_PROP_DOUBLE_TOTAL;}
public:
   //--- Constructor por defecto
                     COrder(void){;}
protected:
   //--- Constructor paramétrico protegido
                     COrder(ENUM_ORDER_STATUS order_status,const ulong ticket);
                     
   //--- Obtiene y retorna las propiedades de tipo entero de la orden seleccionada de sus parámetros
   long              OrderMagicNumber(void)        const;
   long              OrderTicket(void)             const;
   long              OrderTicketFrom(void)         const;
   long              OrderTicketTo(void)           const;
   long              OrderPositionID(void)         const;
   long              OrderPositionByID(void)       const;
   long              OrderOpenTimeMSC(void)        const;
   long              OrderCloseTimeMSC(void)       const;
   long              OrderType(void)               const;
   long              OrderTypeByDirection(void)    const;
   long              OrderTypeFilling(void)        const;
   long              OrderTypeTime(void)           const;
   long              OrderReason(void)             const;
   long              DealOrder(void)               const;
   long              DealEntry(void)               const;
   bool              OrderCloseByStopLoss(void)    const;
   bool              OrderCloseByTakeProfit(void)  const;
   datetime          OrderOpenTime(void)           const;
   datetime          OrderCloseTime(void)          const;
   datetime          OrderExpiration(void)         const;
   datetime          PositionTimeUpdate(void)      const;
   datetime          PositionTimeUpdateMSC(void)   const;
   
   //--- Obtiene y retorna las propiedades de tipo real de la orden seleccionada de sus parámetros: (1) precio de apertura, (2) precio de cierre, (3) beneficio,
   //---  (4) comisión, (5) swap, (6) volumen, (7) volumen no ejecutado (8) precio de StopLoss, (9) precio de TakeProfit (10) precio de colocación de la orden StopLimit
   double            OrderOpenPrice(void)          const;
   double            OrderClosePrice(void)         const;
   double            OrderProfit(void)             const;
   double            OrderCommission(void)         const;
   double            OrderSwap(void)               const;
   double            OrderVolume(void)             const;
   double            OrderVolumeCurrent(void)      const;
   double            OrderStopLoss(void)           const;
   double            OrderTakeProfit(void)         const;
   double            OrderPriceStopLimit(void)     const;
   
   //--- Obtiene y retorna las propiedades de tipo string de la orden seleccionada de sus parámetros: (1) símbolo, (2) comentario, (3) identificador bursátil,
   string            OrderSymbol(void)             const;
   string            OrderComment(void)            const;
   string            OrderExternalID(void)         const;
   
//---
  };
//+------------------------------------------------------------------+
//| Constructor paramétrico Closed                                   |
//+------------------------------------------------------------------+
COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket)
  {
  }
//+------------------------------------------------------------------+

En este momento, solo han sido declarados los métodos para obtener las propiedades, pero estos no han sido aún implementados, aunque la clase se compile sin errores. En el constructor de la clase, con la ayuda de los métodos recién añadidos, se rellenarán las matrices de las propiedades de la orden, de las cuales recibiremos en los sucesivo en nuestros programas cualquier propiedad solicitada.

En la clase CObject de la biblioteca estándar se ha declarado un método virtual de comparación de objetos según las propiedades establecidas. Pero la implementación de este método se debe realizar en las clases herederas. Por eso, vamos a añadir a la clase de orden abstracta un método para comparar los objetos COrder entre sí según cualquiera de sus propiedades, además de varios métodos públicos para acceder a las propiedades de la orden y métodos virtuales que retornen las banderas de soporte de la propiedad específica de un objeto orden. La implementación de esos métodos se realizará en los objetos-herederos de la clase COrder. Esto será necesario en lo sucesivo para seleccionar las órdenes de la lista-colección según cualquiera de sus propiedades. Por defecto, si en la clase heredera no se ha implementado alguno de estos métodos, se retornará una bandera que indique el soporte de la orden para esa propiedad.

//+------------------------------------------------------------------+
//|                                                        Order.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"
#include <Object.mqh>
#include "..\Defines.mqh"
//+------------------------------------------------------------------+
//| Clase de orden abstracta                                         |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:
   ulong             m_ticket;                                    // Ticket de la orden/transacción seleccionada (MQL5)
   long              m_long_prop[ORDER_PROP_INTEGER_TOTAL];       // Propiedades de tipo entero
   double            m_double_prop[ORDER_PROP_DOUBLE_TOTAL];      // Propiedades de tipo real
   string            m_string_prop[ORDER_PROP_STRING_TOTAL];      // Propiedades de tipo string
   
   //--- Retorna el índice de la matriz según el cual está realmente ubicada la propiedad double
   int               IndexProp(ENUM_ORDER_PROP_DOUBLE property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL;                        }
   //--- Retorna el índice de la matriz según el cual está realmente ubicada la propiedad string
   int               IndexProp(ENUM_ORDER_PROP_STRING property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL-ORDER_PROP_DOUBLE_TOTAL;}
public:
   //--- Constructor por defecto
                     COrder(void){;}
protected:
   //--- Constructor paramétrico protegido
                     COrder(ENUM_ORDER_STATUS order_status,const ulong ticket);
                     
   //--- Obtiene y retorna las propiedades de tipo entero de la orden seleccionada de sus parámetros
   long              OrderMagicNumber(void)        const;
   long              OrderTicket(void)             const;
   long              OrderTicketFrom(void)         const;
   long              OrderTicketTo(void)           const;
   long              OrderPositionID(void)         const;
   long              OrderPositionByID(void)       const;
   long              OrderOpenTimeMSC(void)        const;
   long              OrderCloseTimeMSC(void)       const;
   long              OrderType(void)               const;
   long              OrderTypeByDirection(void)    const;
   long              OrderTypeFilling(void)        const;
   long              OrderTypeTime(void)           const;
   long              OrderReason(void)             const;
   long              DealOrder(void)               const;
   long              DealEntry(void)               const;
   bool              OrderCloseByStopLoss(void)    const;
   bool              OrderCloseByTakeProfit(void)  const;
   datetime          OrderOpenTime(void)           const;
   datetime          OrderCloseTime(void)          const;
   datetime          OrderExpiration(void)         const;
   datetime          PositionTimeUpdate(void)      const;
   datetime          PositionTimeUpdateMSC(void)   const;
   
   //--- Obtiene y retorna las propiedades de tipo real de la orden seleccionada de sus parámetros: (1) precio de apertura, (2) precio de cierre, (3) beneficio,
   //---  (4) comisión, (5) swap, (6) volumen, (7) volumen no ejecutado (8) precio de StopLoss, (9) precio de TakeProfit (10) precio de colocación de la orden StopLimit
   double            OrderOpenPrice(void)          const;
   double            OrderClosePrice(void)         const;
   double            OrderProfit(void)             const;
   double            OrderCommission(void)         const;
   double            OrderSwap(void)               const;
   double            OrderVolume(void)             const;
   double            OrderVolumeCurrent(void)      const;
   double            OrderStopLoss(void)           const;
   double            OrderTakeProfit(void)         const;
   double            OrderPriceStopLimit(void)     const;
   
   //--- Obtiene y retorna las propiedades de tipo string de la orden seleccionada de sus parámetros: (1) símbolo, (2) comentario, (3) identificador bursátil,
   string            OrderSymbol(void)             const;
   string            OrderComment(void)            const;
   string            OrderExternalID(void)         const;
   
public:
   //--- Retorna las propiedades de tipo (1) entero, (2) real y (3) string de la matriz de propiedades
   long              GetProperty(ENUM_ORDER_PROP_INTEGER property)      const { return m_long_prop[property];                    }
   double            GetProperty(ENUM_ORDER_PROP_DOUBLE property)       const { return m_double_prop[this.IndexProp(property)];  }
   string            GetProperty(ENUM_ORDER_PROP_STRING property)       const { return m_string_prop[this.IndexProp(property)];  }
   
   //--- Retorna la bandera de la orden que soporta la propiedad
   virtual bool      SupportProperty(ENUM_ORDER_PROP_INTEGER property)        { return true; }
   virtual bool      SupportProperty(ENUM_ORDER_PROP_DOUBLE property)         { return true; }
   virtual bool      SupportProperty(ENUM_ORDER_PROP_STRING property)         { return true; }
   
   //--- Compara los objetos COrder entre sí según todas las propiedades posibles
   virtual int       Compare(const CObject *node,const int mode=0) const;
   
//---
  };
//+------------------------------------------------------------------+
//| Constructor paramétrico Closed                                   |
//+------------------------------------------------------------------+
COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket)
  {
  }
//+------------------------------------------------------------------+

Implementando el método de comparación de dos órdenes entre sí según la propiedad establecida:

//+------------------------------------------------------------------+
//| Compara los objetos COrder entre sí según todas las propiedades  |
//| posibles                                                         |
//+------------------------------------------------------------------+
int COrder::Compare(const CObject *node,const int mode=0) const
  {
   const COrder *order_compared=node;
//--- comparando las propiedades de tipo entero de dos órdenes
   if(mode<ORDER_PROP_INTEGER_TOTAL)
     {
      long value_compared=order_compared.GetProperty((ENUM_ORDER_PROP_INTEGER)mode);
      long value_current=this.GetProperty((ENUM_ORDER_PROP_INTEGER)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- comparando las propiedades de tipo real de dos órdenes
   else if(mode<ORDER_PROP_DOUBLE_TOTAL+ORDER_PROP_INTEGER_TOTAL)
     {
      double value_compared=order_compared.GetProperty((ENUM_ORDER_PROP_DOUBLE)mode);
      double value_current=this.GetProperty((ENUM_ORDER_PROP_DOUBLE)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- comparando las propiedades de tipo string de dos órdenes
   else if(mode<ORDER_PROP_DOUBLE_TOTAL+ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_STRING_TOTAL)
     {
      string value_compared=order_compared.GetProperty((ENUM_ORDER_PROP_STRING)mode);
      string value_current=this.GetProperty((ENUM_ORDER_PROP_STRING)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
   return 0;
  }
//+------------------------------------------------------------------+

Se transmite al método el puntero al objeto orden cuya propiedad se debe comparar con un cierto valor, y el propio valor de la enumeración de propiedades de la orden.
A continuación, todo resulta sencillo: si el valor de la orden es superior al valor con el que realizamos la comparación, se retorna 1, si es inferior, se retorna -1, de lo contario, se retorna 0. La lista de órdenes en la que se realiza la comparación debe ser previamente clasificada según la propiedad a comparar.

Ahora, vamos a implementar los métodos para obtener las propiedades de la orden y grabarlas en las matrices de propiedades. Dichos métodos fueron anteriormente declarados en la sección privada. Dado que los métodos para obtener las propiedades de la orden son multiplataforma, vamos a analizarlos usando como ejemplo la obtención del número mágico del experto:

//+------------------------------------------------------------------+
//| Retorna el número mágico                                         |
//+------------------------------------------------------------------+
long COrder::OrderMagicNumber() const
  {
#ifdef __MQL4__
   return ::OrderMagicNumber();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_MAGIC);           break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_MAGIC);                 break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_MAGIC);   break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_MAGIC); break;
      default                             : res=0;                                              break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+

Si se trata del código para MQL4, el número mágico será retornado por la función mql4 OrderMagicNumber(). De lo contrario, comprobaremos el estado de la orden y, dependiendo de qué tenemos precisamente entre manos, retornaremos el número mágico de una posición, una orden o una transacción.

De la misma forma se han hecho los demás métodos de lectura y escritura de las propiedades de la orden/transacción/posición seleccionada. El lector podrá analizarlos por su propia cuenta.

Métodos para obtener las propiedades de tipo entero de una orden/transacción/posición:
//+------------------------------------------------------------------+
//| Retorna el ticket                                                |
//+------------------------------------------------------------------+
long COrder::OrderTicket(void) const
  {
#ifdef __MQL4__
   return ::OrderTicket();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     :
      case ORDER_STATUS_MARKET_PENDING    :
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     :
      case ORDER_STATUS_DEAL              : res=(long)m_ticket;                                 break;
      default                             : res=0;                                              break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| Retorna el ticket de la orden padre                              |
//+------------------------------------------------------------------+
long COrder::OrderTicketFrom(void) const
  {
   long ticket=0;
#ifdef __MQL4__
   string order_comment=::OrderComment();
   if(::StringFind(order_comment,"from #")>WRONG_VALUE) ticket=::StringToInteger(::StringSubstr(order_comment,6));
#endif
   return ticket;
  }
//+------------------------------------------------------------------+
//| Retorna el ticket de la orden derivada                           |
//+------------------------------------------------------------------+
long COrder::OrderTicketTo(void) const
  {
   long ticket=0;
#ifdef __MQL4__
   string order_comment=::OrderComment();
   if(::StringFind(order_comment,"to #")>WRONG_VALUE) ticket=::StringToInteger(::StringSubstr(order_comment,4));
#endif
   return ticket;
  }
//+------------------------------------------------------------------+
//| Retorna el identificador de la posición                          |
//+------------------------------------------------------------------+
long COrder::OrderPositionID(void) const
  {
#ifdef __MQL4__
   return ::OrderMagicNumber();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_IDENTIFIER);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_POSITION_ID);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_POSITION_ID); break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_POSITION_ID);   break;
      default                             : res=0;                                                    break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| Retorna el identificador de la posición opuesta                  |
//+------------------------------------------------------------------+
long COrder::OrderPositionByID(void) const
  {
#ifdef __MQL4__
   return 0;
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_POSITION_BY_ID);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_POSITION_BY_ID); break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| Retorna la hora de apertura en milisegundos                      |
//+------------------------------------------------------------------+
long COrder::OrderOpenTimeMSC(void) const
  {
#ifdef __MQL4__
   return (long)::OrderOpenTime();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_TIME_MSC);                 break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_TIME_SETUP_MSC);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_TIME_SETUP_MSC); break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_TIME_MSC);         break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| Retorna la hora de cierre en milisegundos                        |
//+------------------------------------------------------------------+
long COrder::OrderCloseTimeMSC(void) const
  {
#ifdef __MQL4__
   return (long)::OrderCloseTime();
#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_TIME_DONE_MSC);     break;
      case ORDER_STATUS_DEAL              : res=(datetime)::HistoryDealGetInteger(m_ticket,DEAL_TIME_MSC);  break;
      default                             : res=0;                                                          break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| Retorna el tipo                                                  |
//+------------------------------------------------------------------+
long COrder::OrderType(void) const
  {
#ifdef __MQL4__
   return (long)::OrderType();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_TYPE);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_TYPE);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_TYPE);  break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_TYPE);    break;
      default                             : res=0;                                              break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| Retorna el tipo según la dirección                               |
//+------------------------------------------------------------------+
long COrder::OrderTypeByDirection(void) const
  {
   ENUM_ORDER_STATUS status=(ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS);
   if(status==ORDER_STATUS_MARKET_ACTIVE)
     {
      return(this.OrderType()==POSITION_TYPE_BUY ? ORDER_TYPE_BUY : ORDER_TYPE_SELL);
     }
   if(status==ORDER_STATUS_MARKET_PENDING || status==ORDER_STATUS_HISTORY_PENDING)
     {
      return
        (
         this.OrderType()==ORDER_TYPE_BUY_LIMIT || 
         this.OrderType()==ORDER_TYPE_BUY_STOP 
         #ifdef __MQL5__                        ||
         this.OrderType()==ORDER_TYPE_BUY_STOP_LIMIT 
         #endif ? 
         ORDER_TYPE_BUY : 
         ORDER_TYPE_SELL
        );
     }
   if(status==ORDER_STATUS_HISTORY_ORDER)
     {
      return this.OrderType();
     }
   return WRONG_VALUE;
  }
//+------------------------------------------------------------------+
//| 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_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_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_ACTIVE     : res=::PositionGetInteger(POSITION_REASON);          break;
      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 
  }
//+------------------------------------------------------------------+
//| Orden sobre cuya base se ha ejecutado la transacción             |
//+------------------------------------------------------------------+
long COrder::DealOrder(void) const
  {
#ifdef __MQL4__
   return ::OrderTicket();
#else 
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_IDENTIFIER);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_POSITION_ID);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_POSITION_ID); break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_ORDER);         break;
      default                             : res=0;                                                    break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Dirección de la transacción IN, OUT, IN/OUT                      |
//+------------------------------------------------------------------+
long COrder::DealEntry(void) const
  {
#ifdef __MQL4__
   return ::OrderType();
#else 
   long res=WRONG_VALUE;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_DEAL  : res=::HistoryDealGetInteger(m_ticket,DEAL_ENTRY);break;
      default                 : res=WRONG_VALUE;                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna la bandera de cierre de posición por StopLoss            |
//+------------------------------------------------------------------+
bool COrder::OrderCloseByStopLoss(void) const
  {
#ifdef __MQL4__
   return(::StringFind(::OrderComment(),"[sl")>WRONG_VALUE);
#else 
   return
     (
      this.Status()==ORDER_STATUS_HISTORY_ORDER ? this.OrderReason()==ORDER_REASON_SL : 
      this.Status()==ORDER_STATUS_DEAL ? this.OrderReason()==DEAL_REASON_SL : false
     );
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna la bandera de cierre de posición por TakeProfit          |
//+------------------------------------------------------------------+
bool COrder::OrderCloseByTakeProfit(void) const
  {
#ifdef __MQL4__
   return(::StringFind(::OrderComment(),"[tp")>WRONG_VALUE);
#else 
   return
     (
      this.Status()==ORDER_STATUS_HISTORY_ORDER ? this.OrderReason()==ORDER_REASON_TP : 
      this.Status()==ORDER_STATUS_DEAL ? this.OrderReason()==DEAL_REASON_TP : false
     );
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna la hora de apertura                                      |
//+------------------------------------------------------------------+
datetime COrder::OrderOpenTime(void) const
  {
#ifdef __MQL4__
   return ::OrderOpenTime();
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=(datetime)::PositionGetInteger(POSITION_TIME);                 break;
      case ORDER_STATUS_MARKET_PENDING    : res=(datetime)::OrderGetInteger(ORDER_TIME_SETUP);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=(datetime)::HistoryOrderGetInteger(m_ticket,ORDER_TIME_SETUP); break;
      case ORDER_STATUS_DEAL              : res=(datetime)::HistoryDealGetInteger(m_ticket,DEAL_TIME);         break;
      default                             : res=0;                                                             break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna la hora de cierre                                        |
//+------------------------------------------------------------------+
datetime COrder::OrderCloseTime(void) const
  {
#ifdef __MQL4__
   return ::OrderCloseTime();
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=(datetime)::HistoryOrderGetInteger(m_ticket,ORDER_TIME_DONE);  break;
      case ORDER_STATUS_DEAL              : res=(datetime)::HistoryDealGetInteger(m_ticket,DEAL_TIME);         break;
      default                             : res=0;                                                             break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna la hora de expiración                                    |
//+------------------------------------------------------------------+
datetime COrder::OrderExpiration(void) const
  {
#ifdef __MQL4__
   return ::OrderExpiration();
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=(datetime)::OrderGetInteger(ORDER_TIME_EXPIRATION);                  break;
      case ORDER_STATUS_HISTORY_PENDING   : res=(datetime)::HistoryOrderGetInteger(m_ticket,ORDER_TIME_EXPIRATION);  break;
      default                             : res=0;                                                                   break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Hora de cambio de la posición en segundos                        |
//+------------------------------------------------------------------+
datetime COrder::PositionTimeUpdate(void) const
  {
#ifdef __MQL4__
   return 0;
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE  : res=(datetime)::PositionGetInteger(POSITION_TIME_UPDATE); break;
      default                          : res=0;                                                    break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Hora de cambio de la posición en milisegundos                    |
//+------------------------------------------------------------------+
datetime COrder::PositionTimeUpdateMSC(void) const
  {
#ifdef __MQL4__
   return 0;
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE  : res=(datetime)::PositionGetInteger(POSITION_TIME_UPDATE_MSC);break;
      default                          : res=0;                                                       break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

Métodos para obtener las propiedades de tipo real de una orden/transacción/posición:

//+------------------------------------------------------------------+
//| Retorna el precio de apertura                                    |
//+------------------------------------------------------------------+
double COrder::OrderOpenPrice(void) const
  {
#ifdef __MQL4__
   return ::OrderOpenPrice();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetDouble(POSITION_PRICE_OPEN);          break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_PRICE_OPEN);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_PRICE_OPEN);break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetDouble(m_ticket,DEAL_PRICE);       break;
      default                             : res=0;                                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna el precio de cierre                                      |
//+------------------------------------------------------------------+
double COrder::OrderClosePrice(void) const
  {
#ifdef __MQL4__
   return ::OrderClosePrice();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_PRICE_OPEN);break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetDouble(m_ticket,DEAL_PRICE);       break;
      default                             : res=0;                                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna el beneficio                                             |
//+------------------------------------------------------------------+
double COrder::OrderProfit(void) const
  {
#ifdef __MQL4__
   return ::OrderProfit();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE  : res=::PositionGetDouble(POSITION_PROFIT);        break;
      case ORDER_STATUS_DEAL           : res=::HistoryDealGetDouble(m_ticket,DEAL_PROFIT);break;
      default                          : res=0;                                           break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna la comisión                                              |
//+------------------------------------------------------------------+
double COrder::OrderCommission(void) const
  {
#ifdef __MQL4__
   return ::OrderCommission();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_DEAL  : res=::HistoryDealGetDouble(m_ticket,DEAL_COMMISSION);  break;
      default                 : res=0;                                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna el swap                                                  |
//+------------------------------------------------------------------+
double COrder::OrderSwap(void) const
  {
#ifdef __MQL4__
   return ::OrderSwap();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE  : res=::PositionGetDouble(POSITION_SWAP);          break;
      case ORDER_STATUS_DEAL           : res=::HistoryDealGetDouble(m_ticket,DEAL_SWAP);  break;
      default                          : res=0;                                           break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna el volumen                                               |
//+------------------------------------------------------------------+
double COrder::OrderVolume(void) const
  {
#ifdef __MQL4__
   return ::OrderLots();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetDouble(POSITION_VOLUME);                    break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_VOLUME_INITIAL);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_VOLUME_INITIAL);  break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetDouble(m_ticket,DEAL_VOLUME);            break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna el volumen no ejecutado                                  |
//+------------------------------------------------------------------+
double COrder::OrderVolumeCurrent(void) const
  {
#ifdef __MQL4__
   return ::OrderLots();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_VOLUME_CURRENT);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_VOLUME_CURRENT);  break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna el precio de StopLoss                                    |
//+------------------------------------------------------------------+
double COrder::OrderStopLoss(void) const
  {
#ifdef __MQL4__
   return ::OrderStopLoss();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetDouble(POSITION_SL);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_SL);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_SL);  break;
      default                             : res=0;                                           break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna el precio de TakeProfit                                  |
//+------------------------------------------------------------------+
double COrder::OrderTakeProfit(void) const
  {
#ifdef __MQL4__
   return ::OrderTakeProfit();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetDouble(POSITION_TP);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_TP);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_TP);  break;
      default                             : res=0;                                           break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna el precio de colocación de una orden Limit               |
//| al activarse una orden StopLimit                                 |
//+------------------------------------------------------------------+
double COrder::OrderPriceStopLimit(void) const
  {
#ifdef __MQL4__
   return 0;
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_PRICE_STOPLIMIT);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_PRICE_STOPLIMIT); break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

Métodos para obtener las propiedades de tipo string de una orden/transacción/posición:

//+------------------------------------------------------------------+
//| Retorna el símbolo                                               |
//+------------------------------------------------------------------+
string COrder::OrderSymbol(void) const
  {
#ifdef __MQL4__
   return ::OrderSymbol();
#else 
   string res="";
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetString(POSITION_SYMBOL);           break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetString(ORDER_SYMBOL);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetString(m_ticket,ORDER_SYMBOL); break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetString(m_ticket,DEAL_SYMBOL);   break;
      default                             : res="";                                             break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna el comentario                                            |
//+------------------------------------------------------------------+
string COrder::OrderComment(void) const
  {
#ifdef __MQL4__
   return ::OrderComment();
#else 
   string res="";
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetString(POSITION_COMMENT);          break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetString(ORDER_COMMENT);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetString(m_ticket,ORDER_COMMENT);break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetString(m_ticket,DEAL_COMMENT);  break;
      default                             : res="";                                             break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Retorna el identificador bursátil                                |
//+------------------------------------------------------------------+
string COrder::OrderExternalID(void) const
  {
#ifdef __MQL4__
   return "";
#else 
   string res="";
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetString(ORDER_EXTERNAL_ID);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetString(m_ticket,ORDER_EXTERNAL_ID);  break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetString(m_ticket,DEAL_EXTERNAL_ID);    break;
      default                             : res="";                                                   break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

Ya hemos declarado e implementado los métodos privados para obtener las propiedades de los datos de la orden.

Ahora es el mejor momento para implementar el constructor protegido de la clase, escribiendo en el mismo los métodos que guardan todas las propiedades de la orden cuyo ticket se ha transmitido a este constructor.

Implementando el constructor protegido de la clase:

//+------------------------------------------------------------------+
//| Constructor paramétrico Closed                                   |
//+------------------------------------------------------------------+
COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket)
  {
//--- Guardando las propiedades de tipo entero
   m_ticket=ticket;
   m_long_prop[ORDER_PROP_STATUS]                              = order_status;
   m_long_prop[ORDER_PROP_MAGIC]                               = this.OrderMagicNumber();
   m_long_prop[ORDER_PROP_TICKET]                              = this.OrderTicket();
   m_long_prop[ORDER_PROP_TIME_OPEN]                           = (long)(ulong)this.OrderOpenTime();
   m_long_prop[ORDER_PROP_TIME_CLOSE]                          = (long)(ulong)this.OrderCloseTime();
   m_long_prop[ORDER_PROP_TIME_EXP]                            = (long)(ulong)this.OrderExpiration();
   m_long_prop[ORDER_PROP_TYPE]                                = this.OrderType();
   m_long_prop[ORDER_PROP_DIRECTION]                           = this.OrderTypeByDirection();
   m_long_prop[ORDER_PROP_POSITION_ID]                         = this.OrderPositionID();
   m_long_prop[ORDER_PROP_REASON]                              = this.OrderReason();
   m_long_prop[ORDER_PROP_DEAL_ORDER]                          = this.DealOrder();
   m_long_prop[ORDER_PROP_DEAL_ENTRY]                          = this.DealEntry();
   m_long_prop[ORDER_PROP_POSITION_BY_ID]                      = this.OrderPositionByID();
   m_long_prop[ORDER_PROP_TIME_OPEN_MSC]                       = this.OrderOpenTimeMSC();
   m_long_prop[ORDER_PROP_TIME_CLOSE_MSC]                      = this.OrderCloseTimeMSC();
   m_long_prop[ORDER_PROP_TIME_UPDATE]                         = (long)(ulong)this.PositionTimeUpdate();
   m_long_prop[ORDER_PROP_TIME_UPDATE_MSC]                     = (long)(ulong)this.PositionTimeUpdateMSC();
   
//--- Guardando las propiedades de tipo real
   m_double_prop[this.IndexProp(ORDER_PROP_PRICE_OPEN)]        = this.OrderOpenPrice();
   m_double_prop[this.IndexProp(ORDER_PROP_PRICE_CLOSE)]       = this.OrderClosePrice();
   m_double_prop[this.IndexProp(ORDER_PROP_PROFIT)]            = this.OrderProfit();
   m_double_prop[this.IndexProp(ORDER_PROP_COMMISSION)]        = this.OrderCommission();
   m_double_prop[this.IndexProp(ORDER_PROP_SWAP)]              = this.OrderSwap();
   m_double_prop[this.IndexProp(ORDER_PROP_VOLUME)]            = this.OrderVolume();
   m_double_prop[this.IndexProp(ORDER_PROP_SL)]                = this.OrderStopLoss();
   m_double_prop[this.IndexProp(ORDER_PROP_TP)]                = this.OrderTakeProfit();
   m_double_prop[this.IndexProp(ORDER_PROP_VOLUME_CURRENT)]    = this.OrderVolumeCurrent();
   m_double_prop[this.IndexProp(ORDER_PROP_PRICE_STOP_LIMIT)]  = this.OrderPriceStopLimit();
   
//--- Guardando las propiedades de tipo string
   m_string_prop[this.IndexProp(ORDER_PROP_SYMBOL)]            = this.OrderSymbol();
   m_string_prop[this.IndexProp(ORDER_PROP_COMMENT)]           = this.OrderComment();
   m_string_prop[this.IndexProp(ORDER_PROP_EXT_ID)]            = this.OrderExternalID();
   
//--- Guardando las propiedades adicionales de tipo entero
   m_long_prop[ORDER_PROP_PROFIT_PT]                           = this.ProfitInPoints();
   m_long_prop[ORDER_PROP_TICKET_FROM]                         = this.OrderTicketFrom();
   m_long_prop[ORDER_PROP_TICKET_TO]                           = this.OrderTicketTo();
   m_long_prop[ORDER_PROP_CLOSE_BY_SL]                         = this.OrderCloseByStopLoss();
   m_long_prop[ORDER_PROP_CLOSE_BY_TP]                         = this.OrderCloseByTakeProfit();
   
//--- Guardando las propiedades adicionales de tipo real
   m_double_prop[this.IndexProp(ORDER_PROP_PROFIT_FULL)]       = this.ProfitFull();
  }
//+------------------------------------------------------------------+

Cuando iteremos en el ciclo por las órdenes/transacciones/posiciones históricas o de mercado y elijamos una nueva orden/transacción/posición, se creará un nuevo objeto, heredado de esta clase COrder. En este caso, además, en el constructor del heredero que solo recibe el ticket de la orden, se llamará el constructor de la clase donde se introducirán el estado de la orden y su ticket. A continuación, en el constructor de la clase COrder, simplemente se rellenan las matrices de las propiedades de la orden con la ayuda de los métodos descritos más arriba.

De esta forma, cada nueva orden/transacción/posición tendrá sus propiedades únicas. Y todas ellas se guardarán en las listas; el trabajo con estas será la tarea principal de la biblioteca. Todas estas listas podrán ser clasificadas según cualquiera de las propiedades de las órdenes/transacciones/posiciones; también será posible elegir y componer nuevas listas a partir de las seleccionadas. El usuario podrán obtener información de estas nuevas listas. En definitiva, resultará interesante.

En esta etapa, la funcionalidad principal de la clase de orden abstracta COrder está implementada. Esta clase es la clase básica para guardar distintos tipos de órdenes, transacciones y posiciones; heredaremos de ella a la hora de crear objetos divididos por órdenes, transacciones o posiciones.

Ahora, vamos a pensar que la biblioteca se crea para acceder de forma sencilla a los datos y escribir los programas fácilmente.
En el estado actual, tenemos tres métodos públicos para acceder a las propiedades de la orden abstracta GetProperty(XXX). Claro que podemos usar todas las posibilidades de estos, pero no es algo muy cómodo, ya que debemos recordar los nombres de los miembros de las enumeraciones que describen una propiedad concreta de la orden. Por eso, vamos a añadir varios métodos públicos más para obtener los datos necesarios, dando a estos métodos nombres característicos para que sea inmediatamente comprensible qué propiedad se puede obtener usando este o aquel método.

Estos métodos se pueden utilizar en programas personalizados para leer las propiedades de la orden, transacción o posición:
   //--- Retorna (1) el ticket, (2) el ticket de la orden padre, (3) el ticket de la orden derivada, (4) el número mágico, (5) el motivo de colocación de la orden, (6) el identificador de la posición,
   //--- (7) el identificador de la orden opuesta, (8) la bandera de cierre por StopLoss, (9) la bandera de cierre por TakeProfit (10) la hora de apertura, (11) la hora de cierre,
   //--- (12) la hora de apertura en milisegundos, (13) la hora de cierre en milisegundos (14) la fecha de expiración, (15) el tipo, (16) el estado y (17) el tipo de orden según la dirección
   long              Ticket(void)                                       const { return this.GetProperty(ORDER_PROP_TICKET);                     }
   long              TicketFrom(void)                                   const { return this.GetProperty(ORDER_PROP_TICKET_FROM);                }
   long              TicketTo(void)                                     const { return this.GetProperty(ORDER_PROP_TICKET_TO);                  }
   long              Magic(void)                                        const { return this.GetProperty(ORDER_PROP_MAGIC);                      }
   long              Reason(void)                                       const { return this.GetProperty(ORDER_PROP_REASON);                     }
   long              PositionID(void)                                   const { return this.GetProperty(ORDER_PROP_POSITION_ID);                }
   long              PositionByID(void)                                 const { return this.GetProperty(ORDER_PROP_POSITION_BY_ID);             }
   bool              IsCloseByStopLoss(void)                            const { return (bool)this.GetProperty(ORDER_PROP_CLOSE_BY_SL);          }
   bool              IsCloseByTakeProfit(void)                          const { return (bool)this.GetProperty(ORDER_PROP_CLOSE_BY_TP);          }
   datetime          TimeOpen(void)                                     const { return (datetime)this.GetProperty(ORDER_PROP_TIME_OPEN);        }
   datetime          TimeClose(void)                                    const { return (datetime)this.GetProperty(ORDER_PROP_TIME_CLOSE);       }
   datetime          TimeOpenMSC(void)                                  const { return (datetime)this.GetProperty(ORDER_PROP_TIME_OPEN_MSC);    }
   datetime          TimeCloseMSC(void)                                 const { return (datetime)this.GetProperty(ORDER_PROP_TIME_CLOSE_MSC);   }
   datetime          TimeExpiration(void)                               const { return (datetime)this.GetProperty(ORDER_PROP_TIME_EXP);         }
   ENUM_ORDER_TYPE   TypeOrder(void)                                    const { return (ENUM_ORDER_TYPE)this.GetProperty(ORDER_PROP_TYPE);      }
   ENUM_ORDER_STATUS Status(void)                                       const { return (ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS);  }
   ENUM_ORDER_TYPE   TypeByDirection(void)                              const { return (ENUM_ORDER_TYPE)this.GetProperty(ORDER_PROP_DIRECTION); }
   
   //--- Retorna (1) el precio de apertura, (2) el precio de cierre, (3) el beneficio, (4) la comisión, (5) el swap, (6) el volumen, 
   //--- (7) el volumen no ejecutado, (8) el StopLoss y (9) el TakeProfit y (10) el precio de la orden StopLimit
   double            PriceOpen(void)                                    const { return this.GetProperty(ORDER_PROP_PRICE_OPEN);                 }
   double            PriceClose(void)                                   const { return this.GetProperty(ORDER_PROP_PRICE_CLOSE);                }
   double            Profit(void)                                       const { return this.GetProperty(ORDER_PROP_PROFIT);                     }
   double            Comission(void)                                    const { return this.GetProperty(ORDER_PROP_COMMISSION);                 }
   double            Swap(void)                                         const { return this.GetProperty(ORDER_PROP_SWAP);                       }
   double            Volume(void)                                       const { return this.GetProperty(ORDER_PROP_VOLUME);                     }
   double            VolumeCurrent(void)                                const { return this.GetProperty(ORDER_PROP_VOLUME_CURRENT);             }
   double            StopLoss(void)                                     const { return this.GetProperty(ORDER_PROP_SL);                         }
   double            TakeProfit(void)                                   const { return this.GetProperty(ORDER_PROP_TP);                         }
   double            PriceStopLimit(void)                               const { return this.GetProperty(ORDER_PROP_PRICE_STOP_LIMIT);           }
   
   //--- Retorna (1) el símbolo, (2) el comentario y (3) el identificador bursátil
   string            Symbol(void)                                       const { return this.GetProperty(ORDER_PROP_SYMBOL);                     }
   string            Comment(void)                                      const { return this.GetProperty(ORDER_PROP_COMMENT);                    }
   string            ExternalID(void)                                   const { return this.GetProperty(ORDER_PROP_EXT_ID);                     }

   //--- Retorna el beneficio completo de la orden
   double            ProfitFull(void)                                   const { return this.Profit()+this.Comission()+this.Swap();              }
   //--- Retorna el beneficio de la orden en puntos
   int               ProfitInPoints(void) const;

Implementando el método de obtención de beneficio en puntos:

//+------------------------------------------------------------------+
//| Retorna el beneficio de la orden en puntos                       |
//+------------------------------------------------------------------+
int COrder::ProfitInPoints(void) const
  {
   ENUM_ORDER_TYPE type=this.TypeOrder();
   string symbol=this.Symbol();
   double point=::SymbolInfoDouble(symbol,SYMBOL_POINT);
   if(type>ORDER_TYPE_SELL || point==0) return 0;
   if(this.Status()==ORDER_STATUS_HISTORY_ORDER)
      return int(type==ORDER_TYPE_BUY ? (this.PriceClose()-this.PriceOpen())/point : type==ORDER_TYPE_SELL ? (this.PriceOpen()-this.PriceClose())/point : 0);
   else if(this.Status()==ORDER_STATUS_MARKET_ACTIVE)
     {
      if(type==ORDER_TYPE_BUY)
         return int((::SymbolInfoDouble(symbol,SYMBOL_BID)-this.PriceOpen())/point);
      else if(type==ORDER_TYPE_SELL)
         return int((this.PriceOpen()-::SymbolInfoDouble(symbol,SYMBOL_ASK))/point);
     }
   return 0;
  }
//+------------------------------------------------------------------+

Vamos a añadir los métodos públicos para describir algunas propiedades del objeto de la orden, para que el lector pueda mostrarlas de una forma conveniente bajo demanda:

   //--- Retorna la descripción de una propiedad de tipo (1) entero, (2) real y (3) string de una orden
   string            GetPropertyDescription(ENUM_ORDER_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_ORDER_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_ORDER_PROP_STRING property);
   //--- Retorna el nombre del estado de la orden
   string            StatusDescription(void) const;
   //--- Retorna el nombre de la orden o posición
   string            TypeDescription(void) const;
   //--- Retorna el nombre de la dirección de la transacción
   string            DealEntryDescription(void) const;
   //--- Retorna el tipo según la dirección de la orden/posición
   string            DirectionDescription(void) const;
   //--- Muestra en el diario de registro la descripción de las propiedades de la orden (full_prop=true - todas las propiedades, false - solo aquellas con soporte)
   void              Print(const bool full_prop=false);

Antes de empezar a implementar estos métodos, debemos resolver otro problema: para la biblioteca, así como para los programas basados ​​en ella, vamos a necesitar distintas funciones de servicio. Para este caso, por ejemplo, necesitaremos la función para mostrar el tiempo en milisegundos y la función para recibir mensajes en uno de los dos idiomas. El idioma del mensaje dependerá del idioma del terminal.

Vamos a crear un nuevo archivo de inclusión en la carpeta raíz de la biblioteca

y ponerle el nombre DELib. Este será el archivo de la biblioteca con las funciones de servicio que estarán disponibles para su utilización tanto por parte de las clases de la biblioteca como por parte de los programas basados ​​en la biblioteca.

Después de pulsar el botón "Listo", se creará un archivo de plantilla:

//+------------------------------------------------------------------+
//|                                                        DELib.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"
//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+
// #define MacrosHello   "Hello, world!"
// #define MacrosYear    2010
//+------------------------------------------------------------------+
//| DLL imports                                                      |
//+------------------------------------------------------------------+
// #import "user32.dll"
//   int      SendMessageA(int hWnd,int Msg,int wParam,int lParam);
// #import "my_expert.dll"
//   int      ExpertRecalculate(int wParam,int lParam);
// #import
//+------------------------------------------------------------------+
//| EX5 imports                                                      |
//+------------------------------------------------------------------+
// #import "stdlib.ex5"
//   string ErrorDescription(int error_code);
// #import
//+------------------------------------------------------------------+

Incluimos en él de inmediato nuestro archivo Defines.mqh y cambiamos el aspecto de la plantilla según nuestras necesidades:

//+------------------------------------------------------------------+
//|                                                        DELib.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 strict  // Es necesario para mql4
//+------------------------------------------------------------------+
//| Archivos de inclusión                                            |
//+------------------------------------------------------------------+
#include "Defines.mqh"
//+------------------------------------------------------------------+
//| Funciones de servicio                                            |
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+

Puesto que hemos incluido nuestro archivo Defines.mqh en este archivo, ahora es posible incluir en el archivo de clase COrder no el archivo Defines.mqh, sino este nuevo archivo, para que ambos estén disponibles en la biblioteca; lo haremos en una línea, no en dos.
Sustituimos la directiva include en el archivo conex Order.mqh
:

//+------------------------------------------------------------------+
//|                                                        Order.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"
#include <Object.mqh>
#include "..\DELib.mqh"
//+------------------------------------------------------------------+
//| Clase de orden abstracta                                         |
//+------------------------------------------------------------------+

Vamos también a añadir la definición del idioma del país del usuario como una macrosustitución en el archivo 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"
//+------------------------------------------------------------------+
//| Macrosustituciones                                               |
//+------------------------------------------------------------------+
#define COUNTRY_LANG    "Russian"
//+------------------------------------------------------------------+

De esta forma, el usuario podrá configurar el idioma en el que se mostrarán los mensajes si el idioma del terminal no es el inglés. Bien es cierto que, en este caso, deberemos sustituir todos los futuros mensajes que introduzcamos aquí del idioma ruso a aquel que necesitemos.

Añadimos al archivo DELib.mqh la función para retornar los mensajes en uno de los dos idiomas:

//+------------------------------------------------------------------+
//|                                                        DELib.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 strict  // Es necesario para mql4
//+------------------------------------------------------------------+
//| Archivos de inclusión                                            |
//+------------------------------------------------------------------+
#include "Defines.mqh"
//+------------------------------------------------------------------+
//| Funciones de servicio                                            |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Retorna el texto en uno de los dos idiomas                       |
//+------------------------------------------------------------------+
string TextByLanguage(const string text_country_lang,const string text_en)
  {
   return(TerminalInfoString(TERMINAL_LANGUAGE)==COUNTRY_LANG ? text_country_lang : text_en);
  }
//+------------------------------------------------------------------+

La función comprueba el idioma del terminal, y si coincide con el idioma escrito en la macrosustitución COUNTRY_LANG, se mostrará el texto que se ha tansmitido en el primer parámetro de la función, de lo contrario, se retornará el texto contenido en el segundo parámetro de la función (inglés).

Vamos a añadir también la función para mostrar la hora en milisegundos:

//+------------------------------------------------------------------+
//|                                                        DELib.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 strict  // Es necesario para mql4
//+------------------------------------------------------------------+
//| Archivos de inclusión                                            |
//+------------------------------------------------------------------+
#include "Defines.mqh"
//+------------------------------------------------------------------+
//| Funciones de servicio                                            |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Retorna el texto en uno de los dos idiomas                       |
//+------------------------------------------------------------------+
string TextByLanguage(const string text_country_lang,const string text_en)
  {
   return(TerminalInfoString(TERMINAL_LANGUAGE)==COUNTRY_LANG ? text_country_lang : text_en);
  }
//+------------------------------------------------------------------+
//| Retorna la hora con milisegundos                                 |
//+------------------------------------------------------------------+
string TimeMSCtoString(const long time_msc)
  {
   return TimeToString(time_msc/1000,TIME_DATE|TIME_MINUTES|TIME_SECONDS)+"."+IntegerToString(time_msc%1000,3,'0');
  }
//+------------------------------------------------------------------+

Aquí todo es muy sencillo: se transmite a la función la hora establecida en milisegundos. Para calcular la hora en segundos, debemos dividir el valor transmitido a la función por 1000, y para calcular los milisegundos, debemos tomar el resto de esta división. Todos los valores obtenidos se formatean en forma de línea y se retornan al programa que realizó la llamada.

A veces, será necesario obtener el valor del número de dígitos decimales en el lote del símbolo. Vamos a insertar esta función en nuestro archivo de funciones de servicio:

//+------------------------------------------------------------------+
//| Retorna el número de dígitos decimales en el lote del símbolo    |
//+------------------------------------------------------------------+
uint DigitsLots(const string symbol_name) 
  { 
   return (int)ceil(fabs(log(SymbolInfoDouble(symbol_name,SYMBOL_VOLUME_STEP))/log(10)));
  }
//+------------------------------------------------------------------+

Aparte de las funciones de servicio generales, para mostrar los mensajes en el registro, vamos a necesitar tres métodos que se encargan de retornar la descripción del motivo para colocar la orden, la dirección y el tipo de transacción. Añadimos los tres métodos a la sección protegida de la clase COrder:

   //--- Retorna la descripción del (1) motivo, (2) la dirección y (3) el tipo de transacción.
   string            GetReasonDescription(const long reason)            const;
   string            GetEntryDescription(const long deal_entry)         const;
   string            GetTypeDealDescription(const long type_deal)       const;

Así como la implementación de los mismos:

//+------------------------------------------------------------------+
//| 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-программы mql4","Placed from mql4 program")   :
      TextByLanguage("Свойство не поддерживается в MQL4","Property is not supported in MQL4")
     );
#else 
   string res="";
   switch(this.Status())
     {
      case ORDER_STATUS_MARKET_ACTIVE        : 
         res=
           (
            reason==POSITION_REASON_CLIENT   ?  TextByLanguage("Позиция открыта из десктопного терминала","Position is open from the desktop terminal") :
            reason==POSITION_REASON_MOBILE   ?  TextByLanguage("Позиция открыта из мобильного приложения","Position is open from the mobile app") :
            reason==POSITION_REASON_WEB      ?  TextByLanguage("Позиция открыта из веб-платформы","Position is open from the web platform") :
            reason==POSITION_REASON_EXPERT   ?  TextByLanguage("Позиция открыта из советника или скрипта","The position is open from the EA or script") : ""
           );
         break;
      case ORDER_STATUS_MARKET_PENDING       :
      case ORDER_STATUS_HISTORY_PENDING      : 
      case ORDER_STATUS_HISTORY_ORDER        : 
         res=
           (
            reason==ORDER_REASON_CLIENT      ?  TextByLanguage("Ордер выставлен из десктопного терминала","Order is set from the desktop terminal") :
            reason==ORDER_REASON_MOBILE      ?  TextByLanguage("Ордер выставлен из мобильного приложения","Order is set from the mobile app") :
            reason==ORDER_REASON_WEB         ?  TextByLanguage("Ордер выставлен из веб-платформы","Oder is set from the web platform") :
            reason==ORDER_REASON_EXPERT      ?  TextByLanguage("Ордер выставлен советником или скриптом","Order is set from the EA or script") :
            reason==ORDER_REASON_SL          ?  TextByLanguage("Срабатывание StopLoss","Due to StopLoss") :
            reason==ORDER_REASON_TP          ?  TextByLanguage("Срабатывание TakeProfit","Due to TakeProfit") :
            reason==ORDER_REASON_SO          ?  TextByLanguage("Ордер выставлен в результате наступления Stop Out","Due to Stop Out") : ""
           );
         break;
      case ORDER_STATUS_DEAL                 : 
         res=
           (
            reason==DEAL_REASON_CLIENT       ?  TextByLanguage("Сделка проведена из десктопного терминала","Deal was carried out from the desktop terminal") :
            reason==DEAL_REASON_MOBILE       ?  TextByLanguage("Сделка проведена из мобильного приложения","Deal was carried out from the mobile app") :
            reason==DEAL_REASON_WEB          ?  TextByLanguage("Сделка проведена из веб-платформы","Deal was carried out from the web platform") :
            reason==DEAL_REASON_EXPERT       ?  TextByLanguage("Сделка проведена из советника или скрипта","Deal was carried out from the EA or script") :
            reason==DEAL_REASON_SL           ?  TextByLanguage("Срабатывание StopLoss","Due to StopLoss") :
            reason==DEAL_REASON_TP           ?  TextByLanguage("Срабатывание TakeProfit","Due to TakeProfit") :
            reason==DEAL_REASON_SO           ?  TextByLanguage("Сделка проведена в результате наступления Stop Out","Due to Stop Out") :
            reason==DEAL_REASON_ROLLOVER     ?  TextByLanguage("Сделка проведена по причине переноса позиции","Due to position rollover") :
            reason==DEAL_REASON_VMARGIN      ?  TextByLanguage("Сделка проведена по причине начисления/списания вариационной маржи","Due to variation margin") :
            reason==DEAL_REASON_SPLIT        ?  TextByLanguage("Сделка проведена по причине сплита (понижения цены) инструмента","Due to the split") : ""
           );
         break;
      default                                : res="";   break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

Se comprueba la pertenencia a MQL4 o MQL5 y, de acuerdo con el estado de la orden, se comprueba el motivo de la colocación de la orden transmitido en los parámetros de entrada; luego se retorna su descripción.

//+------------------------------------------------------------------+
//| Descripción de la dirección de la transacción                    |
//+------------------------------------------------------------------+
string COrder::GetEntryDescription(const long deal_entry) const
  {
#ifdef __MQL4__
   return TextByLanguage("Свойство не поддерживается в MQL4","Property is not supported in MQL4");
#else 
   string res="";
   switch(this.Status())
     {
      case ORDER_STATUS_MARKET_ACTIVE     : 
         res=TextByLanguage("Свойство не поддерживается у позиции","Property not supported for position"); 
         break;
      case ORDER_STATUS_MARKET_PENDING    :
      case ORDER_STATUS_HISTORY_PENDING   : 
         res=TextByLanguage("Свойство не поддерживается у отложенного ордера","The property is not supported for a pending order"); 
         break;
      case ORDER_STATUS_HISTORY_ORDER     : 
         res=TextByLanguage("Свойство не поддерживается у исторического ордера","The property is not supported for a history order"); 
         break;
      case ORDER_STATUS_DEAL              : 
         res=
           (
            deal_entry==DEAL_ENTRY_IN     ?  TextByLanguage("Вход в рынок","Entry to the market") :
            deal_entry==DEAL_ENTRY_OUT    ?  TextByLanguage("Выход из рынка","Out from the market") :
            deal_entry==DEAL_ENTRY_INOUT  ?  TextByLanguage("Разворот","Turning in the opposite direction") :
            deal_entry==DEAL_ENTRY_OUT_BY ?  TextByLanguage("Закрытие встречной позицией","Closing the opposite position") : ""
           );
         break;
      default                             : res=""; break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

Se comprueba la pertenencia a MQL4 o MQL5 y, de acuerdo con el estado de la orden, se comprueba la dirección de la transacción transmitida en los parámetros de entrada; luego se retorna su descripción.

//+------------------------------------------------------------------+
//| Retorna el nombre del tipo de transacción                        |
//+------------------------------------------------------------------+
string COrder::GetTypeDealDescription(const long deal_type) const
  {
#ifdef __MQL4__
   return TextByLanguage("Свойство не поддерживается в MQL4","Property is not supported in MQL4");
#else 
   string res="";
   switch(this.Status())
     {
      case ORDER_STATUS_MARKET_ACTIVE     : 
         res=TextByLanguage("Свойство не поддерживается у позиции","Property not supported for position"); 
         break;
      case ORDER_STATUS_MARKET_PENDING    :
      case ORDER_STATUS_HISTORY_PENDING   : 
         res=TextByLanguage("Свойство не поддерживается у отложенного ордера","The property is not supported for a pending order"); 
         break;
      case ORDER_STATUS_HISTORY_ORDER     : 
         res=TextByLanguage("Свойство не поддерживается у исторического ордера","The property is not supported for a history order"); 
         break;
      case ORDER_STATUS_DEAL              : 
         res=
           (
            deal_type==DEAL_TYPE_BUY                      ?  TextByLanguage("Сделка на покупку","The deal to buy") :
            deal_type==DEAL_TYPE_SELL                     ?  TextByLanguage("Сделка на продажу","The deal to sell") :
            deal_type==DEAL_TYPE_BALANCE                  ?  TextByLanguage("Начисление баланса","Balance accrual") :
            deal_type==DEAL_TYPE_CREDIT                   ?  TextByLanguage("Начисление кредита","Credit accrual") :
            deal_type==DEAL_TYPE_CHARGE                   ?  TextByLanguage("Дополнительные сборы","Extra charges") :
            deal_type==DEAL_TYPE_CORRECTION               ?  TextByLanguage("Корректирующая запись","Corrective entry") :
            deal_type==DEAL_TYPE_BONUS                    ?  TextByLanguage("Перечисление бонусов","Bonuses") :
            deal_type==DEAL_TYPE_COMMISSION               ?  TextByLanguage("Дополнительные комиссии","Additional comissions") :
            deal_type==DEAL_TYPE_COMMISSION_DAILY         ?  TextByLanguage("Комиссия, начисляемая в конце торгового дня","Commission, accrued at the end of the trading day") :
            deal_type==DEAL_TYPE_COMMISSION_MONTHLY       ?  TextByLanguage("Комиссия, начисляемая в конце месяца","Commission, accrued at the end of the month") :
            deal_type==DEAL_TYPE_COMMISSION_AGENT_DAILY   ?  TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Agency commission charged at the end of the trading day") :
            deal_type==DEAL_TYPE_COMMISSION_AGENT_MONTHLY ?  TextByLanguage("Агентская комиссия, начисляемая в конце месяца","") :
            deal_type==DEAL_TYPE_INTEREST                 ?  TextByLanguage("Начисления процентов на свободные средства","Agency commission charged at the end of the month") :
            deal_type==DEAL_TYPE_BUY_CANCELED             ?  TextByLanguage("Отмененная сделка покупки","Canceled buy transaction") :
            deal_type==DEAL_TYPE_SELL_CANCELED            ?  TextByLanguage("Отмененная сделка продажи","Canceled sell transaction") :
            deal_type==DEAL_DIVIDEND                      ?  TextByLanguage("Начисление дивиденда","Accrued dividends") :
            deal_type==DEAL_DIVIDEND_FRANKED              ?  TextByLanguage("Начисление франкированного дивиденда","Accrual of franked dividend") :
            deal_type==DEAL_TAX                           ?  TextByLanguage("Начисление налога","Tax accrual") : ""
           );
         break;
      default                             : res=""; break;
     }
   return res;
#endif 
  }

Se comprueba la pertenencia a MQL4 o MQL5 y, de acuerdo con el estado de la orden, se comprueba el tipo de la transacción transmitido en los parámetros de entrada; luego se retorna su descripción.

Implementando los métodos para describir las propiedades de la orden:

//+------------------------------------------------------------------+
//| Retorna la descripción de la propiedad de tipo entero de la      |
//| orden                                                            |
//+------------------------------------------------------------------+
string COrder::GetPropertyDescription(ENUM_ORDER_PROP_INTEGER property)
  {
   return
     (
   //--- Propiedades generales
      property==ORDER_PROP_MAGIC             ?  TextByLanguage("Магик","Magic")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TICKET            ?  TextByLanguage("Тикет","Ticket")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          " #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TICKET_FROM       ?  TextByLanguage("Тикет родительского ордера","Ticket of parent order")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          " #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TICKET_TO         ?  TextByLanguage("Тикет наследуемого ордера","Inherited order ticket")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          " #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TIME_OPEN         ?  TextByLanguage("Время открытия","Time open")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS)
         )  :
      property==ORDER_PROP_TIME_CLOSE        ?  TextByLanguage("Время закрытия","Time close")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS)
         )  :
      property==ORDER_PROP_TIME_EXP          ?  TextByLanguage("Дата экспирации","Date of expiration")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          (this.GetProperty(property)==0     ?  TextByLanguage(": Не задана",": Not set") :
          ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS))
         )  :
      property==ORDER_PROP_TYPE              ?  TextByLanguage("Тип","Type")+": "+this.TypeDescription()                   :
      property==ORDER_PROP_DIRECTION         ?  TextByLanguage("Тип по направлению","Type by direction")+": "+this.DirectionDescription() :
      
      property==ORDER_PROP_REASON            ?  TextByLanguage("Причина","Reason")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+this.GetReasonDescription(this.GetProperty(property))
         )  :
      property==ORDER_PROP_POSITION_ID       ?  TextByLanguage("Идентификатор позиции","Position identifier")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_DEAL_ORDER        ?  TextByLanguage("Сделка на основании ордера","Deal by order")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_DEAL_ENTRY        ?  TextByLanguage("Направление сделки","Deal entry")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+this.GetEntryDescription(this.GetProperty(property))
         )  :
      property==ORDER_PROP_POSITION_BY_ID    ?  TextByLanguage("Идентификатор встречной позиции","Identifier opposite position")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TIME_OPEN_MSC     ?  TextByLanguage("Время открытия в милисекундах","Opening time in milliseconds")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+(string)this.GetProperty(property)+" > "+TimeMSCtoString(this.GetProperty(property))
         )  :
      property==ORDER_PROP_TIME_CLOSE_MSC    ?  TextByLanguage("Время закрытия в милисекундах","Closing time in milliseconds")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+(string)this.GetProperty(property)+" > "+TimeMSCtoString(this.GetProperty(property))
         )  :
      property==ORDER_PROP_TIME_UPDATE       ?  TextByLanguage("Время изменения позиции","Position change time")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+(this.GetProperty(property)!=0 ? ::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS) : "0")
         )  :
      property==ORDER_PROP_TIME_UPDATE_MSC   ?  TextByLanguage("Время изменения позиции в милисекундах","Time to change the position in milliseconds")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+(this.GetProperty(property)!=0 ? (string)this.GetProperty(property)+" > "+TimeMSCtoString(this.GetProperty(property)) : "0")
         )  :
   //--- Propiedad adicional
      property==ORDER_PROP_STATUS            ?  TextByLanguage("Статус","Status")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": \""+this.StatusDescription()+"\""
         )  :
      property==ORDER_PROP_PROFIT_PT         ?  TextByLanguage("Прибыль в пунктах","Profit in points")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_CLOSE_BY_SL       ?  TextByLanguage("Закрытие по StopLoss","Close by StopLoss")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+(this.GetProperty(property) ? TextByLanguage("Да","Yes") : TextByLanguage("Нет","No"))
         )  :
      property==ORDER_PROP_CLOSE_BY_TP       ?  TextByLanguage("Закрытие по TakeProfit","Close by TakeProfit")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+(this.GetProperty(property) ? TextByLanguage("Да","Yes") : TextByLanguage("Нет","No"))
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Retorna la descripción de la propiedad de tipo real de la orden  |
//+------------------------------------------------------------------+
string COrder::GetPropertyDescription(ENUM_ORDER_PROP_DOUBLE property)
  {
   int dg=(int)::SymbolInfoInteger(this.GetProperty(ORDER_PROP_SYMBOL),SYMBOL_DIGITS);
   int dgl=(int)DigitsLots(this.GetProperty(ORDER_PROP_SYMBOL));
   return
     (
      //--- Propiedades generales
      property==ORDER_PROP_PRICE_CLOSE       ?  TextByLanguage("Цена закрытия","Price close")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      property==ORDER_PROP_PRICE_OPEN        ?  TextByLanguage("Цена открытия","Price open")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      property==ORDER_PROP_SL                ?  TextByLanguage("Цена StopLoss","Price StopLoss")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          (this.GetProperty(property)==0     ?  TextByLanguage(": Отсутствует",": Not set"):": "+::DoubleToString(this.GetProperty(property),dg))
         )  :
      property==ORDER_PROP_TP                ?  TextByLanguage("Цена TakeProfit","Price TakeProfit")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          (this.GetProperty(property)==0     ?  TextByLanguage(": Отсутствует",": Not set"):": "+::DoubleToString(this.GetProperty(property),dg))
         )  :
      property==ORDER_PROP_PROFIT            ?  TextByLanguage("Прибыль","Profit")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+::DoubleToString(this.GetProperty(property),2)
         )  :
      property==ORDER_PROP_COMMISSION        ?  TextByLanguage("Комиссия","Comission")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+::DoubleToString(this.GetProperty(property),2)
         )  :
      property==ORDER_PROP_SWAP              ?  TextByLanguage("Своп","Swap")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+::DoubleToString(this.GetProperty(property),2)
         )  :
      property==ORDER_PROP_VOLUME            ?  TextByLanguage("Объём","Volume")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+::DoubleToString(this.GetProperty(property),dgl)
          ) :
      property==ORDER_PROP_VOLUME_CURRENT    ?  TextByLanguage("Невыполненный объём","Unfulfilled volume")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+::DoubleToString(this.GetProperty(property),dgl)
          ) :
      property==ORDER_PROP_PRICE_STOP_LIMIT  ? 
         TextByLanguage("Цена постановки Limit ордера при активации StopLimit ордера","The price of placing Limit order when StopLimit order are activated")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+::DoubleToString(this.GetProperty(property),dg)
          ) :
      //--- Propiedad adicional
      property==ORDER_PROP_PROFIT_FULL       ?  TextByLanguage("Прибыль+комиссия+своп","Profit+Comission+Swap")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+::DoubleToString(this.GetProperty(property),2)
          ) :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Retorna la descripción de la propiedad de tipo string de la      |
//| orden                                                            |
//+------------------------------------------------------------------+
string COrder::GetPropertyDescription(ENUM_ORDER_PROP_STRING property)
  {
   return
     (
      property==ORDER_PROP_SYMBOL         ?  TextByLanguage("Символ","Symbol")+": \""+this.GetProperty(property)+"\""            :
      property==ORDER_PROP_COMMENT        ?  TextByLanguage("Комментарий","Comment")+
         (this.GetProperty(property)==""  ?  TextByLanguage(": Отсутствует",": Not set"):": \""+this.GetProperty(property)+"\"") :
      property==ORDER_PROP_EXT_ID         ?  TextByLanguage("Идентификатор на бирже","Exchange identifier")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
         (this.GetProperty(property)==""  ?  TextByLanguage(": Отсутствует",": Not set"):": \""+this.GetProperty(property)+"\"")):
      ""
     );
  }
//+------------------------------------------------------------------+

Implementación de métodos para retornar el nombre del estado de la orden, nombre de la orden o posición, nombre de la dirección de la transacción, descripción del tipo según la dirección de la orden/posición y el método de muestra conveniente de la descripción de las propiedades de la orden en el diario de registro.

Primero, añadimos al archivo Defines.mqh otra macrosustitución para que resulte más cómodo mostrar el nombre de la función/método en el diario de registro:

//+------------------------------------------------------------------+
//| Macrosustituciones                                               |
//+------------------------------------------------------------------+
#define COUNTRY_LANG    "Russian"            // Idioma del país
#define DFUN           (__FUNCTION__+": ")   // "Descripción de la función"
//+------------------------------------------------------------------+

Ahora, en lugar de escribir todas las líneas que están inscritas en la macro, podemos simplemente escribir DFUN, y el compilador sustituirá la aparición de esta definición por las líneas escritas en la macro.

//+------------------------------------------------------------------+
//| Retorna el nombre del estado de la orden                         |
//+------------------------------------------------------------------+
string COrder::StatusDescription(void) const
  {
   ENUM_ORDER_STATUS status=this.Status();
   ENUM_ORDER_TYPE   type=(ENUM_ORDER_TYPE)this.TypeOrder();
   return
     (
      status==ORDER_STATUS_BALANCE        ?  TextByLanguage("Балансная операция","Balance operation") :
      status==ORDER_STATUS_CREDIT         ?  TextByLanguage("Кредитная операция","Credits operation") :
      status==ORDER_STATUS_HISTORY_ORDER  || status==ORDER_STATUS_HISTORY_PENDING                     ?  
      (
       type>ORDER_TYPE_SELL ? TextByLanguage("Отложенный ордер","Pending order")                      :
       TextByLanguage("Ордер на ","The order to ")+(type==ORDER_TYPE_BUY ? TextByLanguage("покупку","buy") : TextByLanguage("продажу","sell"))
      )                                                                                               :
      status==ORDER_STATUS_DEAL           ?  TextByLanguage("Сделка","Deal")                          :
      status==ORDER_STATUS_MARKET_ACTIVE  ?  TextByLanguage("Позиция","Active position")              :
      status==ORDER_STATUS_MARKET_PENDING ?  TextByLanguage("Установленный отложенный ордер","Active pending order") :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Retorna el nombre de la orden o posición                         |
//+------------------------------------------------------------------+
string COrder::TypeDescription(void) const
  {
   if(this.Status()==ORDER_STATUS_DEAL)
      return this.GetTypeDealDescription(this.TypeOrder());
   else switch(this.TypeOrder())
     {
      case ORDER_TYPE_BUY              :  return "Buy";
      case ORDER_TYPE_BUY_LIMIT        :  return "Buy Limit";
      case ORDER_TYPE_BUY_STOP         :  return "Buy Stop";
      case ORDER_TYPE_SELL             :  return "Sell";
      case ORDER_TYPE_SELL_LIMIT       :  return "Sell Limit";
      case ORDER_TYPE_SELL_STOP        :  return "Sell Stop";
      #ifdef __MQL4__
      case ORDER_TYPE_BALANCE          :  return TextByLanguage("Балансовая операция","Balance operation");
      case ORDER_TYPE_CREDIT           :  return TextByLanguage("Кредитная операция","Credit operation");
      #else 
      case ORDER_TYPE_BUY_STOP_LIMIT   :  return "Buy Stop Limit";
      case ORDER_TYPE_SELL_STOP_LIMIT  :  return "Sell Stop Limit";
      #endif 
      default                          :  return TextByLanguage("Неизвестный тип","Unknown type");
     }
  }
//+------------------------------------------------------------------+
//| Retorna el nombre de la dirección de la transacción              |
//+------------------------------------------------------------------+
string COrder::DealEntryDescription(void) const
  {
   return(this.Status()==ORDER_STATUS_DEAL ? this.GetEntryDescription(this.GetProperty(ORDER_PROP_DEAL_ENTRY)) : "");
  }
//+------------------------------------------------------------------+
//| Retorna el tipo según la dirección de la orden/posición          |
//+------------------------------------------------------------------+
string COrder::DirectionDescription(void) const
  {
   if(this.Status()==ORDER_STATUS_DEAL)
      return this.TypeDescription();
   switch(this.TypeByDirection())
     {
      case ORDER_TYPE_BUY  :  return "Buy";
      case ORDER_TYPE_SELL :  return "Sell";
      default              :  return TextByLanguage("Неизвестный тип","Unknown type");
     }
  }
//+------------------------------------------------------------------+
//| Muestra las propiedades de la orden en el diario de registro     |
//+------------------------------------------------------------------+
void COrder::Print(const bool full_prop=false)
  {
   ::Print("============= ",TextByLanguage("Начало списка параметров: \"","The beginning of the parameter list: \""),this.StatusDescription(),"\" =============");
   int beg=0, end=ORDER_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_ORDER_PROP_INTEGER prop=(ENUM_ORDER_PROP_INTEGER)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=ORDER_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_ORDER_PROP_DOUBLE prop=(ENUM_ORDER_PROP_DOUBLE)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=ORDER_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_ORDER_PROP_STRING prop=(ENUM_ORDER_PROP_STRING)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("================== ",TextByLanguage("Конец списка параметров: \"","End of the parameter list: \""),this.StatusDescription(),"\" ==================\n");
  }
//+------------------------------------------------------------------+

Ahora que ya hemos implementado todos los métodos de clase de orden abstracta COrder, podemos intentar ver qué hemos sacado de todo ello en esta etapa.

Ponemos a prueba la orden abstracta COrder

Vamos a realizar una prueba y analizar cómo se crean y rellenan con datos los objetos básicos COrder. A continuación, haremos sobre su base objetos por tipos, que guardaremos posteriormente en colecciones.

Vamos a crear en el catálogo Experts, o en su subcarpeta, una carpeta que llamaremos TestDoEasy; dentro de la carpeta recién creada, crearemos una más: Part01. En ella se ubicará el archivo del asesor de prueba.

Clicamos en el navegador del editor con el botón derecho sobre la carpeta Part01, y elegimos del menú desplegable "Nuevo archivo", o bien pulsamos Ctrl+N en la carpeta Part01 destacada. Se abrirá la ventana del Wizard MQL, donde estará directamente seleccionado el punto "Asesor (plantilla)"; pulsamos el botón "Continuar" y, en la siguiente página, en el campo de edición, completamos la ruta al archivo ya escrita con el nombre del nuevo archivo "TestDoEasyPart01". A continuación, pulsamos "Continuar" hasta que lleguemos hasta el final del funcionamiento del wizard. Como no necesitamos ningún procesamiento de eventos adicional para la prueba, podemos dejar vacíos todos los campos con casillas de verificación. Pulsamos el botón "Listo", con lo que se creará la plantilla del nuevo experto:
//+------------------------------------------------------------------+
//|                                             TestDoEasyPart01.mq5 |
//|                        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"
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
  }
//+------------------------------------------------------------------+

No vamos a darle muchas vueltas al tema: tenemos una prueba sencillísima, así que solo tenemos que incluir la clase de nuestra orden abstracta COrder y la clase CArrayObj de la biblioteca estándar y crear la lista de objetos:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart01.mq5 |
//|                        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\Objects\Order.mqh>
#include <Arrays\ArrayObj.mqh>
//--- global variables
CArrayObj      list_all_orders;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+

A continuación, definimos en OnInit() para la lista de objetos la bandera de lista ordenada y la rellenamos en dos ciclos con todas las transacciones y órdenes históricas.

Acto seguido, y de nuevo en el ciclo, mostramos en el diario de registro del terminal "Expertos" los datos de cada uno de los objetos que se encuentran en la lista rellenada:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   list_all_orders.Sort();
   list_all_orders.Clear();
   if(!HistorySelect(0,TimeCurrent()))
     {
      Print(DFUN,TextByLanguage(": Не удалось получить историю сделок и ордеров",": Failed to get the history of deals and orders"));
      return INIT_FAILED;
     }
//--- Transacciones
   int total_deals=HistoryDealsTotal();
   for(int i=0; i<total_deals; i++)
     {
      ulong deal_ticket=::HistoryDealGetTicket(i);
      if(deal_ticket==0) continue;
      COrder *deal=new COrder(ORDER_STATUS_DEAL,deal_ticket);
      if(deal==NULL) continue;
      list_all_orders.InsertSort(deal);
     }
//--- Órdenes
   int total_orders=HistoryOrdersTotal();
   for(int i=0; i<total_orders; i++)
     {
      ulong order_ticket=::HistoryOrderGetTicket(i);
      if(order_ticket==0) continue;
      ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)::HistoryOrderGetInteger(order_ticket,ORDER_TYPE);
      if(type==ORDER_TYPE_BUY || type==ORDER_TYPE_SELL)
        {
         COrder *order=new COrder(ORDER_STATUS_HISTORY_ORDER,order_ticket);
         if(order==NULL) continue;
         list_all_orders.InsertSort(order);
        }
      else
        {
         COrder *order=new COrder(ORDER_STATUS_HISTORY_PENDING,order_ticket);
         if(order==NULL) continue;
         list_all_orders.InsertSort(order);
        }
     }
//--- Mostrando en el diario de registro
   int total=list_all_orders.Total();
   for(int i=0;i<total;i++)
     {
      COrder *order=list_all_orders.At(i);
      if(order==NULL)
         continue;
      order.Print();
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Si tratamos de compilar el asesor ahora, obtendremos tres errores:


Los errores nos indican que no es posible recurrir a los métodos protegidos de la clase desde el exterior. Y es que hemos hecho el constructor COrder precisamente protegido, en la sección protected de la clase. Después, cuando hagamos objetos por tipos basándonoes en esta clase, todo saldrá normal, sus constructores estarán abiertos. Pero, ahora que estamos haciendo la prueba, vamos a pasar este constructor a la sección pública de la clase COrder:


Ahora todo se compila sin errores, y en el diario de registro del terminal se muestran los datos de todas las órdenes y transacciones de la historia de la cuenta comercial.


Se muestran absolutamente todas las propiedades de cada orden/transacción, incluso de aquellas que no tienen soporte.


El asunto es que hemos desarrollado los métodos que retornan las banderas de soporte de propiedades concretas por esa orden para que sean virtuales, y que sean redefinidos en las clases derivadas. Ya a partir de ellos, se muestran los datos en el diario de registro. En ese caso, todo deberá mostrarse correctamente, si existe una propiedad no soportada por la orden, dicha propiedad no se mostrará en el diario de registro, puesto que, por defecto, el método Print(const bool full_prop=false) de la clase COrder dispone de una bandera para desactivar la muestra de propiedades no soportadas en el diario de registro, mientras que los métodos virtuales SupportProperty() de esta clase simplemente retornan true para cualquier propiedad.

¿Qué es lo próximo?

Ya hemos finalizado la primera parte, bastante voluminosa, por cierto. Hemos creado un objeto básico para coleccionar órdenes y transacciones históricas, así como órdenes y posiciones de mercado. Como ya dijimos, este objeto carece de valor por el momento, pero estamos solo dando los primeros pasos: se trata de un objeto básico que usaremos posteriormente para construir un sistema de almacenamiento y muestra de información en el sistema de órdenes. A continuación, utilizando los mismos principios, construiremos los objetos y colecciones que necesitemos. Asimismo, automatizaremos la recopilación de los datos que necesitamos de forma constante.


En la próxima parte, utilizaremos esta orden abstracta para crear varias clases que describan tipos concretos de órdenes, transacciones y posiciones. Crearemos la primera colección (una colección de órdenes y transacciones históricas), además de la base y el objeto principal de la biblioteca — Engine. En etapas posteriores de la descripción de la creación de esta biblioteca, aprenderemos a librarnos de los errores surgidos al intentar coompilar la biblioteca en MQL4.

Más abajo, adjuntamos un directorio con todos los archivos analizados en esta primera parte de la serie. El lector podrá descargarlos y estudiarlos al daetalle. Si tiene preguntas, observaciones o sugerencias, podrá concretarlas en los comentarios.

Conviene destacar que la biblioteca se está creando de forma paralela a la escritura de los artículos, por eso, de un artículo a otro, se introducen en los métodos y variables de la misma las correcciones necesarias. En relación con esto, podrían observarse ciertas discordancias poco significativas en el contenido de los archivos y textos de la descripción en el artículo. Por ejemplo, el lector podrá encontrar correcciones en los comentarios, recolocaciones en la ubicación de los miembros de las enumeraciones, etc. Esto no influye en el desempeño de los ejemplos adjuntos.

Volver al contenido.