Precisión de la representación de precios y pasos de cambio

Hemos visto ya dos propiedades interrelacionadas del símbolo de trabajo del gráfico: el paso mínimo de cambio de precio (Point) y la precisión de la presentación del precio, que se expresa en el número de decimales (Digits). También están disponibles en las Variables predefinidas. Para obtener propiedades similares de un símbolo arbitrario, debe consultar las propiedades SYMBOL_POINT y SYMBOL_DIGITS, respectivamente. La propiedad SYMBOL_POINT está estrechamente relacionada con el cambio mínimo de precio (conocido en un programa MQL como la propiedad SYMBOL_TRADE_TICK_SIZE) y su valor (SYMBOL_TRADE_TICK_VALUE), normalmente en la divisa de la cuenta de trading (pero algunos símbolos pueden ser configurados para usar la divisa base; puede contactar con su bróker para obtener más detalles si es necesario). En la tabla siguiente se muestra el grupo completo de estas propiedades:

Identificador

Descripción

SYMBOL_DIGITS

El número de decimales

SYMBOL_POINT

El valor de un punto en la divisa de cotización

SYMBOL_TRADE_TICK_VALUE

SYMBOL_TRADE_TICK_VALUE_PROFIT value

SYMBOL_TRADE_TICK_VALUE_PROFIT

Valor actual del tick para una posición rentable

SYMBOL_TRADE_TICK_VALUE_LOSS

Valor actual del tick para una posición perdedora

SYMBOL_TRADE_TICK_SIZE

Variación mínima del precio en la divisa de cotización

Todas las propiedades excepto SYMBOL_DIGITS son números reales y se solicitan utilizando la función SymbolInfoDouble. La propiedad SYMBOL_DIGITS está disponible a través de SymbolInfoInteger. Para probar el trabajo con estas propiedades, utilizaremos las clases ya creadas SymbolFilter y SymbolMonitor, que llamarán automáticamente a la función deseada para cualquier propiedad.

También mejoraremos la clase SymbolFilter añadiendo una nueva sobrecarga del método select, que podrá rellenar no sólo un array con los nombres de los símbolos adecuados, sino también otro array con los valores de su propiedad específica.

En un caso más general, puede que nos interesen varias propiedades para cada símbolo al mismo tiempo, por lo que es aconsejable utilizar no uno de los tipos de datos integrados para el array de salida, sino un tipo compuesto especial con diferentes campos.

En programación, estos tipos se denominan tuplas y son en cierto modo equivalentes a las estructuras MQL5.

template<typename T1,typename T2,typename T3// we can describe up to 64 fields
struct Tuple3                                 // MQL5 allows 64 template parameters
{
   T1 _1;
   T2 _2;
   T3 _3;
};

No obstante, las estructuras requieren una descripción preliminar con todos los campos, mientras que no conocemos de antemano el número y la lista de propiedades de los símbolos solicitados. Por lo tanto, para simplificar el código, representaremos nuestra tupla como un vector en la segunda dimensión de un array dinámico que recibe los resultados de la consulta.

T array[][S];

Como tipo de datos T podemos utilizar cualquiera de las enumeraciones y tipos integrados utilizados para las propiedades. El tamaño S debe coincidir con el número de propiedades solicitadas.

A decir verdad, tal simplificación nos limita en una consulta a valores de los mismos tipos, es decir, sólo enteros, sólo reales o sólo cadenas. Sin embargo, las condiciones de filtrado pueden incluir cualquier propiedad. Implementaremos el enfoque con tuplas un poco más adelante, utilizando el ejemplo de filtros de otras entidades de trading: órdenes, transacciones y posiciones.

Así, la nueva versión del método SymbolFilter::select toma como entrada una referencia al array property con identificadores de propiedades para leer desde los símbolos filtrados. Los nombres de los símbolos en sí y los valores de estas propiedades se escribirán en los arrays de salida symbols y data.

   template<typename E,typename V>
   bool select(const bool watchconst E &property[], string &symbols[],
      V &data[][], const bool sort = falseconst
   {
      // the size of the array of requested properties must match the output tuple
      const int q = ArrayRange(data1);
      if(ArraySize(property) != qreturn false;
      
      const int n = SymbolsTotal(watch);
      // iterate over characters
      for(int i = 0i < n; ++i)
      {
         const string s = SymbolName(iwatch);
         // access to the symbol properties is provided by the monitor
         SymbolMonitor m(s);
         // check all filter conditions
         if(match(mlongs)
         && match(mdoubles)
         && match(mstrings))
         {
            // properties of a suitable symbol are written to arrays
            const int k = EXPAND(data);
            for(int j = 0j < q; ++j)
            {
               data[k][j] = m.get(property[j]);
            }
            PUSH(symbolss);
         }
      }
 
      if(sort)
      {
         ...
      }
      return true;
   }

Además, el nuevo método puede ordenar el array de salida por la primera dimensión (la primera propiedad solicitada): esta funcionalidad se deja para su estudio por separado mediante códigos fuente. Para activar la clasificación, ajuste el parámetro sort en true. Los arrays con nombres de símbolos y datos se ordenan de forma coherente.

Para evitar tuplas en el código de llamada cuando sólo se necesita solicitar una propiedad de los caracteres filtrados, se implementa la opción select siguiente en SymbolFilter: en su interior se definen arrays intermedios de propiedades (properties) y valores (tuples) con tamaño 1 en la segunda dimensión, que se utilizan para llamar a la versión completa anterior de select.

   template<typename E,typename V>
   bool select(const bool watchconst E propertystring &symbols[], V &data[],
      const bool sort = falseconst
   {
      E properties[1] = {property};
      V tuples[][1];
      
      const bool result = select(watchpropertiessymbolstuplessort);
      ArrayCopy(datatuples);
      return result;
   }

Utilizando el filtro avanzado, vamos a intentar construir una lista de símbolos ordenados por el valor de tick SYMBOL_TRADE_TICK_VALUE (véase el archivo SymbolFilterTickValue.mq5). Suponiendo que la divisa de depósito sea USD, deberíamos obtener un valor igual a 1.0 para los instrumentos de Forex cotizados en USD (del tipo XXXUSD). Para otros activos, veremos valores no triviales.

#include <MQL5Book/SymbolFilter.mqh>
   
input bool MarketWatchOnly = true;
   
void OnStart()
{
   SymbolFilter f;      // filter object
   string symbols[];    // array with symbol names
   double tickValues[]; // array for results
   
   // apply the filter without conditions, fill and sort the array
   f.select(MarketWatchOnlySYMBOL_TRADE_TICK_VALUEsymbolstickValuestrue);
   
   PrintFormat("===== Tick values of the symbols (%d) =====",
      ArraySize(tickValues));
   ArrayPrint(symbols);
   ArrayPrint(tickValues5);
}

He aquí el resultado de ejecutar el script:

===== Tick values of the symbols (13) =====
"BTCUSD" "USDRUB" "XAUUSD" "USDSEK" "USDCNH" "USDCAD" "USDJPY" "NZDUSD" "AUDUSD" "EURUSD" "GBPUSD" "USDCHF" "SP500m"
 0.00100  0.01309  0.10000  0.10955  0.15744  0.80163  0.87319  1.00000  1.00000  1.00000  1.00000  1.09212 10.00000