English Русский 中文 Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Creación de indicadores de tick en MQL5

Creación de indicadores de tick en MQL5

MetaTrader 5Indicadores | 16 diciembre 2013, 11:54
3 595 0
Denis Zyatkevich
Denis Zyatkevich

Introducción

En el trading, es aconsejable disponer de tanta información como sea posible para poder tener una perspectiva adecuada de los cambios en los precios. Para ello puede usar el gráfico de tick. Vamos a crear un gráfico de tick en MQL5.

Este artículo describe cómo crear dos indicadores: un gráfico de precios de tick y un gráfico de "velas de tick" que representa las velas que contienen un determinado número de ticks. Cada uno de los indicadores considerados escriben el valor del precio en el archivo para la construcción de los datos del indicador tras el reinicio del terminal de cliente (estos datos pueden ser también usados por los demás programas).

Crear un indicador de tick

Vamos a escribir un indicador de tick en MQL5 que representa el dato del tick sobre el gráfico. En la Figura 1 se muestra un ejemplo de este indicador:

Figura 1. Ejemplo de un gráfico de tick

El indicador traza dos líneas: precio de compra y precio de venta. La representación de cada uno de estos precios puede desactivarse en las opciones del indicador.

El indicador guarda los precios del símbolo actual recibidos del broker en un archivo de texto con el siguiente formato: tiempo del servidor, precio de compra y precio de venta.

2010.03.26 19:43:02 1.33955 1.33968

El nombre del archivo se corresponde con el nombre del instrumento financiero (por ejemplo EURUSD.txt). Los archivos se encuentran en la siguiente carpeta: MT5_Folder\MQL5\Files. La carpeta adicional para el archivo y el nombre del archivo pueden establecerse en las opciones del indicador (puede ser útil si hay varios indicadores vinculados a los gráficos con el mismo símbolo).

Para crear un indicador, ejecute el terminal de cliente de MetaTrader 5 y el Editor del Lenguaje MetaQuotes presionado la tecla F4. Vamos a comenzar a escribir el código de un programa.

Especificaremos que el indicador debe ser trazado en una ventana independiente bajo el gráfico de precio:

// indicator in a separate window
#property indicator_separate_window

Las dos líneas de indicadores (precios de compra y de venta, respectivamente) deberían representarse y debemos usar dos trazados:

// two graphic plots are used: for Bid and Ask lines
#property indicator_plots 2

Debemos especificar dos buffers de indicador que contengan los datos a trazar en el gráfico:

// two indicator's buffers
#property indicator_buffers 2

Para cada una de las líneas de los indicadores, vamos a definir un tipo de representación DRAW_LINE, estilo de representación STYLE_SOLID (línea continua) y las etiquetas de texto "compra" y "venta".

// drawing type of a Bid line
#property indicator_type1 DRAW_LINE
// drawing color of a Bid line
#property indicator_color1 Red
// drawing style of a Bid line
#property indicator_style1 STYLE_SOLID
// text label of a Bid line
#property indicator_label1 "Bid"
// drawing type of an Ask line
#property indicator_type2 DRAW_LINE
// drawing color of an Ask line
#property indicator_color2 Blue
// drawing style of an Ask line
#property indicator_style2 STYLE_SOLID
// text label of an Ask line
#property indicator_label2 "Ask"

 Vamos a especificar las variables de entrada cuyos valores pueden ser cambiados por el usuario en el menú de opciones del indicador.

// the BidLineEnable indicates showing of a Bid line
input bool BidLineEnable=true; // Show Bid Line
// the AskLineEnable indicates showing of an Ask line
input bool AskLineEnable=true; // Show Ask Line
// the path_prefix defines a path and file name prefix
input string path_prefix=""; // FileName Prefix

las variables BidLineEnable  y AskLineEnable  nos permiten habilitar y deshabilitar la visibilidad de las líneas de compra y venta en el indicador. La variable path_prefix nos permite especificar el prefijo del nombre de archivo situado antes del nombre del archivo. Usando esta variable podemos especificar también la ruta a un subdirectorio. Por ejemplo, si path_prefix = "MyBroker/test_", la ruta hacia los archivos será: "MetaTrader5_Folder\MQL5\Files\MyBroker", para el símbolo "EURUSD" el nombre del archivo será "test_EURUSD.txt".

Vamos a declarar las variables a nivel global que serán usadas en varias funciones del indicador. Los valores de estas variables se guardan entre las llamadas del indicador:

// the tick_stored variable is a number of served quotes
int ticks_stored;
// the BidBuffer[] and AskBuffer[] arrays - are indicator's buffers
double BidBuffer[],AskBuffer[];

La variable tick_stored se usará para almacenar el número de cotizaciones disponibles. BidBuffer[] y AskBuffer[]  son matrices dinámicas usadas como buffers de indicador, el dato de precio, y representadas en el gráfico como líneas de compra y venta que se guardan en estos buffers.

La función OnInit indica que las matrices BidBuffer[]  y AskBuffer[] contienen los datos para el trazado. Vamos a especificar que los datos con valores del buffer de indicador iguales a cero no deben ser representados en el gráfico.

void OnInit()
  {
   // the BidBuffer[] is an indicator buffer
   SetIndexBuffer(0,BidBuffer,INDICATOR_DATA);
   // the AskBuffer[] is an indicator buffer
   SetIndexBuffer(1,AskBuffer,INDICATOR_DATA);
   // setting EMPTY_VALUE for a Bid line
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0);
   // setting EMPTY_VALUE for an Ask line
   PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0);
  }

A continuación creamos la función OnCalculate y listamos todos los parámetros pasados a la función cuando ésta es llamada: 

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])

Vamos a declarar las variables:

// the file_handle variable is a file handle
// the BidPosition and AskPosition - are positions of Bid and Ask prices in the string;
// the line_string_len is a length of a string, read from the file, i is a loop counter;
int file_handle,BidPosition,AskPosition,line_string_len,i;
// the last_price_bid is the last Bid quote
double last_price_bid=SymbolInfoDouble(Symbol(),SYMBOL_BID);
// the last_price_ask is the last Ask quote
double last_price_ask=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
// the filename is a name of a file, the file_buffer is a string, 
// used as a buffer for reading and writing of string data
string filename,file_buffer;

La variables file_handle de un tipo entero se usará para almacenar el controlador del archivo en las operaciones del archivo; BidPosition y AskPosition se usarán para almacenar las posiciones iniciales de los precios de compra y venta en el string; line_string_len se usará para la longitud del string leído del archivo, y la variable i se usará como un contador de lazo. Los valores de los últimos precios de compra y venta recibidos se guardan en las variables last_price_bid y last_price_ask. La variable del string del nombre de archivo se usa para almacenar el nombre del archivo y la variable file_buffer es un string usado para leer y escribir en el archivo.

El nombre del archivo se construye a partir de la variable path_prefix, nombre del instrumento financiero y de extensión de archivo ".txt". Es preferible usar la función StringConcatenate en lugar de una concatenación de strings mediante el operador adición, ya que funciona más rápido y economiza más memoria.

// File name formation from the path_prefix variable, name
// of financial instrument and ".Txt" symbols
StringConcatenate(filename,path_prefix,Symbol(),".txt");

Abrimos el archivo usando la función FileOpen para su uso posterior:

// Opening a file for reading and writing, codepage ANSI, shared reading mode
file_handle=FileOpen(filename,FILE_READ|FILE_WRITE|FILE_ANSI|FILE_SHARE_READ);

Utilizamos las etiquetas FILE_READ y FILE_WRITE ya que leeremos y escribiremos los datos en el archivo. La etiqueta FILE_ANSI indica que va a usar una página de código ANSI (el tipo por defecto es Unicode). La etiqueta FILE_SHARE_READ significa que se permite a otras aplicaciones el acceso compartido mientras se trabaja sobre ella.

Cuando se ejecuta por primera vez el indicador no hay ningún dato en este (o el período del gráfico ha cambiado):

// At first execution of OnCalculate function, we are reading the quotes from a file
 if(prev_calculated==0)
  {
   // Reading the first line from the file and determine the length of a string
   line_string_len=StringLen(FileReadString(file_handle))+2;
   // if file is large (contains more quotes than rates_total/2)
   if(FileSize(file_handle)>(ulong)line_string_len*rates_total/2)
     {
      // Setting file pointer to read the latest rates_total/2 quotes
      FileSeek(file_handle,-line_string_len*rates_total/2,SEEK_END);
      // Moving file pointer to the beginning of the next line
      FileReadString(file_handle);
     }
   // if file size is small
   else
     {
      // Moving file pointer at the beginning of a file
      FileSeek(file_handle,0,SEEK_SET);
     }
   // Reset the counter of stored quotes
   ticks_stored=0;
   // Reading until the end of the file
   while(FileIsEnding(file_handle)==false)
    {
      // Reading a string from the file
      file_buffer=FileReadString(file_handle);
      // Processing of string if its length is larger than 6 characters
      if(StringLen(file_buffer)>6)
        {
         // Finding the start position of Bid price in the line
         BidPosition=StringFind(file_buffer," ",StringFind(file_buffer," ")+1)+1;
         // Finding the start position of Ask price in the line
         AskPosition=StringFind(file_buffer," ",BidPosition)+1;
         // If the Bid line should be plotted, adding this value to BidBuffer[] array
         if(BidLineEnable) 
         BidBuffer[ticks_stored]=StringToDouble(StringSubstr(file_buffer,BidPosition,AskPosition-BidPosition-1));
         // If the Ask line should be plotted, adding this value to AskBuffer[] array
         if(AskLineEnable) 
         AskBuffer[ticks_stored]=StringToDouble(StringSubstr(file_buffer,AskPosition));
         // Increasing the counter of stored quotes
         ticks_stored++;
        }
     }
  }

Limitaremos el número de cotizaciones que deben leerse del archivo a la mitad del número de barras del gráfico. Primero leemos el string del archivo y determinamos su longitud. Al final de cada línea hay dos caracteres adicionales con los códigos 10 y 13 ("nueva línea" y "tecla enter") y por tanto debemos incrementar la longitud de la línea en 2.

Asumiremos que la longitud media de las restantes líneas del archivo es la misma. Si la longitud del archivo es mayor que el producto de la longitud de una línea por el número de rates_total/2 (por ejemplo, si el archivo contiene más cotizaciones que rates_total/2), leeremos solo rates_total/2 cotizaciones. Para hacer esto, establecemos el puntero de archivo a una distancia igual al producto de la longitud de un string por rates_total/2 (desde el final del archivo) y leemos una línea del archivo para alinear el puntero de archivo al principio de una línea.

Nótese que comparamos dos valores usando el operador if. Hay diferentes tipos: la longitud del archivo es de tipo ulong; la expresión a la derecha es del tipo int. Por tanto, hacemos que la expresión del lado derecho sea del tipo ulong.

Si el archivo contiene menos cotizaciones que rates_total/2, entonces movemos el puntero de archivo al principio. 

Establecemos el contador de cotizaciones a cero y leemos las líneas de un archivo hasta llegar al final. El procesamiento de los strings se realiza para aquellos que tienen una longitud mayor de seis caracteres -es la longitud mínima, y que contienen un carácter para la fecha, el tiempo, compra, venta y los separadores entre ellos. Extraemos los valores de compra y venta de un string y leemos en el archivo si la línea correspondiente debe trazarse y a continuación incrementamos el contador de cotizaciones.

Si el dato fue leído con anterioridad, movemos el puntero de archivo al final usando la función FileSeek (el nuevo dato es escrito en el archivo). Usando la función StringConcatenate generamos el string que será escrito en un archivo a través de la función FileWrite. Añadimos nuevos valores de los precios de compra y venta a las matrices BidBuffer[]  y AskBuffer[] si las líneas correspondientes deben trazarse e incrementamos el contador de cotizaciones.

  // If the data have been read before
else
  {
   // Moving file pointer at the end of the file
   FileSeek(file_handle,0,SEEK_END);
   // Forming a string, that should be written to the file
   StringConcatenate(file_buffer,TimeCurrent()," ",DoubleToString(last_price_bid,_Digits)," ",DoubleToString(last_price_ask,_Digits));
   // Writing a string to the file
   FileWrite(file_handle,file_buffer);
   // If the Bid line should be plotted, adding the last Bid price to the BidBuffer[] array
   if(BidLineEnable) BidBuffer[ticks_stored]=last_price_bid;
   // If the Ask line should be plotted, adding the last Ask price to the AskBuffer[] array
   if(AskLineEnable) AskBuffer[ticks_stored]=last_price_ask;
   // Increasing the quotes counter
   ticks_stored++;
  }

Uno podría preguntarse ¿por qué no leer el dato de un archivo dentro de la función OnInit ? La razón es la siguiente: la longitud de las matrices dinámicas BidBuffer[] y AskBuffer[]  no ha sido definida y esta indica cuándo se llama a la función OnCalculate.

Cerramos el archivo abierto anteriormente:

// Closing the file
FileClose(file_handle);

En caso de que el contador de cotizaciones sea igual o mayor que el número de barras del gráfico después de la lectura del archivo o tras añadirlo a las matrices BidBuffer[] y AskBuffer[], se eliminarán la mitad de las cotizaciones antiguas y las que queden se desplazarán a su lugar.

// If number of quotes is more or equal than number of bars in the chart
if(ticks_stored>=rates_total)
  {
   // Removing the first tick_stored/2 quotes and shifting remaining quotes
   for(i=ticks_stored/2;i<ticks_stored;i++)
     {
      // If the Bid line should be plotted, shifting the values of BidBuffer[] array on tick_stored/2
      if(BidLineEnable) BidBuffer[i-ticks_stored/2]=BidBuffer[i];
      // If the Ask line should be plotted, shifting the values of AskBuffer[] array on tick_stored/2
      if(AskLineEnable) AskBuffer[i-ticks_stored/2]=AskBuffer[i];
     }
   // Changing the value of a counter
   ticks_stored-=ticks_stored/2;
  }

Las matrices BidBuffer[] y AskBuffer[] de los buffers del indicador no son series de tiempo y, por tanto, los elementos recientes tienen el índice igual a tick_stored-1 y la barra del gráfico tiene un índice igual a rates_total-1. Para combinarlas al mismo nivel vamos a cambiar la línea del indicador usando la función PlotIndexSetInteger:

// Shifting the Bid line to align with the price chart
PlotIndexSetInteger(0,PLOT_SHIFT,rates_total-ticks_stored);
// Shifting the Ask line to align with the price chart
PlotIndexSetInteger(1,PLOT_SHIFT,rates_total-ticks_stored);

Los valores de los precios recibidos se almacenan en BidBuffer[] con el índice igual a AskBuffer[]  (si las líneas correspondientes deben ser trazadas) para mostrarlos en la esquina superior izquierda de la ventana del indicador.

// If the Bid line should be plotted, placing the value to the last element 
// of BidBuffer [] array to show the last Bid price in the indicator's window  
if(BidLineEnable) BidBuffer[rates_total-1]=last_price_bid;
// If the Ask line should be plotted, placing the value to the last element 
// of AskBuffer [] array to show the last Ask price in the indicator's window
if(AskLineEnable) AskBuffer[rates_total-1]=last_price_ask;

La ejecución de la función OnCalculate se produce al devolver rates_total (es posible devolver cualquier número diferente a cero) y el código de la función termina con llaves.

// Return from OnCalculate(), return a value, different from zero   
return(rates_total);
}

Ya hemos escrito el indicador de tick. Todo el código fuente del indicador puede descargarse en el enlace al final del artículo.

Crear el indicador de "velas de tick".

Ahora vamos a crear el indicador que se encarga de representar las "velas de tick". Al contrario de lo que ocurre con los gráficos de velas convencionales, donde cada vela se corresponde con un determinado período de tiempo, los gráficos de "velas de tick" tienen una estructura diferente: cada vela tiene un número predefinido de ticks y estos son recibidos del broker (velas de igual volumen). Este indicador tiene el aspecto que se muestra en la Figura 2:


Figura 2. El indicador de "velas de tick"

El indicador de "velas de tick" y el indicador de velas antes descritos escriben todas las cotizaciones recibidas en el archivo. El formato de datos y la información sobre la ubicación del archivo son los mismos. La ruta del archivo, prefijo del nombre, número de ticks para una vela y tipo de precio (compra o venta) pueden establecerse en las opciones del indicador.

Para crear un indicador ejecute el terminal de cliente de MetaTrader 5 y el Editor del Lenguaje MetaQuotes presionado la tecla F4.

Vamos a especificar que el indicador debe ser trazado en una ventana independiente:

// Indicator is plotted in a separate window
#property indicator_separate_window

El indicador solo tiene un trazado gráfico: velas de colores.

// One graphic plot is used, color candles
#property indicator_plots 1

Necesitamos cuatro buffers para mostrar las velas de colores y almacenar los valores de los datos sobre precio (abierto, alto, bajo y cerrar) para cada vela. También necesitamos un buffer adicional para guardar los índices de los colores de las velas.

// We need 4 buffers for OHLC prices and one - for the index of color
#property indicator_buffers 5

Vamos a especificar el tipo de representación: DRAW_COLOR_CANDLES - velas de colores.

// Specifying the drawing type - color candles
#property indicator_type1 DRAW_COLOR_CANDLES

Vamos ahora a especificar los colores que se van a usar para las velas:

// Specifying the colors for the candles
#property indicator_color1 Gray,Red,Green

Creamos el price_types de tipo de enumeración que contiene uno de los siguientes valores: compra o venta:

/ / Declaration of the enumeration
enum price_types
  (
   Bid,
   Ask
  )

Especificamos los parámetros de entrada que pueden ser cambiados por el usuario a través del menú de opciones del indicador:

// The ticks_in_candle input variable specifies the number of ticks,
// corresponding to one candle
input int ticks_in_candle=16; //Tick Count in Candles
// The applied_price input variable of price_types type indicates 
// the type of the data, that is used in the indicator: Bid or Ask prices.
input price_types applied_price=0; // Price
// The path_prefix input variable specifies the path and prefix to the file name
input string path_prefix=""; // FileName Prefix

La variable ticks_in_candle indica el número de ticks para una vela. La variable applied_price indica el tipo de precio utilizado para la construcción de las velas: compra o venta. El directorio y el prefijo del nombre del archivo de los datos de los ticks históricos pueden indicarse en la variable path_prefix.

Las variables con valores que pueden situarse entre las llamadas del indicador se declaran a nivel global.

// The ticks_stored variable contains the number of stored quotes
int ticks_stored;
// The TicksBuffer [] array is used to store the incoming prices
// The OpenBuffer [], HighBuffer [], LowBuffer [] and CloseBuffer [] arrays
// are used to store the OHLC prices of the candles
// The ColorIndexBuffer [] array is used to store the index of color candles
double TicksBuffer[],OpenBuffer[],HighBuffer[],LowBuffer[],CloseBuffer[],ColorIndexBuffer[];

La variable ticks_stored se usa para almacenar el número de cotizaciones disponibles. La matriz TicksBuffer[] se usa para almacenar las cotizaciones recibidas. Las matrices OpenBuffer[], HighBuffer[], LowBuffer[] y CloseBuffer[] se usan para almacenar los precios de las velas (apertura, más alto, más bajo y cierre) que se representarán en el gráfico. La matriz ColorIndexBuffer[]  se usa para almacenar el índice del color de las velas.

La función OnInit indica que las matrices OpenBuffer[], HighBuffer[], LowBuffer[] y CloseBuffer[] se usan como buffers de indicador; la matriz ColorIndexBuffer[] contiene el índice del color de las velas; y la matriz TicksBuffer[] se usa para cálculos intermedios:

void OnInit()
  {
   // The OpenBuffer[] array is an indicator buffer
   SetIndexBuffer(0,OpenBuffer,INDICATOR_DATA);
   // The HighBuffer[] array is an indicator buffer
   SetIndexBuffer(1,HighBuffer,INDICATOR_DATA);
   // The LowBuffer[] array is an indicator buffer
   SetIndexBuffer(2,LowBuffer,INDICATOR_DATA);
   // The CloseBuffer[] array is an indicator buffer
   SetIndexBuffer(3,CloseBuffer,INDICATOR_DATA);
   // The ColorIndexBuffer[] array is the buffer of the color index
   SetIndexBuffer(4,ColorIndexBuffer,INDICATOR_COLOR_INDEX);
   // The TicksBuffer[] array is used for intermediate calculations
   SetIndexBuffer(5,TicksBuffer,INDICATOR_CALCULATIONS);

A continuación vamos a especificar las matrices OpenBuffer[], HighBuffer[], LowBuffer[], CloseBuffer[] y ColorIndexBuffer[] como series de tiempo (por ejemplo, el dato mas reciente tiene índice 0):

     // The indexation of OpenBuffer[] array as timeseries
   ArraySetAsSeries(OpenBuffer,true);
   // The indexation of HighBuffer[] array as timeseries
   ArraySetAsSeries(HighBuffer,true);
   // The indexation of LowBuffer[] array as timeseries
   ArraySetAsSeries(LowBuffer,true);
   // The indexation of CloseBuffer[] array as timeseries
   ArraySetAsSeries(CloseBuffer,true);
   // The indexation of the ColorIndexBuffer [] array as timeseries
   ArraySetAsSeries(ColorIndexBuffer,true);

Los valores de los buffers de indicador iguales a 0 no se representarán en el gráfico:

    // The null values of Open prices (0th graphic plot) should not be plotted
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0);
   // The null values of High prices (1st graphic plot) should not be plotted
   PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0);
   // The null values of Low prices (2nd graphic plot) should not be plotted
   PlotIndexSetDouble(2,PLOT_EMPTY_VALUE,0);
   // The null values of Close prices (3rd graphic plot) should not be plotted
   PlotIndexSetDouble(3,PLOT_EMPTY_VALUE,0);

Hemos completado la escritura de la función OnInit y cerramos la función con una llave.

Es el momento de escribir la función OnCalculate. Vamos a especificar todos los parámetros que se pasan a la función:

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {

Declaramos las variables que serán usadas en la función OnInit.

// the file_handle variable is a file handle
// the BidPosition and AskPosition - are positions of Bid and Ask prices in the string;
// the line_string_len is a length of a string, read from the file, 
// CandleNumber - number of candle, for which the prices OHLC are determined,
// i - loop counter;
int file_handle,BidPosition,AskPosition,line_string_len,CandleNumber,i;
// The last_price_bid variable is the recent received Bid price
double last_price_bid=SymbolInfoDouble(Symbol(),SYMBOL_BID);
// The last_price_ask variable is the recent received Ask price
double last_price_ask=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
// the filename is a name of a file, the file_buffer is a string, 
// used as a buffer for reading and writing of string data
string filename,file_buffer;

La variable file_handle de tipo entero se usa para almacenar el controlador del archivo en las operaciones de archivo. Las variables BidPosition y AskPosition se usan para almacenar las posiciones iniciales de los precios de compra y venta en el string. La variable line_string_len es una longitud de string leída de un archivo. La variable CandleNumber es el índice de la vela calculada y la variable i se usa como un contador de lazo.

Los nuevos precios de compra y venta recibidos se guardan en las variables de tipo doble last_price_bid  y last_price_ask. La variable filename de tipo string se usa para guardar un nombre de archivo. La variable file_buffer es un string usado en operaciones de archivos.

El tamaño de la matriz no se establece automáticamente, al contrario que las matricesOpenBuffer[], HighBuffer[], LowBuffer[], CloseBuffer[]  y ColorIndexBuffer[], que son los buffers del indicador. Por tanto, vamos a establecer el tamaño de una matriz TicksBuffer[]  igual al tamaño de las matrices OpenBuffer[], HighBuffer[], LowBuffer[], CloseBuffer[] y ColorIndexBuffer[]:

// Setting the size of TicksBuffer[] array
ArrayResize(TicksBuffer,ArraySize(CloseBuffer));

Establecemos el nombre del archivo a partir de la variable path_prefix, el nombre del instrumento financiero con extensión ".txt":

// File name formation from the path_prefix variable, name
// of financial instrument and ".Txt" symbols
StringConcatenate(filename,path_prefix,Symbol(),".txt");

Vamos a abrir el archivo con los parámetros descritos anteriormente para los indicadores.

// Opening a file for reading and writing, codepage ANSI, shared reading mode
file_handle=FileOpen(filename,FILE_READ|FILE_WRITE|FILE_ANSI|FILE_SHARE_READ);

Si la función OnCalculate es llamada la primera vez y no hay ningún dato en la matriz TicksBuffer[], los leemos del archivo:

if(prev_calculated==0)
  {
   // Reading the first line from the file and determine the length of a string
   line_string_len=StringLen(FileReadString(file_handle))+2;
   // if file is large (contains more quotes than rates_total/2)
   if(FileSize(file_handle)>(ulong)line_string_len*rates_total/2)
     {
      // Setting file pointer to read the latest rates_total/2 quotes
      FileSeek(file_handle,-line_string_len*rates_total/2,SEEK_END);
      // Moving file pointer to the beginning of the next line
      FileReadString(file_handle);
     }
   // if file size is small
   else
     {
      // Moving file pointer at the beginning of a file
      FileSeek(file_handle,0,SEEK_SET);
     }
   // Reset the counter of stored quotes
   ticks_stored=0;
   // Reading until the end of the file
   while(FileIsEnding(file_handle)==false)
     {
      // Reading a string from thefile
      file_buffer=FileReadString(file_handle);
      // Processing of string if its length is larger than 6 characters
      if(StringLen(file_buffer)>6)
        {
         // Finding the start position of Bid price in the line
         BidPosition=StringFind(file_buffer," ",StringFind(file_buffer," ")+1)+1;
          //Finding the start position of Ask price in the line
         AskPosition=StringFind(file_buffer," ",BidPosition)+1;
         // If the Bid prices are used, adding the Bid price to TicksBuffer[] array
         if(applied_price==0)
         TicksBuffer[ticks_stored]=StringToDouble(StringSubstr(file_buffer,BidPosition,AskPosition-BidPosition-1));
         // If the Ask prices are used, adding the Ask price to TicksBuffer[] array
         if(applied_price==1)
         TicksBuffer[ticks_stored]=StringToDouble(StringSubstr(file_buffer,AskPosition));
         // Increasing the counter of stored quotes
         ticks_stored++;
        }
     }
  }

La lectura de las cotizaciones del archivo ha sido descrita anteriormente con mayor detalle y es igual que para el anterior indicador.

Si las cotizaciones se han leído antes en la matriz TicksBuffer[], escribimos nuevos valores de precio en el archivo, establecemos un nuevo precio en la matriz TicksBuffer[] e incrementamos el contador de cotizaciones.

// If the data have been read before
else
  {
   // Moving file pointer at the end of the file
   FileSeek(file_handle,0,SEEK_END);
   // Forming a string, that should be written to the file
   StringConcatenate(file_buffer,TimeCurrent()," ",DoubleToString(last_price_bid,_Digits)," ",DoubleToString(last_price_ask,_Digits));
   // Writing a string to the file
   FileWrite(file_handle,file_buffer);
   // If the Bid prices are used, adding the last Bid price to TicksBuffer[] array
   if(applied_price==0) TicksBuffer[ticks_stored]=last_price_bid;
   // If the Ask prices are used, adding the last Ask price to TicksBuffer[] array
   if(applied_price==1) TicksBuffer[ticks_stored]=last_price_ask;
   // Increasing the quotes counter
   ticks_stored++;
  }

Cierre del archivo:

// Closing the file
FileClose(file_handle);

Si el número de cotizaciones guardadas alcanza o supera el valor del número de barras en el gráfico de precio, eliminamos la mitad de los datos antiguos y cambiamos los datos restantes:

// If number of quotes is more or equal than number of bars in the chart
if(ticks_stored>=rates_total)
  {
   // Removing the first tick_stored/2 quotes and shifting remaining quotes
   for(i=ticks_stored/2;i<ticks_stored;i++)
     {
      // Shifting the data to the beginning in the TicksBuffer[] array on tick_stored/2
      TicksBuffer[i-ticks_stored/2]=TicksBuffer[i];
     }
   // Changing the quotes counter
   ticks_stored-=ticks_stored/2;
  }

Vamos a calcular los valores OHLC para cada vela y establecer estos valores en los correspondientes buffers de indicador:

  // We assign the CandleNumber with a number of invalid candle
   CandleNumber=-1;
   // Search for all the price data available for candle formation
   for(i=0;i<ticks_stored;i++)
     {
      // If this candle is forming already
      if(CandleNumber==(int)(MathFloor((ticks_stored-1)/ticks_in_candle)-MathFloor(i/ticks_in_candle)))
        {
         // The current quote is still closing price of the current candle
         CloseBuffer[CandleNumber]=TicksBuffer[i];
         // If the current price is greater than the highest price of the current candle,
          // it will be a new highest price of the candle
         if(TicksBuffer[i]>HighBuffer[CandleNumber]) HighBuffer[CandleNumber]=TicksBuffer[i];
         // If the current price is lower than the lowest price of the current candle, 
          // it will be a new lowest price of the candle
         if(TicksBuffer[i]<LowBuffer[CandleNumber]) LowBuffer[CandleNumber]=TicksBuffer[i];
         // If the candle is bullish, it will have a color with index 2 (green)
         if(CloseBuffer[CandleNumber]>OpenBuffer[CandleNumber]) ColorIndexBuffer[CandleNumber]=2;
         // If the candle is bearish, it will have a color with index 1 (red)
         if(CloseBuffer[CandleNumber]<OpenBuffer[CandleNumber]) ColorIndexBuffer[CandleNumber]=1;
         // If the opening and closing prices are equal, then the candle will have a color with index 0 (grey)
         if(CloseBuffer[CandleNumber]==OpenBuffer[CandleNumber]) ColorIndexBuffer[CandleNumber]=0;
        }
      // If this candle hasn't benn calculated yet
      else
        {
         // Let's determine the index of a candle
         CandleNumber=(int)(MathFloor((ticks_stored-1)/ticks_in_candle)-MathFloor(i/ticks_in_candle));
         // The current quote will be the opening price of a candle
         OpenBuffer[CandleNumber]=TicksBuffer[i];
         // The current quote will be the highest price of a candle
         HighBuffer[CandleNumber]=TicksBuffer[i];
         // The current quote will be the lowest price of a candle
         LowBuffer[CandleNumber]=TicksBuffer[i];
         // The current quote will be the closing price of a candle
         CloseBuffer[CandleNumber]=TicksBuffer[i];
         // The candle will have a color with index 0 (gray)
         ColorIndexBuffer[CandleNumber]=0;
        }
     }

La ejecución de la función OnCalculate se completa al devolver un valor distinto a cero, lo que significa que la matriz TicksBuffer[] tiene ya los datos y no es necesario leerlos en la siguiente llamada de la función. Situamos la llave de cierre al final de la función.

 // Return from OnCalculate(), return a value, different from zero   
 return(rates_total);
}

Al final del artículo hay un enlace que permite descargar todo el código fuente del indicador.

Conclusión

En este artículo hemos visto cómo se crean dos indicadores de tick: el indicador de gráfico de tick y el "indicador de velas".

Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/60

Archivos adjuntos |
tickindicator.mq5 (18.37 KB)
Guía paso a paso para escribir un Expert Advisor en MQL5 para principiantes Guía paso a paso para escribir un Expert Advisor en MQL5 para principiantes
La programación de los Expert Advisors en MQL5 es sencilla, y se puede aprender con facilidad. En esta guía paso a paso, podrás ver los pasos básicos que requiere la escritura de un Expert Advisor sencillo, basándose en una elaborada estrategia de trading. La guía incluye la estructura de un Expert Advisor, el uso de los funciones de trading e indicadores técnicos integrados, los detalles del modo depuración y el uso del Simulador de estrategias.
Los estilos de representación en MQL5 Los estilos de representación en MQL5
Hay 6 estilos de representación en MQL4 y 18 en MQL5. Por ello, merece la pena dedicar un artículo a una introducción sobre los estilos de representación en MQL5. En este artículo vamos a tratar en profundidad los estilos de representación en MQL5. Además, crearemos un indicador para mostrar estos estilos y configurar el trazado.
MQL5: Crea tu propio indicador MQL5: Crea tu propio indicador
¿Qué es un indicador? Se trata de un conjunto de valores calculados y que queremos que se muestren en la pantalla de manera cómoda para nosotros. Los conjuntos de valores se representan en los programas en forma de matrices. De este modo, la creación del indicador consiste en escribir un algoritmo que maneja algunas matrices (matrices de precios) y graba los resultados del procesamiento de otras matrices (valores del indicador). Mediante la descripción de la creación de True Strength Index (Índice de fuerza verdadera), el autor muestra cómo escribir indicadores en MQL5.
Cómo llamar a los indicadores en MQL5 Cómo llamar a los indicadores en MQL5
Con la nueva versión del lenguaje de programación MQL no solo ha cambiado la forma de trabajar con los indicadores, sino que también hay nuevas formas de crearlos. Además, dispone de mayor flexibilidad al trabajar con buffers de indicadores, ya que ahora es posible indicar la dirección de indexado y obtener exactamente el número de indicadores que desee. Este artículo explica los métodos básicos para llamar a los indicadores y obtener los datos a partir del buffer de cada indicador.