English Русский 中文 Deutsch 日本語 Português
preview
Plantillas listas para conectar indicadores en asesores (Parte 3): Indicadores de tendencia

Plantillas listas para conectar indicadores en asesores (Parte 3): Indicadores de tendencia

MetaTrader 5Ejemplos | 13 mayo 2024, 11:36
213 0
Artyom Trishkin
Artyom Trishkin

Contenido


Introducción

El artículo supone la continuación del tema de las plantillas listas para el uso de indicadores en asesores. Ya hemos analizado plantillas para conectar a asesores indicadores de oscilador e indicadores de volumen y Bill Williams.
Hoy veremos la conexión con asesores y el uso de indicadores de tendencia. Como ejemplo (al igual que en artículos anteriores), mostraremos los datos obtenidos de los indicadores en el panel informativo que creamos en el primer artículo de esta serie.

El estilo de presentación de la información en este artículo no diferirá de los artículos anteriores: primero daremos una breve revisión de referencia de cada indicador de tendencia y luego veremos códigos concisos para conectar y utilizar los indicadores en asesores.

Para cada indicador, el artículo presentará plantillas listas para utilizar en nuestros propios programas:

  • Variables de entrada y globales.
  • Inicialización de las variables y creación del manejador de indicador.
  • Desinicialización.
  • Obtención de datos en el asesor desde el indicador.
  • Ejemplo de muestra de los datos obtenidos en el panel informativo.

El artículo es de utilidad práctica, pues ofrece la oportunidad de usar simplemente los códigos del mismo como Copy-Paste en nuestros propios desarrollos.


Adaptive Moving Average

El indicador técnico Media Móvil Adaptativa (Adaptive Moving Average, AMA) se utiliza para construir una media móvil con baja sensibilidad al ruido en las series de precios y se caracteriza por un desfase mínimo para la detección de tendencias. Fue desarrollado y descrito por Perry Kaufman en su libro "Smarter Trading".

Uno de los inconvenientes de los distintos algoritmos de suavizado de series de precio es que los picos aleatorios de precio pueden dar lugar a falsas señales de tendencia. Por otra parte, la suavización provoca un retraso inevitable al señalizar una parada o inversión de tendencia. Este indicador se diseñó para evitar estas dos deficiencias.



Parámetros

La función iAMA() se utiliza para crear el manejador del indicador:

Retorna el manejador del indicador Adaptive Moving Average. Solo un búfer.

int  iAMA(
   string              symbol,             // symbol name
   ENUM_TIMEFRAMES     period,             // period
   int                 ama_period,         // AMA period
   int                 fast_ma_period,     // fast Moving Average period
   int                 slow_ma_period,     // slow Moving Average period
   int                 ama_shift,          // horizontal shift of the indicator
   ENUM_APPLIED_PRICE  applied_price       // price type or handle
   );

symbol

[in]  Nombre simbólico del instrumento sobre cuyos datos se calculará el indicador. NULL indicará el símbolo actual.

period

[in] El valor del periodo puede ser uno de los valores de la enumeración ENUM_TIMEFRAMES, 0 indicará el marco temporal actual.

ama_period

[in]  Periodo de cálculo en el que se calcula el factor de eficiencia.

fast_ma_period

[in]  Periodo rápido para calcular el coeficiente de suavizado en momentos de mercado rápido.

slow_ma_period

[in]  Periodo lento para calcular el coeficiente de suavizado en ausencia de tendencia.

ama_turno

[in]  Desplazamiento del indicador respecto al gráfico de precios.

applied_price

[in]  Precio utilizado. Puede ser cualquiera de las constantes de precio ENUM_APPLIED_PRICE o el manejador de otro indicador.

Retorna el manejador del indicador técnico especificado, en caso de fallo retorna INVALID_HANDLE. Para liberar la memoria del ordenador de un indicador que ya no se utiliza, se usará la función IndicatorRelease(), a la que se le transmitirá el manejador de este indicador.

Para crear un indicador en el asesor, declararemos las variables de entrada y globales:

//+------------------------------------------------------------------+
//|                                                 TestTrendAMA.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Undefined state
   LINE_STATE_UP,          // Upward
   LINE_STATE_DOWN,        // Downward
   LINE_STATE_TURN_UP,     // Upward reversal
   LINE_STATE_TURN_DOWN,   // Downward reversal
   LINE_STATE_STOP_UP,     // Upward stop
   LINE_STATE_STOP_DOWN,   // Downward stop
   LINE_STATE_ABOVE,       // Above value
   LINE_STATE_UNDER,       // Below value
   LINE_STATE_CROSS_UP,    // Crossing value upwards
   LINE_STATE_CROSS_DOWN,  // Crossing value downwards
   LINE_STATE_TOUCH_BELOW, // Touching value from below 
   LINE_STATE_TOUCH_ABOVE, // Touch value from above
   LINE_STATE_EQUALS,      // Equal to value
  };
//--- input parameters
input uint                 InpPeriod      =  9;             /* Period         */
input uint                 InpPeriodFast  =  2;             /* Fast EMA Period*/
input uint                 InpPeriodSlow  =  30;            /* Slow EMA Period*/
input int                  InpShift       =  0;             /* AMA Shift      */
input ENUM_APPLIED_PRICE   InpPrice       =  PRICE_CLOSE;   /* Applied Price  */
//--- global variables
int      handle=INVALID_HANDLE;  // Indicator handle
int      period=0;               // AMA calculation period
int      period_fast=0;          // Fast EMA calculation period
int      period_slow=0;          // Slow EMA calculation period
int      ind_digits=0;           // Number of decimal places in the indicator values
string   ind_title;              // Indicator description

La enumeración ENUM_LINE_STATE se creará para simplificar la obtención del estado de la línea de indicador: su forma (figura) y ubicación relativa a la línea de otro indicador o de algún nivel.
Podrá leer más información sobre la enumeración en el artículo sobre indicadores de oscilador en la sección de parámetros del indicador ATR.

Al utilizar el panel informativo en el asesor, también deberemos declarar las variables globales para él y conectar el archivo de la clase de panel:

//+------------------------------------------------------------------+
//|                                                 TestTrendAMA.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- includes
#include <Dashboard\Dashboard.mqh>
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Undefined state
   LINE_STATE_UP,          // Upward
   LINE_STATE_DOWN,        // Downward
   LINE_STATE_TURN_UP,     // Upward reversal
   LINE_STATE_TURN_DOWN,   // Downward reversal
   LINE_STATE_STOP_UP,     // Upward stop
   LINE_STATE_STOP_DOWN,   // Downward stop
   LINE_STATE_ABOVE,       // Above value
   LINE_STATE_UNDER,       // Below value
   LINE_STATE_CROSS_UP,    // Crossing value upwards
   LINE_STATE_CROSS_DOWN,  // Crossing value downwards
   LINE_STATE_TOUCH_BELOW, // Touching value from below 
   LINE_STATE_TOUCH_ABOVE, // Touch value from above
   LINE_STATE_EQUALS,      // Equal to value
  };
//--- input parameters
input uint                 InpPeriod      =  9;             /* Period         */
input uint                 InpPeriodFast  =  2;             /* Fast EMA Period*/
input uint                 InpPeriodSlow  =  30;            /* Slow EMA Period*/
input int                  InpShift       =  0;             /* AMA Shift      */
input ENUM_APPLIED_PRICE   InpPrice       =  PRICE_CLOSE;   /* Applied Price  */
//--- global variables
int      handle=INVALID_HANDLE;  // Indicator handle
int      period=0;               // AMA calculation period
int      period_fast=0;          // Fast EMA calculation period
int      period_slow=0;          // Slow EMA calculation period
int      ind_digits=0;           // Number of decimal places in the indicator values
string   ind_title;              // Indicator description
//--- variables for the panel
int      mouse_bar_index;        // Index of the bar the data is taken from
CDashboard *panel=NULL;          // Pointer to the panel object


Inicialización

Establecimiento de los valores de las variables globales para el indicador y creación de su manejador:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Indicator
//--- Set and adjust the calculation period and levels if necessary
   period=int(InpPeriod<1 ? 9 : InpPeriod);
   period_fast=int(InpPeriodFast<1 ? 2 : InpPeriodFast);
   period_slow=int(InpPeriodSlow<1 ? 30 : InpPeriodSlow);
//--- Set the indicator name and the number of decimal places
   ind_title=StringFormat("AMA(%lu,%lu,%lu)",period,period_fast,period_slow);
   ind_digits=Digits()+1;
//--- Create indicator handle
   ResetLastError();
   handle=iAMA(Symbol(),PERIOD_CURRENT,period,period_fast,period_slow,InpShift,InpPrice);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Successful initialization
   return(INIT_SUCCEEDED);
  }

Al utilizar un panel informativo en el asesor, crearemos un panel:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Indicator
//--- Set and adjust the calculation period and levels if necessary
   period=int(InpPeriod<1 ? 9 : InpPeriod);
   period_fast=int(InpPeriodFast<1 ? 2 : InpPeriodFast);
   period_slow=int(InpPeriodSlow<1 ? 30 : InpPeriodSlow);
//--- Set the indicator name and the number of decimal places
   ind_title=StringFormat("AMA(%lu,%lu,%lu)",period,period_fast,period_slow);
   ind_digits=Digits()+1;
//--- Create indicator handle
   ResetLastError();
   handle=iAMA(Symbol(),PERIOD_CURRENT,period,period_fast,period_slow,InpShift,InpPrice);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Dashboard
//--- Create the panel
   panel=new CDashboard(1,20,20,197,225);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Set font parameters
   panel.SetFontParams("Calibri",9);
//--- Display the panel with the "Symbol, Timeframe description" header text
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
//--- Create a table with ID 0 to display bar data in it
   panel.CreateNewTable(0);
//--- Draw a table with ID 0 on the panel background
   panel.DrawGrid(0,2,20,6,2,18,96);

//--- Create a table with ID 1 to display indicator data in it
   panel.CreateNewTable(1);
//--- Get the Y2 table coordinate with ID 0 and
//--- set the Y1 coordinate for the table with ID 1
   int y1=panel.TableY2(0)+22;
//--- Draw a table with ID 1 on the panel background
   panel.DrawGrid(1,2,y1,3,2,18,96);
   
//--- Display tabular data in the journal
   panel.GridPrint(0,2);
   panel.GridPrint(1,2);
//--- Initialize the variable with the index of the mouse cursor bar
   mouse_bar_index=0;
//--- Display the data of the current bar on the panel
   DrawData(mouse_bar_index,TimeCurrent());

//--- Successful initialization
   return(INIT_SUCCEEDED);
  }


Desinicialización

En el manejador OnDeinit() del asesor liberaremos el manejador del indicador:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Release handle of the indicator
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Clear all comments on the chart
   Comment("");
  }

Al utilizar un panel informativo, eliminaremos el objeto de panel creado:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Release handle of the indicator
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Clear all comments on the chart
   Comment("");
   
//--- If the panel object exists, delete it
   if(panel!=NULL)
      delete panel;
  }


Obtención de datos

A continuación se presentarán las funciones generales de la obtención de datos usando el manejador del indicador. Ya analizamos dichas funciones en el artículo sobre la conexión de osciladores en asesores. Podrá utilizar las funciones presentadas "tal cual" en sus programas:

//+------------------------------------------------------------------+
//| Return the indicator data on the specified bar                   |
//+------------------------------------------------------------------+
double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
  {
   double array[1]={0};
   ResetLastError();
   if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
     {
      PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
      return EMPTY_VALUE;
     }
   return array[0];
  }
//+------------------------------------------------------------------+
//| Return the state of the indicator line                           |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
  {
//--- Get the values of the indicator line with the shift (0,1,2) relative to the passed index
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
   const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
//--- If at least one of the values could not be obtained, return an undefined value 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Line upward reversal (value2>value1 && value0>value1)
   if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_TURN_UP;
//--- Line upward direction (value2<=value1 && value0>value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_UP;
//--- Line upward stop (value2<=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_UP;
//--- Line downward reversal (value2<value1 && value0<value1)
   if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_TURN_DOWN;
//--- Line downward direction (value2>=value1 && value0<value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_DOWN;
//--- Line downward stop (value2>=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_DOWN;
//--- Undefined state
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Return the state of the line relative to the specified level     |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
  {
//--- Get the values of the indicator line with the shift (0,1) relative to the passed index
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
//--- If at least one of the values could not be obtained, return an undefined value 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Define the second level to compare
   double level=(level1==EMPTY_VALUE ? level0 : level1);
//--- The line is below the level (value1<level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_UNDER;
//--- The line is above the level (value1>level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_ABOVE;
//--- The line crossed the level upwards (value1<=level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_CROSS_UP;
//--- The line crossed the level downwards (value1>=level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_CROSS_DOWN;
//--- The line touched the level from below (value1<level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- The line touched the level from above (value1>level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Line is equal to the level value (value1==level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_EQUALS;
//--- Undefined state
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Return the indicator line state description                      |
//+------------------------------------------------------------------+
string LineStateDescription(const ENUM_LINE_STATE state)
  {
   switch(state)
     {
      case LINE_STATE_UP         :  return "Up";
      case LINE_STATE_STOP_UP    :  return "Stop Up";
      case LINE_STATE_TURN_UP    :  return "Turn Up";
      case LINE_STATE_DOWN       :  return "Down";
      case LINE_STATE_STOP_DOWN  :  return "Stop Down";
      case LINE_STATE_TURN_DOWN  :  return "Turn Down";
      case LINE_STATE_ABOVE      :  return "Above level";
      case LINE_STATE_UNDER      :  return "Under level";
      case LINE_STATE_CROSS_UP   :  return "Crossing Up";
      case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
      case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
      case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
      case LINE_STATE_EQUALS     :  return "Equals";
      default                    :  return "Unknown";
     }
  }

Al utilizar el panel informativo, los datos se mostrarán en el panel utilizando la función:

//+------------------------------------------------------------------+
//| Display data from the specified timeseries index to the panel    |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Declare the variables to receive data in them
   MqlTick  tick={0};
   MqlRates rates[1];

//--- Exit if unable to get the current prices
   if(!SymbolInfoTick(Symbol(),tick))
      return;
//--- Exit if unable to get the bar data by the specified index
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Set font parameters for bar and indicator data headers
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Set font parameters for bar and indicator data
   panel.SetFontParams(name,9);

//--- Display the data of the specified bar in table 0 on the panel
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Display the indicator data from the specified bar on the panel in table 1
   panel.DrawText(ind_title, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value=IndicatorValue(handle,index,0);
   string value_str=(value!=EMPTY_VALUE ? DoubleToString(value,ind_digits) : "");
   panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,100);
   
//--- Display a description of the indicator line state
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state=LineState(handle,index,0);
   panel.DrawText(LineStateDescription(state),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,90);
   
//--- Redraw the chart to immediately display all changes on the panel
   ChartRedraw(ChartID());
  }

Además, al utilizar un panel informativo, el manejador de eventos OnChartEvent() del asesor llamará al manejador de eventos del panel informativo y procesará los eventos para obtener el índice de la barra bajo el cursor:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Handling the panel
//--- Call the panel event handler
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- If the cursor moves or a click is made on the chart
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Declare the variables to record time and price coordinates in them
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- If the cursor coordinates are converted to date and time
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- write the bar index where the cursor is located to a global variable
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Display the bar data under the cursor on the panel 
         DrawData(mouse_bar_index,time);
        }
     }

//--- If we received a custom event, display the appropriate message in the journal
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Here we can implement handling a click on the close button on the panel
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }

Tras compilar y ejecutar el asesor en el gráfico, podremos controlar el valor y el estado de la línea de indicador en el panel informativo:



El archivo del asesor de pruebas "TestTrendAMA.mq5" podrá consultarse en los archivos adjuntos.


Average Directional Movement Index

El indicador técnico Índice de Dirección Media del Movimiento (Average Directional Movement Index, ADX) ayuda a determinar la presencia de una tendencia de precios. Se basa en los enfoques descritos en el libro "New Concepts in Technical Trading Systems" de Welles Wilder.

El método comercial más sencillo basado en un sistema direccional consiste en comparar dos indicadores direccionales un +DI de periodo 14 y un -DI de periodo 14. Para ello, o bien los gráficos de los indicadores se trazan uno encima del otro, o bien se resta +DI de -DI. W. Wilder sugiere comprar cuando +DI suba por encima de -DI y vender cuando +DI caiga por debajo de -DI.

El autor también complementa estas sencillas reglas comerciales con la "regla de los puntos extremos". Sirve para eliminar las señales falsas y reducir el número de transacciones realizadas. Según el principio de los puntos extremos, deberemos marcar un "punto extremo" en el momento de la intersección de +DI y -DI. Si +DI supera a -DI, ese punto será el precio máximo del día de cruce. Si +DI cae por debajo de -DI, ese punto será el precio mínimo del día de cruce.

El punto extremo se utilizará entonces como nivel de entrada en el mercado. Por lo tanto, después de una señal de compra (+DI ha subido por encima de -DI), deberemos esperar hasta que el precio suba por encima del punto extremo y solo entonces comprar. Si el precio no logra superar el nivel del punto extremo, deberemos mantener una posición corta.



Parámetros

La función iADX() se utiliza para crear el manejador del indicador:

Retorna el manejador del indicador Average Directional Movement Index.

int  iADX(
   string           symbol,         // symbol name
   ENUM_TIMEFRAMES  period,         // period
   int              adx_period      // averaging period
   );

symbol

[in]  Nombre simbólico del instrumento sobre cuyos datos se calculará el indicador. NULL indicará el símbolo actual.

period

[in] El valor del periodo puede ser uno de los valores de la enumeración ENUM_TIMEFRAMES, 0 indicará el marco temporal actual.

adx_period

[in]  Periodo de cálculo del índice.

Números de los búferes: 0 — MAIN_LINE, 1 — PLUSDI_LINE, 2 — MINUSDI_LINE.

Retorna el manejador del indicador técnico especificado, en caso de fallo retorna INVALID_HANDLE. Para liberar la memoria del ordenador de un indicador que ya no se utiliza, se usará la función IndicatorRelease(), a la que se le transmitirá el manejador de este indicador.

Para crear un indicador en el asesor, declararemos las variables de entrada y globales:

//+------------------------------------------------------------------+
//|                                                 TestTrendADX.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Undefined state
   LINE_STATE_UP,          // Upward
   LINE_STATE_DOWN,        // Downward
   LINE_STATE_TURN_UP,     // Upward reversal
   LINE_STATE_TURN_DOWN,   // Downward reversal
   LINE_STATE_STOP_UP,     // Upward stop
   LINE_STATE_STOP_DOWN,   // Downward stop
   LINE_STATE_ABOVE,       // Above value
   LINE_STATE_UNDER,       // Below value
   LINE_STATE_CROSS_UP,    // Crossing value upwards
   LINE_STATE_CROSS_DOWN,  // Crossing value downwards
   LINE_STATE_TOUCH_BELOW, // Touching value from below 
   LINE_STATE_TOUCH_ABOVE, // Touch value from above
   LINE_STATE_EQUALS,      // Equal to value
  };
//--- input parameters
input uint  InpPeriod   =  14;   /* Period   */
//--- global variables
int      handle=INVALID_HANDLE;  // Indicator handle
int      period=0;               // ADX calculation period
int      ind_digits=0;           // Number of decimal places in the indicator values
string   ind_title;              // Indicator description

Al utilizar el panel informativo en el asesor, también deberemos declarar las variables globales para él y conectar el archivo de la clase de panel:

//+------------------------------------------------------------------+
//|                                                 TestTrendADX.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- includes
#include <Dashboard\Dashboard.mqh>
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Undefined state
   LINE_STATE_UP,          // Upward
   LINE_STATE_DOWN,        // Downward
   LINE_STATE_TURN_UP,     // Upward reversal
   LINE_STATE_TURN_DOWN,   // Downward reversal
   LINE_STATE_STOP_UP,     // Upward stop
   LINE_STATE_STOP_DOWN,   // Downward stop
   LINE_STATE_ABOVE,       // Above value
   LINE_STATE_UNDER,       // Below value
   LINE_STATE_CROSS_UP,    // Crossing value upwards
   LINE_STATE_CROSS_DOWN,  // Crossing value downwards
   LINE_STATE_TOUCH_BELOW, // Touching value from below 
   LINE_STATE_TOUCH_ABOVE, // Touch value from above
   LINE_STATE_EQUALS,      // Equal to value
  };
//--- input parameters
input uint  InpPeriod   =  14;   /* Period   */
//--- global variables
int      handle=INVALID_HANDLE;  // Indicator handle
int      period=0;               // ADX calculation period
int      ind_digits=0;           // Number of decimal places in the indicator values
string   ind_title;              // Indicator description
//--- variables for the panel
int      mouse_bar_index;        // Index of the bar the data is taken from
CDashboard *panel=NULL;          // Pointer to the panel object


Inicialización

Establecimiento de los valores de las variables globales para el indicador y creación de su manejador:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Indicator
//--- Set and adjust the calculation period and levels if necessary
   period=int(InpPeriod<1 ? 14 : InpPeriod);
//--- Set the indicator name and the number of decimal places
   ind_title=StringFormat("ADX(%lu)",period);
   ind_digits=2;
//--- Create indicator handle
   ResetLastError();
   handle=iADX(Symbol(),PERIOD_CURRENT,period);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Successful initialization
   return(INIT_SUCCEEDED);
  }

Al utilizar un panel informativo en el asesor, crearemos un panel:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Indicator
//--- Set and adjust the calculation period and levels if necessary
   period=int(InpPeriod<1 ? 14 : InpPeriod);
//--- Set the indicator name and the number of decimal places
   ind_title=StringFormat("ADX(%lu)",period);
   ind_digits=2;
//--- Create indicator handle
   ResetLastError();
   handle=iADX(Symbol(),PERIOD_CURRENT,period);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Dashboard
//--- Create the panel
   panel=new CDashboard(1,20,20,197,243);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Set font parameters
   panel.SetFontParams("Calibri",9);
//--- Display the panel with the "Symbol, Timeframe description" header text
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
//--- Create a table with ID 0 to display bar data in it
   panel.CreateNewTable(0);
//--- Draw a table with ID 0 on the panel background
   panel.DrawGrid(0,2,20,6,2,18,96);

//--- Create a table with ID 1 to display indicator data in it
   panel.CreateNewTable(1);
//--- Get the Y2 table coordinate with ID 0 and
//--- set the Y1 coordinate for the table with ID 1
   int y1=panel.TableY2(0)+22;
//--- Draw a table with ID 1 on the panel background
   panel.DrawGrid(1,2,y1,4,2,18,96);
   
//--- Display tabular data in the journal
   panel.GridPrint(0,2);
   panel.GridPrint(1,2);
//--- Initialize the variable with the index of the mouse cursor bar
   mouse_bar_index=0;
//--- Display the data of the current bar on the panel
   DrawData(mouse_bar_index,TimeCurrent());

//--- Successful initialization
   return(INIT_SUCCEEDED);
  }


Desinicialización

En el manejador OnDeinit() del asesor liberaremos el manejador del indicador:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Release handle of the indicator
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Clear all comments on the chart
   Comment("");
  }

Al utilizar un panel informativo, eliminaremos el objeto de panel creado:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Release handle of the indicator
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Clear all comments on the chart
   Comment("");
   
//--- If the panel object exists, delete it
   if(panel!=NULL)
      delete panel;
  }


Obtención de datos

Funciones generales para obtener los datos según el manejador del indicador:

//+------------------------------------------------------------------+
//| Return the indicator data on the specified bar                   |
//+------------------------------------------------------------------+
double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
  {
   double array[1]={0};
   ResetLastError();
   if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
     {
      PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
      return EMPTY_VALUE;
     }
   return array[0];
  }
//+------------------------------------------------------------------+
//| Return the state of the indicator line                           |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
  {
//--- Get the values of the indicator line with the shift (0,1,2) relative to the passed index
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
   const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
//--- If at least one of the values could not be obtained, return an undefined value 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Line upward reversal (value2>value1 && value0>value1)
   if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_TURN_UP;
//--- Line upward direction (value2<=value1 && value0>value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_UP;
//--- Line upward stop (value2<=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_UP;
//--- Line downward reversal (value2<value1 && value0<value1)
   if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_TURN_DOWN;
//--- Line downward direction (value2>=value1 && value0<value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_DOWN;
//--- Line downward stop (value2>=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_DOWN;
//--- Undefined state
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Return the state of the line relative to the specified level     |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
  {
//--- Get the values of the indicator line with the shift (0,1) relative to the passed index
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
//--- If at least one of the values could not be obtained, return an undefined value 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Define the second level to compare
   double level=(level1==EMPTY_VALUE ? level0 : level1);
//--- The line is below the level (value1<level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_UNDER;
//--- The line is above the level (value1>level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_ABOVE;
//--- The line crossed the level upwards (value1<=level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_CROSS_UP;
//--- The line crossed the level downwards (value1>=level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_CROSS_DOWN;
//--- The line touched the level from below (value1<level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- The line touched the level from above (value1>level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Line is equal to the level value (value1==level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_EQUALS;
//--- Undefined state
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Return the indicator line state description                      |
//+------------------------------------------------------------------+
string LineStateDescription(const ENUM_LINE_STATE state)
  {
   switch(state)
     {
      case LINE_STATE_UP         :  return "Up";
      case LINE_STATE_STOP_UP    :  return "Stop Up";
      case LINE_STATE_TURN_UP    :  return "Turn Up";
      case LINE_STATE_DOWN       :  return "Down";
      case LINE_STATE_STOP_DOWN  :  return "Stop Down";
      case LINE_STATE_TURN_DOWN  :  return "Turn Down";
      case LINE_STATE_ABOVE      :  return "Above level";
      case LINE_STATE_UNDER      :  return "Under level";
      case LINE_STATE_CROSS_UP   :  return "Crossing Up";
      case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
      case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
      case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
      case LINE_STATE_EQUALS     :  return "Equals";
      default                    :  return "Unknown";
     }
  }

Al utilizar el panel informativo, los datos se mostrarán en el panel utilizando la función:

//+------------------------------------------------------------------+
//| Display data from the specified timeseries index to the panel    |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Declare the variables to receive data in them
   MqlTick  tick={0};
   MqlRates rates[1];

//--- Exit if unable to get the current prices
   if(!SymbolInfoTick(Symbol(),tick))
      return;
//--- Exit if unable to get the bar data by the specified index
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Set font parameters for bar and indicator data headers
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Set font parameters for bar and indicator data
   panel.SetFontParams(name,9);

//--- Display the data of the specified bar in table 0 on the panel
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Display the ADX line data from the specified bar on the panel in table 1
   panel.DrawText(ind_title, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value=IndicatorValue(handle,index,MAIN_LINE);
   string value_str=(value!=EMPTY_VALUE ? DoubleToString(value,ind_digits) : "");
   panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,100);

//--- Display the +DI line data from the specified bar on the panel in table 1
   panel.DrawText("+DI", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   double value_dip=IndicatorValue(handle,index,PLUSDI_LINE);
   string value_dip_str=(value_dip!=EMPTY_VALUE ? DoubleToString(value_dip,ind_digits) : "");
   panel.DrawText(value_dip_str,panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,100);

//--- Display the -DI line data from the specified bar on the panel in table 1
   panel.DrawText("-DI", panel.CellX(1,2,0)+2, panel.CellY(1,2,0)+2);
   double value_dim=IndicatorValue(handle,index,MINUSDI_LINE);
   string value_dim_str=(value_dim!=EMPTY_VALUE ? DoubleToString(value_dim,ind_digits) : "");
   panel.DrawText(value_dim_str,panel.CellX(1,2,1)+2,panel.CellY(1,2,1)+2,clrNONE,100);
   
//--- Display a description of +DI and -DI lines ratio
   panel.DrawText("+DI vs -DI", panel.CellX(1,3,0)+2, panel.CellY(1,3,0)+2);
   ENUM_LINE_STATE state=LineStateRelative(handle,index,1,value_dim,IndicatorValue(handle,index+1,2));
   string state_di_str=LineStateDescription(state);
   color clr=clrNONE;
   if(state==LINE_STATE_ABOVE)
     {
      state_di_str="+DI > -DI";
      clr=clrGreen;
     }
   if(state==LINE_STATE_CROSS_UP)
      clr=clrGreen;
   if(state==LINE_STATE_UNDER)
     {
      state_di_str="+DI < -DI";
      clr=clrRed;
     }
   if(state==LINE_STATE_CROSS_DOWN)
      clr=clrRed;
   panel.DrawText(state_di_str,panel.CellX(1,3,1)+2,panel.CellY(1,3,1)+2,clr,90);
   
//--- Redraw the chart to immediately display all changes on the panel
   ChartRedraw(ChartID());
  }

Además de estas líneas de indicador, el panel mostrará una descripción de la relación de las líneas +DI y -DI, que son en realidad las líneas de señal del indicador.

Además, al utilizar un panel informativo, el manejador de eventos OnChartEvent() del asesor llamará al manejador de eventos del panel informativo y procesará los eventos para obtener el índice de la barra bajo el cursor:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Handling the panel
//--- Call the panel event handler
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- If the cursor moves or a click is made on the chart
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Declare the variables to record time and price coordinates in them
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- If the cursor coordinates are converted to date and time
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- write the bar index where the cursor is located to a global variable
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Display the bar data under the cursor on the panel 
         DrawData(mouse_bar_index,time);
        }
     }

//--- If we received a custom event, display the appropriate message in the journal
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Here we can implement handling a click on the close button on the panel
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }

Tras compilar y ejecutar el asesor en el gráfico, podremos controlar el valor y el estado de la línea de indicador en el panel informativo:


Podrá ver el archivo del asesor de pruebas "TestTrendADX.mq5" en los archivos adjuntos.


Average Directional Movement Index Wilder

El indicador técnico Índice de Dirección Media del Movimiento de Wilder (Average Directional Movement Index Wilder, ADX Wilder) ayuda a determinar la presencia de una tendencia de precios. Este indicador está construido en estricta conformidad con el algoritmo descrito por Welles Wilder en el libro "New Concepts of Technical Trading Systems".

Las reglas comerciales de este indicador se describen en la sección "Average Directional Movement Index".



Parámetros

La función iADXWilder() se utiliza para crear el manejador del indicador:

Return the handle of the Average Directional Movement Index by Welles Wilder.

int  iADXWilder(
   string           symbol,         // symbol name
   ENUM_TIMEFRAMES  period,         // period
   int              adx_period      // averaging period
   );

symbol

[in]  Nombre simbólico del instrumento sobre cuyos datos se calculará el indicador. NULL indicará el símbolo actual.

period

[in] El valor del periodo puede ser uno de los valores de la enumeración ENUM_TIMEFRAMES, 0 indicará el marco temporal actual.

adx_period

[in]  Periodo de cálculo del índice.

Números de los búferes: 0 — MAIN_LINE, 1 — PLUSDI_LINE, 2 — MINUSDI_LINE.

Retorna el manejador del indicador técnico especificado, en caso de fallo retorna INVALID_HANDLE. Para liberar la memoria del ordenador de un indicador que ya no se utiliza, se usará la función IndicatorRelease(), a la que se le transmitirá el manejador de este indicador.

Para crear un indicador en el asesor, declararemos las variables de entrada y globales:

//+------------------------------------------------------------------+
//|                                           TestTrendADXWilder.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Undefined state
   LINE_STATE_UP,          // Upward
   LINE_STATE_DOWN,        // Downward
   LINE_STATE_TURN_UP,     // Upward reversal
   LINE_STATE_TURN_DOWN,   // Downward reversal
   LINE_STATE_STOP_UP,     // Upward stop
   LINE_STATE_STOP_DOWN,   // Downward stop
   LINE_STATE_ABOVE,       // Above value
   LINE_STATE_UNDER,       // Below value
   LINE_STATE_CROSS_UP,    // Crossing value upwards
   LINE_STATE_CROSS_DOWN,  // Crossing value downwards
   LINE_STATE_TOUCH_BELOW, // Touching value from below 
   LINE_STATE_TOUCH_ABOVE, // Touch value from above
   LINE_STATE_EQUALS,      // Equal to value
  };
//--- input parameters
input uint  InpPeriod   =  14;   /* Period   */
//--- global variables
int      handle=INVALID_HANDLE;  // Indicator handle
int      period=0;               // ADX Wilder calculation period
int      ind_digits=0;           // Number of decimal places in the indicator values
string   ind_title;              // Indicator description

Al utilizar el panel informativo en el asesor, también deberemos declarar las variables globales para él y conectar el archivo de la clase de panel:

//+------------------------------------------------------------------+
//|                                           TestTrendADXWilder.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- includes
#include <Dashboard\Dashboard.mqh>
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Undefined state
   LINE_STATE_UP,          // Upward
   LINE_STATE_DOWN,        // Downward
   LINE_STATE_TURN_UP,     // Upward reversal
   LINE_STATE_TURN_DOWN,   // Downward reversal
   LINE_STATE_STOP_UP,     // Upward stop
   LINE_STATE_STOP_DOWN,   // Downward stop
   LINE_STATE_ABOVE,       // Above value
   LINE_STATE_UNDER,       // Below value
   LINE_STATE_CROSS_UP,    // Crossing value upwards
   LINE_STATE_CROSS_DOWN,  // Crossing value downwards
   LINE_STATE_TOUCH_BELOW, // Touching value from below 
   LINE_STATE_TOUCH_ABOVE, // Touch value from above
   LINE_STATE_EQUALS,      // Equal to value
  };
//--- input parameters
input uint  InpPeriod   =  14;   /* Period   */
//--- global variables
int      handle=INVALID_HANDLE;  // Indicator handle
int      period=0;               // ADX Wilder calculation period
int      ind_digits=0;           // Number of decimal places in the indicator values
string   ind_title;              // Indicator description
//--- variables for the panel
int      mouse_bar_index;        // Index of the bar the data is taken from
CDashboard *panel=NULL;          // Pointer to the panel object


Inicialización

Establecimiento de los valores de las variables globales para el indicador y creación de su manejador:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Indicator
//--- Set and adjust the calculation period and levels if necessary
   period=int(InpPeriod<1 ? 14 : InpPeriod);
//--- Set the indicator name and the number of decimal places
   ind_title=StringFormat("ADX Wilder(%lu)",period);
   ind_digits=2;
//--- Create indicator handle
   ResetLastError();
   handle=iADXWilder(Symbol(),PERIOD_CURRENT,period);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Successful initialization
   return(INIT_SUCCEEDED);
  }

Al utilizar un panel informativo en el asesor, crearemos un panel:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Indicator
//--- Set and adjust the calculation period and levels if necessary
   period=int(InpPeriod<1 ? 14 : InpPeriod);
//--- Set the indicator name and the number of decimal places
   ind_title=StringFormat("ADX Wilder(%lu)",period);
   ind_digits=2;
//--- Create indicator handle
   ResetLastError();
   handle=iADXWilder(Symbol(),PERIOD_CURRENT,period);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Dashboard
//--- Create the panel
   panel=new CDashboard(1,20,20,197,243);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Set font parameters
   panel.SetFontParams("Calibri",9);
//--- Display the panel with the "Symbol, Timeframe description" header text
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
//--- Create a table with ID 0 to display bar data in it
   panel.CreateNewTable(0);
//--- Draw a table with ID 0 on the panel background
   panel.DrawGrid(0,2,20,6,2,18,96);

//--- Create a table with ID 1 to display indicator data in it
   panel.CreateNewTable(1);
//--- Get the Y2 table coordinate with ID 0 and
//--- set the Y1 coordinate for the table with ID 1
   int y1=panel.TableY2(0)+22;
//--- Draw a table with ID 1 on the panel background
   panel.DrawGrid(1,2,y1,4,2,18,96);
   
//--- Display tabular data in the journal
   panel.GridPrint(0,2);
   panel.GridPrint(1,2);
//--- Initialize the variable with the index of the mouse cursor bar
   mouse_bar_index=0;
//--- Display the data of the current bar on the panel
   DrawData(mouse_bar_index,TimeCurrent());

//--- Successful initialization
   return(INIT_SUCCEEDED);
  }


Desinicialización

En el manejador OnDeinit() del asesor liberaremos el manejador del indicador:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Release handle of the indicator
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Clear all comments on the chart
   Comment("");
  }

Al utilizar un panel informativo, eliminaremos el objeto de panel creado:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Release handle of the indicator
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Clear all comments on the chart
   Comment("");
   
//--- If the panel object exists, delete it
   if(panel!=NULL)
      delete panel;
  }


Obtención de datos

Funciones generales para obtener los datos según el manejador del indicador:

//+------------------------------------------------------------------+
//| Return the indicator data on the specified bar                   |
//+------------------------------------------------------------------+
double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
  {
   double array[1]={0};
   ResetLastError();
   if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
     {
      PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
      return EMPTY_VALUE;
     }
   return array[0];
  }
//+------------------------------------------------------------------+
//| Return the state of the indicator line                           |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
  {
//--- Get the values of the indicator line with the shift (0,1,2) relative to the passed index
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
   const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
//--- If at least one of the values could not be obtained, return an undefined value 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Line upward reversal (value2>value1 && value0>value1)
   if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_TURN_UP;
//--- Line upward direction (value2<=value1 && value0>value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_UP;
//--- Line upward stop (value2<=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_UP;
//--- Line downward reversal (value2<value1 && value0<value1)
   if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_TURN_DOWN;
//--- Line downward direction (value2>=value1 && value0<value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_DOWN;
//--- Line downward stop (value2>=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_DOWN;
//--- Undefined state
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Return the state of the line relative to the specified level     |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
  {
//--- Get the values of the indicator line with the shift (0,1) relative to the passed index
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
//--- If at least one of the values could not be obtained, return an undefined value 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Define the second level to compare
   double level=(level1==EMPTY_VALUE ? level0 : level1);
//--- The line is below the level (value1<level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_UNDER;
//--- The line is above the level (value1>level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_ABOVE;
//--- The line crossed the level upwards (value1<=level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_CROSS_UP;
//--- The line crossed the level downwards (value1>=level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_CROSS_DOWN;
//--- The line touched the level from below (value1<level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- The line touched the level from above (value1>level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Line is equal to the level value (value1==level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_EQUALS;
//--- Undefined state
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Return the indicator line state description                      |
//+------------------------------------------------------------------+
string LineStateDescription(const ENUM_LINE_STATE state)
  {
   switch(state)
     {
      case LINE_STATE_UP         :  return "Up";
      case LINE_STATE_STOP_UP    :  return "Stop Up";
      case LINE_STATE_TURN_UP    :  return "Turn Up";
      case LINE_STATE_DOWN       :  return "Down";
      case LINE_STATE_STOP_DOWN  :  return "Stop Down";
      case LINE_STATE_TURN_DOWN  :  return "Turn Down";
      case LINE_STATE_ABOVE      :  return "Above level";
      case LINE_STATE_UNDER      :  return "Under level";
      case LINE_STATE_CROSS_UP   :  return "Crossing Up";
      case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
      case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
      case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
      case LINE_STATE_EQUALS     :  return "Equals";
      default                    :  return "Unknown";
     }
  }

Al utilizar el panel informativo, los datos se mostrarán en el panel utilizando la función:

//+------------------------------------------------------------------+
//| Display data from the specified timeseries index to the panel    |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Declare the variables to receive data in them
   MqlTick  tick={0};
   MqlRates rates[1];

//--- Exit if unable to get the current prices
   if(!SymbolInfoTick(Symbol(),tick))
      return;
//--- Exit if unable to get the bar data by the specified index
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Set font parameters for bar and indicator data headers
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Set font parameters for bar and indicator data
   panel.SetFontParams(name,9);

//--- Display the data of the specified bar in table 0 on the panel
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Display the ADX line data from the specified bar on the panel in table 1
   panel.DrawText(ind_title, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value=IndicatorValue(handle,index,MAIN_LINE);
   string value_str=(value!=EMPTY_VALUE ? DoubleToString(value,ind_digits) : "");
   panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,100);

//--- Display the +DI line data from the specified bar on the panel in table 1
   panel.DrawText("+DI", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   double value_dip=IndicatorValue(handle,index,PLUSDI_LINE);
   string value_dip_str=(value_dip!=EMPTY_VALUE ? DoubleToString(value_dip,ind_digits) : "");
   panel.DrawText(value_dip_str,panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,100);

//--- Display the -DI line data from the specified bar on the panel in table 1
   panel.DrawText("-DI", panel.CellX(1,2,0)+2, panel.CellY(1,2,0)+2);
   double value_dim=IndicatorValue(handle,index,MINUSDI_LINE);
   string value_dim_str=(value_dim!=EMPTY_VALUE ? DoubleToString(value_dim,ind_digits) : "");
   panel.DrawText(value_dim_str,panel.CellX(1,2,1)+2,panel.CellY(1,2,1)+2,clrNONE,100);
   
//--- Display a description of +DI and -DI lines ratio
   panel.DrawText("+DI vs -DI", panel.CellX(1,3,0)+2, panel.CellY(1,3,0)+2);
   ENUM_LINE_STATE state=LineStateRelative(handle,index,1,value_dim,IndicatorValue(handle,index+1,2));
   string state_di_str=LineStateDescription(state);
   color clr=clrNONE;
   if(state==LINE_STATE_ABOVE)
     {
      state_di_str="+DI > -DI";
      clr=clrGreen;
     }
   if(state==LINE_STATE_CROSS_UP)
      clr=clrGreen;
   if(state==LINE_STATE_UNDER)
     {
      state_di_str="+DI < -DI";
      clr=clrRed;
     }
   if(state==LINE_STATE_CROSS_DOWN)
      clr=clrRed;
   panel.DrawText(state_di_str,panel.CellX(1,3,1)+2,panel.CellY(1,3,1)+2,clr,90);
   
//--- Redraw the chart to immediately display all changes on the panel
   ChartRedraw(ChartID());
  }

El panel muestra los datos de las líneas del indicador y la descripción de la relación de las líneas +DI y -DI, que son en realidad las líneas de señal del indicador.

Además, al utilizar un panel informativo, el manejador de eventos OnChartEvent() del asesor llamará al manejador de eventos del panel informativo y procesará los eventos para obtener el índice de la barra bajo el cursor:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Handling the panel
//--- Call the panel event handler
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- If the cursor moves or a click is made on the chart
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Declare the variables to record time and price coordinates in them
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- If the cursor coordinates are converted to date and time
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- write the bar index where the cursor is located to a global variable
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Display the bar data under the cursor on the panel 
         DrawData(mouse_bar_index,time);
        }
     }

//--- If we received a custom event, display the appropriate message in the journal
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Here we can implement handling a click on the close button on the panel
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }

Tras compilar y ejecutar el asesor en el gráfico, podremos controlar el valor y el estado de la línea de indicador en el panel informativo:



Podrá ver el archivo del asesor de pruebas "TestTrendADXWilder.mq5" en los archivos adjuntos.


Bollinger Bands®

Las Bandas de Bollinger (BB) son similares a Envelopes. La diferencia entre ellas es que los límites de las Bandas Comerciales (Envelopes) se sitúan por encima y por debajo de la curva de la media móvil a una distancia fija, expresada en porcentaje, mientras que los límites de las Bandas de Bollinger se construyen a distancias iguales a un cierto número de desviaciones estándar. Como la magnitud de la desviación típica depende de la volatilidad, las propias bandas ajustarán su anchura: esta aumentará cuando el mercado sea volátil y disminuirá en periodos más estables.

Las Bandas de Bollinger se suelen trazar en un gráfico de precios, pero también se pueden dibujar en un gráfico de indicadores. Al igual que sucede con Envelopes, la interpretación de las Bandas de Bollinger se basa en el hecho de que los precios tienden a mantenerse dentro de los límites superior e inferior de la banda. Una característica distintiva de las Bandas de Bollinger es su anchura variable, debida a la volatilidad de los precios. Durante los periodos de cambios significativos en los precios (es decir, de alta volatilidad), las bandas se ampliarán, dando espacio a los precios. Durante los periodos de estancamiento (es decir, de baja volatilidad), las bandas se estrecharán, manteniendo los precios dentro de sus límites.

Las características de este indicador incluyen:

  1. cambios bruscos de precios que suelen producirse tras un estrechamiento de la banda correspondiente a una disminución de la volatilidad;
  2. si los precios se salen de la banda, cabrá esperar que continúe la tendencia actual;
  3. Si los máximos y mínimos fuera de la banda van seguidos de máximos y mínimos dentro de la banda, es posible que se produzca un cambio de tendencia;
  4. Los movimientos de precio que han partido de uno de los bordes de la banda suelen alcanzar el borde opuesto.

Esta última observación resultará útil para predecir las referencias de precio.



Parámetros

La función iBands() se utiliza para crear el manejador del indicador:

Retorna el manejador del indicador Bollinger Bands®.

int  iBands(
   string              symbol,            // symbol name
   ENUM_TIMEFRAMES     period,            // period
   int                 bands_period,      // central line calculation period
   int                 bands_shift,       // horizontal shift of the indicator
   double              deviation,         // number of standard deviations
   ENUM_APPLIED_PRICE  applied_price      // price type or handle
   );

symbol

[in]  Nombre simbólico del instrumento sobre cuyos datos se calculará el indicador. NULL indicará el símbolo actual.

period

[in] El valor del periodo puede ser uno de los valores de la enumeración ENUM_TIMEFRAMES, 0 indicará el marco temporal actual.

bands_period

[in]  Periodo de promediación de la línea principal del indicador.

bands_shift

[in]  Desplazamiento del indicador respecto al gráfico de precios.

deviation

[in]  Desviación de la línea principal.

applied_price

[in]  Precio utilizado. Puede ser cualquiera de las constantes de precio ENUM_APPLIED_PRICE o el manejador de otro indicador.

Retorna el manejador del indicador técnico especificado, en caso de fallo retorna INVALID_HANDLE. Para liberar la memoria del ordenador de un indicador que ya no se utiliza, se usará la función IndicatorRelease(), a la que se le transmitirá el manejador de este indicador.

Números de los búferes: 0 — BASE_LINE, 1 — UPPER_BAND, 2 — LOWER_BAND

Para crear un indicador en el asesor, declararemos las variables de entrada y globales:

//+------------------------------------------------------------------+
//|                                               TestTrendBands.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Undefined state
   LINE_STATE_UP,          // Upward
   LINE_STATE_DOWN,        // Downward
   LINE_STATE_TURN_UP,     // Upward reversal
   LINE_STATE_TURN_DOWN,   // Downward reversal
   LINE_STATE_STOP_UP,     // Upward stop
   LINE_STATE_STOP_DOWN,   // Downward stop
   LINE_STATE_ABOVE,       // Above value
   LINE_STATE_UNDER,       // Below value
   LINE_STATE_CROSS_UP,    // Crossing value upwards
   LINE_STATE_CROSS_DOWN,  // Crossing value downwards
   LINE_STATE_TOUCH_BELOW, // Touching value from below 
   LINE_STATE_TOUCH_ABOVE, // Touch value from above
   LINE_STATE_EQUALS,      // Equal to value
  };
//--- input parameters
input uint                 InpPeriod   =  20;            /* Period         */
input double               InpDeviation=  2.0;           /* Deviation      */
input int                  InpShift    =  0;             /* Shift          */
input ENUM_APPLIED_PRICE   InpPrice    =  PRICE_CLOSE;   /* Applied Price  */
//--- global variables
int      handle=INVALID_HANDLE;  // Indicator handle
int      period=0;               // Bollinger Bands calculation period
int      ind_digits=0;           // Number of decimal places in the indicator values
string   ind_title;              // Indicator description

Al utilizar el panel informativo en el asesor, también deberemos declarar las variables globales para él y conectar el archivo de la clase de panel:

//+------------------------------------------------------------------+
//|                                               TestTrendBands.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- includes
#include <Dashboard\Dashboard.mqh>
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Undefined state
   LINE_STATE_UP,          // Upward
   LINE_STATE_DOWN,        // Downward
   LINE_STATE_TURN_UP,     // Upward reversal
   LINE_STATE_TURN_DOWN,   // Downward reversal
   LINE_STATE_STOP_UP,     // Upward stop
   LINE_STATE_STOP_DOWN,   // Downward stop
   LINE_STATE_ABOVE,       // Above value
   LINE_STATE_UNDER,       // Below value
   LINE_STATE_CROSS_UP,    // Crossing value upwards
   LINE_STATE_CROSS_DOWN,  // Crossing value downwards
   LINE_STATE_TOUCH_BELOW, // Touching value from below 
   LINE_STATE_TOUCH_ABOVE, // Touch value from above
   LINE_STATE_EQUALS,      // Equal to value
  };
//--- input parameters
input uint                 InpPeriod   =  20;            /* Period         */
input double               InpDeviation=  2.0;           /* Deviation      */
input int                  InpShift    =  0;             /* Shift          */
input ENUM_APPLIED_PRICE   InpPrice    =  PRICE_CLOSE;   /* Applied Price  */
//--- global variables
int      handle=INVALID_HANDLE;  // Indicator handle
int      period=0;               // Bollinger Bands calculation period
int      ind_digits=0;           // Number of decimal places in the indicator values
string   ind_title;              // Indicator description
//--- variables for the panel
int      mouse_bar_index;        // Index of the bar the data is taken from
CDashboard *panel=NULL;          // Pointer to the panel object


Inicialización

Establecimiento de los valores de las variables globales para el indicador y creación de su manejador:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Indicator
//--- Set and adjust the calculation period and levels if necessary
   period=int(InpPeriod<1 ? 20 : InpPeriod);
//--- Set the indicator name and the number of decimal places
   ind_title=StringFormat("Bands(%lu)",period);
   ind_digits=Digits()+1;
//--- Create indicator handle
   ResetLastError();
   handle=iBands(Symbol(),PERIOD_CURRENT,period,InpShift,InpDeviation,InpPrice);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Successful initialization
   return(INIT_SUCCEEDED);
  }

Al utilizar un panel informativo en el asesor, crearemos un panel:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Indicator
//--- Set and adjust the calculation period and levels if necessary
   period=int(InpPeriod<1 ? 20 : InpPeriod);
//--- Set the indicator name and the number of decimal places
   ind_title=StringFormat("Bands(%lu)",period);
   ind_digits=Digits()+1;
//--- Create indicator handle
   ResetLastError();
   handle=iBands(Symbol(),PERIOD_CURRENT,period,InpShift,InpDeviation,InpPrice);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Dashboard
//--- Create the panel
   panel=new CDashboard(1,20,20,225,243);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Set font parameters
   panel.SetFontParams("Calibri",9);
//--- Display the panel with the "Symbol, Timeframe description" header text
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
//--- Create a table with ID 0 to display bar data in it
   panel.CreateNewTable(0);
//--- Draw a table with ID 0 on the panel background
   panel.DrawGrid(0,2,20,6,2,18,110);

//--- Create a table with ID 1 to display indicator data in it
   panel.CreateNewTable(1);
//--- Get the Y2 table coordinate with ID 0 and
//--- set the Y1 coordinate for the table with ID 1
   int y1=panel.TableY2(0)+22;
//--- Draw a table with ID 1 on the panel background
   panel.DrawGrid(1,2,y1,4,2,18,110);
   
//--- Display tabular data in the journal
   panel.GridPrint(0,2);
   panel.GridPrint(1,2);
//--- Initialize the variable with the index of the mouse cursor bar
   mouse_bar_index=0;
//--- Display the data of the current bar on the panel
   DrawData(mouse_bar_index,TimeCurrent());

//--- Successful initialization
   return(INIT_SUCCEEDED);
  }


Desinicialización

En el manejador OnDeinit() del asesor liberaremos el manejador del indicador:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Release handle of the indicator
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Clear all comments on the chart
   Comment("");
  }

Al utilizar un panel informativo, eliminaremos el objeto de panel creado:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Release handle of the indicator
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Clear all comments on the chart
   Comment("");
   
//--- If the panel object exists, delete it
   if(panel!=NULL)
      delete panel;
  }


Obtención de datos

Funciones generales para obtener los datos según el manejador del indicador:

//+------------------------------------------------------------------+
//| Return the indicator data on the specified bar                   |
//+------------------------------------------------------------------+
double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
  {
   double array[1]={0};
   ResetLastError();
   if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
     {
      PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
      return EMPTY_VALUE;
     }
   return array[0];
  }
//+------------------------------------------------------------------+
//| Return the state of the indicator line                           |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
  {
//--- Get the values of the indicator line with the shift (0,1,2) relative to the passed index
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
   const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
//--- If at least one of the values could not be obtained, return an undefined value 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Line upward reversal (value2>value1 && value0>value1)
   if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_TURN_UP;
//--- Line upward direction (value2<=value1 && value0>value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_UP;
//--- Line upward stop (value2<=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_UP;
//--- Line downward reversal (value2<value1 && value0<value1)
   if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_TURN_DOWN;
//--- Line downward direction (value2>=value1 && value0<value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_DOWN;
//--- Line downward stop (value2>=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_DOWN;
//--- Undefined state
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Return the state of the line relative to the specified level     |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
  {
//--- Get the values of the indicator line with the shift (0,1) relative to the passed index
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
//--- If at least one of the values could not be obtained, return an undefined value 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Define the second level to compare
   double level=(level1==EMPTY_VALUE ? level0 : level1);
//--- The line is below the level (value1<level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_UNDER;
//--- The line is above the level (value1>level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_ABOVE;
//--- The line crossed the level upwards (value1<=level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_CROSS_UP;
//--- The line crossed the level downwards (value1>=level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_CROSS_DOWN;
//--- The line touched the level from below (value1<level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- The line touched the level from above (value1>level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Line is equal to the level value (value1==level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_EQUALS;
//--- Undefined state
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Return the indicator line state description                      |
//+------------------------------------------------------------------+
string LineStateDescription(const ENUM_LINE_STATE state)
  {
   switch(state)
     {
      case LINE_STATE_UP         :  return "Up";
      case LINE_STATE_STOP_UP    :  return "Stop Up";
      case LINE_STATE_TURN_UP    :  return "Turn Up";
      case LINE_STATE_DOWN       :  return "Down";
      case LINE_STATE_STOP_DOWN  :  return "Stop Down";
      case LINE_STATE_TURN_DOWN  :  return "Turn Down";
      case LINE_STATE_ABOVE      :  return "Above level";
      case LINE_STATE_UNDER      :  return "Under level";
      case LINE_STATE_CROSS_UP   :  return "Crossing Up";
      case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
      case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
      case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
      case LINE_STATE_EQUALS     :  return "Equals";
      default                    :  return "Unknown";
     }
  }

Al utilizar el panel informativo, los datos se mostrarán en el panel utilizando la función:

//+------------------------------------------------------------------+
//| Display data from the specified timeseries index to the panel    |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Declare the variables to receive data in them
   MqlTick  tick={0};
   MqlRates rates[1];

//--- Exit if unable to get the current prices
   if(!SymbolInfoTick(Symbol(),tick))
      return;
//--- Exit if unable to get the bar data by the specified index
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Set font parameters for bar and indicator data headers
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Set font parameters for bar and indicator data
   panel.SetFontParams(name,9);

//--- Display the data of the specified bar in table 0 on the panel
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Display the Bands Upper line data from the specified bar on the panel in table 1
   panel.DrawText(ind_title+" Upper", panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value=IndicatorValue(handle,index,UPPER_BAND);
   string value_str=(value!=EMPTY_VALUE ? DoubleToString(value,ind_digits) : "");
   panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,100);

//--- Display the Bands Lower line data from the specified bar on the panel in table 1
   panel.DrawText(ind_title+" Lower", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   double value_dip=IndicatorValue(handle,index,LOWER_BAND);
   string value_dip_str=(value_dip!=EMPTY_VALUE ? DoubleToString(value_dip,ind_digits) : "");
   panel.DrawText(value_dip_str,panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,100);

//--- Display the Bands Middle line data from the specified bar on the panel in table 1
   panel.DrawText(ind_title+" Middle", panel.CellX(1,2,0)+2, panel.CellY(1,2,0)+2);
   double value_dim=IndicatorValue(handle,index,BASE_LINE);
   string value_dim_str=(value_dim!=EMPTY_VALUE ? DoubleToString(value_dim,ind_digits) : "");
   panel.DrawText(value_dim_str,panel.CellX(1,2,1)+2,panel.CellY(1,2,1)+2,clrNONE,100);
   
//--- Redraw the chart to immediately display all changes on the panel
   ChartRedraw(ChartID());
  }

Además, al utilizar un panel informativo, el manejador de eventos OnChartEvent() del asesor llamará al manejador de eventos del panel informativo y procesará los eventos para obtener el índice de la barra bajo el cursor:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Handling the panel
//--- Call the panel event handler
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- If the cursor moves or a click is made on the chart
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Declare the variables to record time and price coordinates in them
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- If the cursor coordinates are converted to date and time
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- write the bar index where the cursor is located to a global variable
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Display the bar data under the cursor on the panel 
         DrawData(mouse_bar_index,time);
        }
     }

//--- If we received a custom event, display the appropriate message in the journal
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Here we can implement handling a click on the close button on the panel
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }

Tras compilar y ejecutar el asesor en el gráfico, podremos controlar el valor y el estado de la línea de indicador en el panel informativo:


Podrá ver el archivo del asesor de pruebas "TestTrendBands.mq5" en los archivos adjuntos.


Double Exponential Moving Average

El indicador técnico Media Móvil Exponencial Doble (Double Exponential Moving Average, DEMA) fue desarrollado por Patrick Mulloy y publicado en febrero de 1994 en la revista "Technical Analysis of Stocks & Commodities". Fue diseñado para suavizar series de precios y se aplica directamente al gráfico de precios de un instrumento financiero. Además, puede usarse para suavizar los valores de otros indicadores.

La ventaja de este indicador es que elimina las señales falsas si el precio se mueve en forma de dientes de sierra y ayuda a mantener una posición si se da una tendencia fuerte.



Parámetros

La función iDEMA() se utiliza para crear el manejador del indicador:

Retorna el manejador del indicador Double Exponential Moving Average. Solo un búfer.

int  iDEMA(
   string              symbol,            // symbol name
   ENUM_TIMEFRAMES     period,            // period
   int                 ma_period,         // averaging period
   int                 ma_shift,          // horizontal shift of the indicator
   ENUM_APPLIED_PRICE  applied_price      // price type or handle
   );

symbol

[in]  Nombre simbólico del instrumento sobre cuyos datos se calculará el indicador. NULL indicará el símbolo actual.

period

[in] El valor del periodo puede ser uno de los valores de la enumeración ENUM_TIMEFRAMES, 0 indicará el marco temporal actual.

ma_period

[in]  Periodo (número de barras) de cálculo del indicador.

ma_shift

[in]  Desplazamiento del indicador respecto al gráfico de precios.

applied_price

[in]  Precio utilizado. Puede ser cualquiera de las constantes de precio ENUM_APPLIED_PRICE o el manejador de otro indicador.

Retorna el manejador del indicador técnico especificado, en caso de fallo retorna INVALID_HANDLE. Para liberar la memoria del ordenador de un indicador que ya no se utiliza, se usará la función IndicatorRelease(), a la que se le transmitirá el manejador de este indicador.

Para crear un indicador en el asesor, declararemos las variables de entrada y globales:

//+------------------------------------------------------------------+
//|                                                TestTrendDEMA.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Undefined state
   LINE_STATE_UP,          // Upward
   LINE_STATE_DOWN,        // Downward
   LINE_STATE_TURN_UP,     // Upward reversal
   LINE_STATE_TURN_DOWN,   // Downward reversal
   LINE_STATE_STOP_UP,     // Upward stop
   LINE_STATE_STOP_DOWN,   // Downward stop
   LINE_STATE_ABOVE,       // Above value
   LINE_STATE_UNDER,       // Below value
   LINE_STATE_CROSS_UP,    // Crossing value upwards
   LINE_STATE_CROSS_DOWN,  // Crossing value downwards
   LINE_STATE_TOUCH_BELOW, // Touching value from below 
   LINE_STATE_TOUCH_ABOVE, // Touch value from above
   LINE_STATE_EQUALS,      // Equal to value
  };
//--- input parameters
input uint                 InpPeriod      =  14;            /* Period         */
input int                  InpShift       =  0;             /* DEMA Shift     */
input ENUM_APPLIED_PRICE   InpPrice       =  PRICE_CLOSE;   /* Applied Price  */
//--- global variables
int      handle=INVALID_HANDLE;  // Indicator handle
int      period=0;               // DEMA calculation period
int      ind_digits=0;           // Number of decimal places in the indicator values
string   ind_title;              // Indicator description

Al utilizar el panel informativo en el asesor, también deberemos declarar las variables globales para él y conectar el archivo de la clase de panel:

//+------------------------------------------------------------------+
//|                                                TestTrendDEMA.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- includes
#include <Dashboard\Dashboard.mqh>
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Undefined state
   LINE_STATE_UP,          // Upward
   LINE_STATE_DOWN,        // Downward
   LINE_STATE_TURN_UP,     // Upward reversal
   LINE_STATE_TURN_DOWN,   // Downward reversal
   LINE_STATE_STOP_UP,     // Upward stop
   LINE_STATE_STOP_DOWN,   // Downward stop
   LINE_STATE_ABOVE,       // Above value
   LINE_STATE_UNDER,       // Below value
   LINE_STATE_CROSS_UP,    // Crossing value upwards
   LINE_STATE_CROSS_DOWN,  // Crossing value downwards
   LINE_STATE_TOUCH_BELOW, // Touching value from below 
   LINE_STATE_TOUCH_ABOVE, // Touch value from above
   LINE_STATE_EQUALS,      // Equal to value
  };
//--- input parameters
input uint                 InpPeriod      =  14;            /* Period         */
input int                  InpShift       =  0;             /* DEMA Shift     */
input ENUM_APPLIED_PRICE   InpPrice       =  PRICE_CLOSE;   /* Applied Price  */
//--- global variables
int      handle=INVALID_HANDLE;  // Indicator handle
int      period=0;               // DEMA calculation period
int      ind_digits=0;           // Number of decimal places in the indicator values
string   ind_title;              // Indicator description
//--- variables for the panel
int      mouse_bar_index;        // Index of the bar the data is taken from
CDashboard *panel=NULL;          // Pointer to the panel object


Inicialización

Establecimiento de los valores de las variables globales para el indicador y creación de su manejador:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Indicator
//--- Set and adjust the calculation period and levels if necessary
   period=int(InpPeriod<1 ? 14 : InpPeriod);
//--- Set the indicator name and the number of decimal places
   ind_title=StringFormat("DEMA(%lu)",period);
   ind_digits=Digits()+1;
//--- Create indicator handle
   ResetLastError();
   handle=iDEMA(Symbol(),PERIOD_CURRENT,period,InpShift,InpPrice);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Successful initialization
   return(INIT_SUCCEEDED);
  }

Al utilizar un panel informativo en el asesor, crearemos un panel:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Indicator
//--- Set and adjust the calculation period and levels if necessary
   period=int(InpPeriod<1 ? 14 : InpPeriod);
//--- Set the indicator name and the number of decimal places
   ind_title=StringFormat("DEMA(%lu)",period);
   ind_digits=Digits()+1;
//--- Create indicator handle
   ResetLastError();
   handle=iDEMA(Symbol(),PERIOD_CURRENT,period,InpShift,InpPrice);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Dashboard
//--- Create the panel
   panel=new CDashboard(1,20,20,197,225);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Set font parameters
   panel.SetFontParams("Calibri",9);
//--- Display the panel with the "Symbol, Timeframe description" header text
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
//--- Create a table with ID 0 to display bar data in it
   panel.CreateNewTable(0);
//--- Draw a table with ID 0 on the panel background
   panel.DrawGrid(0,2,20,6,2,18,96);

//--- Create a table with ID 1 to display indicator data in it
   panel.CreateNewTable(1);
//--- Get the Y2 table coordinate with ID 0 and
//--- set the Y1 coordinate for the table with ID 1
   int y1=panel.TableY2(0)+22;
//--- Draw a table with ID 1 on the panel background
   panel.DrawGrid(1,2,y1,3,2,18,96);
   
//--- Display tabular data in the journal
   panel.GridPrint(0,2);
   panel.GridPrint(1,2);
//--- Initialize the variable with the index of the mouse cursor bar
   mouse_bar_index=0;
//--- Display the data of the current bar on the panel
   DrawData(mouse_bar_index,TimeCurrent());

//--- Successful initialization
   return(INIT_SUCCEEDED);
  }


Desinicialización

En el manejador OnDeinit() del asesor liberaremos el manejador del indicador:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Release handle of the indicator
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Clear all comments on the chart
   Comment("");
  }

Al utilizar un panel informativo, eliminaremos el objeto de panel creado:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Release handle of the indicator
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Clear all comments on the chart
   Comment("");
   
//--- If the panel object exists, delete it
   if(panel!=NULL)
      delete panel;
  }


Obtención de datos

Funciones generales para obtener los datos según el manejador del indicador:

//+------------------------------------------------------------------+
//| Return the indicator data on the specified bar                   |
//+------------------------------------------------------------------+
double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
  {
   double array[1]={0};
   ResetLastError();
   if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
     {
      PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
      return EMPTY_VALUE;
     }
   return array[0];
  }
//+------------------------------------------------------------------+
//| Return the state of the indicator line                           |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
  {
//--- Get the values of the indicator line with the shift (0,1,2) relative to the passed index
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
   const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
//--- If at least one of the values could not be obtained, return an undefined value 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Line upward reversal (value2>value1 && value0>value1)
   if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_TURN_UP;
//--- Line upward direction (value2<=value1 && value0>value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_UP;
//--- Line upward stop (value2<=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_UP;
//--- Line downward reversal (value2<value1 && value0<value1)
   if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_TURN_DOWN;
//--- Line downward direction (value2>=value1 && value0<value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_DOWN;
//--- Line downward stop (value2>=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_DOWN;
//--- Undefined state
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Return the state of the line relative to the specified level     |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
  {
//--- Get the values of the indicator line with the shift (0,1) relative to the passed index
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
//--- If at least one of the values could not be obtained, return an undefined value 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Define the second level to compare
   double level=(level1==EMPTY_VALUE ? level0 : level1);
//--- The line is below the level (value1<level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_UNDER;
//--- The line is above the level (value1>level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_ABOVE;
//--- The line crossed the level upwards (value1<=level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_CROSS_UP;
//--- The line crossed the level downwards (value1>=level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_CROSS_DOWN;
//--- The line touched the level from below (value1<level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- The line touched the level from above (value1>level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Line is equal to the level value (value1==level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_EQUALS;
//--- Undefined state
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Return the indicator line state description                      |
//+------------------------------------------------------------------+
string LineStateDescription(const ENUM_LINE_STATE state)
  {
   switch(state)
     {
      case LINE_STATE_UP         :  return "Up";
      case LINE_STATE_STOP_UP    :  return "Stop Up";
      case LINE_STATE_TURN_UP    :  return "Turn Up";
      case LINE_STATE_DOWN       :  return "Down";
      case LINE_STATE_STOP_DOWN  :  return "Stop Down";
      case LINE_STATE_TURN_DOWN  :  return "Turn Down";
      case LINE_STATE_ABOVE      :  return "Above level";
      case LINE_STATE_UNDER      :  return "Under level";
      case LINE_STATE_CROSS_UP   :  return "Crossing Up";
      case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
      case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
      case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
      case LINE_STATE_EQUALS     :  return "Equals";
      default                    :  return "Unknown";
     }
  }

Al utilizar el panel informativo, los datos se mostrarán en el panel utilizando la función:

//+------------------------------------------------------------------+
//| Display data from the specified timeseries index to the panel    |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Declare the variables to receive data in them
   MqlTick  tick={0};
   MqlRates rates[1];

//--- Exit if unable to get the current prices
   if(!SymbolInfoTick(Symbol(),tick))
      return;
//--- Exit if unable to get the bar data by the specified index
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Set font parameters for bar and indicator data headers
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Set font parameters for bar and indicator data
   panel.SetFontParams(name,9);

//--- Display the data of the specified bar in table 0 on the panel
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Display the indicator data from the specified bar on the panel in table 1
   panel.DrawText(ind_title, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value=IndicatorValue(handle,index,0);
   string value_str=(value!=EMPTY_VALUE ? DoubleToString(value,ind_digits) : "");
   panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,100);
   
//--- Display a description of the indicator line state
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state=LineState(handle,index,0);
   panel.DrawText(LineStateDescription(state),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,90);
   
//--- Redraw the chart to immediately display all changes on the panel
   ChartRedraw(ChartID());
  }

Además, al utilizar un panel informativo, el manejador de eventos OnChartEvent() del asesor llamará al manejador de eventos del panel informativo y procesará los eventos para obtener el índice de la barra bajo el cursor:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Handling the panel
//--- Call the panel event handler
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- If the cursor moves or a click is made on the chart
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Declare the variables to record time and price coordinates in them
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- If the cursor coordinates are converted to date and time
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- write the bar index where the cursor is located to a global variable
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Display the bar data under the cursor on the panel 
         DrawData(mouse_bar_index,time);
        }
     }

//--- If we received a custom event, display the appropriate message in the journal
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Here we can implement handling a click on the close button on the panel
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }

Tras compilar y ejecutar el asesor en el gráfico, podremos controlar el valor y el estado de la línea de indicador en el panel informativo:


Podrá ver el archivo del asesor de pruebas "TestTrendDEMA.mq5" en los archivos adjuntos.


Envelopes

El indicador técnico Líneas Envolventes (Envoltorios, Envelopes) se forma con dos medias móviles, una de las cuales se encuentra desplazada hacia arriba, mientras que la otra se encuentra desplazada hacia abajo. La selección del valor relativo óptimo del desplazamiento del límite de la banda vendrá determinada por la volatilidad del mercado: cuanto mayor sea, mayor será el desplazamiento.

Envelopes definen los límites superior e inferior del intervalo normal de fluctuaciones de los precios de los valores. La señal de venta se producirá cuando el precio alcance el límite superior de la banda, mientras que la señal de compra se producirá cuando alcance el límite inferior.

El uso del indicador técnico Envelopes se basa en la lógica natural del comportamiento del mercado: cuando los precios alcanzan valores extremos (es decir, el límite superior o inferior de la banda) bajo la presión de compradores o vendedores especialmente ansiosos, suelen estabilizarse, regresando a niveles más realistas. El mismo principio se utiliza al interpretar las Bandas de Bolinger (Bollinger Bands®, BB).



Parámetros

La función iEnvelopes() se utiliza para crear el manejador del indicador:

Retorna el manejador del indicador Envelopes.

int  iEnvelopes(
   string              symbol,            // symbol name
   ENUM_TIMEFRAMES     period,            // period
   int                 ma_period,         // central line calculation period
   int                 ma_shift,          // horizontal shift of the indicator
   ENUM_MA_METHOD      ma_method,         // smoothing type
   ENUM_APPLIED_PRICE  applied_price,     // price type or handle
   double              deviation          // deviation of channel borders from the central line
   );
symbol

[in]  Nombre simbólico del instrumento sobre cuyos datos se calculará el indicador. NULL indicará el símbolo actual.

period

[in] El valor del periodo puede ser uno de los valores de la enumeración ENUM_TIMEFRAMES, 0 indicará el marco temporal actual.

ma_period

[in]  Periodo de promediación de la línea principal del indicador.

ma_shift

[in]  Desplazamiento del indicador respecto al gráfico de precios.

ma_method

[in]  Método de promediación. Puede ser cualquiera de los valores de la enumeración ENUM_MA_METHOD.

applied_price

[in]  Precio utilizado. Puede ser cualquiera de las constantes de precio ENUM_APPLIED_PRICE o el manejador de otro indicador.

deviation

[in] Desviación respecto a la línea principal en porcentaje.

Retorna el manejador del indicador técnico especificado, en caso de fallo retorna INVALID_HANDLE. Para liberar la memoria del ordenador de un indicador que ya no se utiliza, se usará la función IndicatorRelease(), a la que se le transmitirá el manejador de este indicador.

Números de los búferes: 0 — UPPER_LINE, 1 — LOWER_LINE.


Para crear un indicador en el asesor, declararemos las variables de entrada y globales:

//+------------------------------------------------------------------+
//|                                           TestTrendEnvelopes.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Undefined state
   LINE_STATE_UP,          // Upward
   LINE_STATE_DOWN,        // Downward
   LINE_STATE_TURN_UP,     // Upward reversal
   LINE_STATE_TURN_DOWN,   // Downward reversal
   LINE_STATE_STOP_UP,     // Upward stop
   LINE_STATE_STOP_DOWN,   // Downward stop
   LINE_STATE_ABOVE,       // Above value
   LINE_STATE_UNDER,       // Below value
   LINE_STATE_CROSS_UP,    // Crossing value upwards
   LINE_STATE_CROSS_DOWN,  // Crossing value downwards
   LINE_STATE_TOUCH_BELOW, // Touching value from below 
   LINE_STATE_TOUCH_ABOVE, // Touch value from above
   LINE_STATE_EQUALS,      // Equal to value
  };
//--- input parameters
input uint                 InpPeriod   =  14;            /* Period         */
input double               InpDeviation=  0.1;           /* Deviation      */
input int                  InpShift    =  0;             /* Shift          */
input ENUM_MA_METHOD       InpMethod   =  MODE_SMA;      /* Method         */
input ENUM_APPLIED_PRICE   InpPrice    =  PRICE_CLOSE;   /* Applied Price  */
//--- global variables
int      handle=INVALID_HANDLE;  // Indicator handle
int      period=0;               // Envelopes calculation period
int      ind_digits=0;           // Number of decimal places in the indicator values
string   ind_title;              // Indicator description

Al utilizar el panel informativo en el asesor, también deberemos declarar las variables globales para él y conectar el archivo de la clase de panel:

//+------------------------------------------------------------------+
//|                                           TestTrendEnvelopes.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- includes
#include <Dashboard\Dashboard.mqh>
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Undefined state
   LINE_STATE_UP,          // Upward
   LINE_STATE_DOWN,        // Downward
   LINE_STATE_TURN_UP,     // Upward reversal
   LINE_STATE_TURN_DOWN,   // Downward reversal
   LINE_STATE_STOP_UP,     // Upward stop
   LINE_STATE_STOP_DOWN,   // Downward stop
   LINE_STATE_ABOVE,       // Above value
   LINE_STATE_UNDER,       // Below value
   LINE_STATE_CROSS_UP,    // Crossing value upwards
   LINE_STATE_CROSS_DOWN,  // Crossing value downwards
   LINE_STATE_TOUCH_BELOW, // Touching value from below 
   LINE_STATE_TOUCH_ABOVE, // Touch value from above
   LINE_STATE_EQUALS,      // Equal to value
  };
//--- input parameters
input uint                 InpPeriod   =  14;            /* Period         */
input double               InpDeviation=  0.1;           /* Deviation      */
input int                  InpShift    =  0;             /* Shift          */
input ENUM_MA_METHOD       InpMethod   =  MODE_SMA;      /* Method         */
input ENUM_APPLIED_PRICE   InpPrice    =  PRICE_CLOSE;   /* Applied Price  */
//--- global variables
int      handle=INVALID_HANDLE;  // Indicator handle
int      period=0;               // Envelopes calculation period
int      ind_digits=0;           // Number of decimal places in the indicator values
string   ind_title;              // Indicator description
//--- variables for the panel
int      mouse_bar_index;        // Index of the bar the data is taken from
CDashboard *panel=NULL;          // Pointer to the panel object


Inicialización

Establecimiento de los valores de las variables globales para el indicador y creación de su manejador:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Indicator
//--- Set and adjust the calculation period and levels if necessary
   period=int(InpPeriod<1 ? 14 : InpPeriod);
//--- Set the indicator name and the number of decimal places
   ind_title=StringFormat("Envelopes(%lu)",period);
   ind_digits=Digits()+1;
//--- Create indicator handle
   ResetLastError();
   handle=iEnvelopes(Symbol(),PERIOD_CURRENT,period,InpShift,InpMethod,InpPrice,InpDeviation);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Successful initialization
   return(INIT_SUCCEEDED);
  }

Al utilizar un panel informativo en el asesor, crearemos un panel:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Indicator
//--- Set and adjust the calculation period and levels if necessary
   period=int(InpPeriod<1 ? 14 : InpPeriod);
//--- Set the indicator name and the number of decimal places
   ind_title=StringFormat("Envelopes(%lu)",period);
   ind_digits=Digits()+1;
//--- Create indicator handle
   ResetLastError();
   handle=iEnvelopes(Symbol(),PERIOD_CURRENT,period,InpShift,InpMethod,InpPrice,InpDeviation);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Dashboard
//--- Create the panel
   panel=new CDashboard(1,20,20,257,225);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Set font parameters
   panel.SetFontParams("Calibri",9);
//--- Display the panel with the "Symbol, Timeframe description" header text
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
//--- Create a table with ID 0 to display bar data in it
   panel.CreateNewTable(0);
//--- Draw a table with ID 0 on the panel background
   panel.DrawGrid(0,2,20,6,2,18,126);

//--- Create a table with ID 1 to display indicator data in it
   panel.CreateNewTable(1);
//--- Get the Y2 table coordinate with ID 0 and
//--- set the Y1 coordinate for the table with ID 1
   int y1=panel.TableY2(0)+22;
//--- Draw a table with ID 1 on the panel background
   panel.DrawGrid(1,2,y1,3,2,18,126);
   
//--- Display tabular data in the journal
   panel.GridPrint(0,2);
   panel.GridPrint(1,2);
//--- Initialize the variable with the index of the mouse cursor bar
   mouse_bar_index=0;
//--- Display the data of the current bar on the panel
   DrawData(mouse_bar_index,TimeCurrent());

//--- Successful initialization
   return(INIT_SUCCEEDED);
  }


Desinicialización

En el manejador OnDeinit() del asesor liberaremos el manejador del indicador:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Release handle of the indicator
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Clear all comments on the chart
   Comment("");
  }

Al utilizar un panel informativo, eliminaremos el objeto de panel creado:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Release handle of the indicator
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Clear all comments on the chart
   Comment("");
   
//--- If the panel object exists, delete it
   if(panel!=NULL)
      delete panel;
  }


Obtención de datos

Funciones generales para obtener los datos según el manejador del indicador:

//+------------------------------------------------------------------+
//| Return the indicator data on the specified bar                   |
//+------------------------------------------------------------------+
double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
  {
   double array[1]={0};
   ResetLastError();
   if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
     {
      PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
      return EMPTY_VALUE;
     }
   return array[0];
  }
//+------------------------------------------------------------------+
//| Return the state of the indicator line                           |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
  {
//--- Get the values of the indicator line with the shift (0,1,2) relative to the passed index
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
   const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
//--- If at least one of the values could not be obtained, return an undefined value 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Line upward reversal (value2>value1 && value0>value1)
   if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_TURN_UP;
//--- Line upward direction (value2<=value1 && value0>value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_UP;
//--- Line upward stop (value2<=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_UP;
//--- Line downward reversal (value2<value1 && value0<value1)
   if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_TURN_DOWN;
//--- Line downward direction (value2>=value1 && value0<value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_DOWN;
//--- Line downward stop (value2>=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_DOWN;
//--- Undefined state
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Return the state of the line relative to the specified level     |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
  {
//--- Get the values of the indicator line with the shift (0,1) relative to the passed index
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
//--- If at least one of the values could not be obtained, return an undefined value 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Define the second level to compare
   double level=(level1==EMPTY_VALUE ? level0 : level1);
//--- The line is below the level (value1<level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_UNDER;
//--- The line is above the level (value1>level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_ABOVE;
//--- The line crossed the level upwards (value1<=level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_CROSS_UP;
//--- The line crossed the level downwards (value1>=level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_CROSS_DOWN;
//--- The line touched the level from below (value1<level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- The line touched the level from above (value1>level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Line is equal to the level value (value1==level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_EQUALS;
//--- Undefined state
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Return the indicator line state description                      |
//+------------------------------------------------------------------+
string LineStateDescription(const ENUM_LINE_STATE state)
  {
   switch(state)
     {
      case LINE_STATE_UP         :  return "Up";
      case LINE_STATE_STOP_UP    :  return "Stop Up";
      case LINE_STATE_TURN_UP    :  return "Turn Up";
      case LINE_STATE_DOWN       :  return "Down";
      case LINE_STATE_STOP_DOWN  :  return "Stop Down";
      case LINE_STATE_TURN_DOWN  :  return "Turn Down";
      case LINE_STATE_ABOVE      :  return "Above level";
      case LINE_STATE_UNDER      :  return "Under level";
      case LINE_STATE_CROSS_UP   :  return "Crossing Up";
      case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
      case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
      case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
      case LINE_STATE_EQUALS     :  return "Equals";
      default                    :  return "Unknown";
     }
  }

Al utilizar el panel informativo, los datos se mostrarán en el panel utilizando la función:

//+------------------------------------------------------------------+
//| Display data from the specified timeseries index to the panel    |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Declare the variables to receive data in them
   MqlTick  tick={0};
   MqlRates rates[1];

//--- Exit if unable to get the current prices
   if(!SymbolInfoTick(Symbol(),tick))
      return;
//--- Exit if unable to get the bar data by the specified index
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Set font parameters for bar and indicator data headers
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Set font parameters for bar and indicator data
   panel.SetFontParams(name,9);

//--- Display the data of the specified bar in table 0 on the panel
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Display the Envelopes Upper line data from the specified bar on the panel in table 1
   panel.DrawText(ind_title+" Upper", panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value=IndicatorValue(handle,index,UPPER_LINE);
   string value_str=(value!=EMPTY_VALUE ? DoubleToString(value,ind_digits) : "");
   panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,100);

//--- Display the Envelopes Lower line data from the specified bar on the panel in table 1
   panel.DrawText(ind_title+" Lower", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   double value_dip=IndicatorValue(handle,index,LOWER_LINE);
   string value_dip_str=(value_dip!=EMPTY_VALUE ? DoubleToString(value_dip,ind_digits) : "");
   panel.DrawText(value_dip_str,panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,100);

//--- Redraw the chart to immediately display all changes on the panel
   ChartRedraw(ChartID());
  }

Además, al utilizar un panel informativo, el manejador de eventos OnChartEvent() del asesor llamará al manejador de eventos del panel informativo y procesará los eventos para obtener el índice de la barra bajo el cursor:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Handling the panel
//--- Call the panel event handler
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- If the cursor moves or a click is made on the chart
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Declare the variables to record time and price coordinates in them
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- If the cursor coordinates are converted to date and time
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- write the bar index where the cursor is located to a global variable
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Display the bar data under the cursor on the panel 
         DrawData(mouse_bar_index,time);
        }
     }

//--- If we received a custom event, display the appropriate message in the journal
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Here we can implement handling a click on the close button on the panel
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }

Tras compilar y ejecutar el asesor en el gráfico, podremos controlar el valor y el estado de la línea de indicador en el panel informativo:


Podrá ver el archivo del asesor de pruebas "TestTrendEnvelopes.mq5" en los archivos adjuntos.


Fractal Adaptive Moving Average

El indicador técnico Media Móvil Adaptativa Fractal (Fractal Adaptive Moving Average, FRAMA) fue desarrollado por John Ehlers. Este indicador se basa en el algoritmo de media móvil exponencial, en el que el factor de suavizado se calcula usando como base la dimensión fractal actual de la serie de precios. La ventaja del indicador FRAMA reside en su capacidad para seguir fuertes movimientos de tendencia y ralentizarse muy intensamente en los momentos de consolidación de los precios.

A este indicador se le aplican todos los tipos de análisis que se utilizan para las medias móviles



Parámetros

La función iFrAMA() se utiliza para crear el manejador del indicador:

Retorna el manejador del indicador Fractal Adaptive Moving Average. Solo un búfer.

int  iFrAMA(
   string              symbol,            // symbol name
   ENUM_TIMEFRAMES     period,            // period
   int                 ma_period,         // averaging period
   int                 ma_shift,          // horizontal shift of the indicator
   ENUM_APPLIED_PRICE  applied_price      // price type or handle
   );
symbol

[in]  Nombre simbólico del instrumento sobre cuyos datos se calculará el indicador. NULL indicará el símbolo actual.

period

[in] El valor del periodo puede ser uno de los valores de la enumeración ENUM_TIMEFRAMES, 0 indicará el marco temporal actual.

ma_period

[in]  Periodo (número de barras) de cálculo del indicador.

ma_shift

[in]  Desplazamiento del indicador respecto al gráfico de precios.

applied_price

[in]  Precio utilizado. Puede ser cualquiera de las constantes de precio ENUM_APPLIED_PRICE o el manejador de otro indicador.

Retorna el manejador del indicador técnico especificado, en caso de fallo retorna INVALID_HANDLE. Para liberar la memoria del ordenador de un indicador que ya no se utiliza, se usará la función IndicatorRelease(), a la que se le transmitirá el manejador de este indicador.

Para crear un indicador en el asesor, declararemos las variables de entrada y globales:

//+------------------------------------------------------------------+
//|                                               TestTrendFRAMA.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Undefined state
   LINE_STATE_UP,          // Upward
   LINE_STATE_DOWN,        // Downward
   LINE_STATE_TURN_UP,     // Upward reversal
   LINE_STATE_TURN_DOWN,   // Downward reversal
   LINE_STATE_STOP_UP,     // Upward stop
   LINE_STATE_STOP_DOWN,   // Downward stop
   LINE_STATE_ABOVE,       // Above value
   LINE_STATE_UNDER,       // Below value
   LINE_STATE_CROSS_UP,    // Crossing value upwards
   LINE_STATE_CROSS_DOWN,  // Crossing value downwards
   LINE_STATE_TOUCH_BELOW, // Touching value from below 
   LINE_STATE_TOUCH_ABOVE, // Touch value from above
   LINE_STATE_EQUALS,      // Equal to value
  };
//--- input parameters
input uint                 InpPeriod      =  14;            /* Period         */
input int                  InpShift       =  0;             /* FRAMA Shift    */
input ENUM_APPLIED_PRICE   InpPrice       =  PRICE_CLOSE;   /* Applied Price  */
//--- global variables
int      handle=INVALID_HANDLE;  // Indicator handle
int      period=0;               // FRAMA calculation period
int      ind_digits=0;           // Number of decimal places in the indicator values
string   ind_title;              // Indicator description

Al utilizar el panel informativo en el asesor, también deberemos declarar las variables globales para él y conectar el archivo de la clase de panel:

//+------------------------------------------------------------------+
//|                                               TestTrendFRAMA.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- includes
#include <Dashboard\Dashboard.mqh>
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Undefined state
   LINE_STATE_UP,          // Upward
   LINE_STATE_DOWN,        // Downward
   LINE_STATE_TURN_UP,     // Upward reversal
   LINE_STATE_TURN_DOWN,   // Downward reversal
   LINE_STATE_STOP_UP,     // Upward stop
   LINE_STATE_STOP_DOWN,   // Downward stop
   LINE_STATE_ABOVE,       // Above value
   LINE_STATE_UNDER,       // Below value
   LINE_STATE_CROSS_UP,    // Crossing value upwards
   LINE_STATE_CROSS_DOWN,  // Crossing value downwards
   LINE_STATE_TOUCH_BELOW, // Touching value from below 
   LINE_STATE_TOUCH_ABOVE, // Touch value from above
   LINE_STATE_EQUALS,      // Equal to value
  };
//--- input parameters
input uint                 InpPeriod      =  14;            /* Period         */
input int                  InpShift       =  0;             /* FRAMA Shift    */
input ENUM_APPLIED_PRICE   InpPrice       =  PRICE_CLOSE;   /* Applied Price  */
//--- global variables
int      handle=INVALID_HANDLE;  // Indicator handle
int      period=0;               // FRAMA calculation period
int      ind_digits=0;           // Number of decimal places in the indicator values
string   ind_title;              // Indicator description
//--- variables for the panel
int      mouse_bar_index;        // Index of the bar the data is taken from
CDashboard *panel=NULL;          // Pointer to the panel object


Inicialización

Establecimiento de los valores de las variables globales para el indicador y creación de su manejador:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Indicator
//--- Set and adjust the calculation period and levels if necessary
   period=int(InpPeriod<1 ? 14 : InpPeriod);
//--- Set the indicator name and the number of decimal places
   ind_title=StringFormat("FRAMA(%lu)",period);
   ind_digits=Digits()+1;
//--- Create indicator handle
   ResetLastError();
   handle=iFrAMA(Symbol(),PERIOD_CURRENT,period,InpShift,InpPrice);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Successful initialization
   return(INIT_SUCCEEDED);
  }

Al utilizar un panel informativo en el asesor, crearemos un panel:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Indicator
//--- Set and adjust the calculation period and levels if necessary
   period=int(InpPeriod<1 ? 14 : InpPeriod);
//--- Set the indicator name and the number of decimal places
   ind_title=StringFormat("FRAMA(%lu)",period);
   ind_digits=Digits()+1;
//--- Create indicator handle
   ResetLastError();
   handle=iFrAMA(Symbol(),PERIOD_CURRENT,period,InpShift,InpPrice);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Dashboard
//--- Create the panel
   panel=new CDashboard(1,20,20,197,225);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Set font parameters
   panel.SetFontParams("Calibri",9);
//--- Display the panel with the "Symbol, Timeframe description" header text
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
//--- Create a table with ID 0 to display bar data in it
   panel.CreateNewTable(0);
//--- Draw a table with ID 0 on the panel background
   panel.DrawGrid(0,2,20,6,2,18,96);

//--- Create a table with ID 1 to display indicator data in it
   panel.CreateNewTable(1);
//--- Get the Y2 table coordinate with ID 0 and
//--- set the Y1 coordinate for the table with ID 1
   int y1=panel.TableY2(0)+22;
//--- Draw a table with ID 1 on the panel background
   panel.DrawGrid(1,2,y1,3,2,18,96);
   
//--- Display tabular data in the journal
   panel.GridPrint(0,2);
   panel.GridPrint(1,2);
//--- Initialize the variable with the index of the mouse cursor bar
   mouse_bar_index=0;
//--- Display the data of the current bar on the panel
   DrawData(mouse_bar_index,TimeCurrent());

//--- Successful initialization
   return(INIT_SUCCEEDED);
  }


Desinicialización

En el manejador OnDeinit() del asesor liberaremos el manejador del indicador:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Release handle of the indicator
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Clear all comments on the chart
   Comment("");
  }

Al utilizar un panel informativo, eliminaremos el objeto de panel creado:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Release handle of the indicator
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Clear all comments on the chart
   Comment("");
   
//--- If the panel object exists, delete it
   if(panel!=NULL)
      delete panel;
  }


Obtención de datos

Funciones generales para obtener los datos según el manejador del indicador:

//+------------------------------------------------------------------+
//| Return the indicator data on the specified bar                   |
//+------------------------------------------------------------------+
double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
  {
   double array[1]={0};
   ResetLastError();
   if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
     {
      PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
      return EMPTY_VALUE;
     }
   return array[0];
  }
//+------------------------------------------------------------------+
//| Return the state of the indicator line                           |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
  {
//--- Get the values of the indicator line with the shift (0,1,2) relative to the passed index
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
   const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
//--- If at least one of the values could not be obtained, return an undefined value 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Line upward reversal (value2>value1 && value0>value1)
   if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_TURN_UP;
//--- Line upward direction (value2<=value1 && value0>value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_UP;
//--- Line upward stop (value2<=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_UP;
//--- Line downward reversal (value2<value1 && value0<value1)
   if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_TURN_DOWN;
//--- Line downward direction (value2>=value1 && value0<value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_DOWN;
//--- Line downward stop (value2>=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_DOWN;
//--- Undefined state
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Return the state of the line relative to the specified level     |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
  {
//--- Get the values of the indicator line with the shift (0,1) relative to the passed index
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
//--- If at least one of the values could not be obtained, return an undefined value 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Define the second level to compare
   double level=(level1==EMPTY_VALUE ? level0 : level1);
//--- The line is below the level (value1<level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_UNDER;
//--- The line is above the level (value1>level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_ABOVE;
//--- The line crossed the level upwards (value1<=level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_CROSS_UP;
//--- The line crossed the level downwards (value1>=level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_CROSS_DOWN;
//--- The line touched the level from below (value1<level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- The line touched the level from above (value1>level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Line is equal to the level value (value1==level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_EQUALS;
//--- Undefined state
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Return the indicator line state description                      |
//+------------------------------------------------------------------+
string LineStateDescription(const ENUM_LINE_STATE state)
  {
   switch(state)
     {
      case LINE_STATE_UP         :  return "Up";
      case LINE_STATE_STOP_UP    :  return "Stop Up";
      case LINE_STATE_TURN_UP    :  return "Turn Up";
      case LINE_STATE_DOWN       :  return "Down";
      case LINE_STATE_STOP_DOWN  :  return "Stop Down";
      case LINE_STATE_TURN_DOWN  :  return "Turn Down";
      case LINE_STATE_ABOVE      :  return "Above level";
      case LINE_STATE_UNDER      :  return "Under level";
      case LINE_STATE_CROSS_UP   :  return "Crossing Up";
      case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
      case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
      case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
      case LINE_STATE_EQUALS     :  return "Equals";
      default                    :  return "Unknown";
     }
  }

Al utilizar el panel informativo, los datos se mostrarán en el panel utilizando la función:

//+------------------------------------------------------------------+
//| Display data from the specified timeseries index to the panel    |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Declare the variables to receive data in them
   MqlTick  tick={0};
   MqlRates rates[1];

//--- Exit if unable to get the current prices
   if(!SymbolInfoTick(Symbol(),tick))
      return;
//--- Exit if unable to get the bar data by the specified index
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Set font parameters for bar and indicator data headers
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Set font parameters for bar and indicator data
   panel.SetFontParams(name,9);

//--- Display the data of the specified bar in table 0 on the panel
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Display the indicator data from the specified bar on the panel in table 1
   panel.DrawText(ind_title, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value=IndicatorValue(handle,index,0);
   string value_str=(value!=EMPTY_VALUE ? DoubleToString(value,ind_digits) : "");
   panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,100);
   
//--- Display a description of the indicator line state
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state=LineState(handle,index,0);
   panel.DrawText(LineStateDescription(state),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,90);
   
//--- Redraw the chart to immediately display all changes on the panel
   ChartRedraw(ChartID());
  }

Además, al utilizar un panel informativo, el manejador de eventos OnChartEvent() del asesor llamará al manejador de eventos del panel informativo y procesará los eventos para obtener el índice de la barra bajo el cursor:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Handling the panel
//--- Call the panel event handler
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- If the cursor moves or a click is made on the chart
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Declare the variables to record time and price coordinates in them
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- If the cursor coordinates are converted to date and time
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- write the bar index where the cursor is located to a global variable
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Display the bar data under the cursor on the panel 
         DrawData(mouse_bar_index,time);
        }
     }

//--- If we received a custom event, display the appropriate message in the journal
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Here we can implement handling a click on the close button on the panel
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }

Tras compilar y ejecutar el asesor en el gráfico, podremos controlar el valor y el estado de la línea de indicador en el panel informativo:


Podrá ver el archivo del asesor de pruebas "TestTrendFRAMA.mq5" en los archivos adjuntos.


Ichimoku Kinko Hyo

El indicador técnico Ichimoku Kinko Hyo está diseñado para determinar la tendencia del mercado, los niveles de apoyo y resistencia y generar señales de compra y venta. El indicador funciona mejor en gráficos semanales y diarios.

Para determinar la dimensionalidad de los parámetros se usan cuatro intervalos temporales de distinta duración. En estos intervalos se basan los valores de las líneas individuales que componen este indicador:

  • Tenkan-sen muestra el precio medio del primer periodo temporal, definido como la suma del máximo y el mínimo de ese periodo dividido por dos;
  • Kijun-sen muestra el valor medio del precio durante el segundo periodo temporal;
  • Senkou Span A muestra el punto medio de la distancia entre las dos líneas anteriores desplazado hacia delante en el valor del segundo intervalo temporal;
  • Senkou Span B muestra el valor medio del precio para el tercer intervalo temporal desplazado hacia delante en el valor del segundo intervalo temporal.

Chikou Span muestra el precio de cierre de la vela actual desplazado hacia atrás en el valor del segundo intervalo temporal. La distancia entre las líneas Senkou se sombrea en un color distinto en el gráfico y se denomina "nube". Si el precio se encuentra entre estas líneas, se considerará que el mercado no es tendencial y entonces los bordes de la nube formarán niveles de apoyo y resistencia.

  • Si el precio se encuentra por encima de la nube, su línea superior formará el primer nivel de apoyo, mientras que la segunda línea formará el segundo nivel de apoyo;
  • Si el precio se encuentra por debajo de la nube, la línea inferior formará el primer nivel de resistencia, mientras que la línea superior formará el segundo nivel de resistencia;
  • Si la línea Chikou Span cruza el gráfico de precios de abajo hacia arriba, será una señal de compra. Si la cruza de arriba hacia abajo, será una señal de venta.

Kijun-sen se utilizará como indicador del movimiento del mercado. Si el precio se encuentra por encima de él, es probable que los precios sigan subiendo. Cuando el precio cruce esta línea, es probable que se produzca un nuevo cambio de tendencia. Otro caso de uso de Kijun-sen sería la señalización. La señal de compra se generará cuando la línea Tenkan-sen cruce Kijun-sen de abajo hacia arriba. El cruce de arriba hacia abajo supondrá una señal de venta. Tenkan-sen se utilizará como indicador de la tendencia del mercado. Si esta línea asciende o desciende, supondrá una tendencia. Cuando se desplace horizontalmente, el mercado habrá entrado en el canal.



Parámetros

La función iIchimoku() se utiliza para crear el manejador del indicador:

Retorna el manejador del indicador Ichimoku Kinko Hyo.

int  iIchimoku(
   string           symbol,            // symbol name
   ENUM_TIMEFRAMES  period,            // period
   int              tenkan_sen,        // Tenkan-sen period
   int              kijun_sen,         // Kijun-sen period
   int              senkou_span_b      // Senkou Span B period
   );
symbol

[in]  Nombre simbólico del instrumento sobre cuyos datos se calculará el indicador. NULL indicará el símbolo actual.

period

[in] El valor del periodo puede ser uno de los valores de la enumeración ENUM_TIMEFRAMES, 0 indicará el marco temporal actual.

tenkan_sen

[in]  Periodo de promediación de Tenkan Sen.

kijun_sen

[in]  Periodo de promediación de Kijun Sen.

senkou_span_b

[in]  Periodo de promediación de Senkou Span B.

Retorna el manejador del indicador técnico especificado, en caso de fallo retorna INVALID_HANDLE. Para liberar la memoria del ordenador de un indicador que ya no se utiliza, se usará la función IndicatorRelease(), a la que se le transmitirá el manejador de este indicador.

Números de los búferes: 0 — TENKANSEN_LINE, 1 — KIJUNSEN_LINE, 2 — SENKOUSPANA_LINE, 3 — SENKOUSPANB_LINE, 4 — CHIKOUSPAN_LINE.


Para crear un indicador en el asesor, declararemos las variables de entrada y globales:

//+------------------------------------------------------------------+
//|                                            TestTrendIchimoku.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Undefined state
   LINE_STATE_UP,          // Upward
   LINE_STATE_DOWN,        // Downward
   LINE_STATE_TURN_UP,     // Upward reversal
   LINE_STATE_TURN_DOWN,   // Downward reversal
   LINE_STATE_STOP_UP,     // Upward stop
   LINE_STATE_STOP_DOWN,   // Downward stop
   LINE_STATE_ABOVE,       // Above value
   LINE_STATE_UNDER,       // Below value
   LINE_STATE_CROSS_UP,    // Crossing value upwards
   LINE_STATE_CROSS_DOWN,  // Crossing value downwards
   LINE_STATE_TOUCH_BELOW, // Touching value from below 
   LINE_STATE_TOUCH_ABOVE, // Touch value from above
   LINE_STATE_EQUALS,      // Equal to value
  };
//--- input parameters
input uint                 InpPeriodTenkan=  9;    /* Tenkan-sen     */
input uint                 InpPeriodKijun =  26;   /* Kijun-sen      */
input uint                 InpPeriodSpanB =  52;   /* Senkou Span B  */
//--- global variables
int      handle=INVALID_HANDLE;  // Indicator handle
int      period_tenkan=0;        // Tenkan-sen line calculation period
int      period_kijun=0;         // Kijun-sen line calculation period
int      period_spanb=0;         // Senkou Span B line calculation period
int      ind_digits=0;           // Number of decimal places in the indicator values
string   ind_title;              // Indicator description

Al utilizar el panel informativo en el asesor, también deberemos declarar las variables globales para él y conectar el archivo de la clase de panel:

//+------------------------------------------------------------------+
//|                                            TestTrendIchimoku.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- includes
#include <Dashboard\Dashboard.mqh>
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Undefined state
   LINE_STATE_UP,          // Upward
   LINE_STATE_DOWN,        // Downward
   LINE_STATE_TURN_UP,     // Upward reversal
   LINE_STATE_TURN_DOWN,   // Downward reversal
   LINE_STATE_STOP_UP,     // Upward stop
   LINE_STATE_STOP_DOWN,   // Downward stop
   LINE_STATE_ABOVE,       // Above value
   LINE_STATE_UNDER,       // Below value
   LINE_STATE_CROSS_UP,    // Crossing value upwards
   LINE_STATE_CROSS_DOWN,  // Crossing value downwards
   LINE_STATE_TOUCH_BELOW, // Touching value from below 
   LINE_STATE_TOUCH_ABOVE, // Touch value from above
   LINE_STATE_EQUALS,      // Equal to value
  };
//--- input parameters
input uint                 InpPeriodTenkan=  9;    /* Tenkan-sen     */
input uint                 InpPeriodKijun =  26;   /* Kijun-sen      */
input uint                 InpPeriodSpanB =  52;   /* Senkou Span B  */
//--- global variables
int      handle=INVALID_HANDLE;  // Indicator handle
int      period_tenkan=0;        // Tenkan-sen line calculation period
int      period_kijun=0;         // Kijun-sen line calculation period
int      period_spanb=0;         // Senkou Span B line calculation period
int      ind_digits=0;           // Number of decimal places in the indicator values
string   ind_title;              // Indicator description
//--- variables for the panel
int      mouse_bar_index;        // Index of the bar the data is taken from
CDashboard *panel=NULL;          // Pointer to the panel object


Inicialización

Establecimiento de los valores de las variables globales para el indicador y creación de su manejador:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Indicator
//--- Set and adjust the calculation period if necessary
   period_tenkan=int(InpPeriodTenkan<1 ? 9 : InpPeriodTenkan);
   period_kijun=int(InpPeriodKijun<1 ? 26 : InpPeriodKijun);
   period_spanb=int(InpPeriodSpanB<1 ? 52 : InpPeriodSpanB);
//--- Set the indicator name and the number of decimal places
   ind_title=StringFormat("Ichimoku Kinko Hyo (%lu,%lu,%lu)",period_tenkan,period_kijun,period_spanb);
   ind_digits=Digits()+1;
//--- Create indicator handle
   ResetLastError();
   handle=iIchimoku(Symbol(),PERIOD_CURRENT,period_tenkan,period_kijun,period_spanb);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Successful initialization
   return(INIT_SUCCEEDED);
  }

Al utilizar un panel informativo en el asesor, crearemos un panel:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Indicator
//--- Set and adjust the calculation period if necessary
   period_tenkan=int(InpPeriodTenkan<1 ? 9 : InpPeriodTenkan);
   period_kijun=int(InpPeriodKijun<1 ? 26 : InpPeriodKijun);
   period_spanb=int(InpPeriodSpanB<1 ? 52 : InpPeriodSpanB);
//--- Set the indicator name and the number of decimal places
   ind_title=StringFormat("Ichimoku Kinko Hyo (%lu,%lu,%lu)",period_tenkan,period_kijun,period_spanb);
   ind_digits=Digits()+1;
//--- Create indicator handle
   ResetLastError();
   handle=iIchimoku(Symbol(),PERIOD_CURRENT,period_tenkan,period_kijun,period_spanb);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Dashboard
//--- Create the panel
   panel=new CDashboard(1,20,20,229,261);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Set font parameters
   panel.SetFontParams("Calibri",9);
//--- Display the panel with the "Symbol, Timeframe description" header text
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
//--- Create a table with ID 0 to display bar data in it
   panel.CreateNewTable(0);
//--- Draw a table with ID 0 on the panel background
   panel.DrawGrid(0,2,20,6,2,18,112);

//--- Create a table with ID 1 to display indicator data in it
   panel.CreateNewTable(1);
//--- Get the Y2 table coordinate with ID 0 and
//--- set the Y1 coordinate for the table with ID 1
   int y1=panel.TableY2(0)+22;
//--- Draw a table with ID 1 on the panel background
   panel.DrawGrid(1,2,y1,5,2,18,112);
   
//--- Display tabular data in the journal
   panel.GridPrint(0,2);
   panel.GridPrint(1,2);
//--- Initialize the variable with the index of the mouse cursor bar
   mouse_bar_index=0;
//--- Display the data of the current bar on the panel
   DrawData(mouse_bar_index,TimeCurrent());

//--- Successful initialization
   return(INIT_SUCCEEDED);
  }


Desinicialización

En el manejador OnDeinit() del asesor liberaremos el manejador del indicador:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Release handle of the indicator
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Clear all comments on the chart
   Comment("");
  }

Al utilizar un panel informativo, eliminaremos el objeto de panel creado:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Release handle of the indicator
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Clear all comments on the chart
   Comment("");
   
//--- If the panel object exists, delete it
   if(panel!=NULL)
      delete panel;
  }


Obtención de datos

Funciones generales para obtener los datos según el manejador del indicador:

//+------------------------------------------------------------------+
//| Return the indicator data on the specified bar                   |
//+------------------------------------------------------------------+
double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
  {
   double array[1]={0};
   ResetLastError();
   if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
     {
      PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
      return EMPTY_VALUE;
     }
   return array[0];
  }
//+------------------------------------------------------------------+
//| Return the state of the indicator line                           |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
  {
//--- Get the values of the indicator line with the shift (0,1,2) relative to the passed index
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
   const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
//--- If at least one of the values could not be obtained, return an undefined value 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Line upward reversal (value2>value1 && value0>value1)
   if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_TURN_UP;
//--- Line upward direction (value2<=value1 && value0>value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_UP;
//--- Line upward stop (value2<=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_UP;
//--- Line downward reversal (value2<value1 && value0<value1)
   if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_TURN_DOWN;
//--- Line downward direction (value2>=value1 && value0<value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_DOWN;
//--- Line downward stop (value2>=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_DOWN;
//--- Undefined state
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Return the state of the line relative to the specified level     |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
  {
//--- Get the values of the indicator line with the shift (0,1) relative to the passed index
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
//--- If at least one of the values could not be obtained, return an undefined value 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Define the second level to compare
   double level=(level1==EMPTY_VALUE ? level0 : level1);
//--- The line is below the level (value1<level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_UNDER;
//--- The line is above the level (value1>level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_ABOVE;
//--- The line crossed the level upwards (value1<=level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_CROSS_UP;
//--- The line crossed the level downwards (value1>=level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_CROSS_DOWN;
//--- The line touched the level from below (value1<level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- The line touched the level from above (value1>level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Line is equal to the level value (value1==level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_EQUALS;
//--- Undefined state
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Return the indicator line state description                      |
//+------------------------------------------------------------------+
string LineStateDescription(const ENUM_LINE_STATE state)
  {
   switch(state)
     {
      case LINE_STATE_UP         :  return "Up";
      case LINE_STATE_STOP_UP    :  return "Stop Up";
      case LINE_STATE_TURN_UP    :  return "Turn Up";
      case LINE_STATE_DOWN       :  return "Down";
      case LINE_STATE_STOP_DOWN  :  return "Stop Down";
      case LINE_STATE_TURN_DOWN  :  return "Turn Down";
      case LINE_STATE_ABOVE      :  return "Above level";
      case LINE_STATE_UNDER      :  return "Under level";
      case LINE_STATE_CROSS_UP   :  return "Crossing Up";
      case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
      case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
      case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
      case LINE_STATE_EQUALS     :  return "Equals";
      default                    :  return "Unknown";
     }
  }

Al utilizar el panel informativo, los datos se mostrarán en el panel utilizando la función:

//+------------------------------------------------------------------+
//| Display data from the specified timeseries index to the panel    |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Declare the variables to receive data in them
   MqlTick  tick={0};
   MqlRates rates[1];

//--- Exit if unable to get the current prices
   if(!SymbolInfoTick(Symbol(),tick))
      return;
//--- Exit if unable to get the bar data by the specified index
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Set font parameters for bar and indicator data headers
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Set font parameters for bar and indicator data
   panel.SetFontParams(name,9);

//--- Display the data of the specified bar in table 0 on the panel
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Get the indicator buffers data
   double tenkan=IndicatorValue(handle,index,TENKANSEN_LINE);     // Tenkan Sen line
   double kijun =IndicatorValue(handle,index,KIJUNSEN_LINE);      // Kijun Sen line
   double spana =IndicatorValue(handle,index,SENKOUSPANA_LINE);   // Senkou Span A line
   double spanb =IndicatorValue(handle,index,SENKOUSPANB_LINE);   // Senkou Span B line
   double chikou=IndicatorValue(handle,index,CHIKOUSPAN_LINE);    // Chikou Span line
   color clr=clrNONE;

//--- Display the Tenkan Sen line data from the specified bar on the panel in table 1
   string tenkan_str=StringFormat("Tenkan-sen(%lu)",period_tenkan);
   panel.DrawText(tenkan_str, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   string value_str=(tenkan!=EMPTY_VALUE ? DoubleToString(tenkan,ind_digits) : " ");
   ENUM_LINE_STATE state_tenkan=LineStateRelative(handle,index,TENKANSEN_LINE,kijun,IndicatorValue(handle,index+1,KIJUNSEN_LINE));
   clr=(state_tenkan==LINE_STATE_CROSS_UP ? clrBlue : state_tenkan==LINE_STATE_CROSS_DOWN ? clrRed : clrNONE);
   panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clr,100);
   
//--- Display the Kijun Sen line data from the specified bar on the panel in table 1
   string kijun_str=StringFormat("Kijun-sen(%lu)",period_kijun);
   panel.DrawText(kijun_str, panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   value_str=(kijun!=EMPTY_VALUE ? DoubleToString(kijun,ind_digits) : " ");
   ENUM_LINE_STATE state_kijun=LineState(handle,index,KIJUNSEN_LINE);
   clr=
     (
      state_kijun==LINE_STATE_UP || state_kijun==LINE_STATE_TURN_UP ? clrBlue : 
      state_kijun==LINE_STATE_DOWN || state_kijun==LINE_STATE_TURN_DOWN ? clrRed : clrNONE
     );
   panel.DrawText(value_str,panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clr,100);

//--- Display the Senkou Span A line data from the specified bar on the panel in table 1
   panel.DrawText("Senkou Span A", panel.CellX(1,2,0)+2, panel.CellY(1,2,0)+2);
   value_str=(spana!=EMPTY_VALUE ? DoubleToString(spana,ind_digits) : " ");
   panel.DrawText(value_str,panel.CellX(1,2,1)+2,panel.CellY(1,2,1)+2,clrNONE,100);
   
//--- Display the Senkou Span B line data from the specified bar on the panel in table 1
   string spanb_str=StringFormat("Senkou Span B(%lu)",period_spanb);
   panel.DrawText(spanb_str, panel.CellX(1,3,0)+2, panel.CellY(1,3,0)+2);
   value_str=(spanb!=EMPTY_VALUE ? DoubleToString(spanb,ind_digits) : " ");
   panel.DrawText(value_str,panel.CellX(1,3,1)+2,panel.CellY(1,3,1)+2,clrNONE,100);
   
//--- Display the Chikou Span line data from the specified bar on the panel in table 1
   panel.DrawText("Chikou Span", panel.CellX(1,4,0)+2, panel.CellY(1,4,0)+2);
   value_str=(chikou!=EMPTY_VALUE ? DoubleToString(chikou,ind_digits) : " ");
   panel.DrawText(value_str,panel.CellX(1,4,1)+2,panel.CellY(1,4,1)+2,clrNONE,100);
   
//--- Redraw the chart to immediately display all changes on the panel
   ChartRedraw(ChartID());
  }

El color de los valores de las líneas Tenkan-sen y Kijun-sen dependerá de su ubicación y dirección mutuas.

Además, al utilizar un panel informativo, el manejador de eventos OnChartEvent() del asesor llamará al manejador de eventos del panel informativo y procesará los eventos para obtener el índice de la barra bajo el cursor:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Handling the panel
//--- Call the panel event handler
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- If the cursor moves or a click is made on the chart
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Declare the variables to record time and price coordinates in them
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- If the cursor coordinates are converted to date and time
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- write the bar index where the cursor is located to a global variable
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Display the bar data under the cursor on the panel 
         DrawData(mouse_bar_index,time);
        }
     }

//--- If we received a custom event, display the appropriate message in the journal
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Here we can implement handling a click on the close button on the panel
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }

Tras compilar y ejecutar el asesor en el gráfico, podremos controlar el valor y el estado de la línea de indicador en el panel informativo:


Podrá ver el archivo del asesor de pruebas "TestTrendIchimoku.mq5" en los archivos adjuntos.


Moving Average

El indicador técnico Media Móvil (Moving Average, MA) muestra el valor medio del precio de un instrumento durante un determinado periodo de tiempo. Al calcular la media móvil, se efectúa el promedio matemático del precio del instrumento durante un periodo determinado. A medida que varía el precio, su valor medio aumentará o disminuirá.

Existen varios tipos de medias móviles: la simple (también se la conoce como aritmética), la exponencial, la suavizada y la ponderada. La media móvil puede calcularse para cualquier conjunto coherente de datos, incluidos los precios de apertura y cierre, los precios máximos y mínimos, el volumen comercial o los valores de otros indicadores. A menudo también se usan las medias móviles de las propias medias móviles.

Lo único en lo que las medias móviles de distintos tipos difieren sustancialmente entre sí es en los diferentes coeficientes de ponderación que se asignan a los últimos datos. En el caso de la Media Móvil Simple (Simple Moving Average), todos los precios del periodo considerado tienen el mismo peso. Las medias móviles exponencial y ponderada (Exponential Moving Average y Linear Weighted Moving Average) otorgan mayor peso a los últimos precios.

El método más frecuente para interpretar una media móvil de precios consiste en comparar su dinámica con la dinámica del propio precio. Cuando el precio del instrumento sube por encima de la Media Móvil, tendremos una señal de compra, y cuando caiga por debajo de la línea del indicador, tendremos una señal de venta.

Este sistema comercial con la media móvil no está destinado a garantizar que la entrada en el mercado se dé estrictamente en su punto más bajo, y la salida se dé estrictamente en su punto más alto. Permite actuar según la tendencia actual: comprar poco después de que los precios hayan alcanzado la base y vender poco después de que se haya formado el pico.

Las medias móviles también pueden aplicarse a indicadores. La interpretación de los indicadores de medias móviles resulta similar a la interpretación de las medias móviles de los precios: si el indicador sube por encima de su Moving Average, significa que el movimiento alcista del indicador continuará, y si el indicador cae por debajo de su Moving Average, significa que su movimiento bajista continuará.

Variaciones de las medias móviles:

  • Simple Moving Average (SMA) - media móvil simple
  • Exponential Moving Average (EMA) - media móvil exponencial
  • Smoothed Moving Average (SMMA) - media móvil suavizada
  • Linear Weighted Moving Average (LWMA) - media móvil ponderada lineal



Parámetros

La función iMA() se utiliza para crear el manejador del indicador:

Return moving average indicator handle. Only one buffer.

int  iMA(
   string               symbol,            // symbol name
   ENUM_TIMEFRAMES      period,            // period
   int                  ma_period,         // averaging period
   int                  ma_shift,          // horizontal shift of the indicator
   ENUM_MA_METHOD       ma_method,         // smoothing type
   ENUM_APPLIED_PRICE   applied_price      // price type or handle
   );
symbol

[in]  Nombre simbólico del instrumento sobre cuyos datos se calculará el indicador. NULL indicará el símbolo actual.

period

[in] El valor del periodo puede ser uno de los valores de la enumeración ENUM_TIMEFRAMES, 0 indicará el marco temporal actual.

ma_period

[in]  Periodo de cálculo de la media móvil.

ma_shift

[in]  Desplazamiento del indicador respecto al gráfico de precios.

ma_method

[in]  Método de promediación. Puede ser cualquiera de los valores ENUM_MA_METHOD.

applied_price

[in]  Precio utilizado. Puede ser cualquiera de las constantes de precio ENUM_APPLIED_PRICE o el manejador de otro indicador.

Retorna el manejador del indicador técnico especificado, en caso de fallo retorna INVALID_HANDLE. Para liberar la memoria del ordenador de un indicador que ya no se utiliza, se usará la función IndicatorRelease(), a la que se le transmitirá el manejador de este indicador.


Para crear un indicador en el asesor, declararemos las variables de entrada y globales:

//+------------------------------------------------------------------+
//|                                                  TestTrendMA.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Undefined state
   LINE_STATE_UP,          // Upward
   LINE_STATE_DOWN,        // Downward
   LINE_STATE_TURN_UP,     // Upward reversal
   LINE_STATE_TURN_DOWN,   // Downward reversal
   LINE_STATE_STOP_UP,     // Upward stop
   LINE_STATE_STOP_DOWN,   // Downward stop
   LINE_STATE_ABOVE,       // Above value
   LINE_STATE_UNDER,       // Below value
   LINE_STATE_CROSS_UP,    // Crossing value upwards
   LINE_STATE_CROSS_DOWN,  // Crossing value downwards
   LINE_STATE_TOUCH_BELOW, // Touching value from below 
   LINE_STATE_TOUCH_ABOVE, // Touch value from above
   LINE_STATE_EQUALS,      // Equal to value
  };
//--- input parameters
input uint                 InpPeriod=  10;            /* Period         */ 
input int                  InpShift =  0;             /* MA Shift       */ 
input ENUM_MA_METHOD       InpMethod=  MODE_SMA;      /* Method         */ 
input ENUM_APPLIED_PRICE   InpPrice =  PRICE_CLOSE;   /* Applied Price  */ 
//--- global variables
int      handle=INVALID_HANDLE;  // Indicator handle
int      period=0;               // MA calculation period
int      ind_digits=0;           // Number of decimal places in the indicator values
string   ind_title;              // Indicator description

Al utilizar el panel informativo en el asesor, también deberemos declarar las variables globales para él y conectar el archivo de la clase de panel:

//+------------------------------------------------------------------+
//|                                                  TestTrendMA.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- includes
#include <Dashboard\Dashboard.mqh>
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Undefined state
   LINE_STATE_UP,          // Upward
   LINE_STATE_DOWN,        // Downward
   LINE_STATE_TURN_UP,     // Upward reversal
   LINE_STATE_TURN_DOWN,   // Downward reversal
   LINE_STATE_STOP_UP,     // Upward stop
   LINE_STATE_STOP_DOWN,   // Downward stop
   LINE_STATE_ABOVE,       // Above value
   LINE_STATE_UNDER,       // Below value
   LINE_STATE_CROSS_UP,    // Crossing value upwards
   LINE_STATE_CROSS_DOWN,  // Crossing value downwards
   LINE_STATE_TOUCH_BELOW, // Touching value from below 
   LINE_STATE_TOUCH_ABOVE, // Touch value from above
   LINE_STATE_EQUALS,      // Equal to value
  };
//--- input parameters
input uint                 InpPeriod=  10;            /* Period         */ 
input int                  InpShift =  0;             /* MA Shift       */ 
input ENUM_MA_METHOD       InpMethod=  MODE_SMA;      /* Method         */ 
input ENUM_APPLIED_PRICE   InpPrice =  PRICE_CLOSE;   /* Applied Price  */ 
//--- global variables
int      handle=INVALID_HANDLE;  // Indicator handle
int      period=0;               // MA calculation period
int      ind_digits=0;           // Number of decimal places in the indicator values
string   ind_title;              // Indicator description
//--- variables for the panel
int      mouse_bar_index;        // Index of the bar the data is taken from
CDashboard *panel=NULL;          // Pointer to the panel object


Inicialización

Establecimiento de los valores de las variables globales para el indicador y creación de su manejador:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Indicator
//--- Set and adjust the calculation period and levels if necessary
   period=int(InpPeriod<1 ? 10 : InpPeriod);
//--- Set the indicator name and the number of decimal places
   ind_title=StringFormat("MA(%lu)",period);
   ind_digits=Digits()+1;
//--- Create indicator handle
   ResetLastError();
   handle=iMA(Symbol(),PERIOD_CURRENT,period,InpShift,InpMethod,InpPrice);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Successful initialization
   return(INIT_SUCCEEDED);
  }

Al utilizar un panel informativo en el asesor, crearemos un panel:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Indicator
//--- Set and adjust the calculation period and levels if necessary
   period=int(InpPeriod<1 ? 10 : InpPeriod);
//--- Set the indicator name and the number of decimal places
   ind_title=StringFormat("MA(%lu)",period);
   ind_digits=Digits()+1;
//--- Create indicator handle
   ResetLastError();
   handle=iMA(Symbol(),PERIOD_CURRENT,period,InpShift,InpMethod,InpPrice);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Dashboard
//--- Create the panel
   panel=new CDashboard(1,20,20,197,225);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Set font parameters
   panel.SetFontParams("Calibri",9);
//--- Display the panel with the "Symbol, Timeframe description" header text
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
//--- Create a table with ID 0 to display bar data in it
   panel.CreateNewTable(0);
//--- Draw a table with ID 0 on the panel background
   panel.DrawGrid(0,2,20,6,2,18,96);

//--- Create a table with ID 1 to display indicator data in it
   panel.CreateNewTable(1);
//--- Get the Y2 table coordinate with ID 0 and
//--- set the Y1 coordinate for the table with ID 1
   int y1=panel.TableY2(0)+22;
//--- Draw a table with ID 1 on the panel background
   panel.DrawGrid(1,2,y1,3,2,18,96);
   
//--- Display tabular data in the journal
   panel.GridPrint(0,2);
   panel.GridPrint(1,2);
//--- Initialize the variable with the index of the mouse cursor bar
   mouse_bar_index=0;
//--- Display the data of the current bar on the panel
   DrawData(mouse_bar_index,TimeCurrent());

//--- Successful initialization
   return(INIT_SUCCEEDED);
  }


Desinicialización

En el manejador OnDeinit() del asesor liberaremos el manejador del indicador:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Release handle of the indicator
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Clear all comments on the chart
   Comment("");
  }

Al utilizar un panel informativo, eliminaremos el objeto de panel creado:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Release handle of the indicator
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Clear all comments on the chart
   Comment("");
   
//--- If the panel object exists, delete it
   if(panel!=NULL)
      delete panel;
  }


Obtención de datos

Funciones generales para obtener los datos según el manejador del indicador:

//+------------------------------------------------------------------+
//| Return the indicator data on the specified bar                   |
//+------------------------------------------------------------------+
double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
  {
   double array[1]={0};
   ResetLastError();
   if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
     {
      PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
      return EMPTY_VALUE;
     }
   return array[0];
  }
//+------------------------------------------------------------------+
//| Return the state of the indicator line                           |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
  {
//--- Get the values of the indicator line with the shift (0,1,2) relative to the passed index
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
   const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
//--- If at least one of the values could not be obtained, return an undefined value 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Line upward reversal (value2>value1 && value0>value1)
   if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_TURN_UP;
//--- Line upward direction (value2<=value1 && value0>value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_UP;
//--- Line upward stop (value2<=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_UP;
//--- Line downward reversal (value2<value1 && value0<value1)
   if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_TURN_DOWN;
//--- Line downward direction (value2>=value1 && value0<value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_DOWN;
//--- Line downward stop (value2>=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_DOWN;
//--- Undefined state
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Return the state of the line relative to the specified level     |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
  {
//--- Get the values of the indicator line with the shift (0,1) relative to the passed index
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
//--- If at least one of the values could not be obtained, return an undefined value 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Define the second level to compare
   double level=(level1==EMPTY_VALUE ? level0 : level1);
//--- The line is below the level (value1<level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_UNDER;
//--- The line is above the level (value1>level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_ABOVE;
//--- The line crossed the level upwards (value1<=level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_CROSS_UP;
//--- The line crossed the level downwards (value1>=level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_CROSS_DOWN;
//--- The line touched the level from below (value1<level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- The line touched the level from above (value1>level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Line is equal to the level value (value1==level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_EQUALS;
//--- Undefined state
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Return the indicator line state description                      |
//+------------------------------------------------------------------+
string LineStateDescription(const ENUM_LINE_STATE state)
  {
   switch(state)
     {
      case LINE_STATE_UP         :  return "Up";
      case LINE_STATE_STOP_UP    :  return "Stop Up";
      case LINE_STATE_TURN_UP    :  return "Turn Up";
      case LINE_STATE_DOWN       :  return "Down";
      case LINE_STATE_STOP_DOWN  :  return "Stop Down";
      case LINE_STATE_TURN_DOWN  :  return "Turn Down";
      case LINE_STATE_ABOVE      :  return "Above level";
      case LINE_STATE_UNDER      :  return "Under level";
      case LINE_STATE_CROSS_UP   :  return "Crossing Up";
      case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
      case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
      case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
      case LINE_STATE_EQUALS     :  return "Equals";
      default                    :  return "Unknown";
     }
  }

Al utilizar el panel informativo, los datos se mostrarán en el panel utilizando la función:

//+------------------------------------------------------------------+
//| Display data from the specified timeseries index to the panel    |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Declare the variables to receive data in them
   MqlTick  tick={0};
   MqlRates rates[1];

//--- Exit if unable to get the current prices
   if(!SymbolInfoTick(Symbol(),tick))
      return;
//--- Exit if unable to get the bar data by the specified index
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Set font parameters for bar and indicator data headers
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Set font parameters for bar and indicator data
   panel.SetFontParams(name,9);

//--- Display the data of the specified bar in table 0 on the panel
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Display the indicator data from the specified bar on the panel in table 1
   panel.DrawText(ind_title, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value=IndicatorValue(handle,index,0);
   string value_str=(value!=EMPTY_VALUE ? DoubleToString(value,ind_digits) : "");
   panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,100);
   
//--- Display a description of the indicator line state
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state=LineState(handle,index,0);
   panel.DrawText(LineStateDescription(state),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,90);
   
//--- Redraw the chart to immediately display all changes on the panel
   ChartRedraw(ChartID());
  }

El panel muestra los valores de los indicadores y el estado de su línea.

Además, al utilizar un panel informativo, el manejador de eventos OnChartEvent() del asesor llamará al manejador de eventos del panel informativo y procesará los eventos para obtener el índice de la barra bajo el cursor:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Handling the panel
//--- Call the panel event handler
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- If the cursor moves or a click is made on the chart
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Declare the variables to record time and price coordinates in them
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- If the cursor coordinates are converted to date and time
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- write the bar index where the cursor is located to a global variable
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Display the bar data under the cursor on the panel 
         DrawData(mouse_bar_index,time);
        }
     }

//--- If we received a custom event, display the appropriate message in the journal
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Here we can implement handling a click on the close button on the panel
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }

Tras compilar y ejecutar el asesor en el gráfico, podremos controlar el valor y el estado de la línea de indicador en el panel informativo:


Podrá ver el archivo del asesor de pruebas "TestTrendMA.mq5" en los archivos adjuntos.


Parabolic SAR Setting

El indicador técnicoSistema Parabólico SAR (Parabolic SAR) se desarrolló para analizar los mercados en tendencia. El indicador se representa en un gráfico de precios. El sentido de este indicador es similar al sentido de la media móvil, con la única diferencia de que Parabolic SAR se mueve con gran aceleración y puede cambiar su posición respecto al precio. En una tendencia alcista (Up Trend) el indicador estará por debajo de los precios, mientras que en una tendencia bajista (Down Trend) estará por encima de los precios.

Si el precio cruza la línea de Parabolic SAR, el indicador se invertirá y sus próximos valores se situarán al otro lado del precio. En este "viraje" del indicador, el precio máximo o mínimo del periodo anterior servirá como punto de referencia. El viraje del indicador supondrá una señal del final (transición a la corrección o el mercado plano) de la tendencia, o de su inversión.

Parabolic SAR destaca en la identificación de puntos de salida del mercado. Las posiciones largas deberán cerrarse cuando el precio caiga por debajo de la línea del indicador técnico, mientras que las posiciones cortas deberán cerrarse cuando el precio suba por encima de la línea de Parabolic SAR. Es decir, deberemos seguir la dirección de movimiento de Parabolic SAR y mantener las posiciones abiertas en el mercado solo en la dirección de este movimiento. Este indicador se usa a menudo como una línea de trailing stop.

Si abrimos una posición larga (es decir, el precio está por encima de la línea de Parabolic SAR), la línea del indicador se moverá hacia arriba independientemente de la dirección en que se muevan los precios. La magnitud de movimiento de la línea de Parabolic SAR dependerá de la magnitud del movimiento del precio.



Parámetros

La función iSAR() se utiliza para crear el manejador del indicador:

Retorna el manejador del indicador Parabolic Stop and Reverse system. Solo un búfer.

int  iSAR(
   string           symbol,      // symbol name
   ENUM_TIMEFRAMES  period,      // period
   double           step,        // price change step — acceleration factor
   double           maximum      // maximum step
   );
symbol

[in]  Nombre simbólico del instrumento sobre cuyos datos se calculará el indicador. NULL indicará el símbolo actual.

period

[in] El valor del periodo puede ser uno de los valores de la enumeración ENUM_TIMEFRAMES, 0 indicará el marco temporal actual.

step

[in]  Paso de cambio de precio, normalmente 0,02.

maximum

[in] Paso máximo, normalmente 0,2.

Retorna el manejador del indicador técnico especificado, en caso de fallo retorna INVALID_HANDLE. Para liberar la memoria del ordenador de un indicador que ya no se utiliza, se usará la función IndicatorRelease(), a la que se le transmitirá el manejador de este indicador.

Para crear un indicador en el asesor, declararemos las variables de entrada y globales:

//+------------------------------------------------------------------+
//|                                                 TestTrendSAR.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Undefined state
   LINE_STATE_UP,          // Upward
   LINE_STATE_DOWN,        // Downward
   LINE_STATE_TURN_UP,     // Upward reversal
   LINE_STATE_TURN_DOWN,   // Downward reversal
   LINE_STATE_STOP_UP,     // Upward stop
   LINE_STATE_STOP_DOWN,   // Downward stop
   LINE_STATE_ABOVE,       // Above value
   LINE_STATE_UNDER,       // Below value
   LINE_STATE_CROSS_UP,    // Crossing value upwards
   LINE_STATE_CROSS_DOWN,  // Crossing value downwards
   LINE_STATE_TOUCH_BELOW, // Touching value from below 
   LINE_STATE_TOUCH_ABOVE, // Touch value from above
   LINE_STATE_EQUALS,      // Equal to value
  };
//--- input parameters
input double   InpStep  =  0.02; /* Step     */ 
input double   InpMax   =  0.2;  /* Maximum  */ 
//--- global variables
int      handle=INVALID_HANDLE;  // Indicator handle
int      period=0;               // MA calculation period
int      ind_digits=0;           // Number of decimal places in the indicator values
string   ind_title;              // Indicator description

Al utilizar el panel informativo en el asesor, también deberemos declarar las variables globales para él y conectar el archivo de la clase de panel:

//+------------------------------------------------------------------+
//|                                                 TestTrendSAR.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- includes
#include <Dashboard\Dashboard.mqh>
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Undefined state
   LINE_STATE_UP,          // Upward
   LINE_STATE_DOWN,        // Downward
   LINE_STATE_TURN_UP,     // Upward reversal
   LINE_STATE_TURN_DOWN,   // Downward reversal
   LINE_STATE_STOP_UP,     // Upward stop
   LINE_STATE_STOP_DOWN,   // Downward stop
   LINE_STATE_ABOVE,       // Above value
   LINE_STATE_UNDER,       // Below value
   LINE_STATE_CROSS_UP,    // Crossing value upwards
   LINE_STATE_CROSS_DOWN,  // Crossing value downwards
   LINE_STATE_TOUCH_BELOW, // Touching value from below 
   LINE_STATE_TOUCH_ABOVE, // Touch value from above
   LINE_STATE_EQUALS,      // Equal to value
  };
//--- input parameters
input double   InpStep  =  0.02; /* Step     */ 
input double   InpMax   =  0.2;  /* Maximum  */ 
//--- global variables
int      handle=INVALID_HANDLE;  // Indicator handle
int      period=0;               // MA calculation period
int      ind_digits=0;           // Number of decimal places in the indicator values
string   ind_title;              // Indicator description
//--- variables for the panel
int      mouse_bar_index;        // Index of the bar the data is taken from
CDashboard *panel=NULL;          // Pointer to the panel object


Inicialización

Establecimiento de los valores de las variables globales para el indicador y creación de su manejador:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Indicator
//--- Set the indicator name and the number of decimal places
   ind_title=StringFormat("SAR(%.2f,%.2f)",InpStep,InpMax);
   ind_digits=Digits();
//--- Create indicator handle
   ResetLastError();
   handle=iSAR(Symbol(),PERIOD_CURRENT,InpStep,InpMax);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Successful initialization
   return(INIT_SUCCEEDED);
  }

Al utilizar un panel informativo en el asesor, crearemos un panel:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Indicator
//--- Set the indicator name and the number of decimal places
   ind_title=StringFormat("SAR(%.2f,%.2f)",InpStep,InpMax);
   ind_digits=Digits();
//--- Create indicator handle
   ResetLastError();
   handle=iSAR(Symbol(),PERIOD_CURRENT,InpStep,InpMax);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Dashboard
//--- Create the panel
   panel=new CDashboard(1,20,20,197,225);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Set font parameters
   panel.SetFontParams("Calibri",9);
//--- Display the panel with the "Symbol, Timeframe description" header text
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
//--- Create a table with ID 0 to display bar data in it
   panel.CreateNewTable(0);
//--- Draw a table with ID 0 on the panel background
   panel.DrawGrid(0,2,20,6,2,18,96);

//--- Create a table with ID 1 to display indicator data in it
   panel.CreateNewTable(1);
//--- Get the Y2 table coordinate with ID 0 and
//--- set the Y1 coordinate for the table with ID 1
   int y1=panel.TableY2(0)+22;
//--- Draw a table with ID 1 on the panel background
   panel.DrawGrid(1,2,y1,3,2,18,96);
   
//--- Display tabular data in the journal
   panel.GridPrint(0,2);
   panel.GridPrint(1,2);
//--- Initialize the variable with the index of the mouse cursor bar
   mouse_bar_index=0;
//--- Display the data of the current bar on the panel
   DrawData(mouse_bar_index,TimeCurrent());

//--- Successful initialization
   return(INIT_SUCCEEDED);
  }


Desinicialización

En el manejador OnDeinit() del asesor liberaremos el manejador del indicador:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Release handle of the indicator
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Clear all comments on the chart
   Comment("");
  }

Al utilizar un panel informativo, eliminaremos el objeto de panel creado:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Release handle of the indicator
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Clear all comments on the chart
   Comment("");
   
//--- If the panel object exists, delete it
   if(panel!=NULL)
      delete panel;
  }


Obtención de datos

Funciones generales para obtener los datos según el manejador del indicador:

//+------------------------------------------------------------------+
//| Return the indicator data on the specified bar                   |
//+------------------------------------------------------------------+
double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
  {
   double array[1]={0};
   ResetLastError();
   if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
     {
      PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
      return EMPTY_VALUE;
     }
   return array[0];
  }
//+------------------------------------------------------------------+
//| Return the state of the indicator line                           |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
  {
//--- Get the values of the indicator line with the shift (0,1,2) relative to the passed index
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
   const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
//--- If at least one of the values could not be obtained, return an undefined value 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Line upward reversal (value2>value1 && value0>value1)
   if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_TURN_UP;
//--- Line upward direction (value2<=value1 && value0>value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_UP;
//--- Line upward stop (value2<=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_UP;
//--- Line downward reversal (value2<value1 && value0<value1)
   if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_TURN_DOWN;
//--- Line downward direction (value2>=value1 && value0<value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_DOWN;
//--- Line downward stop (value2>=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_DOWN;
//--- Undefined state
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Return the state of the line relative to the specified level     |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
  {
//--- Get the values of the indicator line with the shift (0,1) relative to the passed index
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
//--- If at least one of the values could not be obtained, return an undefined value 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Define the second level to compare
   double level=(level1==EMPTY_VALUE ? level0 : level1);
//--- The line is below the level (value1<level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_UNDER;
//--- The line is above the level (value1>level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_ABOVE;
//--- The line crossed the level upwards (value1<=level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_CROSS_UP;
//--- The line crossed the level downwards (value1>=level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_CROSS_DOWN;
//--- The line touched the level from below (value1<level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- The line touched the level from above (value1>level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Line is equal to the level value (value1==level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_EQUALS;
//--- Undefined state
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Return the indicator line state description                      |
//+------------------------------------------------------------------+
string LineStateDescription(const ENUM_LINE_STATE state)
  {
   switch(state)
     {
      case LINE_STATE_UP         :  return "Up";
      case LINE_STATE_STOP_UP    :  return "Stop Up";
      case LINE_STATE_TURN_UP    :  return "Turn Up";
      case LINE_STATE_DOWN       :  return "Down";
      case LINE_STATE_STOP_DOWN  :  return "Stop Down";
      case LINE_STATE_TURN_DOWN  :  return "Turn Down";
      case LINE_STATE_ABOVE      :  return "Above level";
      case LINE_STATE_UNDER      :  return "Under level";
      case LINE_STATE_CROSS_UP   :  return "Crossing Up";
      case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
      case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
      case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
      case LINE_STATE_EQUALS     :  return "Equals";
      default                    :  return "Unknown";
     }
  }

Al utilizar el panel informativo, los datos se mostrarán en el panel utilizando la función:

//+------------------------------------------------------------------+
//| Display data from the specified timeseries index to the panel    |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Declare the variables to receive data in them
   MqlTick  tick={0};
   MqlRates rates[1];

//--- Exit if unable to get the current prices
   if(!SymbolInfoTick(Symbol(),tick))
      return;
//--- Exit if unable to get the bar data by the specified index
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Set font parameters for bar and indicator data headers
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Set font parameters for bar and indicator data
   panel.SetFontParams(name,9);

//--- Display the data of the specified bar in table 0 on the panel
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Display the indicator data from the specified bar on the panel in table 1
   panel.DrawText(ind_title, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value=IndicatorValue(handle,index,0);
   string value_str=(value!=EMPTY_VALUE ? DoubleToString(value,ind_digits) : "");
   panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,100);
   
//--- Display the header of the indicator line state description
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   
//--- Save Close value on 'index' bar to the variable
   double close0=rates[0].close;
//--- If failed to get bar data by index+1, leave
   if(CopyRates(Symbol(),PERIOD_CURRENT,index+1,1,rates)!=1)
      return;
//--- Save Close value on 'index+1' bar to the variable and get the indicator value on 'index+1' bar
   double close1=rates[0].close;
   double value1=IndicatorValue(handle,index+1,0);
//--- Get and adjust the state of the indicator line
   ENUM_LINE_STATE state=LineState(handle,index,0);
   if(value<close0 && value1>close1)
      state=LINE_STATE_TURN_UP;
   if(state==LINE_STATE_STOP_DOWN)
      state=LINE_STATE_UP;
   if(value>close0 && value1<close1)
      state=LINE_STATE_TURN_DOWN;
   if(state==LINE_STATE_STOP_UP)
      state=LINE_STATE_DOWN;
//--- Display a description of the indicator line state
   panel.DrawText(LineStateDescription(state),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,90);
   
//--- Redraw the chart to immediately display all changes on the panel
   ChartRedraw(ChartID());
  }

Además, al utilizar un panel informativo, el manejador de eventos OnChartEvent() del asesor llamará al manejador de eventos del panel informativo y procesará los eventos para obtener el índice de la barra bajo el cursor:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Handling the panel
//--- Call the panel event handler
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- If the cursor moves or a click is made on the chart
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Declare the variables to record time and price coordinates in them
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- If the cursor coordinates are converted to date and time
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- write the bar index where the cursor is located to a global variable
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Display the bar data under the cursor on the panel 
         DrawData(mouse_bar_index,time);
        }
     }

//--- If we received a custom event, display the appropriate message in the journal
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Here we can implement handling a click on the close button on the panel
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }

Tras compilar y ejecutar el asesor en el gráfico, podremos controlar el valor y el estado de la línea de indicador en el panel informativo:


Podrá ver el archivo del asesor de pruebas "TestTrendSAR.mq5" en los archivos adjuntos.


Standard Deviation

El indicador técnico de Desviación estándar (Standard Deviation, StdDev) mide la volatilidad del mercado. Este indicador caracteriza la magnitud de las fluctuaciones de precio en relación con la media móvil. Así, si el valor del indicador es alto, el mercado será volátil y los precios de las barras estarán bastante dispersos con respecto a la media móvil. Si el valor del indicador es pequeño, el mercado se caracterizará por una baja volatilidad y los precios de las barras estarán lo suficientemente cerca de la media móvil.

Normalmente, este indicador se utilizará como componente de otros indicadores. Por ejemplo, al calcular Bollinger Bands®, el valor de la desviación estándar del instrumento se añade a su media móvil

La dinámica del mercado consiste en la alternancia sucesiva de periodos de reposo y estallidos de actividad, por lo que el planteamiento de este indicador será sencillo:

  • si el valor del indicador es demasiado pequeño, es decir, el mercado está en reposo absoluto, tendrá sentido esperar un estallido de actividad en breve;
  • Por el contrario, si el indicador es extremadamente alto, indicará que es probable que esta actividad disminuya pronto.


    Parámetros

    La función iStdDev() se utiliza para crear el manejador del indicador:

    Retorna el manejador del indicador Standard Deviation. Solo un búfer.

    int  iStdDev(
       string              symbol,            // symbol name
       ENUM_TIMEFRAMES     period,            // period
       int                 ma_period,         // averaging period
       int                 ma_shift,          // horizontal shift of the indicator
       ENUM_MA_METHOD      ma_method,         // smoothing type
       ENUM_APPLIED_PRICE  applied_price      // price type or handle
       );
    
    symbol

    [in]  Nombre simbólico del instrumento sobre cuyos datos se calculará el indicador. NULL indicará el símbolo actual.

    period

    [in] El valor del periodo puede ser uno de los valores de la enumeración ENUM_TIMEFRAMES, 0 indicará el marco temporal actual.

    ma_period

    [in]  Periodo medio de cálculo del indicador.

    ma_shift

    [in]  Desplazamiento del indicador respecto al gráfico de precios.

    ma_method

    [in]  Método de promediación. Puede ser cualquiera de los valores ENUM_MA_METHOD.

    applied_price

    [in]  Precio utilizado. Puede ser cualquiera de las constantes de precio ENUM_APPLIED_PRICE o el manejador de otro indicador.

    Retorna el manejador del indicador técnico especificado, en caso de fallo retorna INVALID_HANDLE. Para liberar la memoria del ordenador de un indicador que ya no se utiliza, se usará la función IndicatorRelease(), a la que se le transmitirá el manejador de este indicador.

    Para crear un indicador en el asesor, declararemos las variables de entrada y globales:

    //+------------------------------------------------------------------+
    //|                                              TestTrendStdDev.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    //--- enums
    enum ENUM_LINE_STATE
      {
       LINE_STATE_NONE,        // Undefined state
       LINE_STATE_UP,          // Upward
       LINE_STATE_DOWN,        // Downward
       LINE_STATE_TURN_UP,     // Upward reversal
       LINE_STATE_TURN_DOWN,   // Downward reversal
       LINE_STATE_STOP_UP,     // Upward stop
       LINE_STATE_STOP_DOWN,   // Downward stop
       LINE_STATE_ABOVE,       // Above value
       LINE_STATE_UNDER,       // Below value
       LINE_STATE_CROSS_UP,    // Crossing value upwards
       LINE_STATE_CROSS_DOWN,  // Crossing value downwards
       LINE_STATE_TOUCH_BELOW, // Touching value from below 
       LINE_STATE_TOUCH_ABOVE, // Touch value from above
       LINE_STATE_EQUALS,      // Equal to value
      };
    //--- input parameters
    input uint                 InpPeriod=  20;            /* Period         */
    input int                  InpShift =  0;             /* StdDev Shift   */
    input ENUM_MA_METHOD       InpMethod=  MODE_SMA;      /* Method         */
    input ENUM_APPLIED_PRICE   InpPrice =  PRICE_CLOSE;   /* Applied Price  */
    //--- global variables
    int      handle=INVALID_HANDLE;  // Indicator handle
    int      period=0;               // Standard Deviation calculation period
    int      ind_digits=0;           // Number of decimal places in the indicator values
    string   ind_title;              // Indicator description
    
    

    Al utilizar el panel informativo en el asesor, también deberemos declarar las variables globales para él y conectar el archivo de la clase de panel:

    //+------------------------------------------------------------------+
    //|                                              TestTrendStdDev.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    //--- includes
    #include <Dashboard\Dashboard.mqh>
    //--- enums
    enum ENUM_LINE_STATE
      {
       LINE_STATE_NONE,        // Undefined state
       LINE_STATE_UP,          // Upward
       LINE_STATE_DOWN,        // Downward
       LINE_STATE_TURN_UP,     // Upward reversal
       LINE_STATE_TURN_DOWN,   // Downward reversal
       LINE_STATE_STOP_UP,     // Upward stop
       LINE_STATE_STOP_DOWN,   // Downward stop
       LINE_STATE_ABOVE,       // Above value
       LINE_STATE_UNDER,       // Below value
       LINE_STATE_CROSS_UP,    // Crossing value upwards
       LINE_STATE_CROSS_DOWN,  // Crossing value downwards
       LINE_STATE_TOUCH_BELOW, // Touching value from below 
       LINE_STATE_TOUCH_ABOVE, // Touch value from above
       LINE_STATE_EQUALS,      // Equal to value
      };
    //--- input parameters
    input uint                 InpPeriod=  20;            /* Period         */
    input int                  InpShift =  0;             /* StdDev Shift   */
    input ENUM_MA_METHOD       InpMethod=  MODE_SMA;      /* Method         */
    input ENUM_APPLIED_PRICE   InpPrice =  PRICE_CLOSE;   /* Applied Price  */
    //--- global variables
    int      handle=INVALID_HANDLE;  // Indicator handle
    int      period=0;               // Standard Deviation calculation period
    int      ind_digits=0;           // Number of decimal places in the indicator values
    string   ind_title;              // Indicator description
    //--- variables for the panel
    int      mouse_bar_index;        // Index of the bar the data is taken from
    CDashboard *panel=NULL;          // Pointer to the panel object
    
    


    Inicialización

    Establecimiento de los valores de las variables globales para el indicador y creación de su manejador:

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Indicator
    //--- Set and adjust the calculation period and levels if necessary
       period=int(InpPeriod<1 ? 20 : InpPeriod<2 ? 2 : InpPeriod);
    //--- Set the indicator name and the number of decimal places
       ind_title=StringFormat("StdDev(%lu)",period);
       ind_digits=Digits()+1;
    //--- Create indicator handle
       ResetLastError();
       handle=iStdDev(Symbol(),PERIOD_CURRENT,period,InpShift,InpMethod,InpPrice);
       if(handle==INVALID_HANDLE)
         {
          PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
          return INIT_FAILED;
         }
    
    //--- Successful initialization
       return(INIT_SUCCEEDED);
      }
    
    

    Al utilizar un panel informativo en el asesor, crearemos un panel:

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Indicator
    //--- Set and adjust the calculation period and levels if necessary
       period=int(InpPeriod<1 ? 20 : InpPeriod<2 ? 2 : InpPeriod);
    //--- Set the indicator name and the number of decimal places
       ind_title=StringFormat("StdDev(%lu)",period);
       ind_digits=Digits()+1;
    //--- Create indicator handle
       ResetLastError();
       handle=iStdDev(Symbol(),PERIOD_CURRENT,period,InpShift,InpMethod,InpPrice);
       if(handle==INVALID_HANDLE)
         {
          PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
          return INIT_FAILED;
         }
    
    //--- Dashboard
    //--- Create the panel
       panel=new CDashboard(1,20,20,197,225);
       if(panel==NULL)
         {
          Print("Error. Failed to create panel object");
          return INIT_FAILED;
         }
    //--- Set font parameters
       panel.SetFontParams("Calibri",9);
    //--- Display the panel with the "Symbol, Timeframe description" header text
       panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
    //--- Create a table with ID 0 to display bar data in it
       panel.CreateNewTable(0);
    //--- Draw a table with ID 0 on the panel background
       panel.DrawGrid(0,2,20,6,2,18,96);
    
    //--- Create a table with ID 1 to display indicator data in it
       panel.CreateNewTable(1);
    //--- Get the Y2 table coordinate with ID 0 and
    //--- set the Y1 coordinate for the table with ID 1
       int y1=panel.TableY2(0)+22;
    //--- Draw a table with ID 1 on the panel background
       panel.DrawGrid(1,2,y1,3,2,18,96);
       
    //--- Display tabular data in the journal
       panel.GridPrint(0,2);
       panel.GridPrint(1,2);
    //--- Initialize the variable with the index of the mouse cursor bar
       mouse_bar_index=0;
    //--- Display the data of the current bar on the panel
       DrawData(mouse_bar_index,TimeCurrent());
    
    //--- Successful initialization
       return(INIT_SUCCEEDED);
      }
    
    


    Desinicialización

    En el manejador OnDeinit() del asesor liberaremos el manejador del indicador:

    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
      {
    //--- destroy timer
       EventKillTimer();
       
    //--- Release handle of the indicator
       ResetLastError();
       if(!IndicatorRelease(handle))
          PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
    //--- Clear all comments on the chart
       Comment("");
      }
    
    

    Al utilizar un panel informativo, eliminaremos el objeto de panel creado:

    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
      {
    //--- destroy timer
       EventKillTimer();
       
    //--- Release handle of the indicator
       ResetLastError();
       if(!IndicatorRelease(handle))
          PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
    //--- Clear all comments on the chart
       Comment("");
       
    //--- If the panel object exists, delete it
       if(panel!=NULL)
          delete panel;
      }
    
    


    Obtención de datos

    Funciones generales para obtener los datos según el manejador del indicador:

    //+------------------------------------------------------------------+
    //| Return the indicator data on the specified bar                   |
    //+------------------------------------------------------------------+
    double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
      {
       double array[1]={0};
       ResetLastError();
       if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
         {
          PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
          return EMPTY_VALUE;
         }
       return array[0];
      }
    //+------------------------------------------------------------------+
    //| Return the state of the indicator line                           |
    //+------------------------------------------------------------------+
    ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
      {
    //--- Get the values of the indicator line with the shift (0,1,2) relative to the passed index
       const double value0=IndicatorValue(ind_handle,index,  buffer_num);
       const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
       const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
    //--- If at least one of the values could not be obtained, return an undefined value 
       if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
          return LINE_STATE_NONE;
    //--- Line upward reversal (value2>value1 && value0>value1)
       if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
          return LINE_STATE_TURN_UP;
    //--- Line upward direction (value2<=value1 && value0>value1)
       else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
          return LINE_STATE_UP;
    //--- Line upward stop (value2<=value1 && value0==value1)
       else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
          return LINE_STATE_STOP_UP;
    //--- Line downward reversal (value2<value1 && value0<value1)
       if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
          return LINE_STATE_TURN_DOWN;
    //--- Line downward direction (value2>=value1 && value0<value1)
       else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
          return LINE_STATE_DOWN;
    //--- Line downward stop (value2>=value1 && value0==value1)
       else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
          return LINE_STATE_STOP_DOWN;
    //--- Undefined state
       return LINE_STATE_NONE;
      }
    //+------------------------------------------------------------------+
    //| Return the state of the line relative to the specified level     |
    //+------------------------------------------------------------------+
    ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
      {
    //--- Get the values of the indicator line with the shift (0,1) relative to the passed index
       const double value0=IndicatorValue(ind_handle,index,  buffer_num);
       const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
    //--- If at least one of the values could not be obtained, return an undefined value 
       if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
          return LINE_STATE_NONE;
    //--- Define the second level to compare
       double level=(level1==EMPTY_VALUE ? level0 : level1);
    //--- The line is below the level (value1<level && value0<level0)
       if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
          return LINE_STATE_UNDER;
    //--- The line is above the level (value1>level && value0>level0)
       if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
          return LINE_STATE_ABOVE;
    //--- The line crossed the level upwards (value1<=level && value0>level0)
       if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
          return LINE_STATE_CROSS_UP;
    //--- The line crossed the level downwards (value1>=level && value0<level0)
       if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
          return LINE_STATE_CROSS_DOWN;
    //--- The line touched the level from below (value1<level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_TOUCH_BELOW;
    //--- The line touched the level from above (value1>level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_TOUCH_BELOW;
    //--- Line is equal to the level value (value1==level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_EQUALS;
    //--- Undefined state
       return LINE_STATE_NONE;
      }
    //+------------------------------------------------------------------+
    //| Return the indicator line state description                      |
    //+------------------------------------------------------------------+
    string LineStateDescription(const ENUM_LINE_STATE state)
      {
       switch(state)
         {
          case LINE_STATE_UP         :  return "Up";
          case LINE_STATE_STOP_UP    :  return "Stop Up";
          case LINE_STATE_TURN_UP    :  return "Turn Up";
          case LINE_STATE_DOWN       :  return "Down";
          case LINE_STATE_STOP_DOWN  :  return "Stop Down";
          case LINE_STATE_TURN_DOWN  :  return "Turn Down";
          case LINE_STATE_ABOVE      :  return "Above level";
          case LINE_STATE_UNDER      :  return "Under level";
          case LINE_STATE_CROSS_UP   :  return "Crossing Up";
          case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
          case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
          case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
          case LINE_STATE_EQUALS     :  return "Equals";
          default                    :  return "Unknown";
         }
      }
    
    

    Al utilizar el panel informativo, los datos se mostrarán en el panel utilizando la función:

    //+------------------------------------------------------------------+
    //| Display data from the specified timeseries index to the panel    |
    //+------------------------------------------------------------------+
    void DrawData(const int index,const datetime time)
      {
    //--- Declare the variables to receive data in them
       MqlTick  tick={0};
       MqlRates rates[1];
    
    //--- Exit if unable to get the current prices
       if(!SymbolInfoTick(Symbol(),tick))
          return;
    //--- Exit if unable to get the bar data by the specified index
       if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
          return;
    
    //--- Set font parameters for bar and indicator data headers
       int  size=0;
       uint flags=0;
       uint angle=0;
       string name=panel.FontParams(size,flags,angle);
       panel.SetFontParams(name,9,FW_BOLD);
       panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
       panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
    //--- Set font parameters for bar and indicator data
       panel.SetFontParams(name,9);
    
    //--- Display the data of the specified bar in table 0 on the panel
       panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
       panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
       panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
       panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
       panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
       panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);
    
    //--- Display the indicator data from the specified bar on the panel in table 1
       panel.DrawText(ind_title, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
       double value=IndicatorValue(handle,index,0);
       string value_str=(value!=EMPTY_VALUE ? DoubleToString(value,ind_digits) : "");
       panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,100);
       
    //--- Display a description of the indicator line state
       panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
       ENUM_LINE_STATE state=LineState(handle,index,0);
       panel.DrawText(LineStateDescription(state),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,90);
       
    //--- Redraw the chart to immediately display all changes on the panel
       ChartRedraw(ChartID());
      }
    
    

    El panel muestra el valor de la línea indicadora y su estado.

    Además, al utilizar un panel informativo, el manejador de eventos OnChartEvent() del asesor llamará al manejador de eventos del panel informativo y procesará los eventos para obtener el índice de la barra bajo el cursor:

    //+------------------------------------------------------------------+
    //| ChartEvent function                                              |
    //+------------------------------------------------------------------+
    void OnChartEvent(const int id,
                      const long &lparam,
                      const double &dparam,
                      const string &sparam)
      {
    //--- Handling the panel
    //--- Call the panel event handler
       panel.OnChartEvent(id,lparam,dparam,sparam);
    
    //--- If the cursor moves or a click is made on the chart
       if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
         {
          //--- Declare the variables to record time and price coordinates in them
          datetime time=0;
          double price=0;
          int wnd=0;
          //--- If the cursor coordinates are converted to date and time
          if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
            {
             //--- write the bar index where the cursor is located to a global variable
             mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
             //--- Display the bar data under the cursor on the panel 
             DrawData(mouse_bar_index,time);
            }
         }
    
    //--- If we received a custom event, display the appropriate message in the journal
       if(id>CHARTEVENT_CUSTOM)
         {
          //--- Here we can implement handling a click on the close button on the panel
          PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
         }
      }
    
    

    Tras compilar y ejecutar el asesor en el gráfico, podremos controlar el valor y el estado de la línea de indicador en el panel informativo:


    Podrá ver el archivo del asesor de pruebas "TestTrendStdDev.mq5" en los archivos adjuntos.


    Triple Exponential Moving Average

    El indicador técnico Triple Exponential Moving Average (TEMA) fue desarrollado por Patrcik Malloy y publicado en la revista Technical Analysis of Stocks & Commodities. Su principio de cálculo es el mismo que el del indicador DEMA (Double Exponential Moving Average) El nombre de suavizado exponencial triple no refleja del todo bien el algoritmo que incorpora. Es una mezcla única de media móvil exponencial simple, doble y triple, que ofrece menos latencia que cada una de ellas por separado.

    El indicador TEMA puede usarse en lugar de las medias móviles tradicionales. Puede usarse para suavizar los datos de precios y otros indicadores.



    Parámetros

    La función iTEMA() se utiliza para crear el manejador del indicador:

    Retorna el manejador del indicador Triple Exponential Moving Average. Solo un búfer.

    int  iTEMA(
       string              symbol,            // symbol name
       ENUM_TIMEFRAMES     period,            // period
       int                 ma_period,         // averaging period
       int                 ma_shift,          // horizontal shift of the indicator
       ENUM_APPLIED_PRICE  applied_price      // price type or handle
       );
    

    symbol

    [in]  Nombre simbólico del instrumento sobre cuyos datos se calculará el indicador. NULL indicará el símbolo actual.

    period

    [in] El valor del periodo puede ser uno de los valores de la enumeración ENUM_TIMEFRAMES, 0 indicará el marco temporal actual.

    ma_period

    [in]  Periodo (número de barras) de cálculo del indicador.

    ma_shift

    [in]  Desplazamiento del indicador respecto al gráfico de precios.

    applied_price

    [in]  Precio utilizado. Puede ser cualquiera de las constantes de precio ENUM_APPLIED_PRICE o el manejador de otro indicador.

    Retorna el manejador del indicador técnico especificado, en caso de fallo retorna INVALID_HANDLE. Para liberar la memoria del ordenador de un indicador que ya no se utiliza, se usará la función IndicatorRelease(), a la que se le transmitirá el manejador de este indicador.

    Para crear un indicador en el asesor, declararemos las variables de entrada y globales:

    //+------------------------------------------------------------------+
    //|                                                TestTrendTEMA.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    //--- enums
    enum ENUM_LINE_STATE
      {
       LINE_STATE_NONE,        // Undefined state
       LINE_STATE_UP,          // Upward
       LINE_STATE_DOWN,        // Downward
       LINE_STATE_TURN_UP,     // Upward reversal
       LINE_STATE_TURN_DOWN,   // Downward reversal
       LINE_STATE_STOP_UP,     // Upward stop
       LINE_STATE_STOP_DOWN,   // Downward stop
       LINE_STATE_ABOVE,       // Above value
       LINE_STATE_UNDER,       // Below value
       LINE_STATE_CROSS_UP,    // Crossing value upwards
       LINE_STATE_CROSS_DOWN,  // Crossing value downwards
       LINE_STATE_TOUCH_BELOW, // Touching value from below 
       LINE_STATE_TOUCH_ABOVE, // Touch value from above
       LINE_STATE_EQUALS,      // Equal to value
      };
    //--- input parameters
    input uint                 InpPeriod=  14;            /* Period         */
    input int                  InpShift =  0;             /* TEMA Shift     */
    input ENUM_APPLIED_PRICE   InpPrice =  PRICE_CLOSE;   /* Applied Price  */
    //--- global variables
    int      handle=INVALID_HANDLE;  // Indicator handle
    int      period=0;               // Triple Exponential Moving Average calculation period
    int      ind_digits=0;           // Number of decimal places in the indicator values
    string   ind_title;              // Indicator description
    
    

    Al utilizar el panel informativo en el asesor, también deberemos declarar las variables globales para él y conectar el archivo de la clase de panel:

    //+------------------------------------------------------------------+
    //|                                                TestTrendTEMA.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    //--- includes
    #include <Dashboard\Dashboard.mqh>
    //--- enums
    enum ENUM_LINE_STATE
      {
       LINE_STATE_NONE,        // Undefined state
       LINE_STATE_UP,          // Upward
       LINE_STATE_DOWN,        // Downward
       LINE_STATE_TURN_UP,     // Upward reversal
       LINE_STATE_TURN_DOWN,   // Downward reversal
       LINE_STATE_STOP_UP,     // Upward stop
       LINE_STATE_STOP_DOWN,   // Downward stop
       LINE_STATE_ABOVE,       // Above value
       LINE_STATE_UNDER,       // Below value
       LINE_STATE_CROSS_UP,    // Crossing value upwards
       LINE_STATE_CROSS_DOWN,  // Crossing value downwards
       LINE_STATE_TOUCH_BELOW, // Touching value from below 
       LINE_STATE_TOUCH_ABOVE, // Touch value from above
       LINE_STATE_EQUALS,      // Equal to value
      };
    //--- input parameters
    input uint                 InpPeriod=  14;            /* Period         */
    input int                  InpShift =  0;             /* StdDev Shift   */
    input ENUM_APPLIED_PRICE   InpPrice =  PRICE_CLOSE;   /* Applied Price  */
    //--- global variables
    int      handle=INVALID_HANDLE;  // Indicator handle
    int      period=0;               // Triple Exponential Moving Average calculation period
    int      ind_digits=0;           // Number of decimal places in the indicator values
    string   ind_title;              // Indicator description
    //--- variables for the panel
    int      mouse_bar_index;        // Index of the bar the data is taken from
    CDashboard *panel=NULL;          // Pointer to the panel object
    
    


    Inicialización

    Establecimiento de los valores de las variables globales para el indicador y creación de su manejador:

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Indicator
    //--- Set and adjust the calculation period and levels if necessary
       period=int(InpPeriod<1 ? 14 : InpPeriod);
    //--- Set the indicator name and the number of decimal places
       ind_title=StringFormat("TEMA(%lu)",period);
       ind_digits=Digits()+1;
    //--- Create indicator handle
       ResetLastError();
       handle=iTEMA(Symbol(),PERIOD_CURRENT,period,InpShift,InpPrice);
       if(handle==INVALID_HANDLE)
         {
          PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
          return INIT_FAILED;
         }
    
    //--- Successful initialization
       return(INIT_SUCCEEDED);
      }
    
    

    Al utilizar un panel informativo en el asesor, crearemos un panel:

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Indicator
    //--- Set and adjust the calculation period and levels if necessary
       period=int(InpPeriod<1 ? 14 : InpPeriod);
    //--- Set the indicator name and the number of decimal places
       ind_title=StringFormat("TEMA(%lu)",period);
       ind_digits=Digits()+1;
    //--- Create indicator handle
       ResetLastError();
       handle=iTEMA(Symbol(),PERIOD_CURRENT,period,InpShift,InpPrice);
       if(handle==INVALID_HANDLE)
         {
          PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
          return INIT_FAILED;
         }
    
    //--- Dashboard
    //--- Create the panel
       panel=new CDashboard(1,20,20,197,225);
       if(panel==NULL)
         {
          Print("Error. Failed to create panel object");
          return INIT_FAILED;
         }
    //--- Set font parameters
       panel.SetFontParams("Calibri",9);
    //--- Display the panel with the "Symbol, Timeframe description" header text
       panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
    //--- Create a table with ID 0 to display bar data in it
       panel.CreateNewTable(0);
    //--- Draw a table with ID 0 on the panel background
       panel.DrawGrid(0,2,20,6,2,18,96);
    
    //--- Create a table with ID 1 to display indicator data in it
       panel.CreateNewTable(1);
    //--- Get the Y2 table coordinate with ID 0 and
    //--- set the Y1 coordinate for the table with ID 1
       int y1=panel.TableY2(0)+22;
    //--- Draw a table with ID 1 on the panel background
       panel.DrawGrid(1,2,y1,3,2,18,96);
       
    //--- Display tabular data in the journal
       panel.GridPrint(0,2);
       panel.GridPrint(1,2);
    //--- Initialize the variable with the index of the mouse cursor bar
       mouse_bar_index=0;
    //--- Display the data of the current bar on the panel
       DrawData(mouse_bar_index,TimeCurrent());
    
    //--- Successful initialization
       return(INIT_SUCCEEDED);
      }
    
    


    Desinicialización

    En el manejador OnDeinit() del asesor liberaremos el manejador del indicador:

    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
      {
    //--- destroy timer
       EventKillTimer();
       
    //--- Release handle of the indicator
       ResetLastError();
       if(!IndicatorRelease(handle))
          PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
    //--- Clear all comments on the chart
       Comment("");
      }
    
    

    Al utilizar un panel informativo, eliminaremos el objeto de panel creado:

    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
      {
    //--- destroy timer
       EventKillTimer();
       
    //--- Release handle of the indicator
       ResetLastError();
       if(!IndicatorRelease(handle))
          PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
    //--- Clear all comments on the chart
       Comment("");
       
    //--- If the panel object exists, delete it
       if(panel!=NULL)
          delete panel;
      }
    
    


    Obtención de datos

    Funciones generales para obtener los datos según el manejador del indicador:

    //+------------------------------------------------------------------+
    //| Return the indicator data on the specified bar                   |
    //+------------------------------------------------------------------+
    double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
      {
       double array[1]={0};
       ResetLastError();
       if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
         {
          PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
          return EMPTY_VALUE;
         }
       return array[0];
      }
    //+------------------------------------------------------------------+
    //| Return the state of the indicator line                           |
    //+------------------------------------------------------------------+
    ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
      {
    //--- Get the values of the indicator line with the shift (0,1,2) relative to the passed index
       const double value0=IndicatorValue(ind_handle,index,  buffer_num);
       const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
       const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
    //--- If at least one of the values could not be obtained, return an undefined value 
       if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
          return LINE_STATE_NONE;
    //--- Line upward reversal (value2>value1 && value0>value1)
       if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
          return LINE_STATE_TURN_UP;
    //--- Line upward direction (value2<=value1 && value0>value1)
       else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
          return LINE_STATE_UP;
    //--- Line upward stop (value2<=value1 && value0==value1)
       else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
          return LINE_STATE_STOP_UP;
    //--- Line downward reversal (value2<value1 && value0<value1)
       if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
          return LINE_STATE_TURN_DOWN;
    //--- Line downward direction (value2>=value1 && value0<value1)
       else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
          return LINE_STATE_DOWN;
    //--- Line downward stop (value2>=value1 && value0==value1)
       else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
          return LINE_STATE_STOP_DOWN;
    //--- Undefined state
       return LINE_STATE_NONE;
      }
    //+------------------------------------------------------------------+
    //| Return the state of the line relative to the specified level     |
    //+------------------------------------------------------------------+
    ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
      {
    //--- Get the values of the indicator line with the shift (0,1) relative to the passed index
       const double value0=IndicatorValue(ind_handle,index,  buffer_num);
       const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
    //--- If at least one of the values could not be obtained, return an undefined value 
       if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
          return LINE_STATE_NONE;
    //--- Define the second level to compare
       double level=(level1==EMPTY_VALUE ? level0 : level1);
    //--- The line is below the level (value1<level && value0<level0)
       if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
          return LINE_STATE_UNDER;
    //--- The line is above the level (value1>level && value0>level0)
       if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
          return LINE_STATE_ABOVE;
    //--- The line crossed the level upwards (value1<=level && value0>level0)
       if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
          return LINE_STATE_CROSS_UP;
    //--- The line crossed the level downwards (value1>=level && value0<level0)
       if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
          return LINE_STATE_CROSS_DOWN;
    //--- The line touched the level from below (value1<level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_TOUCH_BELOW;
    //--- The line touched the level from above (value1>level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_TOUCH_BELOW;
    //--- Line is equal to the level value (value1==level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_EQUALS;
    //--- Undefined state
       return LINE_STATE_NONE;
      }
    //+------------------------------------------------------------------+
    //| Return the indicator line state description                      |
    //+------------------------------------------------------------------+
    string LineStateDescription(const ENUM_LINE_STATE state)
      {
       switch(state)
         {
          case LINE_STATE_UP         :  return "Up";
          case LINE_STATE_STOP_UP    :  return "Stop Up";
          case LINE_STATE_TURN_UP    :  return "Turn Up";
          case LINE_STATE_DOWN       :  return "Down";
          case LINE_STATE_STOP_DOWN  :  return "Stop Down";
          case LINE_STATE_TURN_DOWN  :  return "Turn Down";
          case LINE_STATE_ABOVE      :  return "Above level";
          case LINE_STATE_UNDER      :  return "Under level";
          case LINE_STATE_CROSS_UP   :  return "Crossing Up";
          case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
          case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
          case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
          case LINE_STATE_EQUALS     :  return "Equals";
          default                    :  return "Unknown";
         }
      }
    
    

    Al utilizar el panel informativo, los datos se mostrarán en el panel utilizando la función:

    //+------------------------------------------------------------------+
    //| Display data from the specified timeseries index to the panel    |
    //+------------------------------------------------------------------+
    void DrawData(const int index,const datetime time)
      {
    //--- Declare the variables to receive data in them
       MqlTick  tick={0};
       MqlRates rates[1];
    
    //--- Exit if unable to get the current prices
       if(!SymbolInfoTick(Symbol(),tick))
          return;
    //--- Exit if unable to get the bar data by the specified index
       if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
          return;
    
    //--- Set font parameters for bar and indicator data headers
       int  size=0;
       uint flags=0;
       uint angle=0;
       string name=panel.FontParams(size,flags,angle);
       panel.SetFontParams(name,9,FW_BOLD);
       panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
       panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
    //--- Set font parameters for bar and indicator data
       panel.SetFontParams(name,9);
    
    //--- Display the data of the specified bar in table 0 on the panel
       panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
       panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
       panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
       panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
       panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
       panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);
    
    //--- Display the indicator data from the specified bar on the panel in table 1
       panel.DrawText(ind_title, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
       double value=IndicatorValue(handle,index,0);
       string value_str=(value!=EMPTY_VALUE ? DoubleToString(value,ind_digits) : "");
       panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,100);
       
    //--- Display a description of the indicator line state
       panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
       ENUM_LINE_STATE state=LineState(handle,index,0);
       panel.DrawText(LineStateDescription(state),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,90);
       
    //--- Redraw the chart to immediately display all changes on the panel
       ChartRedraw(ChartID());
      }
    
    

    El panel muestra los valores de la línea indicadora y su estado.

    Además, al utilizar un panel informativo, el manejador de eventos OnChartEvent() del asesor llamará al manejador de eventos del panel informativo y procesará los eventos para obtener el índice de la barra bajo el cursor:

    //+------------------------------------------------------------------+
    //| ChartEvent function                                              |
    //+------------------------------------------------------------------+
    void OnChartEvent(const int id,
                      const long &lparam,
                      const double &dparam,
                      const string &sparam)
      {
    //--- Handling the panel
    //--- Call the panel event handler
       panel.OnChartEvent(id,lparam,dparam,sparam);
    
    //--- If the cursor moves or a click is made on the chart
       if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
         {
          //--- Declare the variables to record time and price coordinates in them
          datetime time=0;
          double price=0;
          int wnd=0;
          //--- If the cursor coordinates are converted to date and time
          if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
            {
             //--- write the bar index where the cursor is located to a global variable
             mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
             //--- Display the bar data under the cursor on the panel 
             DrawData(mouse_bar_index,time);
            }
         }
    
    //--- If we received a custom event, display the appropriate message in the journal
       if(id>CHARTEVENT_CUSTOM)
         {
          //--- Here we can implement handling a click on the close button on the panel
          PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
         }
      }
    
    

    Tras compilar y ejecutar el asesor en el gráfico, podremos controlar el valor y el estado de la línea de indicador en el panel informativo:


    Podrá ver el archivo del asesor de pruebas "TestTrendTEMA.mq5" en los archivos adjuntos.


    Variable Index Dynamic Average

    El indicador técnico Media Móvil con Periodo de Promediación Dinámico (VIDYA) fue desarrollado por Tushar Chande y supone una metodología original para el cálculo de la media móvil exponencial (EMA) con un periodo de promediación que cambia dinámicamente. El periodo de promediación depende de la volatilidad del mercado, y como medida de volatilidad se eligió Chande Momentum Oscillator (CMO). Este oscilador mide la relación entre la suma de los incrementos positivos y la suma de los incrementos negativos en un periodo concreto (periodo CMO). El valor CMO se usa como coeficiente para el factor de suavizado EMA. Así, el indicador VIDYA tiene dos parámetros a configurar: el periodo del oscilador CMO y el periodo de suavizado de la media móvil exponencial (periodo EMA).



    Parámetros

    La función iVIDyA() se utiliza para crear el manejador del indicador:

    Retorna el manejador del indicador Variable Index Dynamic Average. Solo un búfer.

    int  iVIDyA(
       string              symbol,            // symbol name
       ENUM_TIMEFRAMES     period,            // period
       int                 cmo_period,        // Chande Momentum period
       int                 ema_period,        // smoothing factor period
       int                 ma_shift,          // horizontal shift of the indicator
       ENUM_APPLIED_PRICE  applied_price      // price type or handle
       );
    

    symbol

    [in]  Nombre simbólico del instrumento sobre cuyos datos se calculará el indicador. NULL indicará el símbolo actual.

    period

    [in] El valor del periodo puede ser uno de los valores de la enumeración ENUM_TIMEFRAMES, 0 indicará el marco temporal actual.

    cmo_period

    [in] Periodo (número de barras) para el cálculo de Chande Momentum Oscillator.

    ema_period

    [in] Periodo (número de barras) de EMA para calcular el factor de suavizado.

    ma_shift

    [in]  Desplazamiento del indicador respecto al gráfico de precios.

    applied_price

    [in]  Precio utilizado. Puede ser cualquiera de las constantes de precio ENUM_APPLIED_PRICE o el manejador de otro indicador.

    Retorna el manejador del indicador técnico especificado, en caso de fallo retorna INVALID_HANDLE. Para liberar la memoria del ordenador de un indicador que ya no se utiliza, se usará la función IndicatorRelease(), a la que se le transmitirá el manejador de este indicador.

    Para crear un indicador en el asesor, declararemos las variables de entrada y globales:

    //+------------------------------------------------------------------+
    //|                                               TestTrendVIDYA.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    //--- enums
    enum ENUM_LINE_STATE
      {
       LINE_STATE_NONE,        // Undefined state
       LINE_STATE_UP,          // Upward
       LINE_STATE_DOWN,        // Downward
       LINE_STATE_TURN_UP,     // Upward reversal
       LINE_STATE_TURN_DOWN,   // Downward reversal
       LINE_STATE_STOP_UP,     // Upward stop
       LINE_STATE_STOP_DOWN,   // Downward stop
       LINE_STATE_ABOVE,       // Above value
       LINE_STATE_UNDER,       // Below value
       LINE_STATE_CROSS_UP,    // Crossing value upwards
       LINE_STATE_CROSS_DOWN,  // Crossing value downwards
       LINE_STATE_TOUCH_BELOW, // Touching value from below 
       LINE_STATE_TOUCH_ABOVE, // Touch value from above
       LINE_STATE_EQUALS,      // Equal to value
      };
    //--- input parameters
    input uint                 InpPeriodCMO=  9;             /* CMO Period     */
    input int                  InpShift    =  0;             /* VIDYA Shift    */
    input uint                 InpPeriodEMA=  12;            /* EMA Period     */
    input ENUM_APPLIED_PRICE   InpPrice    =  PRICE_CLOSE;   /* Applied Price  */
    //--- global variables
    int      handle=INVALID_HANDLE;  // Indicator handle
    int      period_cmo=0;           // CMO calculation period
    int      period_ema=0;           // EMA calculation period
    int      ind_digits=0;           // Number of decimal places in the indicator values
    string   ind_title;              // Indicator description
    
    

    Al utilizar el panel informativo en el asesor, también deberemos declarar las variables globales para él y conectar el archivo de la clase de panel:

    //+------------------------------------------------------------------+
    //|                                               TestTrendVIDYA.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    //--- includes
    #include <Dashboard\Dashboard.mqh>
    //--- enums
    enum ENUM_LINE_STATE
      {
       LINE_STATE_NONE,        // Undefined state
       LINE_STATE_UP,          // Upward
       LINE_STATE_DOWN,        // Downward
       LINE_STATE_TURN_UP,     // Upward reversal
       LINE_STATE_TURN_DOWN,   // Downward reversal
       LINE_STATE_STOP_UP,     // Upward stop
       LINE_STATE_STOP_DOWN,   // Downward stop
       LINE_STATE_ABOVE,       // Above value
       LINE_STATE_UNDER,       // Below value
       LINE_STATE_CROSS_UP,    // Crossing value upwards
       LINE_STATE_CROSS_DOWN,  // Crossing value downwards
       LINE_STATE_TOUCH_BELOW, // Touching value from below 
       LINE_STATE_TOUCH_ABOVE, // Touch value from above
       LINE_STATE_EQUALS,      // Equal to value
      };
    //--- input parameters
    input uint                 InpPeriodCMO=  9;             /* CMO Period     */
    input int                  InpShift    =  0;             /* VIDYA Shift    */
    input uint                 InpPeriodEMA=  12;            /* EMA Period     */
    input ENUM_APPLIED_PRICE   InpPrice    =  PRICE_CLOSE;   /* Applied Price  */
    //--- global variables
    int      handle=INVALID_HANDLE;  // Indicator handle
    int      period_cmo=0;           // CMO calculation period
    int      period_ema=0;           // EMA calculation period
    int      ind_digits=0;           // Number of decimal places in the indicator values
    string   ind_title;              // Indicator description
    //--- variables for the panel
    int      mouse_bar_index;        // Index of the bar the data is taken from
    CDashboard *panel=NULL;          // Pointer to the panel object
    
    


    Inicialización

    Establecimiento de los valores de las variables globales para el indicador y creación de su manejador:

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Indicator
    //--- Set and adjust the calculation period and levels if necessary
       period_cmo=int(InpPeriodCMO<1 ? 9 : InpPeriodCMO);
       period_ema=int(InpPeriodEMA<1 ? 12 : InpPeriodEMA);
    //--- Set the indicator name and the number of decimal places
       ind_title=StringFormat("VIDYA(%lu,%lu)",period_cmo,period_ema);
       ind_digits=Digits()+1;
    //--- Create indicator handle
       ResetLastError();
       handle=iVIDyA(Symbol(),PERIOD_CURRENT,period_cmo,period_ema,InpShift,InpPrice);
       if(handle==INVALID_HANDLE)
         {
          PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
          return INIT_FAILED;
         }
    
    //--- Successful initialization
       return(INIT_SUCCEEDED);
      }
    
    

    Al utilizar un panel informativo en el asesor, crearemos un panel:

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Indicator
    //--- Set and adjust the calculation period and levels if necessary
       period_cmo=int(InpPeriodCMO<1 ? 9 : InpPeriodCMO);
       period_ema=int(InpPeriodEMA<1 ? 12 : InpPeriodEMA);
    //--- Set the indicator name and the number of decimal places
       ind_title=StringFormat("VIDYA(%lu,%lu)",period_cmo,period_ema);
       ind_digits=Digits()+1;
    //--- Create indicator handle
       ResetLastError();
       handle=iVIDyA(Symbol(),PERIOD_CURRENT,period_cmo,period_ema,InpShift,InpPrice);
       if(handle==INVALID_HANDLE)
         {
          PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
          return INIT_FAILED;
         }
    
    //--- Dashboard
    //--- Create the panel
       panel=new CDashboard(1,20,20,197,225);
       if(panel==NULL)
         {
          Print("Error. Failed to create panel object");
          return INIT_FAILED;
         }
    //--- Set font parameters
       panel.SetFontParams("Calibri",9);
    //--- Display the panel with the "Symbol, Timeframe description" header text
       panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
    //--- Create a table with ID 0 to display bar data in it
       panel.CreateNewTable(0);
    //--- Draw a table with ID 0 on the panel background
       panel.DrawGrid(0,2,20,6,2,18,96);
    
    //--- Create a table with ID 1 to display indicator data in it
       panel.CreateNewTable(1);
    //--- Get the Y2 table coordinate with ID 0 and
    //--- set the Y1 coordinate for the table with ID 1
       int y1=panel.TableY2(0)+22;
    //--- Draw a table with ID 1 on the panel background
       panel.DrawGrid(1,2,y1,3,2,18,96);
       
    //--- Display tabular data in the journal
       panel.GridPrint(0,2);
       panel.GridPrint(1,2);
    //--- Initialize the variable with the index of the mouse cursor bar
       mouse_bar_index=0;
    //--- Display the data of the current bar on the panel
       DrawData(mouse_bar_index,TimeCurrent());
    
    //--- Successful initialization
       return(INIT_SUCCEEDED);
      }
    
    


    Desinicialización

    En el manejador OnDeinit() del asesor liberaremos el manejador del indicador:

    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
      {
    //--- destroy timer
       EventKillTimer();
       
    //--- Release handle of the indicator
       ResetLastError();
       if(!IndicatorRelease(handle))
          PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
    //--- Clear all comments on the chart
       Comment("");
      }
    
    

    Al utilizar un panel informativo, eliminaremos el objeto de panel creado:

    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
      {
    //--- destroy timer
       EventKillTimer();
       
    //--- Release handle of the indicator
       ResetLastError();
       if(!IndicatorRelease(handle))
          PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
    //--- Clear all comments on the chart
       Comment("");
       
    //--- If the panel object exists, delete it
       if(panel!=NULL)
          delete panel;
      }
    
    


    Obtención de datos

    Funciones generales para obtener los datos según el manejador del indicador:

    //+------------------------------------------------------------------+
    //| Return the indicator data on the specified bar                   |
    //+------------------------------------------------------------------+
    double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
      {
       double array[1]={0};
       ResetLastError();
       if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
         {
          PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
          return EMPTY_VALUE;
         }
       return array[0];
      }
    //+------------------------------------------------------------------+
    //| Return the state of the indicator line                           |
    //+------------------------------------------------------------------+
    ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
      {
    //--- Get the values of the indicator line with the shift (0,1,2) relative to the passed index
       const double value0=IndicatorValue(ind_handle,index,  buffer_num);
       const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
       const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
    //--- If at least one of the values could not be obtained, return an undefined value 
       if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
          return LINE_STATE_NONE;
    //--- Line upward reversal (value2>value1 && value0>value1)
       if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
          return LINE_STATE_TURN_UP;
    //--- Line upward direction (value2<=value1 && value0>value1)
       else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
          return LINE_STATE_UP;
    //--- Line upward stop (value2<=value1 && value0==value1)
       else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
          return LINE_STATE_STOP_UP;
    //--- Line downward reversal (value2<value1 && value0<value1)
       if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
          return LINE_STATE_TURN_DOWN;
    //--- Line downward direction (value2>=value1 && value0<value1)
       else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
          return LINE_STATE_DOWN;
    //--- Line downward stop (value2>=value1 && value0==value1)
       else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
          return LINE_STATE_STOP_DOWN;
    //--- Undefined state
       return LINE_STATE_NONE;
      }
    //+------------------------------------------------------------------+
    //| Return the state of the line relative to the specified level     |
    //+------------------------------------------------------------------+
    ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
      {
    //--- Get the values of the indicator line with the shift (0,1) relative to the passed index
       const double value0=IndicatorValue(ind_handle,index,  buffer_num);
       const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
    //--- If at least one of the values could not be obtained, return an undefined value 
       if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
          return LINE_STATE_NONE;
    //--- Define the second level to compare
       double level=(level1==EMPTY_VALUE ? level0 : level1);
    //--- The line is below the level (value1<level && value0<level0)
       if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
          return LINE_STATE_UNDER;
    //--- The line is above the level (value1>level && value0>level0)
       if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
          return LINE_STATE_ABOVE;
    //--- The line crossed the level upwards (value1<=level && value0>level0)
       if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
          return LINE_STATE_CROSS_UP;
    //--- The line crossed the level downwards (value1>=level && value0<level0)
       if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
          return LINE_STATE_CROSS_DOWN;
    //--- The line touched the level from below (value1<level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_TOUCH_BELOW;
    //--- The line touched the level from above (value1>level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_TOUCH_BELOW;
    //--- Line is equal to the level value (value1==level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_EQUALS;
    //--- Undefined state
       return LINE_STATE_NONE;
      }
    //+------------------------------------------------------------------+
    //| Return the indicator line state description                      |
    //+------------------------------------------------------------------+
    string LineStateDescription(const ENUM_LINE_STATE state)
      {
       switch(state)
         {
          case LINE_STATE_UP         :  return "Up";
          case LINE_STATE_STOP_UP    :  return "Stop Up";
          case LINE_STATE_TURN_UP    :  return "Turn Up";
          case LINE_STATE_DOWN       :  return "Down";
          case LINE_STATE_STOP_DOWN  :  return "Stop Down";
          case LINE_STATE_TURN_DOWN  :  return "Turn Down";
          case LINE_STATE_ABOVE      :  return "Above level";
          case LINE_STATE_UNDER      :  return "Under level";
          case LINE_STATE_CROSS_UP   :  return "Crossing Up";
          case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
          case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
          case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
          case LINE_STATE_EQUALS     :  return "Equals";
          default                    :  return "Unknown";
         }
      }
    
    

    Al utilizar el panel informativo, los datos se mostrarán en el panel utilizando la función:

    //+------------------------------------------------------------------+
    //| Display data from the specified timeseries index to the panel    |
    //+------------------------------------------------------------------+
    void DrawData(const int index,const datetime time)
      {
    //--- Declare the variables to receive data in them
       MqlTick  tick={0};
       MqlRates rates[1];
    
    //--- Exit if unable to get the current prices
       if(!SymbolInfoTick(Symbol(),tick))
          return;
    //--- Exit if unable to get the bar data by the specified index
       if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
          return;
    
    //--- Set font parameters for bar and indicator data headers
       int  size=0;
       uint flags=0;
       uint angle=0;
       string name=panel.FontParams(size,flags,angle);
       panel.SetFontParams(name,9,FW_BOLD);
       panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
       panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
    //--- Set font parameters for bar and indicator data
       panel.SetFontParams(name,9);
    
    //--- Display the data of the specified bar in table 0 on the panel
       panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
       panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
       panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
       panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
       panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
       panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);
    
    //--- Display the indicator data from the specified bar on the panel in table 1
       panel.DrawText(ind_title, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
       double value=IndicatorValue(handle,index,0);
       string value_str=(value!=EMPTY_VALUE ? DoubleToString(value,ind_digits) : "");
       panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,100);
       
    //--- Display a description of the indicator line state
       panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
       ENUM_LINE_STATE state=LineState(handle,index,0);
       panel.DrawText(LineStateDescription(state),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,90);
       
    //--- Redraw the chart to immediately display all changes on the panel
       ChartRedraw(ChartID());
      }
    
    

    El panel muestra los valores de la línea indicadora y su estado.

    Además, al utilizar un panel informativo, el manejador de eventos OnChartEvent() del asesor llamará al manejador de eventos del panel informativo y procesará los eventos para obtener el índice de la barra bajo el cursor:

    //+------------------------------------------------------------------+
    //| ChartEvent function                                              |
    //+------------------------------------------------------------------+
    void OnChartEvent(const int id,
                      const long &lparam,
                      const double &dparam,
                      const string &sparam)
      {
    //--- Handling the panel
    //--- Call the panel event handler
       panel.OnChartEvent(id,lparam,dparam,sparam);
    
    //--- If the cursor moves or a click is made on the chart
       if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
         {
          //--- Declare the variables to record time and price coordinates in them
          datetime time=0;
          double price=0;
          int wnd=0;
          //--- If the cursor coordinates are converted to date and time
          if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
            {
             //--- write the bar index where the cursor is located to a global variable
             mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
             //--- Display the bar data under the cursor on the panel 
             DrawData(mouse_bar_index,time);
            }
         }
    
    //--- If we received a custom event, display the appropriate message in the journal
       if(id>CHARTEVENT_CUSTOM)
         {
          //--- Here we can implement handling a click on the close button on the panel
          PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
         }
      }
    
    

    Tras compilar y ejecutar el asesor en el gráfico, podremos controlar el valor y el estado de la línea de indicador en el panel informativo:


    Podrá ver el archivo del asesor de pruebas "TestTrendVIDYA.mq5" en los archivos adjuntos.


    Conclusión

    Hoy hemos analizado todos los tipos de indicadores técnicos disponibles en el terminal MetaTrader 5, su conexión con los indicadores y asesores y la obtención de datos. La cuestión de la utilización de indicadores de periodo y símbolo múltiples en los asesores y los indicadores sigue abierta. El próximo artículo estará dedicado a la creación de un conjunto de herramientas para conectar indicadores de periodo y símbolo múltiples a asesores e indicadores, obteniendo datos y señales de ellos.

    Todos los códigos presentados anteriormente pueden copiarse "tal cual" directamente del artículo y usarse en desarrollos propios. Podrá ver todos los asesores de prueba presentados en los archivos adjuntos.


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

    Archivos adjuntos |
    TestTrendADX.mq5 (33.4 KB)
    TestTrendAMA.mq5 (32.34 KB)
    TestTrendBands.mq5 (32.75 KB)
    TestTrendDEMA.mq5 (31.44 KB)
    TestTrendFRAMA.mq5 (31.45 KB)
    TestTrendMA.mq5 (31.57 KB)
    TestTrendSAR.mq5 (32.49 KB)
    TestTrendStdDev.mq5 (31.65 KB)
    TestTrendTEMA.mq5 (31.47 KB)
    TestTrendVIDYA.mq5 (31.89 KB)
    Dashboard.mqh (217.85 KB)
    Indicadores alternativos de riesgo y rentabilidad en MQL5 Indicadores alternativos de riesgo y rentabilidad en MQL5
    En este artículo, presentaremos una aplicación de varias medidas de rentabilidad y riesgo consideradas alternativas al ratio de Sharpe e investigaremos diferentes curvas de capital hipotéticas para analizar sus características.
    Estimamos la rentabilidad futura usando intervalos de confianza Estimamos la rentabilidad futura usando intervalos de confianza
    En este artículo, nos adentraremos en la aplicación de técnicas de bootstrapping como forma de evaluar la rentabilidad futura de una estrategia automatizada.
    La estacionalidad en el mercado de divisas y oportunidades para aprovecharla La estacionalidad en el mercado de divisas y oportunidades para aprovecharla
    Toda persona moderna está familiarizada con el concepto de estacionalidad, por ejemplo, todos estamos acostumbrados al aumento del precio de las verduras frescas en invierno o a la subida del precio del combustible durante las heladas severas, pero pocas personas saben que existen patrones similares en el mercado de divisas.
    Lenguaje de programación visual Drakon: una herramienta de comunicación para desarrolladores y clientes MQL Lenguaje de programación visual Drakon: una herramienta de comunicación para desarrolladores y clientes MQL
    DRAKON es un lenguaje de programación visual especialmente diseñado para simplificar la interacción entre especialistas de distintas ramas (biólogos, físicos, ingenieros...) y programadores en proyectos espaciales rusos (por ejemplo, al crear el complejo "Burán"). En este artículo, hablaremos sobre cómo DRAKON hace que la creación de algoritmos sea accesible e intuitiva, incluso si nunca nos hemos enfrentado al código. Asimismo, también veremos cómo el lenguaje DRAKON ayuda tanto al cliente a explicar sus pensamientos al encargar robots comerciales, como al programador a cometer menos errores en funciones complejas.