- Símbolos y marcos temporales
- Aspectos técnicos de la organización y el almacenamiento de series temporales
- Obtención de características de arrays de precios
- Número de barras disponibles (Bars/iBars)
- Índice de la barra de búsqueda por tiempo (iBarShift)
- Visión general de las funciones Copy para obtener arrays de comillas
- Obtener cotizaciones como un array de estructuras MqlRates
- Solicitud independiente de arrays de precios, volúmenes, diferenciales, tiempo
- Lectura de precio, volumen, diferencial y hora por índice de barras
- Encontrar los valores máximo y mínimo de una serie temporal
- Trabajar con arrays de ticks reales en estructuras MqlTick
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
|
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 |
COPY_TICKS_TRADE |
2 |
Ticks con cambios de Last y Volume |
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()
|
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
|
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)
|
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
|
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()
|
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(TickType, 0, TickCount, stats);
|
La propia función CalcTickStats es muy interesante.
int CalcTickStats(const string symbol, const COPY_TICKS type,
|
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
|
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
|
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
|
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)
|
Las estadísticas de cada barra se almacenan en la estructura DeltaVolumePerBar.
struct DeltaVolumePerBar
|
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()
|
Y aquí está el algoritmo principal.
for(int i = 0; i < BarCount; ++i)
|
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
|
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",
|
A continuación se muestran algunos registros de los modos TRADE_TICKS e INFO_TICKS.
Delta volumes per intraday bar
|
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
|
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.