Trabajar con arrays de ticks reales en estructuras MqlTick

MetaTrader 5 ofrece la posibilidad de trabajar no sólo con la historia de las cotizaciones (barras), sino también con la historia de los ticks reales. Desde la interfaz de usuario, todos los datos históricos están disponibles en el cuadro de diálogo Symbols, que tiene tres pestañas: Specification, Bars y Ticks. Cuando se selecciona un elemento específico en la lista de símbolos en forma de árbol de la primera pestaña, al cambiar a las pestañas Bars y Ticks se pueden solicitar cotizaciones en forma de barras o ticks, respectivamente.

Desde los programas MQL, el historial de ticks reales también está disponible utilizando las funciones CopyTicks y CopyTicksRange.

int CopyTicks(const string symbol, MqlTick &ticks[], uint flags = COPY_TICKS_ALL, ulong from = 0, uint count = 0)

int CopyTicksRange(const string symbol, MqlTick &ticks[], uint flags = COPY_TICKS_ALL, ulong from = 0, ulong to = 0)

Ambas funciones solicitan ticks para el instrumento symbol especificado en el array ticks pasado por referencia. La estructura MqlTick contiene toda la información sobre un tick y se describe en MQL5 de la siguiente manera:

struct MqlTick
{
   datetime time;        // time of this price update
   double   bid;         // current Bid price
   double   ask;         // current Ask price
   double   last;        // Last trade price
   ulong    volume;      // volume for Last price
   long     time_msc;    // time of this price update in milliseconds
   uint     flags;       // flags (which fields of the structure have changed)
   double   volume_real// volume for the Last price with increased accuracy
};

El campo flags está destinado a almacenar una máscara de bits de signos, cuyos campos de la estructura de ticks contienen valores modificados.

Constante

Valor

Descripción

TICK_FLAG_BID

2

Precio de oferta modificado

TICK_FLAG_ASK

4

Precio de venta modificado

TICK_FLAG_LAST

8

Último precio modificado

TICK_FLAG_VOLUME

16

Volumen modificado

TICK_FLAG_BUY

32

El tick se generó como resultado de una operación de compra

TICK_FLAG_SELL

64

El tick se generó como resultado de una operación de venta

Esto era necesario porque cada tick siempre rellena todos los campos, independientemente de si los datos han cambiado en comparación con el tick anterior. Esto le permite disponer siempre del estado actual de los precios en cualquier momento, sin necesidad de buscar valores anteriores en el historial de ticks. Por ejemplo, sólo el precio de oferta podría cambiar con un tick, pero además del nuevo precio, se indicarán otros parámetros en la estructura: anteriores Ask, Last, volumen, etc.

Al mismo tiempo, debe tener en cuenta que, dependiendo del tipo de instrumento, algunos campos de los ticks pueden ser siempre cero (y los bits de máscara correspondientes nunca se activan para ellos). En particular, para los instrumentos Forex, por regla general, los campos last, volume y volume_real permanecen vacíos.

El array receptor ticks puede ser de tamaño fijo o dinámico. Las funciones no copiarán en un array fijo más ticks que los correspondientes al tamaño del array, independientemente del número real de ticks en el intervalo de tiempo solicitado (especificado por los parámetros from/to en la función CopyTicksRange) o en el parámetro count de la función CopyTicks. En el array ticks, los ticks más antiguos se colocan en primer lugar y los más recientes, en último lugar.

En los parámetros de ambas funciones, las lecturas de tiempo se especifican en milisegundos desde el 01.01.1970 00:00:00. En la función CopyTicks, el rango de ticks solicitados se establece mediante el valor inicial from y el número de ticks count, y en CopyTicksRange se establece mediante from y to (ambos valores están incluidos).

En otras palabras: CopyTicksRange está diseñado para recibir ticks en un intervalo específico, y su número no se conoce de antemano. CopyTicks garantiza un máximo de count ticks, pero no permite determinar de antemano qué intervalo de tiempo cubrirán estos ticks.

El orden cronológico de los valores from y to en CopyTicksRange no es importante: la función dará ticks en cualquier caso, empezando por el mínimo de los dos valores y terminando por el máximo.

La función CopyTicks evalúa el parámetro from como el borde izquierdo con el tiempo mínimo y cuenta desde él count ticks hacia el futuro. No obstante, hay una excepción importante: from = 0 (por defecto) se trata como el momento actual en el tiempo, y los ticks se cuentan desde él hacia el pasado. Esto permite obtener siempre el número especificado de últimos ticks. Cuando count = 0 (por defecto), la función no copia más de 2000 ticks.

Ambas funciones devuelven el número de ticks copiados, o -1 en caso de error. En concreto, GetLastError puede devolver los siguientes códigos de error:

  • ERR_HISTORY_TIMEOUT: el tiempo de espera de sincronización de ticks ha expirado; la función ha devuelto todo lo que tenía.
  • ERR_HISTORY_SMALL_BUFFER: el búfer estático es demasiado pequeño, por lo que da tanto como cabe en el array.
  • ERR_NOT_ENOUGH_MEMORY: no se ha podido asignar la cantidad de memoria necesaria para obtener el historial de ticks del rango especificado en un array dinámico.

El parámetro flags define el tipo de ticks solicitados.

Constante

Valor

Descripción

COPY_TICKS_INFO

1

Ticks causados por cambios de Bid y/o Ask
(TICK_FLAG_BID, TICK_FLAG_ASK)

COPY_TICKS_TRADE

2

Ticks con cambios de Last y Volume
(TICK_FLAG_LAST, TICK_FLAG_VOLUME, TICK_FLAG_BUY, TICK_FLAG_SELL)

COPY_TICKS_ALL

3

Todos los ticks

Para cualquier tipo de solicitud, los campos restantes de la estructura MqlTick, que no coincidan con las banderas, contendrán los valores reales anteriores. Por ejemplo, si sólo se han solicitado ticks de información (COPY_TICKS_INFO), los campos restantes seguirán rellenándose en ellos. Significa que si sólo ha cambiado el precio de Bid, se escribirán los últimos valores conocidos en los campos ask y volume. Para averiguar qué ha cambiado en el tick, analice su campo flags (estará el valor TICK_FLAG_BID o TICK_FLAG_ASK, o una combinación de ambos). Si un tick tiene valores cero de los precios Bid y Ask, y las banderas indican que estos precios han cambiado (flags == TICK_FLAG_BID | TICK_FLAG_ASK), entonces ello indica el vaciado del libro de órdenes.

Del mismo modo, si se solicitaron ticks de trading (COPY_TICKS_TRADE), se registrarán los últimos valores de precio conocidos en sus campos bid y ask. En este caso, el campo flags puede tener una combinación de TICK_FLAG_LAST, TICK_FLAG_VOLUME, TICK_FLAG_BUY, TICK_FLAG_SELL.

Cuando se solicita COPY_TICKS_ALL, se devuelven todos los ticks.

Al llamar a cualquiera de las funciones de CopyTicks/CopyTicksRange se comprueba la sincronización de la base de ticks almacenada en el disco duro para el símbolo dado. Si no hay suficientes ticks en la base de datos local, los que falten se descargarán automáticamente del servidor de operaciones. En este caso, los ticks se sincronizarán teniendo en cuenta la fecha más antigua de los parámetros de consulta y hasta el momento actual. Después, todos los ticks entrantes para este símbolo irán a la base de datos de ticks y la mantendrán actualizada en un estado sincronizado.

Los datos por ticks son mucho mayores que las cotizaciones por minutos. Cuando solicite por primera vez un historial de ticks o inicie las pruebas por tick reales, descargarlos puede llevar mucho tiempo. El historial de datos de tick se almacena en archivos con formato interno TKC en el directorio {terminal_dir}/bases/{server_name}/ticks/{symbol_name}. Cada archivo contiene información correspondiente a un mes.

En los indicadores, las funciones devuelven el resultado inmediatamente, es decir, copian los ticks disponibles por símbolo e inician el proceso en segundo plano de sincronización de la base de ticks si no hay datos suficientes. Todos los indicadores de un símbolo funcionan en un hilo común, por lo que no tienen derecho a esperar a que se complete la sincronización. Una vez finalizada la sincronización, la siguiente llamada a la función devolverá todos los ticks solicitados.

En los Asesores Expertos y scripts, las funciones pueden esperar hasta 45 segundos para obtener un resultado: a diferencia de un indicador, cada Asesor Experto y script se ejecuta en su propio hilo y, por lo tanto, puede esperar a que la sincronización se complete dentro de un tiempo de espera. Si durante este tiempo todavía no se sincronizan los ticks en la cantidad requerida, entonces sólo se devolverán los ticks disponibles, y la sincronización continuará en segundo plano.

Recordemos que los ticks en tiempo real se transmiten a los gráficos como eventos: los indicadores reciben notificaciones de nuevos ticks en el gráfico OnCalculate, mientras que los Asesores Expertos los reciben en el manejador OnTick. Hay que tener en cuenta que el sistema no garantiza la entrega de todos los eventos. Si llegan nuevos ticks al terminal mientras el programa está procesando el evento OnCalculate/OnTick actual, es posible que no se añadan a su cola nuevos eventos para este programa «ocupado» (véase la sección Visión general de las funciones de gestión de eventos). Además, pueden llegar varios ticks al mismo tiempo, pero sólo se generará un evento para cada programa MQL: el evento de estado actual del mercado. En este caso, puede utilizar la función CopyTicks para solicitar todos los ticks que se han producido desde el procesamiento anterior del evento. He aquí el aspecto de este algoritmo en pseudocódigo:

void processAllTicks()
{
   static ulong prev = 0;
   if(!prev)
   {
      MqlTick ticks[];
      const int n = CopyTicks(_SymbolticksCOPY_TICKS_ALLprev + 11000000);
      if(n > 0)
      {
         prev = ticks[n - 1].time_msc;
         ... // processing all missed ticks
      }
   }
   else
   {
      MqlTick tick;
      SymbolInfoTick(_Symboltick);
      prev = tick.time_msc;
      ... // processing the first tick
   }

La función SymbolInfoTick utilizada aquí rellena una única estructura MqlTick pasada por referencia con los datos del último tick. Lo estudiaremos en una secciónaparte.

Tenga en cuenta que al llamar a CopyTicks se añade un milisegundo a la antigua marca de tiempo prev. De este modo se garantiza que no se vuelva a procesar el tick anterior. No obstante, si hubiera varios ticks dentro de un milisegundo correspondiente a prev, este algoritmo los omitirá. Si desea cubrir absolutamente todos los ticks, debe recordar el número de ticks disponibles con el tiempo prev mientras actualiza la variable prev. En la siguiente llamada a CopyTicks, consulte los ticks desde el momento prev y omita (ignore en el array) el número de ticks «antiguos».

Sin embargo, tenga en cuenta que el algoritmo anterior no es necesario para todos los programas MQL. La mayoría de ellos no analizan cada tick, mientras que el estado actual del precio correspondiente al último tick conocido se difunde rápidamente a los gráficos del modelo de eventos y está disponible por medio de las propiedades de símbolo y gráfico.

Para demostrar las funciones, veamos dos ejemplos, uno para cada función. Para ambos ejemplos se ha desarrollado un archivo de encabezado común TickEnum.mqh, en el que las constantes anteriores para las banderas de tic solicitadas y las banderas de estado de tic se resumen en dos enumeraciones.

enum COPY_TICKS
{
   ALL_TICKS = /* -1 */ COPY_TICKS_ALL,    // all ticks
   INFO_TICKS = /* 1 */ COPY_TICKS_INFO,   // info ticks
   TRADE_TICKS = /* 2 */ COPY_TICKS_TRADE// trade ticks
};
 
enum TICK_FLAGS
{
   TF_BID = /* 2 */ TICK_FLAG_BID
   TF_ASK = /* 4 */ TICK_FLAG_ASK
   TF_BID_ASK = TICK_FLAG_BID | TICK_FLAG_ASK
   
   TF_LAST = /* 8 */ TICK_FLAG_LAST
   TF_BID_LAST = TICK_FLAG_BID | TICK_FLAG_LAST
   TF_ASK_LAST = TICK_FLAG_ASK | TICK_FLAG_LAST
   TF_BID_ASK_LAST = TF_BID_ASK | TICK_FLAG_LAST
   
   TF_VOLUME = /* 16 */ TICK_FLAG_VOLUME
   TF_LAST_VOLUME = TICK_FLAG_LAST | TICK_FLAG_VOLUME
   TF_BID_VOLUME = TICK_FLAG_BID | TICK_FLAG_VOLUME
   TF_BID_ASK_VOLUME = TF_BID_ASK | TICK_FLAG_VOLUME
   TF_BID_ASK_LAST_VOLUME = TF_BID_ASK | TF_LAST_VOLUME
   
   TF_BUY = /* 32 */ TICK_FLAG_BUY
   TF_SELL = /* 64 */ TICK_FLAG_SELL
   TF_BUY_SELL = TICK_FLAG_BUY | TICK_FLAG_SELL
   TF_LAST_VOLUME_BUY = TF_LAST_VOLUME | TICK_FLAG_BUY
   TF_LAST_VOLUME_SELL = TF_LAST_VOLUME | TICK_FLAG_SELL
   TF_LAST_VOLUME_BUY_SELL = TF_BUY_SELL | TF_LAST_VOLUME
   ...
};

El uso de enumeraciones hace que la comprobación de tipos en el código fuente sea más rigurosa, y también facilita la visualización del significado de los valores como cadenas con EnumToString. Además, se han añadido las combinaciones de banderas más populares a la enumeración TICK_FLAGS para optimizar la visualización o el filtrado de los ticks. No es posible dar a los elementos de enumeración los mismos nombres que a las constantes integradas, ya que se produce un conflicto de nombres.

El primer script SeriesTicksStats.mq5 utiliza la función CopyTicks para contar el número de ticks con diferentes banderas establecidas a una profundidad de historia dada.

En los parámetros de entrada, puede establecer el símbolo de trabajo (símbolo del gráfico por defecto), el número de ticks analizados y el modo de solicitud de COPY_TICKS.

input string WorkSymbol = NULL// Symbol (leave empty for current)
input int TickCount = 10000;
input COPY_TICKS TickType = ALL_TICKS;

Las estadísticas de la aparición de cada bandera (cada bit de la máscara de bits) en las propiedades del tick se recogen en la estructura TickFlagStats.

struct TickFlagStats
{
   TICK_FLAGS flag// mask with bit (one or more)
   int count;       // number of ticks with this bit in the flags field 
   string legend;   // bit description
};

La función OnStart describe un array de estructuras TickFlagStats con un tamaño de 8 elementos: 6 de ellos (del 1 al 6 inclusive) se utilizan para los bits TICK_FLAG correspondientes, y los otros dos se utilizan para combinaciones de bits (véase más abajo). Utilizando un bucle simple se rellenan los elementos para los bits/banderas estándar individuales en el array, y después del bucle se rellenan dos máscaras combinadas (en el elemento 0º, los ticks se contarán con un cambio simultáneo de Bid y Ask, y en el elemento 7º contamos los ticks con transacciones simultáneas de Buy y Sell).

void OnStart()
{
   TickFlagStats stats[8] = {};
   for(int k = 1k < 7; ++k)
   {
      stats[k].flag = (TICK_FLAGS)(1 << k);
      stats[k].legend = EnumToString(stats[k].flag);
   }
   stats[0].flag = TF_BID_ASK;  // combination of BID AND ASK
   stats[7].flag = TF_BUY_SELL// combination of BUY AND SELL
   stats[0].legend = "TF_BID_ASK (COMBO)";
   stats[7].legend = "TF_BUY_SELL (COMBO)";
   ...

Confiaremos todo el trabajo principal a la función auxiliar CalcTickStats, pasándole parámetros de entrada y un array preparado para recopilar estadísticas. Después, queda mostrar los números contados en el diario.

   const int count = CalcTickStats(TickType0TickCountstats);
   PrintFormat("%s stats requested: %d (got: %d) on %s"
      EnumToString(TickType),
      TickCountcountStringLen(WorkSymbol) > 0 ? WorkSymbol : _Symbol);
   ArrayPrint(stats);
}

La propia función CalcTickStats es muy interesante.

int CalcTickStats(const string symbolconst COPY_TICKS type
   const datetime startconst int count
   TickFlagStats &stats[])
{
   MqlTick ticks[];
   ResetLastError();
   const int nf = ArraySize(stats);
   const int nt = CopyTicks(symboltickstypestart * 1000count);
   if(nt > -1 && _LastError == 0)
   {
      PrintFormat("Ticks range: %s'%03d - %s'%03d"
         TimeToString(ticks[0].timeTIME_DATE TIME_SECONDS),
         ticks[0].time_msc % 1000
         TimeToString(ticks[nt - 1].timeTIME_DATE TIME_SECONDS),
         ticks[nt - 1].time_msc % 1000);
      
      // loop through ticks
      for(int j = 0j < nt; ++j)
      {
         // loop through TICK_FLAGs (2 4 8 16 32 64) and combinations
         for(int k = 0k < nf; ++k)
         {
            if((ticks[j].flags & stats[k].flag) == stats[k].flag)
            {
               stats[k].count++;
            }
         }
      }
   }
   return nt;
}

Utiliza CopyTicks para solicitar ticks del symbol especificado, de un type específico, a partir de la fecha start, en la cantidad de elementos count. El parámetro start es del tipo datetime, y debe convertirse a milisegundos cuando se pase a CopyTicks. Recuerde que si start = 0 (que es el caso aquí, en la función OnStart), el sistema devolverá los últimos ticks, contando desde la hora actual. Por lo tanto, cada vez que se llame al script, lo más probable es que las estadísticas se actualicen debido a la llegada de nuevos ticks. Las únicas excepciones posibles son las solicitudes en fin de semana o las de instrumentos de poca liquidez.

Si CopyTicks se ejecuta sin errores, nuestro código registra el intervalo de tiempo cubierto por los ticks recibidos.

Por último, en el bucle, recorremos todos los ticks y contamos el número de coincidencias bit a bit en las banderas de ticks y las máscaras de elementos del array de estructuras estadísticas TickFlagStats preparado de antemano.

Es aconsejable ejecutar el script en instrumentos en los que haya información sobre volúmenes y operaciones reales para probar todos los modos de la enumeración COPY_TICKS (recuerde que corresponden a las constantes del parámetro flags en CopyTicks: COPY_TICKS_INFO, COPY_TICKS_TRADE y COPY_TICKS_ALL).

He aquí un ejemplo de entradas de registro al solicitar estadísticas para 100000 ticks de todo tipo (TickType = ALL_TICKS):

Ticks range: 2021.10.11 07:39:53'278 - 2021.10.13 11:51:29'428
ALL_TICKS stats requested: 100000 (got: 100000) on YNDX.MM
    [flag] [count]              [legend]
[0]      6   11323 "TF_BID_ASK (COMBO)" 
[1]      2   26700 "TF_BID"             
[2]      4   33541 "TF_ASK"             
[3]      8   51082 "TF_LAST"            
[4]     16   51082 "TF_VOLUME"          
[5]     32   25654 "TF_BUY"             
[6]     64   28802 "TF_SELL"            
[7]     96    3374 "TF_BUY_SELL (COMBO)"

Esto es lo que se obtiene al solicitar sólo ticks de información (TickType = INFO_TICKS).

Ticks range: 2021.10.07 07:08:24'692 - 2021.10.13 11:54:01'297
INFO_TICKS stats requested: 100000 (got: 100000) on YNDX.MM
    [flag] [count]              [legend]
[0]      6   23115 "TF_BID_ASK (COMBO)" 
[1]      2   60860 "TF_BID"             
[2]      4   62255 "TF_ASK"             
[3]      8       0 "TF_LAST"            
[4]     16       0 "TF_VOLUME"          
[5]     32       0 "TF_BUY"             
[6]     64       0 "TF_SELL"            
[7]     96       0 "TF_BUY_SELL (COMBO)"

Aquí puede comprobar la exactitud de los cálculos: la suma de los números de TF_BID y TF_ASK menos las coincidencias TF_BID_ASK (COMBO) da exactamente 100000 (número total de ticks). Los ticks con volúmenes y precios Last no han entrado en el resultado, como era de esperar.

Ahora vamos a ejecutar el script de nuevo, exclusivamente para ticks de trading (TickType = TRADE_TICKS).

Ticks range: 2021.10.06 20:43:40'024 - 2021.10.13 11:52:40'044
TRADE_TICKS stats requested: 100000 (got: 100000) on YNDX.MM
    [flag] [count]              [legend]
[0]      6       0 "TF_BID_ASK (COMBO)" 
[1]      2       0 "TF_BID"             
[2]      4       0 "TF_ASK"             
[3]      8  100000 "TF_LAST"            
[4]     16  100000 "TF_VOLUME"          
[5]     32   51674 "TF_BUY"             
[6]     64   55634 "TF_SELL"            
[7]     96    7308 "TF_BUY_SELL (COMBO)"

Todos los ticks tenían las banderas TF_LAST y TF_VOLUME, y la mezcla de direcciones de trading se ha producido 7308 veces. De nuevo, la suma de TF_BUY y TF_SELL menos su combinación coincide con el número total de ticks.

El segundo script SeriesTicksDeltaVolume.mq5 utiliza la función CopyTicksRange para calcular los deltas de volumen en cada barra. Como sabe, las cotizaciones de MetaTrader 5 contienen sólo volúmenes impersonales, en los que las compras y las ventas se combinan en un valor para cada barra. Sin embargo, la presencia de un historial de ticks reales permite calcular por separado las sumas de los volúmenes de compra y venta, así como su diferencia. Estas características son factores adicionales importantes para tomar decisiones de trading.

Los parámetros de entrada contienen ajustes similares a los del primer script, en concreto, el nombre del símbolo para el análisis y el modo de solicitud de ticks. Es cierto que, en este caso, deberá especificar además un marco temporal, ya que los deltas de volumen deben calcularse barra por barra. Por defecto se utilizará el marco temporal del gráfico actual. El parámetro BarCount permite especificar el número de barras calculadas.

input string WorkSymbol = NULL// Symbol (leave empty for current)
input ENUM_TIMEFRAMES TimeFrame = PERIOD_CURRENT;
input int BarCount = 100;
input COPY_TICKS TickType = INFO_TICKS;

Las estadísticas de cada barra se almacenan en la estructura DeltaVolumePerBar.

struct DeltaVolumePerBar
{
   datetime time// bar time
   ulong buy;     // net volume of buy operations
   ulong sell;    // net sell operations
   long delta;    // volume difference
};

La función OnStart describe un array de estructuras de este tipo, mientras que su tamaño se asigna para el número especificado de barras.

void OnStart()
{
   DeltaVolumePerBar deltas[];
   ArrayResize(deltasBarCount);
   ZeroMemory(deltas);
   ...

Y aquí está el algoritmo principal.

   for(int i = 0i < BarCount; ++i)
   {
      MqlTick ticks[];
      const datetime next = iTime(WorkSymbolTimeFramei);
      const datetime prev = iTime(WorkSymbolTimeFramei + 1);
      ResetLastError();
      const int n = CopyTicksRange(WorkSymbolticksCOPY_TICKS_ALL
         prev * 1000next * 1000 - 1);
      if(n > -1 && _LastError == 0)
      {
         ...
      }
   }

En el bucle a través de las barras obtenemos el intervalo de tiempo para cada barra: prev y next (la 0ª barra incompleta no se procesa). Cuando llame a CopyTicksRange para este intervalo, recuerde traducir datetime a milisegundos y restar 1 milisegundo desde el borde derecho, ya que este tiempo pertenece a la barra siguiente. En ausencia de errores, procesamos el array de ticks recibidos en un bucle.

         deltas[i].time = prev// remember the bar time
         for(int j = 0j < n; ++j)
         {
            // when real volumes can be available, take them from ticks
            if(TickType == TRADE_TICKS)
            {
               // separately accumulate volumes for buy and sell deals
               if((ticks[j].flags & TICK_FLAG_BUY) != 0)
               {
                  deltas[i].buy += ticks[j].volume;
               }
               if((ticks[j].flags & TICK_FLAG_SELL) != 0)
               {
                  deltas[i].sell += ticks[j].volume;
               }
            }
            // when there are no real volumes, we evaluate them by the price movement up/down
            else
            if(TickType == INFO_TICKS && j > 0)
            {
               if((ticks[j].flags & (TICK_FLAG_ASK | TICK_FLAG_BID)) != 0)
               {
                  const long d = (long)(((ticks[j].ask + ticks[j].bid)
                               - (ticks[j - 1].ask + ticks[j - 1].bid)) / _Point);
                  if(d > 0deltas[i].buy += d;
                  else deltas[i].sell += -d;
               }
            }
         }
         deltas[i].delta = (long)(deltas[i].buy - deltas[i].sell);

Si en la configuración del script se solicitó el análisis por ticks de trading (TRADE_TICKS), compruebe la presencia de las banderas TICK_FLAG_BUY y TICK_FLAG_SELL, y si al menos una de ellos está activada, tenga en cuenta el volumen del campo volume en la variable correspondiente de la estructura DeltaVolumePerBar. Este modo sólo es adecuado para instrumentos bursátiles. En el caso de los instrumentos de Forex, las banderas de volúmenes y dirección de las operaciones de trading no se rellenan, por lo que debe utilizarse un enfoque diferente.

Si en los ajustes se especifican los ticks de información (INFO_TICKS) disponibles para todos los instrumentos, el algoritmo se basa en las siguientes reglas empíricas. Como sabe, la compra hace subir el precio y la venta lo hace bajar. Por lo tanto, podemos suponer que si el precio medio Ask+Bid ha subido en un nuevo tick con respecto al anterior, se ha ejecutado una operación de compra sobre el mismo, y si el precio ha bajado, se ha producido una operación de venta. El volumen puede estimarse aproximadamente como el número de puntos pasados (_Point).

Los resultados de los cálculos se muestran simplemente como un array de estructuras con las estadísticas recopiladas.

   PrintFormat("Delta volumes per intraday bar\nProcessed %d bars on %s %s %s"
      BarCountStringLen(WorkSymbol) > 0 ? WorkSymbol : _Symbol
      EnumToString(TimeFrame == PERIOD_CURRENT ? _Period : TimeFrame),
      EnumToString(TickType));
   ArrayPrint(deltas);
}

A continuación se muestran algunos registros de los modos TRADE_TICKS e INFO_TICKS.

Delta volumes per intraday bar
Processed 100 bars on YNDX.MM PERIOD_H1 TRADE_TICKS
                  [time] [buy] [sell] [delta]
[ 0] 2021.10.13 11:00:00  7912  14169   -6257
[ 1] 2021.10.13 10:00:00  8470  11467   -2997
[ 2] 2021.10.13 09:00:00 10830  13047   -2217
[ 3] 2021.10.13 08:00:00 23682  19478    4204
[ 4] 2021.10.13 07:00:00 14538  11600    2938
[ 5] 2021.10.12 20:00:00  2132   4786   -2654
[ 6] 2021.10.12 19:00:00  9173  13775   -4602
[ 7] 2021.10.12 18:00:00  1297   1719    -422
[ 8] 2021.10.12 17:00:00  3803   2995     808
[ 9] 2021.10.12 16:00:00  6743   7045    -302
[10] 2021.10.12 15:00:00 17286  37286  -20000
[11] 2021.10.12 14:00:00 33263  54157  -20894
[12] 2021.10.12 13:00:00 56060  52659    3401
[13] 2021.10.12 12:00:00 12832  10489    2343
[14] 2021.10.12 11:00:00  7530   6092    1438
[15] 2021.10.12 10:00:00  6268  25201  -18933
...

Los valores, por supuesto, son significativamente diferentes, pero la cuestión no está en los valores absolutos: en ausencia de volúmenes de intercambio, incluso tal emulación de la dinámica delta y de división nos permite observar el comportamiento del mercado desde un ángulo diferente.

Delta volumes per intraday bar
Processed 100 bars on YNDX.MM PERIOD_H1 INFO_TICKS
                  [time]  [buy] [sell] [delta]
[ 0] 2021.10.13 11:00:00   1939   2548    -609
[ 1] 2021.10.13 10:00:00   2222   2400    -178
[ 2] 2021.10.13 09:00:00   2903   2909      -6
[ 3] 2021.10.13 08:00:00   4489   4060     429
[ 4] 2021.10.13 07:00:00   4999   4285     714
[ 5] 2021.10.12 20:00:00   1444   1556    -112
[ 6] 2021.10.12 19:00:00   5464   5867    -403
[ 7] 2021.10.12 18:00:00   2522   2653    -131
[ 8] 2021.10.12 17:00:00   2111   2017      94
[ 9] 2021.10.12 16:00:00   4617   6096   -1479
[10] 2021.10.12 15:00:00   5716   5411     305
[11] 2021.10.12 14:00:00  10044  10866    -822
[12] 2021.10.12 13:00:00  10893  11178    -285
[13] 2021.10.12 12:00:00   2822   2783      39
[14] 2021.10.12 11:00:00   2070   1936     134
[15] 2021.10.12 10:00:00   2053   2303    -250
...

Cuando aprendamos a crear indicadores podremos incrustar este algoritmo en uno de ellos (véase IndDeltaVolume.mq5 en la sección Esperar datos y gestionar la visibilidad) para visualizar los deltas directamente en el gráfico.