Funciones para leer las propiedades de órdenes activas

Los conjuntos de funciones que pueden utilizarse para obtener los valores de todas las propiedades de las órdenes difieren para las órdenes activas y las históricas. En esta sección se describen las funciones para leer las propiedades de las órdenes activas. Para conocer las funciones que permiten acceder a las propiedades de las órdenes en el historial, consulte la sección correspondiente.

Las propiedades de los enteros pueden leerse utilizando la función OrderGetInteger, que tiene dos formas: la primera devuelve directamente el valor de la propiedad; la segunda devuelve un signo lógico de éxito (true) o error (false), y el segundo parámetro pasado por referencia se rellena con el valor de la propiedad.

long OrderGetInteger(ENUM_ORDER_PROPERTY_INTEGER property)

bool OrderGetInteger(ENUM_ORDER_PROPERTY_INTEGER property, long &value)

Ambas funciones permiten obtener la propiedad de orden solicitada de un tipo compatible con enteros (datetime, long/ulong o listado). Aunque el prototipo menciona long, desde un punto de vista técnico, el valor se almacena como una celda de 8 bytes, que puede convertirse a tipos compatibles sin ninguna conversión de la representación interna, en concreto, a ulong, que se utiliza para todos los tickets.

Un par de funciones similares están pensadas para propiedades de tipo real double.

double OrderGetDouble(ENUM_ORDER_PROPERTY_DOUBLE property)

bool OrderGetDouble(ENUM_ORDER_PROPERTY_DOUBLE property, double &value)

Por último, las propiedades de cadena están disponibles a través de un par de funciones OrderGetString.

string OrderGetString(ENUM_ORDER_PROPERTY_STRING property)

bool OrderGetString(ENUM_ORDER_PROPERTY_STRING property, string &value)

Como primer parámetro, todas las funciones toman el identificador de la propiedad que nos interesa. Debe ser un elemento de una de las enumeraciones (ENUM_ORDER_PROPERTY_INTEGER, ENUM_ORDER_PROPERTY_DOUBLE o ENUM_ORDER_PROPERTY_STRING) que se describen en la sección anterior.

Tenga en cuenta que antes de llamar a cualquiera de las funciones anteriores, debe seleccionar primero una orden utilizando OrderSelect o OrderGetTicket.

Para leer todas las propiedades de una orden específica, desarrollaremos la clase OrderMonitor (OrderMonitor.mqh) que funciona según el mismo principio que los monitores de símbolo (SymbolMonitor.mqh) y cuenta de trading (AccountMonitor.mqh) considerados anteriormente.

Estas y otras clases de monitores que se abordan en el libro ofrecen una forma unificada de analizar propiedades mediante versiones sobrecargadas de métodos get virtuales.

Mirando un poco más adelante, digamos que las transacciones y las posiciones tienen la misma agrupación de propiedades según los tres tipos principales de valores, y también necesitamos implementar monitores para ellos. A este respecto, tiene sentido separar el algoritmo general en una clase abstracta base MonitorInterface (TradeBaseMonitor.mqh). Esta es una clase de plantilla con tres parámetros destinados a especificar los tipos de enumeraciones concretas, para los grupos de propiedades entero (I), real (D) y cadena (S).

#include <MQL5Book/EnumToArray.mqh>
   
template<typename I,typename D,typename S>
class MonitorInterface
{
protected:
   bool ready;
public:
   MonitorInterface(): ready(false) { }
   
   bool isReady() const
   {
      return ready;
   }
   ...

Debido al hecho de que encontrar una orden (transacción o posición) en el entorno de trading puede fallar por diversas razones, la clase tiene una variable reservada ready en la que las clases derivadas tendrán que escribir una señal de inicialización de éxito, es decir, la elección de un objeto para leer sus propiedades.

Varios métodos puramente virtuales declaran el acceso a propiedades de los tipos correspondientes.

   virtual long get(const I propertyconst = 0;
   virtual double get(const D propertyconst = 0;
   virtual string get(const S propertyconst = 0;
   virtual long get(const int propertyconst longconst = 0;
   virtual double get(const int propertyconst doubleconst = 0;
   virtual string get(const int propertyconst stringconst = 0;
   ...

En los tres primeros métodos, el tipo de propiedad se especifica mediante uno de los parámetros de la plantilla. En otros tres métodos, el tipo se especifica mediante el segundo parámetro del propio método: esto es necesario porque los últimos métodos no toman las constantes de una enumeración concreta, sino simplemente un número entero como primer parámetro. Por un lado, esto es conveniente para la numeración continua de identificadores (las constantes de enumeración de los tres tipos no se cruzan). Por otra parte, necesitamos otra fuente para determinar el tipo de valor, ya que el tipo devuelto por la función/método no participa en el proceso de elección de la sobrecarga pertinente.

Este enfoque le permite obtener propiedades basadas en varias entradas disponibles en el código de llamada. A continuación, crearemos clases basadas en OrderMonitor (así como en las futuras DealMonitor y PositionMonitor) para seleccionar objetos según un conjunto de condiciones arbitrarias, y allí todos estos métodos estarán en demanda.

Muy a menudo, los programas necesitan obtener una representación de cadena de cualquier propiedad; por ejemplo, para el registro. En los nuevos monitores, esto se implementa mediante los métodos stringify. Obviamente, obtienen los valores de las propiedades solicitadas a través de las llamadas a los métodos get mencionados anteriormente.

   virtual string stringify(const long vconst I propertyconst = 0;
   
   virtual string stringify(const I propertyconst
   {
      return stringify(get(property), property);
   }
   
   virtual string stringify(const D propertyconst string format = NULLconst
   {
      if(format == NULLreturn (string)get(property);
      return StringFormat(formatget(property));
   }
   
   virtual string stringify(const S propertyconst
   {
      return get(property);
   }
   ...

El único método que no ha recibido implementación es la primera versión de stringify para el tipo long. Esto se debe al hecho de que el grupo de propiedades de enteros, como vimos en la sección anterior, en realidad contienen diferentes tipos de aplicaciones, incluyendo fecha y hora, enumeraciones y enteros. Por lo tanto, sólo las clases derivadas pueden proporcionar su conversión a cadenas comprensibles. Esta situación es común a todas las entidades de trading, no sólo a las órdenes, sino también a las transacciones y posiciones, cuyas propiedades estudiaremos más adelante.

Cuando una propiedad de enteros contiene un elemento de enumeración (por ejemplo, ENUM_ORDER_TYPE, ORDER_TYPE_FILLING, etc.), debe utilizar la función EnumToString para convertirla en una cadena. Esta tarea se realiza por medio de un método auxiliar enumstr. Pronto veremos su uso generalizado en clases específicas de monitores, empezando por OrderMonitor tras un par de párrafos.

   template<typename E>
   static string enumstr(const long v)
   {
      return EnumToString((E)v);
   }

Para registrar todas las propiedades de un tipo determinado, hemos creado el método list2log que utiliza stringify en un bucle.

   template<typename E>
   void list2log() const
   {
      E e = (E)0// suppress warning 'possible use of uninitialized variable'
      int array[];
      const int n = EnumToArray(earray0USHORT_MAX);
      Print(typename(E), " Count="n);
      for(int i = 0i < n; ++i)
      {
         e = (E)array[i];
         PrintFormat("% 3d %s=%s"iEnumToString(e), stringify(e));
      }
   }

Por último, para facilitar el registro de las propiedades de los tres grupos, existe un método print que llama a list2log tres veces para cada grupo de propiedades.

   virtual void print() const
   {
      if(!readyreturn;
      
      Print(typename(this));
      list2log<I>();
      list2log<D>();
      list2log<S>();
   }

Teniendo a nuestra disposición una clase de plantilla base MonitorInterface, describimos OrderMonitorInterface, donde especificamos ciertos tipos de enumeración para las órdenes de la sección anterior y proporcionamos una implementación de stringify para las propiedades de enteros de las órdenes.

class OrderMonitorInterface:
   public MonitorInterface<ENUM_ORDER_PROPERTY_INTEGER,
   ENUM_ORDER_PROPERTY_DOUBLE,ENUM_ORDER_PROPERTY_STRING>
{
public:
   // description of properties according to subtypes
   virtual string stringify(const long v,
      const ENUM_ORDER_PROPERTY_INTEGER propertyconst override
   {
      switch(property)
      {
         case ORDER_TYPE:
            return enumstr<ENUM_ORDER_TYPE>(v);
         case ORDER_STATE:
            return enumstr<ENUM_ORDER_STATE>(v);
         case ORDER_TYPE_FILLING:
            return enumstr<ENUM_ORDER_TYPE_FILLING>(v);
         case ORDER_TYPE_TIME:
            return enumstr<ENUM_ORDER_TYPE_TIME>(v);
         case ORDER_REASON:
            return enumstr<ENUM_ORDER_REASON>(v);
         
         case ORDER_TIME_SETUP:
         case ORDER_TIME_EXPIRATION:
         case ORDER_TIME_DONE:
            return TimeToString(vTIME_DATE TIME_SECONDS);
         
         case ORDER_TIME_SETUP_MSC:
         case ORDER_TIME_DONE_MSC:
            return STR_TIME_MSC(v);
      }
      
      return (string)v;
   }
};

La macro STR_TIME_MSC para mostrar el tiempo en milisegundos se define como sigue:

#define STR_TIME_MSC(T) (TimeToString((T) / 1000TIME_DATE | TIME_SECONDS) \
    + StringFormat("'%03d", (T) % 1000))

Ahora estamos listos para describir la clase final para leer las propiedades de cualquier orden: OrderMonitor derivada de OrderMonitorInterface. El ticket de la orden se pasa al constructor, y se selecciona en el entorno de trading mediante OrderSelect.

class OrderMonitorpublic OrderMonitorInterface
{
public:
   const ulong ticket;
   OrderMonitor(const long t): ticket(t)
   {
      if(!OrderSelect(ticket))
      {
         PrintFormat("Error: OrderSelect(%lld) failed: %s",
            ticketE2S(_LastError));
      }
      else
      {
         ready = true;
      }
   }
   ...

La parte principal de trabajo del monitor consiste en redefiniciones de funciones virtuales para la lectura de propiedades. Aquí vemos las llamadas a las funciones OrderGetInteger, OrderGetDouble y OrderGetString.

   virtual long get(const ENUM_ORDER_PROPERTY_INTEGER propertyconst override
   {
      return OrderGetInteger(property);
   }
   
   virtual double get(const ENUM_ORDER_PROPERTY_DOUBLE propertyconst override
   {
      return OrderGetDouble(property);
   }
   
   virtual string get(const ENUM_ORDER_PROPERTY_STRING propertyconst override
   {
      return OrderGetString(property);
   }
   
   virtual long get(const int propertyconst longconst override
   {
      return OrderGetInteger((ENUM_ORDER_PROPERTY_INTEGER)property);
   }
   
   virtual double get(const int propertyconst doubleconst override
   {
      return OrderGetDouble((ENUM_ORDER_PROPERTY_DOUBLE)property);
   }
   
   virtual string get(const int propertyconst string)  const override
   {
      return OrderGetString((ENUM_ORDER_PROPERTY_STRING)property);
   }
};

Este fragmento de código se presenta de forma abreviada: se han eliminado de él los operadores para trabajar con órdenes en el historial. Veremos el código completo de OrderMonitor más adelante, cuando exploremos este aspecto en las secciones siguientes.

Es importante señalar que el objeto monitor no almacena copias de sus propiedades. Por lo tanto, el acceso a los métodos get debe realizarse inmediatamente después de la creación del objeto y, en consecuencia, la llamada OrderSelect. Para leer las propiedades en un período posterior, tendrá que volver a asignar la orden en la caché interna del programa MQL, por ejemplo, llamando al método refresh.

   void refresh()
   {
      ready = OrderSelect(ticket);
   }

Probemos el funcionamiento de OrderMonitor añadiéndolo al Asesor Experto MarketOrderSend.mq5. Una nueva versión llamada MarketOrderSendMonitor.mq5 conecta el archivo OrderMonitor.mqh mediante la directiva #include, y en el cuerpo de la función OnTimer (en el bloque de confirmación exitosa de apertura de una posición en una orden) crea un objeto monitor y llama a su método print.

#include <MQL5Book/OrderMonitor.mqh>
...
void OnTimer()
{
   ...
   const ulong order = (wantToBuy ?
      request.buy(volumePrice) :
      request.sell(volumePrice));
   if(order != 0)
   {
      Print("OK Order: #="order);
      if(request.completed())
      {
         Print("OK Position: P="request.result.position);
         
         OrderMonitor m(order);
         m.print();
         ...
      }
   }
}

En el registro, deberíamos ver nuevas líneas que contienen todas las propiedades de la orden.

OK Order: #=1287846602
Waiting for position for deal D=1270417032
OK Position: P=1287846602
MonitorInterface<ENUM_ORDER_PROPERTY_INTEGER, »
   » ENUM_ORDER_PROPERTY_DOUBLE,ENUM_ORDER_PROPERTY_STRING>
ENUM_ORDER_PROPERTY_INTEGER Count=14
  0 ORDER_TIME_SETUP=2022.03.21 13:28:59
  1 ORDER_TIME_EXPIRATION=1970.01.01 00:00:00
  2 ORDER_TIME_DONE=2022.03.21 13:28:59
  3 ORDER_TYPE=ORDER_TYPE_BUY
  4 ORDER_TYPE_FILLING=ORDER_FILLING_FOK
  5 ORDER_TYPE_TIME=ORDER_TIME_GTC
  6 ORDER_STATE=ORDER_STATE_FILLED
  7 ORDER_MAGIC=1234567890
  8 ORDER_POSITION_ID=1287846602
  9 ORDER_TIME_SETUP_MSC=2022.03.21 13:28:59'572
 10 ORDER_TIME_DONE_MSC=2022.03.21 13:28:59'572
 11 ORDER_POSITION_BY_ID=0
 12 ORDER_TICKET=1287846602
 13 ORDER_REASON=ORDER_REASON_EXPERT
ENUM_ORDER_PROPERTY_DOUBLE Count=7
  0 ORDER_VOLUME_INITIAL=0.01
  1 ORDER_VOLUME_CURRENT=0.0
  2 ORDER_PRICE_OPEN=1.10275
  3 ORDER_PRICE_CURRENT=1.10275
  4 ORDER_PRICE_STOPLIMIT=0.0
  5 ORDER_SL=0.0
  6 ORDER_TP=0.0
ENUM_ORDER_PROPERTY_STRING Count=3
  0 ORDER_SYMBOL=EURUSD
  1 ORDER_COMMENT=
  2 ORDER_EXTERNAL_ID=
TRADE_ACTION_DEAL, EURUSD, ORDER_TYPE_BUY, V=0.01, ORDER_FILLING_FOK, »
   » @ 1.10275, P=1287846602, M=1234567890
DONE, D=1270417032, #=1287846602, V=0.01, @ 1.10275, Bid=1.10275, Ask=1.10275, »
   » Request executed, Req=3

La cuarta línea inicia la salida del método print, que incluye el nombre completo del objeto monitor MonitorInterface junto con los tipos de parámetros (en este caso, triple ENUM_ORDER_PROPERTY) y, a continuación, todas las propiedades de una orden concreta.

No obstante, la impresión de propiedades no es la acción más interesante que puede ofrecer un monitor. La tarea de seleccionar ordenes por condiciones (valores de propiedades arbitrarias) es mucho más demandada entre los Asesores Expertos. Utilizando el monitor como herramienta auxiliar, crearemos un mecanismo de filtrado de órdenes similar al que hemos hecho para los símbolos: SymbolFilter.mqh pertinente.