English Deutsch 日本語
preview
Indicadores personalizados (Parte 1): Guía introductoria paso a paso para desarrollar indicadores personalizados simples en MQL5

Indicadores personalizados (Parte 1): Guía introductoria paso a paso para desarrollar indicadores personalizados simples en MQL5

MetaTrader 5Trading | 13 septiembre 2024, 10:29
47 0
Kelvin Muturi Muigua
Kelvin Muturi Muigua

Introducción

La representación visual de la información del mercado es la piedra angular de la negociación. Sin esta modelización visual de los datos y precios del mercado, la negociación no sería tan viable ni eficaz. Desde los primeros días de los gráficos hasta las sofisticadas herramientas de análisis técnico disponibles hoy en día, los operadores han confiado en las señales visuales para tomar decisiones informadas en los mercados financieros.

Los indicadores MQL5 sirven como potentes herramientas para mejorar este proceso de análisis visual. Al aprovechar los cálculos matemáticos y los algoritmos, los indicadores MQL5 ayudan a los operadores a identificar las oportunidades de obtener beneficios mediante la lectura estadística del comportamiento del mercado. Estos indicadores pueden aplicarse directamente a los gráficos de precios, proporcionando a los operadores una valiosa información sobre la dinámica del mercado.

En esta serie de artículos, exploraremos cómo se crean, personalizan y utilizan los indicadores MQL5 para mejorar las estrategias comerciales en MetaTrader 5. Desde la lógica básica del indicador hasta las opciones de personalización avanzadas, cubriremos los conceptos básicos y gradualmente profundizaremos en los conceptos más avanzados del desarrollo de indicadores a medida que avanzamos con la serie de artículos. El objetivo principal de esta serie de artículos es permitirle crear sus propios indicadores MQL5 personalizados adaptados a sus preferencias y objetivos comerciales.


¿Qué es un indicador?

Un indicador es una herramienta o instrumento utilizado para analizar datos de precios pasados y pronosticar movimientos de precios futuros. Los indicadores se centran principalmente en el análisis de datos del mercado en lugar de en la ejecución de operaciones. No pueden abrir, modificar ni cerrar posiciones y órdenes. Sólo proporcionan información y no ejecutan transacciones.

En esencia, los indicadores son cálculos matemáticos o algoritmos aplicados a datos de precios históricos para generar representaciones visuales del comportamiento del mercado y al mismo tiempo actualizar su estado en datos en tiempo real a medida que se actualizan.

Estas representaciones visuales pueden adoptar diversas formas, incluidos gráficos de líneas, velas, histogramas, flechas o superposiciones en gráficos de precios. Los indicadores ayudan a los traders a interpretar la dinámica del mercado destacando tendencias, identificando posibles reversiones o señalando condiciones de sobrecompra o sobreventa.

Los indicadores se pueden clasificar en diferentes categorías según su funcionalidad, como indicadores de seguimiento de tendencias, indicadores de impulso, indicadores de volatilidad e indicadores de volumen.


Tipos de indicadores en MQL5

Hay dos tipos de indicadores en MQL5: indicadores técnicos e indicadores personalizados.


1. Indicadores técnicos


Estos son los indicadores predeterminados que vienen precargados con MetaTrader 5. Incluyen una amplia gama de indicadores técnicos a los que los traders pueden acceder fácilmente y cargar en sus gráficos de MetaTrader 5 para analizar el mercado. Estos indicadores incluyen herramientas populares como osciladores, seguidores de tendencias e indicadores basados en volumen.

El código fuente de estos indicadores estándar no está fácilmente disponible para su visualización o modificación ya que están integrados en la plataforma MetaTrader 5. La única forma de acceder a ellos desde su código MQL5 es mediante el lenguaje estándar predefinido.Funciones del indicador técnico MQL5. Esta funcionalidad nos brinda la posibilidad de actualizar o personalizar estos indicadores estándar utilizando MQL5 para crear nuevos indicadores personalizados avanzados y herramientas comerciales. Demostraré cómo se puede ampliar la funcionalidad de los indicadores técnicos cuando desarrollemos un indicador personalizado basado en velas multicolores suavizadas a medida que avanzamos con el artículo.

Algunos ejemplos de indicadores técnicos estándar de MetaTrader 5 incluyen:

  • iMA (Promedio móvil simple): Calcula el promedio móvil simple de una serie de precios específica.
  • iRSI (Índice de fuerza relativa): Mide la magnitud de los cambios de precios recientes para evaluar condiciones de sobrecompra o sobreventa.
  • iMACD (Convergencia-divergencia de medias móviles): Identifica la dirección de la tendencia y las posibles reversiones mediante el análisis de la convergencia y divergencia de dos medias móviles.

La lista completa de todas las Funciones del indicador técnico MQL5 se puede encontrar en la referencia de la documentación de MQL5.


2. Indicadores personalizados


Como sugiere su nombre, los indicadores personalizados son herramientas de análisis técnico que usted puede crear para analizar los mercados financieros. Se diferencian de los indicadores incorporados porque permiten realizar cálculos y visualizaciones más específicos en función de sus necesidades comerciales.
Como programador MQL5, puedes crear indicadores basados en cualquier dato disponible de cualquier fuente que elijas. También puede importar un indicador personalizado ya creado o ampliar y modificar los indicadores técnicos MQL5 prediseñados para crear un indicador personalizado más sofisticado y avanzado como lo haremos nosotros.


Beneficios de los indicadores personalizados


A continuación se muestran algunas propiedades y beneficios de los indicadores personalizados:

Flexibilidad inigualable en los cálculos:

    • Puede diseñar indicadores personalizados para utilizar cualquier fórmula de indicador técnico o estrategia comercial que imagine.
    • Con MQL5, puede explorar una amplia gama de cálculos de indicadores personalizados y modelos matemáticos adaptados a sus necesidades específicas.
Visualizaciones altamente personalizables:

  • Puede personalizar cómo aparecen los resultados del indicador en el gráfico.
  • MQL5 ofrece la opción de utilizar estilos de línea, velas, flechas, objetos multicolores y muchos elementos gráficos adicionales para crear visualizaciones claras e informativas alineadas con su estilo de trading.
Aprovechar el trabajo existente:

  • MQL5 ofrece más que simplemente crear indicadores desde cero.
  • Puede aprovechar fuentes de datos externas más allá de los datos de precios típicos para crear una variedad de indicadores de análisis técnico o fundamental.
  • Importe indicadores personalizados prediseñados creados por otros programadores MQL5 para mejorar o ampliar su funcionalidad.
  • Amplíe y modifique los indicadores incorporados para crear indicadores personalizados sofisticados adaptados a sus requisitos comerciales únicos.

Al combinar estas capacidades, MQL5 le permite construir indicadores personalizados que no solo se adaptan a sus necesidades de análisis técnico, sino que también pueden incorporar datos y cálculos más allá de los indicadores tradicionales basados en precios.

Ejemplos de indicadores personalizados gratuitos en la terminal MetaTrader 5: El terminal MetaTrader 5 también proporciona una selección de indicadores de ejemplo a los que puede acceder para utilizar o estudiar y comprender mejor el desarrollo de indicadores en MQL5. El código fuente de los indicadores de ejemplo gratuitos de MetaTrader 5 está disponible y es un recurso valioso para los programadores de MQL5 que buscan aprender y experimentar con la creación de indicadores. Los indicadores de ejemplo MQL5 se almacenan en MQL5\Indicators\Examples yMQL5\Indicators\Free Indicators, carpetas dentro del directorio de instalación de MetaTrader 5.

Acceda a ejemplos de indicadores gratuitos de MT5

Al examinar y modificar indicadores de ejemplo, puede obtener conocimientos sobre las técnicas de programación MQL5, la lógica de los indicadores y las mejores prácticas. Este enfoque práctico fomenta el aprendizaje y le permite desarrollar indicadores personalizados adaptados a sus objetivos comerciales específicos.



Elementos básicos de los indicadores personalizados en MQL5

Antes de estudiar el desarrollo de indicadores personalizados en MQL5, es esencial comprender la estructura básica de un indicador MQL5. Al familiarizarse con los componentes y funciones clave de un indicador, estará mejor preparado para crear, modificar y utilizar indicadores de manera efectiva en MetaTrader 5.


Archivo de indicador personalizado (.mq5)


Un indicador MQL5 normalmente se almacena en un archivo con la extensión ".mq5". Este archivo contiene el código fuente escrito en el lenguaje de programación MQL5, definiendo la lógica y el comportamiento del indicador. Todos los indicadores se almacenan en MQL5\Indicators, carpeta dentro del directorio de instalación de MetaTrader 5.
Utilice el panel Navegador que se encuentra tanto en la terminal de operaciones de MetaTrader 5 como en MetaEditor para obtener acceso a la carpeta Indicadores. También puede acceder a la carpeta Indicador a través de la carpeta "MQL5" utilizando los dos métodos siguientes:
Cómo acceder a los archivos de indicadores desde el terminal de operaciones MetaTrader 5:
  • Haga clic en “Archivo" en el menú superior.
  • Seleccionar“Abrir carpeta de datos”, o usa el atajo de teclado(Ctrl + Mayús + D).
  • Vaya a la carpeta “MQL5/Indicators”.
Abrir la carpeta de datos MT5

Cómo acceder a los archivos de indicadores desde MetaEditor:
  • El panel Navegador de MetaEditor está ubicado en el lado izquierdo de la ventana de MetaEditor de forma predeterminada y proporciona acceso directo a la carpeta MQL5.
  • Si el panel Navegador está deshabilitado, puedes habilitarlo usando el atajo de teclado(Ctrl + D) o localizando y haciendo clic en Ver en la parte superior de la ventana de MetaEditor 5. Desde allí, selecciona la opción que dice Navegador. Al elegir esta opción se habilitará el panel de navegación que le otorga acceso a la carpeta MQL5.
Acceda a la carpeta de indicadores en MetaEditor


Ejemplo 1: El Indicador Personalizado del Histograma de Promedio Móvil Lineal - (LinearMovingAverageHistogram.mq5). Vamos a crear un indicador personalizado para obtener una comprensión visual de los diferentes componentes del código necesarios para construir un indicador personalizado. A nuestro primer indicador personalizado para esta demostración práctica lo llamaremos 'LinearMovingAverageHistogram'. Trazará un promedio móvil ponderado lineal como un histograma y una línea que representa el precio actual en una ventana separada debajo del gráfico de precios.

Indicador de histograma de media móvil lineal

Comencemos creando un nuevo archivo indicador personalizado con el Asistente MQL5.


Cómo crear un nuevo archivo de indicador personalizado con el Asistente MQL5

Paso 1: Abrir el IDE de MetaEditor y lanzar el 'Asistente MQL' utilizando el botón 'Nuevo' del menú.

Asistente para creación de nuevos archivos en MQL5

Paso 2: Selecciona la opción 'Indicador Personalizado' y haz clic en 'Siguiente.'

Crear un nuevo indicador personalizado con el Asistente MQL5

Paso 3: En la sección 'Propiedades Generales', completa el campo de la carpeta y el nombre de tu nuevo indicador personalizado "Indicators\Article\LinearMovingAverageHistogram" y continúa haciendo clic en 'Siguiente.' Creación de un nuevo indicador personalizado con el Asistente MQL5

Paso 4: En la sección 'Manejadores de eventos', selecciona la segunda opción 'OnCalculate(...,prices)', deja sin seleccionar las casillas de verificación OnTimer y OnChartEvent, y haz clic en 'Siguiente' para continuar.

Creación de un nuevo indicador personalizado con el Asistente MQL5

Paso 5: En la sección 'Propiedades de dibujo', selecciona o habilita la casilla 'Indicador en ventana separada'. Desmarca o desactiva las casillas 'Mínimo' y 'Máximo', y deja vacío el cuadro de texto 'Gráficos'. Haz clic en 'Finalizar' para generar el nuevo archivo de indicador personalizado MQL5.

Creación de un nuevo indicador personalizado con el Asistente MQL5

En la carpeta ‘MQL5/Indicators’, encontrarás una nueva subcarpeta llamada ‘Article.’ Esta subcarpeta contiene el archivo del indicador personalizado que acabamos de crear: ‘LinearMovingAverageHistogram.mq5.’ Como parte de las demostraciones prácticas, iremos programando varios indicadores personalizados a lo largo de este artículo. Para mantener una organización adecuada, guardaremos todos los archivos de los indicadores en esta nueva carpeta ‘Articles’.

Ahora tenemos un nuevo archivo de indicador personalizado en MQL5 que solo contiene las funciones obligatorias (OnInit y OnCalculate). Recuerde guardar el nuevo archivo antes de continuar. Así es como se ve el código de nuestro nuevo indicador personalizado generado:

//+------------------------------------------------------------------+
//|                                 LinearMovingAverageHistogram.mq5 |
//|                          Copyright 2024, Wanateki Solutions Ltd. |
//|                                         https://www.wanateki.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Wanateki Solutions Ltd."
#property link      "https://www.wanateki.com"
#property version   "1.00"
#property indicator_separate_window
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
//---

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+


Componentes básicos de un archivo de indicador personalizado (.mq5). El archivo del indicador consta de varias secciones. Analicemos cómo funcionan las diferentes partes del código del indicador:

Sección de Encabezado. La sección de encabezado del indicador se compone de tres partes: los comentarios de encabezado, las directivas de propiedad, los archivos de inclusión externos y las definiciones de variables globales.

A continuación se muestra un desglose de lo que se incluye en la sección de encabezado de nuestro indicador:

1. Comentarios del Encabezado: Esta es la primera sección de nuestro código del indicador. Contiene información comentada sobre el indicador, como su nombre de archivo, información de derechos de autor y un enlace al sitio web del autor. Estos comentarios no afectan de ninguna manera la funcionalidad del código del indicador.

//+------------------------------------------------------------------+
//|                                 LinearMovingAverageHistogram.mq5 |
//|                          Copyright 2024, Wanateki Solutions Ltd. |
//|                                         https://www.wanateki.com |
//+------------------------------------------------------------------+
2. Directivas de Propiedad: Las directivas de propiedad proporcionan información adicional sobre el indicador. Incluyen los derechos de autor, un enlace asociado con el indicador o el autor, la versión actual del indicador e instrucciones específicas sobre cómo mostrar el indicador. La directiva de propiedad más importante es #property indicator_separate_window, que instruye a la plataforma para que muestre el indicador en una ventana separada.
#property copyright "Copyright 2024, Wanateki Solutions Ltd."
#property link      "https://www.wanateki.com"
#property version   "1.00"
#property indicator_separate_window
Las directivas de propiedad copyright, link, author, y description son visibles en la pestaña "Común" del pequeño subventana de configuración del indicador (panel) que aparece cuando estás cargando el indicador en el gráfico. Ventana de entrada de configuración del indicador MT5 (panel)

3. Variables globales:Todas las variables globales se colocan debajo de las directivas de propiedad. Nuestro código indicador actualmente no contiene variables globales ya que no las especificamos al generar el archivo con el Asistente MQL5. Definiremos todas nuestras variables de entrada globales y de usuario debajo de las directivas #property a medida que avanzamos en este artículo.

Funciones de indicador personalizado estándar de MQL5. Debajo de la sección de encabezado encontrará diferentes funciones. Todos los indicadores deben contener las funciones estándar de MQL5 OnInit y OnCalculate. Las funciones creadas por el usuario son opcionales pero se recomiendan para una adecuada organización del código. A continuación se muestra un desglose de las diferentes funciones de nuestro código indicador:1. Función de inicialización del indicador (OnInit()):  La función OnInit() se llama cuando se inicializa el indicador. Normalmente realiza tareas de configuración, como mapear buffers de indicadores e inicializar cualquier variable global. Te presentaré los búferes de indicadores a medida que profundicemos en el artículo. Cuando la función se ejecuta correctamente, devuelve INIT_SUCCEEDED, y cuando la inicialización falla, devuelve INIT_FAILED.
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping

//---
   return(INIT_SUCCEEDED);
  }
2. Función de iteración del indicador (OnCalculate()): La función OnCalculate() en MQL5 es el núcleo principal de todos los cálculos personalizados del indicador. Se llama siempre que hay un cambio en los datos de precios, lo que impulsa al indicador a actualizar sus valores. Hay dos versiones principales de OnCalculate(), que explicaré más adelante en el artículo.
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
//---

//--- return value of prev_calculated for next call
   return(rates_total);
  }
3. Función de desinicialización del indicador (OnDeinit()): La función OnDeinit() no se ha añadido a nuestro código, pero es una función muy importante. Se llama cuando el indicador está terminando y es responsable de ejecutar los procedimientos de desinicialización. Normalmente realiza todas las tareas de limpieza, como la liberación de cualquier indicador técnico.
//+------------------------------------------------------------------+
//| Indicator deinitialization function                              |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   //-- deinitialization code
  }
//+------------------------------------------------------------------+

Cuando compilamos nuestro código de indicador, encontramos una advertencia: "no indicator plot defined for indicator".

Error de compilación del gráfico del indicador MQL5

Esta advertencia significa que nuestro indicador personalizado carece de una definición crucial de cómo deben mostrarse sus datos en el gráfico. Como era de esperar, nuestro código indicador actual sirve como un mero esqueleto sin ningún componente funcional. Para corregir esto, profundicemos más y escribamos los segmentos de código esenciales que darán vida a nuestro indicador.

Propiedades de descripción. Comencemos escribiendo una breve descripción del indicador personalizado en la sección del encabezado del indicador:

#property description "A custom indicator to demonstrate a linear weighted moving average."
#property description "A histogram and line are drawn in a separate window to indicate "
#property description "the moving average direction."
Propiedades de búferes y gráficos. Todos los indicadores personalizados tienen diferentes propiedades que siempre se colocan al principio del archivo, como expliqué anteriormente. Algunas son opcionales, pero las tres siguientes son siempre obligatorias:
  • indicator_separate_window o indicator_chart_window: Se utilizan para especificar si un indicador se trazará en una ventana separada o directamente en la ventana del gráfico.
  • indicator_buffers: Especifica el número de búferes del indicador que utiliza el indicador personalizado.
  • indicator_plots: Especifica el número de gráficos utilizados por el indicador personalizado.

Para cumplir con este requisito, vamos a definir los búferes del indicador y las propiedades del gráfico. Bajo la propiedad 'indicator_separate_window', colocamos el código responsable de especificar los búferes del indicador y los gráficos. Los buffers y gráficos de indicadores se utilizan para mostrar los datos del indicador en los gráficos. Usamos la directiva de propiedad para establecer la cantidad de buffers que estarán disponibles en el código para calcular el indicador. Este número es un entero del 1 al 512. Dado que esta es una directiva de preprocesador, todavía no existen variables en la etapa de preprocesamiento del código fuente y es por eso que debemos especificar un dígito (de 1 a 512) como valor.

Necesitamos dos buffers de indicadores y dos gráficos para almacenar y trazar nuestros nuevos datos de indicadores personalizados. Un buffer indicador para el histograma y otro para la línea que mostrará el precio actual del símbolo. Seguido de un gráfico para el histograma y otro gráfico para la línea de precios.

//--- indicator buffers and plots
#property indicator_buffers 2
#property indicator_plots   2

Directivas de propiedades de etiqueta, tipo y estilo

A continuación, debemos especificar otros detalles, como la etiqueta del indicador, el tipo, el color, el estilo y el ancho tanto del histograma como de la línea de precios.

//--- plots1 details for the ma histogram
#property indicator_label1  "MA_Histogram"
#property indicator_type1   DRAW_HISTOGRAM
#property indicator_color1  clrDodgerBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- plots2 details for the price line
#property indicator_label2  "Current_Price_Line"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrGoldenrod
#property indicator_style2  STYLE_SOLID
#property indicator_width2  2

Variables globales de entrada del usuario

A continuación, especificamos las variables de entrada del usuario para el indicador personalizado (como el período de la media móvil y el desplazamiento) que se utilizarán para almacenar los diferentes parámetros del indicador.

//--- input parameters for the moving averages
input int            _maPeriod = 50;       // MA Period
input int            _maShift = 0;         // MA Shift


Declaración de matrices dinámicas de buffer de indicadores en el ámbito global

A continuación, declaramos el buffer indicador para el histograma de media móvil y la línea de precios. Los buffers de indicadores se encuentran entre los pilares básicos de los indicadores personalizados y son responsables de almacenar los datos del indicador en matrices dinámicas. Debe comenzar declarando una matriz dinámica y luego registrándola como un búfer indicador utilizando una función especial MQL5 'SetIndexBuffer' para convertirla en una matriz especial administrada por terminal. Después de hacer esto, la terminal será responsable de asignar memoria para la matriz y proporcionar acceso público a la misma como una nueva matriz accesible por series de tiempo, sobre la cual se pueden calcular otros indicadores.

Primero declararemos los búferes del indicador de histograma y línea como arreglos dinámicos, y luego, más adelante en la función OnInit(), utilizaremos la función especial SetIndexBuffer de MQL5 para registrarlos y convertirlos en arreglos de series temporales gestionados por el terminal.

//--- indicator buffer
double maHistogramBuffer[], priceLineBuffer[];

Función de inicialización de indicador personalizado - GetInit()

A continuación, vamos a crear una función personalizada que será responsable de inicializar nuestro indicador personalizado. Comience creando una función en blanco que sea del tipo void lo que significa que no devuelve ningún dato. Nombra la función 'GetInit()'. Coloca la función especial 'SetIndexBuffer(...)', que será responsable de convertir nuestros arreglos dinámicos de búferes del indicador que habíamos declarado anteriormente, 'maHistogramBuffer' y 'priceLineBuffer', en arreglos de series temporales gestionados por el terminal.

//+------------------------------------------------------------------+
//| User custom function for custom indicator initialization         |
//+------------------------------------------------------------------+
void GetInit()
  {
//--- set the indicator buffer mapping
   SetIndexBuffer(0, maHistogramBuffer, INDICATOR_DATA);
   SetIndexBuffer(1, priceLineBuffer, INDICATOR_DATA);

  }
//+------------------------------------------------------------------+
A continuación, configuramos la precisión del indicador para que coincida con el valor del dígito del símbolo.
//--- set the indicators accuracy
   IndicatorSetInteger(INDICATOR_DIGITS, _Digits + 1);
A continuación, definimos la primera barra desde donde se empezará a dibujar el índice.
//--- set the first bar from where the index will be drawn
   PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, _maPeriod);
   PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, 0);
Configure el cambio del indicador de promedio móvil al valor especificado por el usuario para el controlador del promedio móvil y utilice un valor cero para la línea de precio. Esto se utilizará al trazar o dibujar el indicador.
//--- set the indicator shifts when drawing
   PlotIndexSetInteger(0, PLOT_SHIFT, _maShift);
   PlotIndexSetInteger(1, PLOT_SHIFT, 0);
A continuación, establecemos el nombre que se mostrará en el Ventana de datos MT5 para los valores del indicador del búfer de datos del indicador. Usamos un control de interruptor para establecer el nombre corto del indicador para la ventana de datos.
//--- set the name to be displayed in the MT5 DataWindow
   IndicatorSetString(INDICATOR_SHORTNAME, "LWMA_Histo" + "(" + string(_maPeriod) + ")");
Para finalizar la función de inicialización del indicador, estableceremos el histograma de dibujo en un valor vacío.
//--- set the drawing histogram and line to an empty value
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0);
   PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, 0.0);

Función personalizada para calcular la media móvil ponderada lineal - GetLWMA()

A continuación, necesitamos crear una función personalizada llamada 'GetLWMA(..)' que será responsable de calcular la media móvil ponderada lineal. La función será de tipo void ya que no queremos que devuelva ningún dato. Aceptará cuatro argumentos como parámetros de función (rates_total, prev_calculated, begin,  &price). 

//+------------------------------------------------------------------+
//|  Function to calculate the linear weighted moving average        |
//+------------------------------------------------------------------+
void GetLWMA(int rates_total, int prev_calculated, int begin, const double &price[])
  {
   int    weight = 0;
   int    x, l, start;
   double sum = 0.0, lsum = 0.0;
//--- first calculation or number of bars was changed
   if(prev_calculated <= _maPeriod + begin + 2)
     {
      start = _maPeriod + begin;
      //--- set empty value for first start bars
      for(x=0; x < start; x++)
        {
         maHistogramBuffer[x] = 0.0;
         priceLineBuffer[x] = price[x];
        }
     }
   else
      start = prev_calculated - 1;

   for(x = start - _maPeriod, l = 1; x < start; x++, l++)
     {
      sum   += price[x] * l;
      lsum  += price[x];
      weight += l;
     }
   maHistogramBuffer[start-1] = sum/weight;
   priceLineBuffer[x] = price[x];
//--- main loop
   for(x=start; x<rates_total && !IsStopped(); x++)
     {
      sum             = sum - lsum + price[x] * _maPeriod;
      lsum            = lsum - price[x - _maPeriod] + price[x];
      maHistogramBuffer[x] = sum / weight;
      priceLineBuffer[x] = price[x];
     }
  }

Función de iteración principal del indicador - OnCalculate()

La función de iteración del indicador personalizado 'OnCalculate(..)' es el corazón de nuestro indicador personalizado y es responsable de actualizar y trazar nuestro indicador cuando hay un nuevo tick o cambio de precio y todos los cálculos necesarios se originan aquí. Actualmente estamos utilizando la forma corta delOnCalculate() función:

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
   return(rates_total);
  }
Esta función toma cuatro parámetros de entrada o argumentos:
  1. rates_total: Este parámetro contiene el valor del número total de elementos del arreglo price[]. Se pasa como parámetro de entrada para calcular los valores del indicador, como lo hicimos anteriormente con la función 'GetLWMA(..)'.
  2. prev_calculated: Este parámetro almacena el resultado de la ejecución de 'OnCalculate(..)' en la llamada anterior. Desempeña un papel clave en el algoritmo para calcular los valores de los indicadores y garantiza que no hagamos cálculos para todo el período histórico en cada 'OnCalculate(..)' llamada de función o cuando ocurre un nuevo cambio de precio.
  3. begin: Este parámetro almacena el número del valor inicial de la matriz de precios, que no contiene datos para el cálculo. En nuestro indicador personalizado, este valor es "_maPeriod" y simplemente le indica a la función 'OnCalculate(...)' que detenga todos los cálculos hasta que todas las barras alcancen el valor almacenado en "_maPeriod", de modo que haya suficientes barras para realizar los cálculos del indicador.
  4. price: Este parámetro almacena el precio aplicado utilizado para calcular los datos del indicador. Este parámetro es especificado por el usuario al cargar el indicador personalizado en el gráfico. Los usuarios tienen la opción de seleccionar apertura, cierre, máximo, mínimo, precio medio (HL / 2), precio típico (HLC / 3), precio ponderado de cierre (HLCC / 4), o valores de los datos de indicadores previamente cargados.
Entrada de precio aplicado para el indicador personalizado
Aquí está el código para nuestra función 'OnCalculate(...)':
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
//--- check if we have enough bars to do the calculations
   if(rates_total < _maPeriod - 1 + begin)
      return(0);

//--- first calculation or number of bars was changed
   if(prev_calculated == 0)
     {
      ArrayInitialize(maHistogramBuffer, 0);
      PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, _maPeriod - 1 + begin);
     }
//--- calculate the linear weighted moving average and plot it on the chart
   GetLWMA(rates_total, prev_calculated, begin, price);

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
Ahora tenemos todos los diferentes segmentos de código de nuestro indicador personalizado recién creado. Asegúrate de que tu archivo 'LinearMovingAverageHistogram' se vea así y contenga todos los componentes del código a continuación:
#property version   "1.00"
#property indicator_separate_window

//--- indicator buffers and plots
#property indicator_buffers 2
#property indicator_plots   2

//--- plots1 details for the ma histogram
#property indicator_label1  "MA_Histogram"
#property indicator_type1   DRAW_HISTOGRAM
#property indicator_color1  clrDodgerBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- plots2 details for the price line
#property indicator_label2  "Current_Price_Line"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrGoldenrod
#property indicator_style2  STYLE_SOLID
#property indicator_width2  2

//--- input parameters for the moving averages
input int            _maPeriod = 50;       // MA Period
input int            _maShift = 0;         // MA Shift

//--- indicator buffer
double maHistogramBuffer[], priceLineBuffer[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- call the custom initialization function
   GetInit();

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
//--- check if we have enough bars to do the calculations
   if(rates_total < _maPeriod - 1 + begin)
      return(0);

//--- first calculation or number of bars was changed
   if(prev_calculated == 0)
     {
      ArrayInitialize(maHistogramBuffer, 0);
      PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, _maPeriod - 1 + begin);
     }
//--- calculate the linear weighted moving average and plot it on the chart
   GetLWMA(rates_total, prev_calculated, begin, price);

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| User custom function for custom indicator initialization         |
//+------------------------------------------------------------------+
void GetInit()
  {
//--- set the indicator buffer mapping
   SetIndexBuffer(0, maHistogramBuffer, INDICATOR_DATA);
   SetIndexBuffer(1, priceLineBuffer, INDICATOR_DATA);

//--- set the indicators accuracy
   IndicatorSetInteger(INDICATOR_DIGITS, _Digits + 1);

//--- set the first bar from where the index will be drawn
   PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, _maPeriod);
   PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, 0);

//--- set the indicator shifts when drawing
   PlotIndexSetInteger(0, PLOT_SHIFT, _maShift);
   PlotIndexSetInteger(1, PLOT_SHIFT, 0);

//--- set the name to be displayed in the MT5 DataWindow
   IndicatorSetString(INDICATOR_SHORTNAME, "LWMA_Histo" + "(" + string(_maPeriod) + ")");
   
//--- set the drawing histogram and line to an empty value
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0);
   PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, 0.0);
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//|  Function to calculate the linear weighted moving average        |
//+------------------------------------------------------------------+
void GetLWMA(int rates_total, int prev_calculated, int begin, const double &price[])
  {
   int    weight = 0;
   int    x, l, start;
   double sum = 0.0, lsum = 0.0;
//--- first calculation or number of bars was changed
   if(prev_calculated <= _maPeriod + begin + 2)
     {
      start = _maPeriod + begin;
      //--- set empty value for first start bars
      for(x=0; x < start; x++)
        {
         maHistogramBuffer[x] = 0.0;
         priceLineBuffer[x] = price[x];
        }
     }
   else
      start = prev_calculated - 1;

   for(x = start - _maPeriod, l = 1; x < start; x++, l++)
     {
      sum   += price[x] * l;
      lsum  += price[x];
      weight += l;
     }
   maHistogramBuffer[start-1] = sum/weight;
   priceLineBuffer[x] = price[x];
//--- main loop
   for(x=start; x<rates_total && !IsStopped(); x++)
     {
      sum             = sum - lsum + price[x] * _maPeriod;
      lsum            = lsum - price[x - _maPeriod] + price[x];
      maHistogramBuffer[x] = sum / weight;
      priceLineBuffer[x] = price[x];
     }
  }
Cuando guarde y compile el indicador personalizado, notará que ahora no contiene advertencias ni errores. Abra su terminal de operaciones MetaTrader 5 para cargarlo y probarlo en sus gráficos.


Más ejemplos prácticos

Ahora que está familiarizado con los componentes básicos de los indicadores personalizados, deberíamos crear algunos indicadores personalizados simples para ayudarnos a consolidar este conocimiento. Seguiremos todos los pasos que definimos anteriormente e implementaremos todos los fundamentos básicos de los indicadores personalizados en los ejemplos siguientes.

Ejemplo 2: El indicador personalizado monitor de spread - SpreadMonitor.mq5

Continuemos con nuestro enfoque práctico y creemos otro indicador personalizado simple que utiliza datos de propagación para mostrar un histograma multicolor en una ventana separada. Este indicador será útil para los símbolos que utilizan un spread flotante y le permitirá monitorear más fácilmente cómo el spread fluctúa o aumenta con el tiempo de una manera visual y fácil de analizar. Utilice el Asistente MQL como lo hicimos anteriormente para crear un nuevo archivo indicador personalizado con el nombre 'SpreadMonitor.mq5'. Recuerda guardarlo en la carpeta 'Article' para mantener una estructura de archivos ordenada y organizada.

En este ejemplo, demostraré cómo crear un indicador multicolor. Cuando el spread actual es mayor que el spread anterior, el histograma cambiará a un color rojo para indicar un aumento en el spread, y cuando el spread actual es menor que el spread anterior, el histograma cambiará a un color azul para indicar que el spread está disminuyendo. Esta función de indicador personalizado se observará mejor en símbolos con un spread flotante y un vistazo rápido al indicador cuando está cargado en un gráfico hace que sea fácil detectar períodos en los que el spread aumenta rápidamente.

Después de haber generado el nuevo archivo del indicador personalizado 'SpreadMonitor.mq5' con el Asistente MQL, añade el siguiente código.

Comience especificando dónde se mostrará el indicador:

//--- indicator window settings
#property indicator_separate_window

Especifique el número de buffers de indicadores y gráficos:

//--- indicator buffers and plots
#property indicator_buffers 2
#property indicator_plots   1

Establezca el tipo y el estilo del indicador y especifique los diferentes colores:

//--- indicator type and style settings
#property indicator_type1   DRAW_COLOR_HISTOGRAM
#property indicator_color1  clrDarkBlue, clrTomato
#property indicator_style1  0
#property indicator_width1  1
#property indicator_minimum 0.0

Declare las matrices dinámicas que se utilizarán como buffers de indicadores en el ámbito global:

//--- indicator buffers
double spreadDataBuffer[];
double histoColorsBuffer[];

Crea la función personalizada que será responsable de inicializar nuestro indicador:

//+------------------------------------------------------------------+
//| User custom function for custom indicator initialization         |
//+------------------------------------------------------------------+
void GetInit(){
}
//+------------------------------------------------------------------+

Dentro de nuestra nueva función de inicialización del indicador 'GetInit()'. Registra y asigna los búferes del indicador:

//--- set and register the indicator buffers mapping
   SetIndexBuffer(0, spreadDataBuffer, INDICATOR_DATA);
   SetIndexBuffer(1, histoColorsBuffer, INDICATOR_COLOR_INDEX);

Establezca el nombre que aparecerá en la ventana de datos de MetaTrader 5 y como etiqueta de la subventana del indicador:

//--- name for mt5 datawindow and the indicator subwindow label
   IndicatorSetString(INDICATOR_SHORTNAME,"Spread Histogram");

Configure los dígitos del indicador para lograr precisión y exactitud:

//--- set the indicators accuracy digits
   IndicatorSetInteger(INDICATOR_DIGITS, 0);

El siguiente paso será crear una función personalizada para calcular el spread. Llamemos a la función 'GetSpreadData()' y especifiquemos que debe aceptar tres parámetros o argumentos. La función será de tipo void ya que no necesitamos que devuelva ningún dato:

//+------------------------------------------------------------------+
//| Custom function for calculating the spread                       |
//+------------------------------------------------------------------+
void GetSpreadData(const int position, const int rates_total, const int& spreadData[])
  {
   spreadDataBuffer[0] = (double)spreadData[0];
   histoColorsBuffer[0] = 0.0;
//---
   for(int x = position; x < rates_total && !IsStopped(); x++)
     {
      double currentSpread = (double)spreadData[x];
      double previousSpread = (double)spreadData[x - 1];

      //--- calculate and save the spread
      spreadDataBuffer[x] = currentSpread;
      if(currentSpread > previousSpread)
        {
         histoColorsBuffer[x] = 1.0; //-- set the histogram to clrTomato
        }
      else
        {
         histoColorsBuffer[x] = 0.0; //-- set the histogram to clrDarkBlue
        }
     }
//---
  }
//+------------------------------------------------------------------+

El indicador personalizado no puede funcionar con una función OnCalculate() vacía. En este ejemplo, utilizaremos la versión larga de OnCalculate() que emplea específicamente diez parámetros para almacenar y procesar los datos del indicador personalizado.

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[])
  {
//--- check if we have enough data start calculating
   if(rates_total < 2) //--- don't do any calculations, exit and reload function
      return(0);

//--- we have new data, starting the calculations
   int position = prev_calculated - 1;

//--- update the position variable
   if(position < 1)
     {
      spreadDataBuffer[0] = 0;
      position = 1;
     }
//--- calculate and get the tick volume
   GetSpreadData(position, rates_total, spread);

//--- Exit function and return new prev_calculated value
   return(rates_total);
  }

El indicador personalizado SpreadMonitor está casi completo. Unamos todos los diferentes segmentos de código y guardemos el archivo antes de compilarlo y cargarlo en un gráfico de MetaTrader 5.

//--- indicator window settings
#property indicator_separate_window

//--- indicator buffers and plots
#property indicator_buffers 2
#property indicator_plots   1

//--- indicator type and style settings
#property indicator_type1   DRAW_COLOR_HISTOGRAM
#property indicator_color1  clrDarkBlue, clrTomato
#property indicator_style1  0
#property indicator_width1  1
#property indicator_minimum 0.0

//--- indicator buffers
double spreadDataBuffer[];
double histoColorsBuffer[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- initialize the indicator
   GetInit();

   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[])
  {
//--- check if we have enough data start calculating
   if(rates_total < 2) //--- don't do any calculations, exit and reload function
      return(0);

//--- we have new data, starting the calculations
   int position = prev_calculated - 1;

//--- update the position variable
   if(position < 1)
     {
      spreadDataBuffer[0] = 0;
      position = 1;
     }
//--- calculate and get the tick volume
   GetSpreadData(position, rates_total, spread);

//--- Exit function and return new prev_calculated value
   return(rates_total);
  }

//+------------------------------------------------------------------+
//| Custom function for calculating the spread                       |
//+------------------------------------------------------------------+
void GetSpreadData(const int position, const int rates_total, const int& spreadData[])
  {
   spreadDataBuffer[0] = (double)spreadData[0];
   histoColorsBuffer[0] = 0.0;
//---
   for(int x = position; x < rates_total && !IsStopped(); x++)
     {
      double currentSpread = (double)spreadData[x];
      double previousSpread = (double)spreadData[x - 1];

      //--- calculate and save the spread
      spreadDataBuffer[x] = currentSpread;
      if(currentSpread > previousSpread)
        {
         histoColorsBuffer[x] = 1.0; //-- set the histogram to clrTomato
        }
      else
        {
         histoColorsBuffer[x] = 0.0; //-- set the histogram to clrDarkBlue
        }
     }
//---
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| User custom function for custom indicator initialization         |
//+------------------------------------------------------------------+
void GetInit()
  {
//--- set and register the indicator buffers mapping
   SetIndexBuffer(0, spreadDataBuffer, INDICATOR_DATA);
   SetIndexBuffer(1, histoColorsBuffer, INDICATOR_COLOR_INDEX);

//--- name for mt5 datawindow and the indicator subwindow label
   IndicatorSetString(INDICATOR_SHORTNAME,"Spread Histogram");

//--- set the indicators accuracy digits
   IndicatorSetInteger(INDICATOR_DIGITS, 0);
  }
//+------------------------------------------------------------------+

Aquí está el indicador personalizado SpreadMonitor cargado en un gráfico de cinco minutos de GBPJPY en MetaTrader 5.

Indicador personalizado SpreadMonitor


Ejemplo 3: Indicador personalizado de velas multicolor suavizadas - (SmoothedCandlesticks.mq5)

Dado que los indicadores son principalmente representaciones visuales de estrategias comerciales, los indicadores multicolores son preferibles a los indicadores que solo muestran datos visuales en un solo color. Los indicadores multicolores facilitan a los operadores identificar rápidamente las señales comerciales generadas por el indicador y esto ayuda a optimizar la eficiencia y la facilidad de uso del indicador.

Tener la capacidad de crear indicadores multicolores es una habilidad muy útil para cualquier desarrollador de MQL5, y en este ejemplo demostraré cómo puede crear un indicador personalizado de velas multicolores suavizadas. Eliminar el ruido del mercado siempre ha sido una prioridad para los traders y este simple indicador lo logra tomando los cálculos de los promedios móviles suavizados para crear velas multicolores que cambian de color según la señal de los promedios móviles. Esto crea un gráfico ordenado que es fácil de interpretar y elimina la necesidad de utilizar la herramienta de gráficos en forma de cruz para detectar si se ha producido un cruce de media móvil.

Las velas se vuelven verdes cuando los precios de apertura, máximo, mínimo y cierre son superiores a la media móvil suavizada, y se vuelven rojas cuando los precios de apertura, máximo, mínimo y cierre son inferiores a la media móvil suavizada. Si el promedio móvil suavizado toca cualquier parte del cuerpo de la vela, lo que significa que está entre los precios alto y bajo de la vela, la vela se volverá gris oscuro para mostrar que el indicador suavizado no genera ninguna señal de entrada.

Este ejemplo también demostrará cómo usar los indicadores estándar de MQL5 a través de las funciones predefinidas de indicadores técnicos de MQL5 que discutimos anteriormente. Utilice el Asistente MQL5 para crear un nuevo archivo de indicador personalizado y asígnele el nombre 'SmoothedCandlesticks.mq5'. Recuerda guardarlo en la carpeta 'Article' junto con los otros archivos de indicadores personalizados que hemos creado anteriormente.


Especifica las directivas de propiedad (#property) del indicador

Comience por especificar dónde se mostrará el indicador, ya sea en la ventana del gráfico o en una ventana separada debajo del gráfico de precios. El indicador funcionará en todos los escenarios y puedes alternar entre mostrarlo en una ventana separada y en la ventana del gráfico para probar cómo aparece visualmente.

//--- specify where to display the indicator
#property indicator_separate_window
//#property indicator_chart_window

Especifique los buffers indicadores. En este indicador, vamos a utilizar seis buffers de indicadores para trazar y mostrar nuestros datos. Tendremos cuatro indicadores para los precios de apertura, cierre, máximo y mínimo de la vela. Un búfer indicador para la línea de promedio móvil suavizada y otro búfer indicador para almacenar los colores de nuestras velas. Esto suma un total de seis búferes de indicador.

//--- indicator buffers
#property indicator_buffers 6

Necesitamos dos gráficos indicadores para el dibujo de nuestro indicador. Un gráfico para dibujar las velas y otro gráfico para la línea de media móvil suavizada. Esto suma un total de dos gráficos de indicador.

//--- indicator plots
#property indicator_plots   2

Especifique los detalles del gráfico para las velas suavizadas. Esto incluye valores para el tipo, color y etiqueta. La etiqueta se muestra en la ventana de datos junto con los datos correspondientes, como el precio. Nuestro indicador utiliza velas multicolores y necesitamos proporcionar un total de tres colores que cambiarán dependiendo de la señal comercial actual como lo expliqué anteriormente. El usuario tendrá la opción de cambiar los colores especificados antes de cargar el indicador en el gráfico en el panel de indicadores.

//--- plots1 details for the smoothed candles
#property indicator_type1   DRAW_COLOR_CANDLES
#property indicator_color1  clrDodgerBlue, clrTomato, clrDarkGray
#property indicator_label1  "Smoothed Candle Open;Smoothed Candle High;Smoothed Candle Low;Smoothed Candle Close;"

Repita el paso anterior y especifique los detalles del segundo gráfico para la línea de promedio móvil suavizada. Solo especificaremos un color para la línea suavizada, ya que no es una línea multicolor.

//--- plots2 details for the smoothing line
#property indicator_label2  "Smoothing Line"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrGoldenrod
#property indicator_style2  STYLE_SOLID
#property indicator_width2  2

SmoothedCandlesticks: selección de color del panel indicador


Variables de entrada del usuario en el ámbito global

Declare las variables de entrada del usuario para capturar y guardar el período promedio móvil suavizado y el precio aplicado para los cálculos.

//--- user input parameters for the moving averages
input int                _maPeriod = 50;                    // Period
input ENUM_APPLIED_PRICE _maAppliedPrice = PRICE_CLOSE;     // Applied Price

SmoothedCandlesticks: entrada de usuario del panel indicador


Variables de indicador, buffers y manejo de indicadores técnicos en el ámbito global

Aquí vamos a declarar matrices dinámicas para almacenar nuestros buffers de indicadores. Anteriormente habíamos utilizado la directiva #property para asignar seis buffers de indicadores. Comencemos primero por declarar cinco buffers de indicadores para el almacenamiento de apertura, cierre, máximo, mínimo y velas. El buffer restante para la línea suavizada se declarará a continuación, ya que utilizaremos la función iMA del indicador técnico estándar MQL5 para administrar este buffer.

//--- indicator buffers
double openBuffer[];
double highBuffer[];
double lowBuffer[];
double closeBuffer[];
double candleColorBuffer[];

A continuación, declaramos el buffer para la línea suavizada y un controlador para obtener acceso a la función del indicador técnico iMA. El uso de la función de indicador técnico estándar MQL5 ya creada nos ahorra tiempo y es más eficiente ya que todos los cálculos de suavizado de las velas se realizarán de manera eficiente con menos código.

//Moving average dynamic array (buffer) and variables
double iMA_Buffer[];
int maHandle; //stores the handle of the iMA indicator

Aquí declaramos e inicializamos nuestra última variable global 'barsCalculated' con un valor de cero. Esta variable entera se utiliza para almacenar el número de barras calculadas a partir del promedio móvil suavizado iMA. Lo usaremos en la función OnCalculate().

//--- integer to store the number of values in the moving average indicator
int barsCalculated = 0;


Función personalizada para la inicialización del indicador - GetInit()

Ahora que hemos terminado con la sección de encabezado de nuestro indicador personalizado, vamos a crear una función personalizada para realizar todas las tareas de inicialización. Llamemos a la función 'GetInit()' y especifiquemos que debe devolver un valor booleano para indicar si la inicialización del indicador fue exitosa. Si la inicialización falla, el indicador debe finalizar y cerrarse. 

En la función de inicialización, realizaremos algunas tareas importantes como: configurar y registrar los buffers del indicador, guardar el nombre corto del indicador y crear el controlador iMA para la línea de suavizado, entre otras tareas de inicialización básicas.

//+------------------------------------------------------------------+
//| User custom function for custom indicator initialization         |
//+------------------------------------------------------------------+
bool GetInit()
  {
//--- set the indicator buffer mapping by assigning the indicator buffer array
   SetIndexBuffer(0, openBuffer, INDICATOR_DATA);
   SetIndexBuffer(1, highBuffer, INDICATOR_DATA);
   SetIndexBuffer(2, lowBuffer, INDICATOR_DATA);
   SetIndexBuffer(3, closeBuffer, INDICATOR_DATA);
   SetIndexBuffer(4, candleColorBuffer, INDICATOR_COLOR_INDEX);

//--- buffer for iMA
   SetIndexBuffer(5, iMA_Buffer, INDICATOR_DATA);

//--- set the price display precision to digits similar to the symbol prices
   IndicatorSetInteger(INDICATOR_DIGITS, _Digits);

//--- set the symbol, timeframe, period and smoothing applied price of the indicator as the short name
   string indicatorShortName = StringFormat("SmoothedCandles(%s, Period %d, %s)", _Symbol,
                               _maPeriod, EnumToString(_maAppliedPrice));
   IndicatorSetString(INDICATOR_SHORTNAME, indicatorShortName);
//IndicatorSetString(INDICATOR_SHORTNAME, "Smoothed Candlesticks");

//--- set line drawing to an empty value
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0);

//--- create the maHandle of the smoothing indicator
   maHandle = iMA(_Symbol, PERIOD_CURRENT, _maPeriod, 0, MODE_SMMA, _maAppliedPrice);

//--- check if the maHandle is created or it failed
   if(maHandle == INVALID_HANDLE)
     {
      //--- creating the handle failed, output the error code
      ResetLastError();
      PrintFormat("Failed to create maHandle of the iMA for symbol %s, error code %d",
                  _Symbol, GetLastError());
      //--- we terminate the program and exit the init function
      return(false);
     }

   return(true); // return true, initialization of the indicator ok
  }
//+------------------------------------------------------------------+

Después de crear la función 'GetInit()', llámala en la función estándar 'OnInit()' del indicador para que realice la tarea prevista.

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- call the custom initialization function
   if(!GetInit())
     {
      return(INIT_FAILED); //-- if initialization failed terminate the app
     }

//---
   return(INIT_SUCCEEDED);
  }


Función principal de iteración del indicador - OnCalculate()

La siguiente tarea será codificar la función estándar 'OnCalculate(....)' para realizar todos los cálculos del indicador. En este ejemplo, utilizaremos la versión larga de esta función estándar que tiene un total de diez parámetros. Esta versión de 'OnCalculate(...)' se basa en cálculos de la serie temporal del período actual. Estos son los parámetros que contiene:

  • rates_total: Contiene el número total de barras en el gráfico cuando se lanza el indicador y se actualiza para reflejar el estado actual del total de barras disponibles a medida que se cargan nuevas barras o datos.
  • prev_calculated: Guarda el número de barras ya procesadas en la llamada anterior. Nos ayuda a saber qué datos ya hemos calculado o procesado, de modo que no tengamos que calcular cada barra en cada llamada a la función OnCalculate(...) o cuando lleguen nuevas barras. OnCalculate(..) devuelve una versión actualizada de esta variable en cada llamada.
  • time, open, high, low, close, tick_volume, volume y spread: Es fácil saber qué contienen estas matrices, ya que sus nombres representan qué datos de barras almacenan y guardan. Nuestro indicador personalizado dependerá de los datos de estas matrices y demostrará cómo utilizarlos, especialmente para un indicador basado en velas como el nuestro.

Añade el cuerpo de la función OnCalculate(...) a nuestro código, ya que contiene todos los cálculos relevantes para el trazado de las velas suavizadas y multicolores, así como la línea de suavizado.

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[])
  {
//--- declare a int to save the number of values copied from the iMA indicator
   int iMA_valuesToCopy;

//--- find the number of values already calculated in the indicator
   int iMA_calculated = BarsCalculated(maHandle);
   if(iMA_calculated <= 0)
     {
      PrintFormat("BarsCalculated() for iMA handle returned %d, error code %d", iMA_calculated, GetLastError());
      return(0);
     }

   int start;
//--- check if it's the indicators first call of OnCalculate() or we have some new uncalculated data
   if(prev_calculated == 0)
     {
      //--- set all the buffers to the first index
      lowBuffer[0] = low[0];
      highBuffer[0] = high[0];
      openBuffer[0] = open[0];
      closeBuffer[0] = close[0];
      start = 1;


      if(iMA_calculated > rates_total)
         iMA_valuesToCopy = rates_total;
      else   //--- copy the calculated bars which are less than the indicator buffers data
         iMA_valuesToCopy = iMA_calculated;
     }
   else
      start = prev_calculated - 1;

   iMA_valuesToCopy = (rates_total - prev_calculated) + 1;

//--- fill the iMA_Buffer array with values of the Moving Average indicator
//--- reset error code
   ResetLastError();
//--- copy a part of iMA_Buffer array with data in the zero index of the the indicator buffer
   if(CopyBuffer(maHandle, 0, 0, iMA_valuesToCopy, iMA_Buffer) < 0)
     {
      //--- if the copying fails, print the error code
      PrintFormat("Failed to copy data from the iMA indicator, error code %d", GetLastError());
      //--- exit the function with zero result to specify that the indicator calculations were not executed
      return(0);
     }

//--- iterate through the main calculations loop and execute all the calculations
   for(int x = start; x < rates_total && !IsStopped(); x++)
     {
      //--- save all the candle array prices in new non-array variables for quick access
      double candleOpen = open[x];
      double candleClose = close[x];
      double candleHigh = high[x];
      double candleLow  = low[x];

      lowBuffer[x] = candleLow;
      highBuffer[x] = candleHigh;
      openBuffer[x] = candleOpen;
      closeBuffer[x] = candleClose;

      //--- scan for the different trends signals and set the required candle color
      candleColorBuffer[x] = 2.0; // set color clrDarkGray - default (signal for no established trend)
      if(candleOpen > iMA_Buffer[x] && candleClose > iMA_Buffer[x] && candleHigh > iMA_Buffer[x] && candleLow > iMA_Buffer[x])
         candleColorBuffer[x]=0.0; // set color clrDodgerBlue - signal for a long/buy trend

      if(candleOpen < iMA_Buffer[x] && candleClose < iMA_Buffer[x] && candleHigh < iMA_Buffer[x] && candleLow < iMA_Buffer[x])
         candleColorBuffer[x]=1.0; // set color clrTomato - signal for a short/sell trend
     }

//--- return the rates_total which includes the prev_calculated value for the next call
   return(rates_total);
  }


Función de desinicialización del indicador - OnDeinit()

La última función es la función estándar 'OnDeinit()' para desinicializar todas las variables y arreglos que necesitan ser liberados. Todas las matrices de buffer se administran automáticamente y no es necesario liberarlas ni desinicializarlas, excepto el identificador iMA. Para garantizar que nuestro indicador libere todos los recursos no utilizados cuando termine, utilizaremos la función MQL5 'IndicatorRelease()' para liberar los recursos que consume la variable 'maHandle'.

//+------------------------------------------------------------------+
//| Indicator deinitialization function                              |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(maHandle != INVALID_HANDLE)
     {
      IndicatorRelease(maHandle);//-- clean up and release the iMA handle
     }
  }
//+------------------------------------------------------------------+

Nuestro indicador ahora está casi completo, aquí están los segmentos de código combinados. Asegúrese de que su código tenga todos los segmentos diferentes en este orden.

//--- specify where to display the indicator
#property indicator_separate_window
//#property indicator_chart_window

//--- indicator buffers
#property indicator_buffers 6

//--- indicator plots
#property indicator_plots   2

//--- plots1 details for the smoothed candles
#property indicator_type1   DRAW_COLOR_CANDLES
#property indicator_color1  clrDodgerBlue, clrTomato, clrDarkGray
#property indicator_label1  "Smoothed Candle Open;Smoothed Candle High;Smoothed Candle Low;Smoothed Candle Close;"

//--- plots2 details for the smoothing line
#property indicator_label2  "Smoothing Line"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrGoldenrod
#property indicator_style2  STYLE_SOLID
#property indicator_width2  2

//--- user input parameters for the moving averages
input int                _maPeriod = 50;                    // Period
input ENUM_APPLIED_PRICE _maAppliedPrice = PRICE_CLOSE;     // Applied Price

//--- indicator buffers
double openBuffer[];
double highBuffer[];
double lowBuffer[];
double closeBuffer[];
double candleColorBuffer[];

//Moving average dynamic array (buffer) and variables
double iMA_Buffer[];
int maHandle; //stores the handle of the iMA indicator

//--- integer to store the number of values in the moving average indicator
int barsCalculated = 0;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- call the custom initialization function
   if(!GetInit())
     {
      return(INIT_FAILED); //-- if initialization failed terminate the app
     }

//---
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[])
  {
//--- declare a int to save the number of values copied from the iMA indicator
   int iMA_valuesToCopy;

//--- find the number of values already calculated in the indicator
   int iMA_calculated = BarsCalculated(maHandle);
   if(iMA_calculated <= 0)
     {
      PrintFormat("BarsCalculated() for iMA handle returned %d, error code %d", iMA_calculated, GetLastError());
      return(0);
     }

   int start;
//--- check if it's the indicators first call of OnCalculate() or we have some new uncalculated data
   if(prev_calculated == 0)
     {
      //--- set all the buffers to the first index
      lowBuffer[0] = low[0];
      highBuffer[0] = high[0];
      openBuffer[0] = open[0];
      closeBuffer[0] = close[0];
      start = 1;


      if(iMA_calculated > rates_total)
         iMA_valuesToCopy = rates_total;
      else   //--- copy the calculated bars which are less than the indicator buffers data
         iMA_valuesToCopy = iMA_calculated;
     }
   else
      start = prev_calculated - 1;

   iMA_valuesToCopy = (rates_total - prev_calculated) + 1;

//--- fill the iMA_Buffer array with values of the Moving Average indicator
//--- reset error code
   ResetLastError();
//--- copy a part of iMA_Buffer array with data in the zero index of the the indicator buffer
   if(CopyBuffer(maHandle, 0, 0, iMA_valuesToCopy, iMA_Buffer) < 0)
     {
      //--- if the copying fails, print the error code
      PrintFormat("Failed to copy data from the iMA indicator, error code %d", GetLastError());
      //--- exit the function with zero result to specify that the indicator calculations were not executed
      return(0);
     }

//--- iterate through the main calculations loop and execute all the calculations
   for(int x = start; x < rates_total && !IsStopped(); x++)
     {
      //--- save all the candle array prices in new non-array variables for quick access
      double candleOpen = open[x];
      double candleClose = close[x];
      double candleHigh = high[x];
      double candleLow  = low[x];

      lowBuffer[x] = candleLow;
      highBuffer[x] = candleHigh;
      openBuffer[x] = candleOpen;
      closeBuffer[x] = candleClose;

      //--- scan for the different trends signals and set the required candle color
      candleColorBuffer[x] = 2.0; // set color clrDarkGray - default (signal for no established trend)
      if(candleOpen > iMA_Buffer[x] && candleClose > iMA_Buffer[x] && candleHigh > iMA_Buffer[x] && candleLow > iMA_Buffer[x])
         candleColorBuffer[x]=0.0; // set color clrDodgerBlue - signal for a long/buy trend

      if(candleOpen < iMA_Buffer[x] && candleClose < iMA_Buffer[x] && candleHigh < iMA_Buffer[x] && candleLow < iMA_Buffer[x])
         candleColorBuffer[x]=1.0; // set color clrTomato - signal for a short/sell trend
     }

//--- return the rates_total which includes the prev_calculated value for the next call
   return(rates_total);
  }

//+------------------------------------------------------------------+
//| Indicator deinitialization function                              |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(maHandle != INVALID_HANDLE)
     {
      IndicatorRelease(maHandle);//-- clean up and release the iMA handle
     }
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| User custom function for custom indicator initialization         |
//+------------------------------------------------------------------+
bool GetInit()
  {
//--- set the indicator buffer mapping by assigning the indicator buffer array
   SetIndexBuffer(0, openBuffer, INDICATOR_DATA);
   SetIndexBuffer(1, highBuffer, INDICATOR_DATA);
   SetIndexBuffer(2, lowBuffer, INDICATOR_DATA);
   SetIndexBuffer(3, closeBuffer, INDICATOR_DATA);
   SetIndexBuffer(4, candleColorBuffer, INDICATOR_COLOR_INDEX);

//--- buffer for iMA
   SetIndexBuffer(5, iMA_Buffer, INDICATOR_DATA);

//--- set the price display precision to digits similar to the symbol prices
   IndicatorSetInteger(INDICATOR_DIGITS, _Digits);

//--- set the symbol, timeframe, period and smoothing applied price of the indicator as the short name
   string indicatorShortName = StringFormat("SmoothedCandles(%s, Period %d, %s)", _Symbol,
                               _maPeriod, EnumToString(_maAppliedPrice));
   IndicatorSetString(INDICATOR_SHORTNAME, indicatorShortName);
//IndicatorSetString(INDICATOR_SHORTNAME, "Smoothed Candlesticks");

//--- set line drawing to an empty value
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0);

//--- create the maHandle of the smoothing indicator
   maHandle = iMA(_Symbol, PERIOD_CURRENT, _maPeriod, 0, MODE_SMMA, _maAppliedPrice);

//--- check if the maHandle is created or it failed
   if(maHandle == INVALID_HANDLE)
     {
      //--- creating the handle failed, output the error code
      ResetLastError();
      PrintFormat("Failed to create maHandle of the iMA for symbol %s, error code %d",
                  _Symbol, GetLastError());
      //--- we terminate the program and exit the init function
      return(false);
     }

   return(true); // return true, initialization of the indicator ok
  }
//+------------------------------------------------------------------+

Guarda y compila el código del indicador, asegurándote de que se compile sin errores ni advertencias. Cárgalo en MetaTrader 5 y prueba cómo funciona utilizando diferentes parámetros de entrada del usuario.

Indicador SmoothedCandlesticks - Ventana de Datos

Indicador de velas japonesas suavizadas


Conclusión

En este artículo, has aprendido qué son los indicadores, los diferentes tipos de indicadores que se encuentran en la plataforma MetaTrader 5, los diversos componentes y elementos básicos de los indicadores personalizados, y has adquirido experiencia práctica al desarrollar algunos indicadores personalizados con MQL5 desde cero. 

El desarrollo de indicadores personalizados con MQL5 es un tema complejo que no se puede abordar por completo en un solo artículo, por lo que continuaremos cubriendo áreas más avanzadas en próximos artículos. Con el conocimiento adquirido en esta guía del artículo, ahora eres capaz de desarrollar tus propios indicadores personalizados simples. Te animo a seguir perfeccionando tus habilidades de programación y te deseo mucho éxito en tu camino como codificador.


Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/14481

Creamos un asesor multidivisa sencillo utilizando MQL5 (Parte 7): Señales de los indicadores ZigZag y Awesome Oscillator Creamos un asesor multidivisa sencillo utilizando MQL5 (Parte 7): Señales de los indicadores ZigZag y Awesome Oscillator
En este artículo, entenderemos por EA multidivisa un EA o robot comercial que utiliza indicadores ZigZag y Awesome Oscillator que filtran mutuamente sus señales.
Variables y tipos de datos extendidos en MQL5 Variables y tipos de datos extendidos en MQL5
Las variables y los tipos de datos son temas muy importantes no solo en la programación MQL5, sino también en cualquier lenguaje de programación. Las variables y los tipos de datos de MQL5 pueden dividirse en simples y extendidos. Aquí veremos las variables y los tipos de datos extendidos. Ya analizamos los sencillos en un artículo anterior.
Particularidades del trabajo con números del tipo double en MQL4 Particularidades del trabajo con números del tipo double en MQL4
En estos apuntes hemos reunido consejos para resolver los errores más frecuentes al trabajar con números del tipo double en los programas en MQL4.
Características del Wizard MQL5 que debe conocer (Parte 17): Negociación con multidivisas Características del Wizard MQL5 que debe conocer (Parte 17): Negociación con multidivisas
La negociación con varias divisas no está disponible por defecto cuando se crea un asesor experto mediante el asistente. Examinamos dos posibles trucos que los operadores pueden utilizar para poner a prueba sus ideas con más de un símbolo a la vez.