Visión general de las funciones para obtener las propiedades de los símbolos

La especificación completa de cada símbolo puede obtenerse consultando sus propiedades: para ello, la API de MQL5 proporciona tres funciones, a saber, SymbolInfoInteger, SymbolInfoDouble y SymbolInfoString, cada una de las cuales se encarga de las propiedades de un tipo concreto. Las propiedades se describen como miembros de tres enumeraciones: ENUM_SYMBOL_INFO_INTEGER, ENUM_SYMBOL_INFO_DOUBLE, and ENUM_SYMBOL_INFO_STRING, respectively. Una técnica similar se utiliza en las API de gráficos y objetos que ya conocemos.

El nombre del símbolo y el identificador de la propiedad solicitada se pasan a cualquiera de las funciones.

Cada una de las funciones se presenta de dos formas: abreviada y completa. La versión abreviada devuelve directamente la propiedad solicitada, mientras que la completa la escribe en el parámetro out pasado por referencia. Por ejemplo, para las propiedades compatibles con un tipo entero, las funciones tienen prototipos como éste:

long SymbolInfoInteger(const string symbol, ENUM_SYMBOL_INFO_INTEGER property)

bool SymbolInfoInteger(const string symbol, ENUM_SYMBOL_INFO_INTEGER property, long &value)

La segunda forma devuelve un indicador booleano de éxito (true) o error (false). Las razones más plausibles por las que una función puede devolver false incluyen un nombre de símbolo no válido (MARKET_UNKNOWN_SYMBOL, 4301) o un identificador no válido para la propiedad solicitada (MARKET_WRONG_PROPERTY, 4303). Los detalles figuran en _LastError.

Como antes, las propiedades de la enumeración ENUM_SYMBOL_INFO_INTEGER son de varios tipos compatibles con enteros: bool, int, long, color, datetime y enumeraciones especiales (todas ellas se abordarán en secciones separadas).

Para las propiedades con un tipo de número real, se definen las siguientes dos formas de la función SymbolInfoDouble:

double SymbolInfoDouble(const string symbol, ENUM_SYMBOL_INFO_DOUBLE property)

bool SymbolInfoDouble(const string symbol, ENUM_SYMBOL_INFO_DOUBLE property, double &value)

Por último, para las propiedades de cadena, las funciones similares tienen el siguiente aspecto:

string SymbolInfoString(const string symbol, ENUM_SYMBOL_INFO_STRING property)

bool SymbolInfoString(const string symbol, ENUM_SYMBOL_INFO_STRING property, string &value)

Las propiedades de varios tipos que se utilizarán a menudo más adelante al desarrollar Asesores Expertos se agrupan lógicamente en las descripciones de las siguientes secciones de este capítulo.

Basándonos en las funciones anteriores, crearemos una clase universal SymbolMonitor (archivo SymbolMonitor.mqh) para obtener las propiedades de cualquier símbolo. Se basará en un conjunto de métodos sobrecargados get para tres enumeraciones.

class SymbolMonitor
{
public:
   const string name;
   SymbolMonitor(): name(_Symbol) { }
   SymbolMonitor(const string s): name(s) { }
   
   long get(const ENUM_SYMBOL_INFO_INTEGER propertyconst
   {
      return SymbolInfoInteger(nameproperty);
   }
   
   double get(const ENUM_SYMBOL_INFO_DOUBLE propertyconst
   {
      return SymbolInfoDouble(nameproperty);
   }
   
   string get(const ENUM_SYMBOL_INFO_STRING propertyconst
   {
      return SymbolInfoString(nameproperty);
   }
   ...

Los otros tres métodos similares permiten eliminar el tipo de enumeración en el primer parámetro y seleccionar la sobrecarga necesaria por parte del compilador gracias al segundo parámetro ficticio (su tipo aquí siempre coincide con el tipo de resultado). Lo utilizaremos en futuras clases de plantillas.

   long get(const int propertyconst longconst
   {
      return SymbolInfoInteger(name, (ENUM_SYMBOL_INFO_INTEGER)property);
   }
 
   double get(const int propertyconst doubleconst
   {
      return SymbolInfoDouble(name, (ENUM_SYMBOL_INFO_DOUBLE)property);
   }
 
   string get(const int propertyconst stringconst
   {
      return SymbolInfoString(name, (ENUM_SYMBOL_INFO_STRING)property);
   }
   ...

Así, mediante la creación de un objeto con el nombre de símbolo deseado, se pueden consultar uniformemente sus propiedades de cualquier tipo. Para consultar y registrar todas las propiedades del mismo tipo podríamos implementar algo como esto:

   // project (draft)
   template<typename E,typename R>
   void list2log()
   {
      E e = (E)0;
      int array[];
      const int n = EnumToArray(earray0USHORT_MAX);
      for(int i = 0i < n; ++i)
      {
         e = (E)array[i];
         R r = get(e);
         PrintFormat("% 3d %s=%s"iEnumToString(e), (string)r);
      }
   }

Sin embargo, debido al hecho de que en las propiedades del tipo long en realidad se «ocultan» valores de otros tipos, que deben mostrarse de una forma específica (por ejemplo, llamando a EnumToString para enumeraciones, imeToString para fecha y hora, etc.), tiene sentido definir otros tres métodos sobrecargados que devuelvan una representación de cadena de la propiedad. Los llamaremos stringify. Entonces, en el proyecto list2log anterior, es posible utilizar stringify en lugar de valores de conversión a (string), y el propio método eliminará un parámetro de plantilla.

   template<typename E>
   void list2log()
   {
      E e = (E)0;
      int array[];
      const int n = EnumToArray(earray0USHORT_MAX);
      for(int i = 0i < n; ++i)
      {
         e = (E)array[i];
         PrintFormat("% 3d %s=%s"iEnumToString(e), stringify(e));
      }
   }

Para los tipos real y cadena, la implementación de stringify parece bastante sencilla.

   string stringify(const ENUM_SYMBOL_INFO_DOUBLE propertyconst string format = NULLconst
   {
      if(format == NULLreturn (string)SymbolInfoDouble(nameproperty);
      return StringFormat(formatSymbolInfoDouble(nameproperty));
   }
   
   string stringify(const ENUM_SYMBOL_INFO_STRING propertyconst
   {
      return SymbolInfoString(nameproperty);
   }

Sin embargo, para ENUM_SYMBOL_INFO_INTEGER, todo es un poco más complicado. Por supuesto, cuando la propiedad es del tipo long o int, basta con convertirla a (string). Todos los demás casos deben analizarse y convertirse individualmente en el operador switch.

   string stringify(const ENUM_SYMBOL_INFO_INTEGER propertyconst
   {
      const long v = SymbolInfoInteger(nameproperty);
      switch(property)
      {
         ...
      }
      
      return (string)v;
   }

Por ejemplo, si una propiedad es de tipo booleano, es conveniente representarla con la cadena «true» o «false» (así será visualmente diferente de los simples números 1 y 0). De cara al futuro, y por poner un ejemplo, digamos que entre las propiedades se encuentra SYMBOL_EXIST, que equivale a la función SymbolExist, es decir, devuelve una indicación booleana de si el carácter especificado existe. Para su procesamiento y otras propiedades lógicas tiene sentido implementar un método auxiliar boolean.

   static string boolean(const long v)
   {
      return v ? "true" : "false";
   }
   
   string stringify(const ENUM_SYMBOL_INFO_INTEGER propertyconst
   {
      const long v = SymbolInfoInteger(nameproperty);
      switch(property)
      {
         case SYMBOL_EXIST:
            return boolean(v);
         ...
      }
      
      return (string)v;
   }

Para las propiedades que son enumeraciones, la solución más adecuada sería un método de plantilla que utilice la función EnumToString.

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

Por ejemplo, la propiedad SYMBOL_SWAP_ROLLOVER3DAYS determina en qué día de la semana se carga un swap triple en las posiciones abiertas de un símbolo, y esta propiedad tiene el tipo ENUM_DAY_OF_WEEK. Así, para procesarlo, podemos escribir lo siguiente dentro de switch:

         case SYMBOL_SWAP_ROLLOVER3DAYS:
            return enumstr<ENUM_DAY_OF_WEEK>(v);

Un caso especial lo presentan las propiedades cuyos valores son combinaciones de banderas de bits. En concreto, para cada símbolo, el bróker establece permisos para órdenes de tipos específicos, como mercado, límite, stop loss, take profit, etc. (analizaremos estos permisos por separado). Cada tipo de orden se indica mediante una constante con un bit habilitado, por lo que su superposición (combinada mediante el operador bitwise OR '|') se almacena en la propiedad SYMBOL_ORDER_MODE, y en ausencia de restricciones, todos los bits se habilitan al mismo tiempo. Para tales propiedades definiremos nuestras propias enumeraciones en nuestro archivo de encabezado; por ejemplo:

enum SYMBOL_ORDER
{
   _SYMBOL_ORDER_MARKET = 1,
   _SYMBOL_ORDER_LIMIT = 2,
   _SYMBOL_ORDER_STOP = 4,
   _SYMBOL_ORDER_STOP_LIMIT = 8,
   _SYMBOL_ORDER_SL = 16,
   _SYMBOL_ORDER_TP = 32,
   _SYMBOL_ORDER_CLOSEBY = 64,
};

Aquí, para cada constante integrada, como SYMBOL_ORDER_MARKET, se declara un elemento correspondiente, cuyo identificador es el mismo que la constante pero va precedido de un guion bajo para evitar conflictos de nombres.

Para representar combinaciones de banderas de tales enumeraciones en forma de cadena, implementamos otro método de plantilla, maskstr.

   template<typename E>
   static string maskstr(const long v)
   {
      string text = "";
      for(int i = 0; ; ++i)
      {
         ResetLastError();
         const string s = EnumToString((E)(1 << i));
         if(_LastError != 0)
         {
            break;
         }
         if((v & (1 << i)) != 0)
         {
            text += s + " ";
         }
      }
      return text;
   }

Su significado es como enumstr, pero se llama a la función EnumToString por cada bit habilitado en el valor de la propiedad, tras lo cual se «pegan» las cadenas resultantes.

Ahora es posible procesar SYMBOL_ORDER_MODE en la sentencia switch de forma similar:

         case SYMBOL_ORDER_MODE:
            return maskstr<SYMBOL_ORDER>(v);

Aquí está el código completo del método stringify para ENUM_SYMBOL_INFO_INTEGER. Nos iremos familiarizando poco a poco con todas las propiedades y enumeraciones en las siguientes secciones.

   string stringify(const ENUM_SYMBOL_INFO_INTEGER propertyconst
   {
      const long v = SymbolInfoInteger(nameproperty);
      switch(property)
      {
         case SYMBOL_SELECT:
         case SYMBOL_SPREAD_FLOAT:
         case SYMBOL_VISIBLE:
         case SYMBOL_CUSTOM:
         case SYMBOL_MARGIN_HEDGED_USE_LEG:
         case SYMBOL_EXIST:
            return boolean(v);
         case SYMBOL_TIME:
            return TimeToString(vTIME_DATE|TIME_SECONDS);
         case SYMBOL_TRADE_CALC_MODE:   
            return enumstr<ENUM_SYMBOL_CALC_MODE>(v);
         case SYMBOL_TRADE_MODE:
            return enumstr<ENUM_SYMBOL_TRADE_MODE>(v);
         case SYMBOL_TRADE_EXEMODE:
            return enumstr<ENUM_SYMBOL_TRADE_EXECUTION>(v);
         case SYMBOL_SWAP_MODE:
            return enumstr<ENUM_SYMBOL_SWAP_MODE>(v);
         case SYMBOL_SWAP_ROLLOVER3DAYS:
            return enumstr<ENUM_DAY_OF_WEEK>(v);
         case SYMBOL_EXPIRATION_MODE:
            return maskstr<SYMBOL_EXPIRATION>(v);
         case SYMBOL_FILLING_MODE:
            return maskstr<SYMBOL_FILLING>(v);
         case SYMBOL_START_TIME:
         case SYMBOL_EXPIRATION_TIME:
            return TimeToString(v);
         case SYMBOL_ORDER_MODE:
            return maskstr<SYMBOL_ORDER>(v);
         case SYMBOL_OPTION_RIGHT:
            return enumstr<ENUM_SYMBOL_OPTION_RIGHT>(v);
         case SYMBOL_OPTION_MODE:
            return enumstr<ENUM_SYMBOL_OPTION_MODE>(v);
         case SYMBOL_CHART_MODE:
            return enumstr<ENUM_SYMBOL_CHART_MODE>(v);
         case SYMBOL_ORDER_GTC_MODE:
            return enumstr<ENUM_SYMBOL_ORDER_GTC_MODE>(v);
         case SYMBOL_SECTOR:
            return enumstr<ENUM_SYMBOL_SECTOR>(v);
         case SYMBOL_INDUSTRY:
            return enumstr<ENUM_SYMBOL_INDUSTRY>(v);
         case SYMBOL_BACKGROUND_COLOR// Bytes: Transparency Blue Green Red
            return StringFormat("TBGR(0x%08X)"v);
      }
      
      return (string)v;
   }

Para probar la clase SymbolMonitor hemos creado un sencillo script SymbolMonitor.mq5. Registra todas las propiedades del símbolo del gráfico de trabajo.

#include <MQL5Book/SymbolMonitor.mqh>
   
void OnStart()
{
   SymbolMonitor m;
   m.list2log<ENUM_SYMBOL_INFO_INTEGER>();
   m.list2log<ENUM_SYMBOL_INFO_DOUBLE>();
   m.list2log<ENUM_SYMBOL_INFO_STRING>();
}

Por ejemplo, si ejecutamos el script en el gráfico EURUSD, podemos obtener los siguientes registros (facilitados de forma abreviada):

ENUM_SYMBOL_INFO_INTEGER Count=36
  0 SYMBOL_SELECT=true
  ...
  4 SYMBOL_TIME=2022.01.12 10:52:22
  5 SYMBOL_DIGITS=5
  6 SYMBOL_SPREAD=0
  7 SYMBOL_TICKS_BOOKDEPTH=10
  8 SYMBOL_TRADE_CALC_MODE=SYMBOL_CALC_MODE_FOREX
  9 SYMBOL_TRADE_MODE=SYMBOL_TRADE_MODE_FULL
 10 SYMBOL_TRADE_STOPS_LEVEL=0
 11 SYMBOL_TRADE_FREEZE_LEVEL=0
 12 SYMBOL_TRADE_EXEMODE=SYMBOL_TRADE_EXECUTION_INSTANT
 13 SYMBOL_SWAP_MODE=SYMBOL_SWAP_MODE_POINTS
 14 SYMBOL_SWAP_ROLLOVER3DAYS=WEDNESDAY
 15 SYMBOL_SPREAD_FLOAT=true
 16 SYMBOL_EXPIRATION_MODE=_SYMBOL_EXPIRATION_GTC _SYMBOL_EXPIRATION_DAY »
    _SYMBOL_EXPIRATION_SPECIFIED _SYMBOL_EXPIRATION_SPECIFIED_DAY
 17 SYMBOL_FILLING_MODE=_SYMBOL_FILLING_FOK
 ... 
 23 SYMBOL_ORDER_MODE=_SYMBOL_ORDER_MARKET _SYMBOL_ORDER_LIMIT _SYMBOL_ORDER_STOP »
    _SYMBOL_ORDER_STOP_LIMIT _SYMBOL_ORDER_SL _SYMBOL_ORDER_TP _SYMBOL_ORDER_CLOSEBY
 ... 
 26 SYMBOL_VISIBLE=true
 27 SYMBOL_CUSTOM=false
 28 SYMBOL_BACKGROUND_COLOR=TBGR(0xFF000000)
 29 SYMBOL_CHART_MODE=SYMBOL_CHART_MODE_BID
 30 SYMBOL_ORDER_GTC_MODE=SYMBOL_ORDERS_GTC
 31 SYMBOL_MARGIN_HEDGED_USE_LEG=false
 32 SYMBOL_EXIST=true
 33 SYMBOL_TIME_MSC=1641984742149
 34 SYMBOL_SECTOR=SECTOR_CURRENCY
 35 SYMBOL_INDUSTRY=INDUSTRY_UNDEFINED
ENUM_SYMBOL_INFO_DOUBLE Count=57
  0 SYMBOL_BID=1.13681
  1 SYMBOL_BIDHIGH=1.13781
  2 SYMBOL_BIDLOW=1.13552
  3 SYMBOL_ASK=1.13681
  4 SYMBOL_ASKHIGH=1.13781
  5 SYMBOL_ASKLOW=1.13552
 ...
 12 SYMBOL_POINT=1e-05
 13 SYMBOL_TRADE_TICK_VALUE=1.0
 14 SYMBOL_TRADE_TICK_SIZE=1e-05
 15 SYMBOL_TRADE_CONTRACT_SIZE=100000.0
 16 SYMBOL_VOLUME_MIN=0.01
 17 SYMBOL_VOLUME_MAX=500.0
 18 SYMBOL_VOLUME_STEP=0.01
 19 SYMBOL_SWAP_LONG=-0.7
 20 SYMBOL_SWAP_SHORT=-1.0
 21 SYMBOL_MARGIN_INITIAL=0.0
 22 SYMBOL_MARGIN_MAINTENANCE=0.0
 ...
 28 SYMBOL_TRADE_TICK_VALUE_PROFIT=1.0
 29 SYMBOL_TRADE_TICK_VALUE_LOSS=1.0
 ...
 43 SYMBOL_MARGIN_HEDGED=100000.0
 ...
 47 SYMBOL_PRICE_CHANGE=0.0132
ENUM_SYMBOL_INFO_STRING Count=15
  0 SYMBOL_BANK=
  1 SYMBOL_DESCRIPTION=Euro vs US Dollar
  2 SYMBOL_PATH=Forex\EURUSD
  3 SYMBOL_CURRENCY_BASE=EUR
  4 SYMBOL_CURRENCY_PROFIT=USD
  5 SYMBOL_CURRENCY_MARGIN=EUR
 ...
 13 SYMBOL_SECTOR_NAME=Currency

En concreto, puede ver que los precios de los símbolos se emiten con 5 dígitos (SYMBOL_DIGITS), el símbolo existe (SYMBOL_EXIST), el tamaño del contrato es 100000.0 (SYMBOL_TRADE_CONTRACT_SIZE), etc. Toda la información corresponde a la especificación.