English Русский 中文 Español Deutsch 日本語 Português
preview
Template pronti per includere indicatori negli Expert Advisor (Parte 2): Indicatori di Volume e Bill Williams

Template pronti per includere indicatori negli Expert Advisor (Parte 2): Indicatori di Volume e Bill Williams

MetaTrader 5Esempi | 6 maggio 2025, 09:29
34 0
Artyom Trishkin
Artyom Trishkin

Contenuto


Introduzione

L'articolo prosegue l’argomento dei template già pronti per l'utilizzo degli indicatori negli EA. In questa sede esamineremo la connessione agli EA e l'utilizzo degli indicatori di volume e di Bill Williams. Visualizzeremo i dati ricevuti dagli indicatori sul pannello creato nel primo articolo di questa serie. Anche il pannello è stato migliorato. Alla fine dell'articolo, esamineremo brevemente le modifiche e i miglioramenti apportati.

Per ogni indicatore considerato, l'articolo presenterà template già pronti da utilizzare in programmi personalizzati:

  • Variabili di ingresso e globali,
  • Inizializzazione delle variabili e creazione di un handle (gestore) dell’indicatore,
  • Deinizializzazione,
  • Ricezione dei dati dell'indicatore nell’EA,
  • Un esempio di visualizzazione dei dati ottenuti sul pannello.
Questo articolo, come i precedenti e i successivi di questa serie, è solo di riferimento e presenta vantaggi pratici, in quanto ci permette di utilizzare semplicemente i codici dell'articolo come copia-incolla.


Indicatori di Volume

Gli indicatori di volume sono quelli che tengono conto del volume. Per il volume del mercato Forex si intende il numero di ticks (variazioni dei prezzi) che sono apparsi nell'intervallo di tempo. Per volume di titoli azionari si intende il volume dei trades eseguiti (in contratti o in termini monetari).


Accumulation/Distribution

Accumulation Distribution (A/D) è determinato dalle variazioni di prezzo e volume. Il volume agisce come un coefficiente di ponderazione al cambiamento del prezzo — più alto è il coefficiente (il volume), maggiore sarà il contributo della variazione di prezzo (per questo periodo di tempo) nel valore dell'indicatore.

In effetti, questo indicatore è una versione del più comunemente utilizzato On Balance Volume. Entrambi vengono utilizzati per confermare le variazioni di prezzo attraverso la misurazione dei rispettivi volumi di vendita.

Quando l'indicatore Accumulation/Distribution cresce, significa accumulo (acquisto) di un particolare titolo, poiché la maggior parte del volume delle vendite è legata a una tendenza al rialzo dei prezzi. Quando l'indicatore scende, significa distribuzione (vendita) del titolo, in quanto la maggior parte delle vendite si svolge durante il movimento dei prezzi verso il basso.

Le divergenze tra l'indicatore Accumulation/Distribution ed il prezzo del titolo indicano l'imminente variazione dei prezzi. Di regola, in caso di tali divergenze, la tendenza del prezzo si muove nella direzione in cui si muove l’indicatore. Pertanto, se l'indicatore sta crescendo ed il prezzo del titolo è in calo, dovrebbe essere previsto un’inversione del prezzo.



Parametri

La funzione iAD() viene utilizzata per creare l’handle dell'indicatore:

Restituisce l'handle dell'indicatore Accumulation/Distribution. Solo un buffer.

int  iAD(
   string               symbol,             // symbol name
   ENUM_TIMEFRAMES      period,             // period
   ENUM_APPLIED_VOLUME  applied_volume      // type of volume used for calculations
   );

symbol

[in] Il nome del simbolo dello strumento finanziario i cui dati devono essere utilizzati per calcolare l'indicatore. NULL indica il simbolo corrente.

period

[in] Il valore del periodo, può essere uno dei valori dell'enumerazione ENUM_TIMEFRAMES, 0 indica il timeframe corrente.

applied_volume

[in] Volume utilizzato. Uno qualsiasi di ENUM_APPLIED_VOLUME.

Restituisce l'handle dell'indicatore tecnico specificato. Se fallisce, restituisce INVALID_HANDLE. Per liberare la memoria del computer da un indicatore inutilizzato, utilizzare IndicatorRelease() a cui viene passato l'handle dell'indicatore.

Dichiarare le variabili di input e globali nell'EA per creare l'indicatore:

//+------------------------------------------------------------------+
//|                                                 TestVolumeAD.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 ENUM_APPLIED_VOLUME  InpVolume   =  VOLUME_TICK;   /* Applied Volume */
//--- global variables
int      handle=INVALID_HANDLE;  // Indicator handle
int      ind_digits=0;           // Number of decimal places in the indicator values
string   ind_title;              // Indicator description

L'enumerazione ENUM_LINE_STATE è stata creata per semplificare l'ottenimento dello stato di una linea dell'indicatore - ovvero la sua forma e posizione rispetto alla linea di un altro indicatore o di un qualsiasi livello.
Per ulteriori informazioni sull'enumerazione, consultare la sezione Parametri ATR dell'articolo precedente.

Quando si utilizza il pannello nell'EA, dichiarare le variabili globali e includere il file della classe del pannello:

//+------------------------------------------------------------------+
//|                                                 TestVolumeAD.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 ENUM_APPLIED_VOLUME  InpVolume   =  VOLUME_TICK;   /* Applied Volume */
//--- global variables
int      handle=INVALID_HANDLE;  // Indicator handle
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


Inizializzazione

Impostazione dei valori delle variabili globali dell'indicatore e creazione del suo handle:

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

//--- Indicator
//--- Set the indicator name and the number of decimal places
   ind_title="A/D";
   ind_digits=0;
//--- Create indicator handle
   ResetLastError();
   handle=iAD(Symbol(),PERIOD_CURRENT,InpVolume);
   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);
  }

Se l'EA prevede l'utilizzo del pannello, crearlo qui:

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

//--- Indicator
//--- Set the indicator name and the number of decimal places
   ind_title="A/D";
   ind_digits=0;
//--- Create indicator handle
   ResetLastError();
   handle=iAD(Symbol(),PERIOD_CURRENT,InpVolume);
   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,199,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,97);

//--- 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,97);
   
//--- 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);
  }
//+------------------------------------------------------------------+


Deinizializzazione

Rilasciare l'handle dell'indicatore nel gestore OnDeinit() dell’EA:

//+------------------------------------------------------------------+
//| 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("");
  }

L'oggetto dashboard creato viene rimosso quando si utilizza il pannello:

//+------------------------------------------------------------------+
//| 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;
  }


Recupero dei dati

Di seguito sono riportate le funzioni generali per ottenere i dati tramite l'handle dell'indicatore. Le funzioni sono state esaminate nell'articolo sul collegamento degli oscillatori agli EA. Le funzioni presentate possono essere utilizzate "così come sono" in programmi personalizzati:

//+------------------------------------------------------------------+
//| 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";
     }
  }

Quando si utilizza il pannello, i dati vengono visualizzati utilizzando la funzione:

//+------------------------------------------------------------------+
//| 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,90);
   
//--- 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());
  }

Inoltre, quando si utilizza il pannello, il gestore degli eventi del pannello viene richiamato nel gestore degli eventi dell’EA OnChartEvent(), così come vengono gestiti gli eventi per la ricezione dell'indice della barra sotto il cursore:

//+------------------------------------------------------------------+
//| 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);
     }
  }

Dopo aver compilato l'EA e averlo lanciato sul grafico, possiamo monitorare lo stato del valore e della linea dell'indicatore sul pannello:


È possibile visualizzare l’EA di test TestVolumeAD.mq5 nei file allegati all'articolo.


Money Flow Index

Money Flow Index (MFI) è un indicatore tecnico che indica la velocità con cui il denaro viene investito in un titolo e poi ritirato. La costruzione e l'interpretazione dell'indicatore sono simili a quelle del Relative Strength Index, con l'unica differenza che il volume è importante per l'MFI.

Nell'analizzare il money flow index occorre tener conto dei seguenti punti:

  • divergenze tra l'indicatore e il movimento dei prezzi. Se i prezzi crescono mentre MFI scende (o viceversa), c'è una grande probabilità di un’inversione del prezzo;
  • Il valore di Money Flow Index, che è più di 80 o meno di 20, segnala di conseguenza un potenziale picco o fondo del mercato.



    Parametri

    La funzione iMFI() viene utilizzata per creare l’handle dell'indicatore:

    int  iMFI(
       string               symbol,             // symbol name
       ENUM_TIMEFRAMES      period,             // period
       int                  ma_period,          // averaging period
       ENUM_APPLIED_VOLUME  applied_volume      // type of volume used for calculations
       );
    

    symbol

    [in] Il nome del simbolo dello strumento finanziario i cui i dati devono essere utilizzati per calcolare l'indicatore. NULL indica il simbolo corrente.

    period

    [in] Il valore del periodo, può essere uno dei valori dell'enumerazione ENUM_TIMEFRAMES, 0 indica il timeframe corrente.

    ma_period

    [in] Periodo (numero di barre) per il calcolo dell'indicatore.

    applied_volume

    [in] Volume utilizzato. Uno qualsiasi di ENUM_APPLIED_VOLUME.

    Restituisce l'handle dell'indicatore tecnico specificato. Se fallisce, restituisce INVALID_HANDLE. Per liberare la memoria del computer da un indicatore inutilizzato, utilizzare IndicatorRelease() a cui viene passato l'handle dell'indicatore.

    Dichiarare le variabili di input e globali nell'EA per creare l'indicatore:

    //+------------------------------------------------------------------+
    //|                                                TestVolumeMFI.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 ENUM_APPLIED_VOLUME  InpVolume   =  VOLUME_TICK;   /* Applied Volume */
    input double               InpOverbough=  80;            /* Overbough level*/
    input double               InpOversold =  20;            /* Oversold level */
    //--- global variables
    int      handle=INVALID_HANDLE;  // Indicator handle
    int      period=0;               // RSI calculation period
    int      ind_digits=0;           // Number of decimal places in the indicator values
    double   overbough=0;            // Overbought level
    double   oversold=0;             // Oversold level
    string   ind_title;              // Indicator description
    
    

    Quando si utilizza il pannello nell'EA, dichiarare le variabili globali e includere il file della classe del pannello:

    //+------------------------------------------------------------------+
    //|                                                TestVolumeMFI.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 ENUM_APPLIED_VOLUME  InpVolume   =  VOLUME_TICK;   /* Applied Volume */
    input double               InpOverbough=  80;            /* Overbough level*/
    input double               InpOversold =  20;            /* Oversold level */
    //--- global variables
    int      handle=INVALID_HANDLE;  // Indicator handle
    int      period=0;               // RSI calculation period
    int      ind_digits=0;           // Number of decimal places in the indicator values
    double   overbough=0;            // Overbought level
    double   oversold=0;             // Oversold level
    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
    
    


    Inizializzazione

    Impostazione dei valori delle variabili globali dell'indicatore e creazione del suo handle:

    //+------------------------------------------------------------------+
    //| 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);
       overbough=InpOverbough;
       oversold=(InpOversold>=overbough ? overbough-0.01 : InpOversold);
    //--- Set the indicator name and the number of decimal places
       ind_title=StringFormat("MFI(%lu)",period);
       ind_digits=Digits();
    //--- Create indicator handle
       ResetLastError();
       handle=iMFI(Symbol(),PERIOD_CURRENT,period,InpVolume);
       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);
      }
    
    

    Se l'EA prevede l'utilizzo del pannello, crearlo qui:

    //+------------------------------------------------------------------+
    //| 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);
       overbough=InpOverbough;
       oversold=(InpOversold>=overbough ? overbough-0.01 : InpOversold);
    //--- Set the indicator name and the number of decimal places
       ind_title=StringFormat("MFI(%lu)",period);
       ind_digits=Digits();
    //--- Create indicator handle
       ResetLastError();
       handle=iMFI(Symbol(),PERIOD_CURRENT,period,InpVolume);
       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,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,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,4,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);
      }
    
    


    Deinizializzazione

    Rilasciare l'handle dell'indicatore nel gestore OnDeinit() dell’EA:

    //+------------------------------------------------------------------+
    //| 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("");
      }
    
    

    L'oggetto dashboard creato viene rimosso quando si utilizza il pannello:

    //+------------------------------------------------------------------+
    //| 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;
      }
    


    Recupero dei dati

    Di seguito sono riportate le funzioni generali per ottenere i dati tramite l'handle dell'indicatore. Le funzioni sono state esaminate nell'articolo sul collegamento degli oscillatori agli EA. Le funzioni presentate possono essere utilizzate "così come sono" in programmi personalizzati:

    //+------------------------------------------------------------------+
    //| 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";
         }
      }
    
    

    Quando si utilizza il pannello, i dati vengono visualizzati utilizzando la funzione:

    //+------------------------------------------------------------------+
    //| 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 relative to the overbought level
       string ovb=StringFormat("%+.2f",overbough);
       panel.DrawText("Overbough", panel.CellX(1,2,0)+2, panel.CellY(1,2,0)+2);
       panel.DrawText(ovb, panel.CellX(1,2,0)+66, panel.CellY(1,2,0)+2);
       ENUM_LINE_STATE state_ovb=LineStateRelative(handle,index,0,overbough);
    //--- The label color changes depending on the value of the line relative to the level
       color clr=(state_ovb==LINE_STATE_CROSS_DOWN ? clrRed : clrNONE);
       string ovb_str=(state_ovb==LINE_STATE_ABOVE ? "Inside the area" : LineStateDescription(state_ovb));
       panel.DrawText(ovb_str,panel.CellX(1,2,1)+2,panel.CellY(1,2,1)+2,clr,100);
       
    //--- Display a description of the indicator line state relative to the oversold level
       panel.DrawText("Oversold", panel.CellX(1,3,0)+2, panel.CellY(1,3,0)+2);
       string ovs=StringFormat("%+.2f",oversold);
       panel.DrawText(ovs, panel.CellX(1,3,0)+68, panel.CellY(1,3,0)+2);
       ENUM_LINE_STATE state_ovs=LineStateRelative(handle,index,0,oversold);
    //--- The label color changes depending on the value of the line relative to the level
       clr=(state_ovs==LINE_STATE_CROSS_UP ? clrBlue : clrNONE);
       string ovs_str=(state_ovs==LINE_STATE_UNDER ? "Inside the area" : LineStateDescription(state_ovs));
       panel.DrawText(ovs_str,panel.CellX(1,3,1)+2,panel.CellY(1,3,1)+2,clr,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);
    //--- The label color changes depending on the location of the line in the overbought/oversold areas
       clr=(state_ovb==LINE_STATE_ABOVE || state_ovb==LINE_STATE_CROSS_DOWN ? clrRed : state_ovs==LINE_STATE_UNDER || state_ovs==LINE_STATE_CROSS_UP ? clrBlue : clrNONE);
       panel.DrawText(LineStateDescription(state),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clr,100);
       
    //--- Redraw the chart to immediately display all changes on the panel
       ChartRedraw(ChartID());
      }
    
    

    La posizione della linea dell'indicatore nelle aree di ipercomprato/ipervenduto è contrassegnata sul pannello con un testo colorato.

    Inoltre, quando si utilizza il pannello, il gestore degli eventi del pannello viene richiamato nel gestore degli eventi dell’EA OnChartEvent(), così come vengono gestiti gli eventi per la ricezione dell'indice della barra sotto il cursore:

    //+------------------------------------------------------------------+
    //| 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);
         }
      }
    
    

    Dopo aver compilato l'EA e averlo lanciato sul grafico, possiamo monitorare lo stato del valore e della linea dell'indicatore sul pannello:


    È possibile visualizzare l’EA di test TestVolumeMFI.mq5 nei file allegati all'articolo.


    On Balance Volume

    ON Balance Volume (OBV) è un indicatore tecnico di momentum che mette in relazione il volume con la variazione del prezzo. L'indicatore, ideato da Joseph Granville, è piuttosto semplice. Se il prezzo di chiusura della barra corrente è superiore a quello della barra precedente, il volume della barra corrente viene aggiunto all'OBV precedente. Se il prezzo di chiusura della barra corrente è inferiore a quello precedente, il volume corrente viene sottratto dal precedente OBV.

    L'assunto di base, per quanto riguarda l'analisi dell'On Balance Volume, è che le variazioni dell'OBV precedono le variazioni di prezzo. La teoria è che lo smart money può essere visto affluire nel titolo da un OBV in aumento. Quando poi il pubblico si sposta nel titolo, sia il titolo che On Balance Volume faranno un balzo in avanti.

    Se il movimento dei prezzi del titolo precede il movimento di OBV, è avvenuta una "non conferma". Le mancate conferme possono verificarsi in corrispondenza dei massimi di mercato toro (quando il titolo sale senza, o prima, dell'OBV) o al fondo del mercato orso (quando il titolo scende senza, o prima, dell'indicatore tecnico On Balance Volume).

    L'OBV è in un trend crescente quando ogni nuovo picco è superiore al picco precedente e ogni nuovo minimo è superiore al precedente. Allo stesso modo, l'On Balance Volume è in un trend ribassista quando ogni picco successivo è inferiore al picco precedente e ogni successivo minimo è inferiore al minimo precedente. Quando l'OBV si muove lateralmente e non fa registrare massimi e minimi consecutivi, si trova in una tendenza dubbia.

    Una volta che una tendenza è stabilita, rimane in forza fino a quando non viene rotta. Ci sono due modi in cui la tendenza dell’On Balance Volume può essere rotta. La prima si verifica quando cambia il trend da un trend al rialzo ad un trend al ribasso o da una trend al ribasso ad un trend al rialzo.

    Il secondo modo in cui la tendenza OBV può essere rotta è se il trend cambia in un trend dubbio e rimane dubbio per più di tre giorni. Pertanto, se il titolo passa da una tendenza al rialzo a una tendenza dubbia e rimane dubbia per soli due giorni prima di tornare a una tendenza al rialzo, si considera che l'On Balance Volume sia sempre stato in una tendenza al rialzo. Quando l'OBV passa a una tendenza al rialzo o al ribasso, si è verificato un "breakout".

    Poiché i breakout dell'OBV normalmente precedono i breakout dei prezzi, gli investitori dovrebbero acquistare long sui breakout al rialzo dell'On Balance Volume. Allo stesso modo, gli investitori dovrebbero vendere short quando l'OBV effettua un breakout al ribasso. Le posizioni devono essere mantenute fino a che il trend non cambia.



    Parametri

    La funzione iOBV() viene utilizzata per creare l’handle dell'indicatore:

    Restituisce l’handle dell'indicatore di On Balance Volume. Solo un buffer.

    int  iOBV(
       string                symbol,             // symbol name
       ENUM_TIMEFRAMES       period,             // period
       ENUM_APPLIED_VOLUME   applied_volume      // type of volume used for calculations
       );
    
    symbol

    [in] Il nome del simbolo dello strumento finanziario i cui i dati devono essere utilizzati per calcolare l'indicatore. NULL indica il simbolo corrente.

    period

    [in] Il valore del periodo, può essere uno dei valori dell'enumerazione ENUM_TIMEFRAMES, 0 indica il timeframe corrente.

    applied_volume

    [in] Volume utilizzato. Uno qualsiasi dei valori dell'enumerazione ENUM_APPLIED_VOLUME.

    Restituisce l'handle dell'indicatore tecnico specificato. Se fallisce, restituisce INVALID_HANDLE. Per liberare la memoria del computer da un indicatore inutilizzato, utilizzare IndicatorRelease() a cui viene passato l'handle dell'indicatore.

    Dichiarare le variabili di input e globali nell'EA per creare l'indicatore:

    //+------------------------------------------------------------------+
    //|                                                TestVolumeOBV.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 ENUM_APPLIED_VOLUME  InpVolume   =  VOLUME_TICK;   /* Applied Volume */
    //--- global variables
    int      handle=INVALID_HANDLE;  // Indicator handle
    int      ind_digits=0;           // Number of decimal places in the indicator values
    string   ind_title;              // Indicator description
    
    

    Quando si utilizza il pannello nell'EA, dichiarare le variabili globali e includere il file della classe del pannello:

    //+------------------------------------------------------------------+
    //|                                                TestVolumeOBV.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 ENUM_APPLIED_VOLUME  InpVolume   =  VOLUME_TICK;   /* Applied Volume */
    //--- global variables
    int      handle=INVALID_HANDLE;  // Indicator handle
    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
    
    


    Inizializzazione

    Impostazione dei valori delle variabili globali dell'indicatore e creazione del suo handle:

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Indicator
    //--- Set the indicator name and the number of decimal places
       ind_title="OBV";
       ind_digits=0;
    //--- Create indicator handle
       ResetLastError();
       handle=iOBV(Symbol(),PERIOD_CURRENT,InpVolume);
       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);
      }
    
    

    Se l'EA prevede l'utilizzo del pannello, crearlo qui:

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Indicator
    //--- Set the indicator name and the number of decimal places
       ind_title="OBV";
       ind_digits=0;
    //--- Create indicator handle
       ResetLastError();
       handle=iOBV(Symbol(),PERIOD_CURRENT,InpVolume);
       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,199,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,97);
    
    //--- 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,97);
       
    //--- 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);
      }
    
    


    Deinizializzazione

    Rilasciare l'handle dell'indicatore nel gestore OnDeinit() dell’EA:

    //+------------------------------------------------------------------+
    //| 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("");
      }
    
    

    L'oggetto dashboard creato viene rimosso quando si utilizza il pannello:

    //+------------------------------------------------------------------+
    //| 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;
      }
    


    Recupero dei dati

    Di seguito sono riportate le funzioni generali per ottenere i dati tramite l'handle dell'indicatore. Le funzioni sono state esaminate nell'articolo sul collegamento degli oscillatori agli EA. Le funzioni presentate possono essere utilizzate "così come sono" in programmi personalizzati:

    //+------------------------------------------------------------------+
    //| 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";
         }
      }
    
    

    Quando si utilizza il pannello, i dati vengono visualizzati utilizzando la funzione:

    //+------------------------------------------------------------------+
    //| 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,90);
       
    //--- 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());
      }
    
    

    Inoltre, quando si utilizza il pannello, il gestore degli eventi del pannello viene richiamato nel gestore degli eventi dell’EA OnChartEvent(), così come vengono gestiti gli eventi per la ricezione dell'indice della barra sotto il cursore:

    //+------------------------------------------------------------------+
    //| 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);
         }
      }
    
    

    Dopo aver compilato l'EA e averlo lanciato sul grafico, possiamo monitorare lo stato del valore e della linea dell'indicatore sul pannello:


    È possibile visualizzare l’EA di test TestVolumeOBV.mq5 nei file allegati all'articolo.


    Volumes

    Per il mercato Forex, l'indicatore Volumes è l'indicatore del numero delle variazioni di prezzo in ogni periodo di un timeframe selezionato. Per i simboli delle azioni questo è un indicatore dei volumi effettivamente scambiati (contratti, denaro, unità, ecc.)



    Parametri

    La funzione iVolumes() viene utilizzata per creare l’handle dell'indicatore:

    Restituisce l'handle dell'indicatore che descrive i volumi. Solo un buffer.

    int  iVolumes(
       string               symbol,             // symbol name
       ENUM_TIMEFRAMES      period,             // period
       ENUM_APPLIED_VOLUME  applied_volume      // volume type
       )
    

    symbol

    [in] Il nome del simbolo dello strumento finanziario i cui i dati devono essere utilizzati per calcolare l'indicatore. NULL significa simbolo attuale.

    period

    [in] Il valore del periodo, può essere uno dei valori dell'enumerazione ENUM_TIMEFRAMES, 0 indica il timeframe corrente.

    applied_volume

    [in] Volume utilizzato. Uno qualsiasi dei valori dell'enumerazione ENUM_APPLIED_VOLUME.

    Restituisce l'handle dell'indicatore tecnico specificato. Se fallisce, restituisce INVALID_HANDLE. Per liberare la memoria del computer da un indicatore inutilizzato, utilizzare IndicatorRelease() a cui viene passato l'handle dell'indicatore.

    Dichiarare le variabili di input e globali nell'EA per creare l'indicatore:

    //+------------------------------------------------------------------+
    //|                                                TestVolumeOBV.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 ENUM_APPLIED_VOLUME  InpVolume   =  VOLUME_TICK;   /* Applied Volume */
    //--- global variables
    int      handle=INVALID_HANDLE;  // Indicator handle
    int      ind_digits=0;           // Number of decimal places in the indicator values
    string   ind_title;              // Indicator description
    
    

    Quando si utilizza il pannello nell'EA, dichiarare le variabili globali e includere il file della classe del pannello:

    //+------------------------------------------------------------------+
    //|                                                TestVolumeOBV.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 ENUM_APPLIED_VOLUME  InpVolume   =  VOLUME_TICK;   /* Applied Volume */
    //--- global variables
    int      handle=INVALID_HANDLE;  // Indicator handle
    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
    
    


    Inizializzazione

    Impostazione dei valori delle variabili globali dell'indicatore e creazione del suo handle:

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Indicator
    //--- Set the indicator name and the number of decimal places
       ind_title="Volumes";
       ind_digits=0;
    //--- Create indicator handle
       ResetLastError();
       handle=iVolumes(Symbol(),PERIOD_CURRENT,InpVolume);
       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);
      }
    
    

    Se l'EA prevede l'utilizzo del pannello, crearlo qui:

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Indicator
    //--- Set the indicator name and the number of decimal places
       ind_title="Volumes";
       ind_digits=0;
    //--- Create indicator handle
       ResetLastError();
       handle=iVolumes(Symbol(),PERIOD_CURRENT,InpVolume);
       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,199,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,97);
    
    //--- 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,97);
       
    //--- 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);
      }
    
    


    Deinizializzazione

    Rilasciare l'handle dell'indicatore nel gestore OnDeinit() dell’EA:

    //+------------------------------------------------------------------+
    //| 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("");
      }
    
    

    L'oggetto dashboard creato viene rimosso quando si utilizza il pannello:

    //+------------------------------------------------------------------+
    //| 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;
      }
    


    Recupero dei dati

    Di seguito sono riportate le funzioni generali per ottenere i dati tramite l'handle dell'indicatore. Le funzioni sono state esaminate nell'articolo sul collegamento degli oscillatori agli EA. Le funzioni presentate possono essere utilizzate "così come sono" in programmi personalizzati:

    //+------------------------------------------------------------------+
    //| 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";
         }
      }
    
    

    Quando si utilizza il pannello, i dati vengono visualizzati utilizzando la funzione:

    //+------------------------------------------------------------------+
    //| 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 value0=IndicatorValue(handle,index,  0);
       double value1=IndicatorValue(handle,index+1,0);
       string value_str=(value0!=EMPTY_VALUE ? DoubleToString(value0,ind_digits) : "");
       color clr=(value0>value1 ? clrGreen : value0<value1 ? clrRed : clrNONE);
       panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,90);
       
    //--- 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,clr,90);
       
    //--- Redraw the chart to immediately display all changes on the panel
       ChartRedraw(ChartID());
      }
    
    

    Il colore del testo dello stato sul pannello corrisponde al colore della colonna dell'indicatore su cui si trova il cursore.

    Inoltre, quando si utilizza il pannello, il gestore degli eventi del pannello viene richiamato nel gestore degli eventi dell’EA OnChartEvent(), così come vengono gestiti gli eventi per la ricezione dell'indice della barra sotto il cursore:

    //+------------------------------------------------------------------+
    //| 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);
         }
      }
    
    

    Dopo aver compilato l'EA e averlo lanciato sul grafico, possiamo monitorare lo stato del valore e della linea dell'indicatore sul pannello:


    È possibile visualizzare l’EA di test TestVolumeVolumes.mq5 nei file allegati all'articolo.


    Indicatori di Bill Williams

    Gli indicatori di Bill Williams sono inclusi in un gruppo separato, perché fanno parte del sistema di trading descritto nei suoi libri.


    Accelerator Oscillator

    Il prezzo è l'ultimo elemento che cambia. Prima delle variazioni di prezzo, la forza motrice del mercato cambia direzione, l'accelerazione della forza motrice deve rallentare e azzerarsi. Quindi, inizia ad accelerare fino a quando il prezzo non inizia a cambiare direzione.

    Accelerazione/Decelerazione, Accelerator/Decelerator Oscillator (AC) misura l'accelerazione e la decelerazione della forza motrice corrente. Questo indicatore cambia direzione prima di qualsiasi cambiamento della forza motrice, che, a sua volta, cambierà la sua direzione prima del prezzo. Se ci si rende conto che l'Accelerazione/Decelerazione è un segnale di allarme anticipato, si ottengono evidenti vantaggi.

    La linea dello zero è fondamentalmente il punto in cui la forza motrice è in equilibrio con l'accelerazione. Se l'accelerazione/decelerazione è superiore a zero, di solito è più facile che l'accelerazione continui il movimento verso l'alto (e viceversa nei casi in cui è inferiore a zero). A differenza dell'Awesome Oscillator, l'attraversamento della linea dello zero non è un segnale. L'unica cosa che deve essere fatta per controllare il mercato e prendere decisioni è guardare i cambiamenti di colore. Per risparmiarti da serie riflessioni, ti è necessario ricordare: non è possibile acquistare con l'aiuto di Acceleration/Deceleration, quando la colonna corrente è colorata in rosso, e non puoi vendere quando la colonna corrente è colorata verde.

    Se si entra nel mercato nella direzione della forza trainante (l'indicatore è superiore a zero, quando si acquista o è inferiore a zero, quando si vende), allora sono sufficienti due colonne verdi per acquistare (due colonne rosse per vendere). Se la forza trainante è diretta contro la posizione da aprire (indicatore sotto lo zero per l'acquisto o sopra lo zero per la vendita), è necessaria una conferma, quindi è necessaria una colonna aggiuntiva. In questo caso l'indicatore deve mostrare tre colonne rosse sopra la linea dello zero per una posizione corta e tre colonne verdi sotto la linea dello zero per una posizione lunga.


    Parametri

    La funzione iAC() viene utilizzata per creare l’handle dell'indicatore:

    Crea l'indicatore Accelerator Oscillator e restituisce il suo handle. Solo un buffer.

    int  iAC(
       string           symbol,     // symbol name
       ENUM_TIMEFRAMES  period      // period
       );
    

    symbol

    [in] Il nome del simbolo dello strumento finanziario i cui i dati devono essere utilizzati per calcolare l'indicatore. NULL indica il simbolo corrente.

    period

    [in] Il valore del periodo, può essere uno dei valori dell'enumerazione ENUM_TIMEFRAMES, 0 indica il timeframe corrente.

    Restituisce l'handle dell'indicatore tecnico specificato. Se fallisce, restituisce INVALID_HANDLE. Per liberare la memoria del computer da un indicatore inutilizzato, utilizzare IndicatorRelease() a cui viene passato l'handle dell'indicatore.

    Dichiarare le variabili globali nell'EA per creare l'indicatore (l'indicatore non ha input, tranne l'impostazione dei colori delle colonne ascendenti e discendenti dell'istogramma ):

    //+------------------------------------------------------------------+
    //|                                               TestWilliamsAC.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
      };
    //--- global variables
    int      handle=INVALID_HANDLE;  // Indicator handle
    int      ind_digits=0;           // Number of decimal places in the indicator values
    string   ind_title;              // Indicator description
    
    

    Quando si utilizza il pannello nell'EA, dichiarare le variabili globali e includere il file della classe del pannello:

    //+------------------------------------------------------------------+
    //|                                               TestWilliamsAC.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
      };
    //--- global variables
    int      handle=INVALID_HANDLE;  // Indicator handle
    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
    
    


    Inizializzazione

    Impostazione dei valori delle variabili globali dell'indicatore e creazione del suo handle:

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Indicator
    //--- Set the indicator name and the number of decimal places
       ind_title="AC";
       ind_digits=Digits()+2;
    //--- Create indicator handle
       ResetLastError();
       handle=iAC(Symbol(),PERIOD_CURRENT);
       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);
      }
    
    

    Se l'EA prevede l'utilizzo del pannello, crearlo qui:

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Indicator
    //--- Set the indicator name and the number of decimal places
       ind_title="AC";
       ind_digits=Digits()+2;
    //--- Create indicator handle
       ResetLastError();
       handle=iAC(Symbol(),PERIOD_CURRENT);
       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,199,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,97);
    
    //--- 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,97);
       
    //--- 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);
      }
    
    


    Deinizializzazione

    Rilasciare l'handle dell'indicatore nel gestore OnDeinit() dell’EA:

    //+------------------------------------------------------------------+
    //| 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("");
      }
    
    

    L'oggetto dashboard creato viene rimosso quando si utilizza il pannello:

    //+------------------------------------------------------------------+
    //| 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;
      }
    


    Recupero dei dati

    Di seguito sono riportate le funzioni generali per ottenere i dati tramite l'handle dell'indicatore. Le funzioni sono state esaminate nell'articolo sul collegamento degli oscillatori agli EA. Le funzioni presentate possono essere utilizzate "così come sono" in programmi personalizzati:

    //+------------------------------------------------------------------+
    //| 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";
         }
      }
    
    

    Quando si utilizza il pannello, i dati vengono visualizzati utilizzando la funzione:

    //+------------------------------------------------------------------+
    //| 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 value0=IndicatorValue(handle,index,  0);
       double value1=IndicatorValue(handle,index+1,0);
       string value_str=(value0!=EMPTY_VALUE ? DoubleToString(value0,ind_digits) : "");
       color clr=(value0>value1 ? clrGreen : value0<value1 ? clrRed : clrNONE);
       panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,90);
       
    //--- 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,clr,90);
       
    //--- Redraw the chart to immediately display all changes on the panel
       ChartRedraw(ChartID());
      }
    
    

    Il colore dei testi di stato delle linee dell’indicatore sul pannello corrisponde al colore delle colonne dell'istogramma su cui si trova il cursore.

    Inoltre, quando si utilizza il pannello, il gestore degli eventi del pannello viene richiamato nel gestore degli eventi dell’EA OnChartEvent(), così come vengono gestiti gli eventi per la ricezione dell'indice della barra sotto il cursore:

    //+------------------------------------------------------------------+
    //| 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);
         }
      }
    
    

    Dopo aver compilato l'EA e averlo lanciato sul grafico, possiamo monitorare lo stato del valore e della linea dell'indicatore sul pannello:


    È possibile visualizzare l’EA di test TestWilliamsAC.mq5 nei file allegati all'articolo.


    Alligator

    La maggior parte del tempo il mercato resta stazionario. Solo per circa il 15-30% del tempo il mercato genera trend e i traders che non si trovano nella borsa stessa traggono la maggior parte dei loro profitti dai trend. Mio nonno ripeteva sempre: "Anche un pollo cieco troverà il suo mais, se gli si dà da mangiare sempre alla stessa ora". Noi chiamiamo il trading sul trend "un mercato di polli ciechi". Ci sono voluti anni, ma abbiamo prodotto un indicatore che ci permette di rimanere sempre all'asciutto fino a quando non raggiungiamo il "mercato dei polli ciechi".
    Bill Williams

    Alligator è una combinazione di Linee di Equilibrio (Medie Mobili), che utilizzano la geometria frattale e la dinamica non lineare.

    • La linea blu (la Mascella dell'Alligatore) è la Linea di Equilibrio per il periodo di tempo utilizzato per costruire il grafico (una media mobile smussata a 13 periodi, spostata di 8 barre nel futuro);
    • La linea rossa (i Denti dell'Alligatore) è la Linea di Equilibrio per un periodo di tempo significativo, inferiore di un ordine ( media mobile smussata a 8 periodi, spostata di 5 barre nel futuro);
    • La linea verde (Labbra dell’Alligatore) è la Linea di Equilibrio per un periodo di tempo significativo, inferiore di un altro ordine ( media mobile smussata a 5 periodi, spostata di 3 barre nel futuro).

    Labbra, Denti e Mascelle dell'Alligatore illustrano l'interazione di diversi periodi temporali. Poiché le tendenze del mercato possono essere identificate solo per il 15-30 percento del tempo, dobbiamo seguire i trend e non lavorare su mercati che fluttuano solo entro determinati periodi di prezzo.

    Quando le Mascelle, i Denti e le Labbra sono chiuse o intrecciate, l'Alligatore sta per addormentarsi o sta già dormendo. Quando dorme, la sua fame aumenta, quindi più dorme, più sarà affamato quando si sveglia. Quando si sveglia, la prima cosa che fa è aprire le fauci e inizia a sbadigliare. Quindi, inizia a sentire l'odore del cibo: carne di un toro o di un orso e inizia a cacciarla. Dopo aver mangiato a sufficienza per sentirsi piuttosto pieno, l’Alligatore comincia a perdere l'interesse per il cibo/prezzo (le Linee di Bilancio si uniscono) - questo è il momento di fissare il profitto.



    Parametri

    La funzione iAlligator() viene utilizzata per creare l’handle dell'indicatore:

    Restituisce l’handle dell'indicatore Alligator.

    int  iAlligator(
       string              symbol,            // symbol name
       ENUM_TIMEFRAMES     period,            // period
       int                 jaw_period,        // period for calculating jaws
       int                 jaw_shift,         // horizontal shift of jaws
       int                 teeth_period,      // period for calculating teeth
       int                 teeth_shift,       // horizontal shift of teeth
       int                 lips_period,       // period for calculating lips
       int                 lips_shift,        // horizontal shift of lips
       ENUM_MA_METHOD      ma_method,         // smoothing type
       ENUM_APPLIED_PRICE  applied_price      // price type or handle
       );
    

    symbol

    [in] Il nome del simbolo dello strumento finanziario i cui i dati devono essere utilizzati per calcolare l'indicatore. NULL indica il simbolo corrente.

    period

    [in] Il valore del periodo, può essere uno dei valori dell'enumerazione ENUM_TIMEFRAMES, 0 indica il timeframe corrente.

    jaw_period

    [in] Periodo di mediazione della linea blu (Mascella dell’Alligatore).

    jaw_shift

    [in] Scostamento della linea blu rispetto al grafico dei prezzi.

    teeth_period

    [in] Periodo di mediazione della linea rossa (Denti dell’Alligatore).

    teeth_shift

    [in] Scostamento della linea rossa rispetto al grafico dei prezzi.

    lips_period

    [in] Periodo di mediazione della linea verde (Labbra dell’Alligatore).

    lips_shift

    [in] Scostamento della linea verde rispetto al grafico dei prezzi.

    ma_method

    [in] Metodo di mediazione. Uno qualsiasi dei valori dell'enumerazione ENUM_MA_METHOD.

    applied_price

    [in] Prezzo applicato. Una qualsiasi delle costanti di prezzo ENUM_APPLIED_PRICE o un altro handle di un indicatore.

    Restituisce l'handle dell'indicatore tecnico specificato. Se fallisce, restituisce INVALID_HANDLE. Per liberare la memoria del computer da un indicatore inutilizzato, utilizzare IndicatorRelease() a cui viene passato l'handle dell'indicatore.

    Indici del buffer: 0 — GATORJAW_LINE, 1 — GATORTEETH_LINE, 2 — GATORLIPS_LINE.


    Dichiarare le variabili di input e globali nell'EA per creare l'indicatore:

    //+------------------------------------------------------------------+
    //|                                        TestWilliamsAlligator.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                 InpPeriodJaws  =  13;            /* Jaws Period    */
    input int                  InpShiftJaws   =  8;             /* Jaws Shift     */
    input uint                 InpPeriodTeeth =  8;             /* Teeth Period   */
    input int                  InpShiftTeeth  =  5;             /* Teeth Shift    */
    input uint                 InpPeriodLips  =  5;             /* Lips Period    */
    input int                  InpShiftLips   =  3;             /* Lips Shift     */
    input ENUM_MA_METHOD       InpMethod      =  MODE_SMMA;     /* Smoothed       */
    input ENUM_APPLIED_PRICE   InpAppliedPrice=  PRICE_MEDIAN;  /* Applied Price  */
    //--- global variables
    int      handle=INVALID_HANDLE;  // Indicator handle
    int      period_jaws=0;          // Jaws line calculation period
    int      period_teeth=0;         // Teeth line calculation period
    int      period_lips=0;          // Lips line calculation period
    int      ind_digits=0;           // Number of decimal places in the indicator values
    string   ind_title;              // Indicator description
    
    

    Quando si utilizza il pannello nell'EA, dichiarare le variabili globali e includere il file della classe del pannello:

    //+------------------------------------------------------------------+
    //|                                        TestWilliamsAlligator.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                 InpPeriodJaws  =  13;            /* Jaws Period    */
    input int                  InpShiftJaws   =  8;             /* Jaws Shift     */
    input uint                 InpPeriodTeeth =  8;             /* Teeth Period   */
    input int                  InpShiftTeeth  =  5;             /* Teeth Shift    */
    input uint                 InpPeriodLips  =  5;             /* Lips Period    */
    input int                  InpShiftLips   =  3;             /* Lips Shift     */
    input ENUM_MA_METHOD       InpMethod      =  MODE_SMMA;     /* Smoothed       */
    input ENUM_APPLIED_PRICE   InpAppliedPrice=  PRICE_MEDIAN;  /* Applied Price  */
    //--- global variables
    int      handle=INVALID_HANDLE;  // Indicator handle
    int      period_jaws=0;          // Jaws line calculation period
    int      period_teeth=0;         // Teeth line calculation period
    int      period_lips=0;          // Lips 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
    
    


    Inizializzazione

    Impostazione dei valori delle variabili globali dell'indicatore e creazione del suo handle:

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Indicator
    //--- Set and adjust the calculation period if necessary
       period_jaws=int(InpPeriodJaws<1 ? 13 : InpPeriodJaws);
       period_teeth=int(InpPeriodTeeth<1 ? 8 : InpPeriodTeeth);
       period_lips=int(InpPeriodLips<1 ? 5 : InpPeriodLips);
    //--- Set the indicator name and the number of decimal places
       ind_title=StringFormat("Alligator(%lu,%lu,%lu)",period_jaws,period_teeth,period_lips);
       ind_digits=Digits();
    //--- Create indicator handle
       ResetLastError();
       handle=iAlligator(Symbol(),PERIOD_CURRENT,period_jaws,InpShiftJaws,period_teeth,InpShiftTeeth,period_lips,InpShiftLips,InpMethod,InpAppliedPrice);
       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);
      }
    
    

    Se l'EA prevede l'utilizzo del pannello, crearlo qui:

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Indicator
    //--- Set and adjust the calculation period if necessary
       period_jaws=int(InpPeriodJaws<1 ? 13 : InpPeriodJaws);
       period_teeth=int(InpPeriodTeeth<1 ? 8 : InpPeriodTeeth);
       period_lips=int(InpPeriodLips<1 ? 5 : InpPeriodLips);
    //--- Set the indicator name and the number of decimal places
       ind_title=StringFormat("Alligator(%lu,%lu,%lu)",period_jaws,period_teeth,period_lips);
       ind_digits=Digits();
    //--- Create indicator handle
       ResetLastError();
       handle=iAlligator(Symbol(),PERIOD_CURRENT,period_jaws,InpShiftJaws,period_teeth,InpShiftTeeth,period_lips,InpShiftLips,InpMethod,InpAppliedPrice);
       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,199,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,97);
    
    //--- 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,97);
       
    //--- 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);
      }
    
    


    Deinizializzazione

    Rilasciare l'handle dell'indicatore nel gestore OnDeinit() dell’EA:

    //+------------------------------------------------------------------+
    //| 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("");
      }
    
    

    L'oggetto dashboard creato viene rimosso quando si utilizza il pannello:

    //+------------------------------------------------------------------+
    //| 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;
      }
    


    Recupero dei dati

    Di seguito sono riportate le funzioni generali per ottenere i dati tramite l'handle dell'indicatore. Le funzioni sono state esaminate nell'articolo sul collegamento degli oscillatori agli EA. Le funzioni presentate possono essere utilizzate "così come sono" in programmi personalizzati:

    //+------------------------------------------------------------------+
    //| 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";
         }
      }
    
    

    Quando si utilizza il pannello, i dati vengono visualizzati utilizzando la funzione:

    //+------------------------------------------------------------------+
    //| 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 lines data
       double value_jaws=IndicatorValue(handle,index,GATORJAW_LINE);
       double value_teeth=IndicatorValue(handle,index,GATORTEETH_LINE);
       double value_lips=IndicatorValue(handle,index,GATORLIPS_LINE);
       
    //--- Display the Jaws line data from the specified bar on the panel in table 1
       string jaws_str=StringFormat("Jaws(%lu)",period_jaws);
       panel.DrawText(jaws_str, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
       string value_str=(value_jaws!=EMPTY_VALUE ? DoubleToString(value_jaws,ind_digits) : "");
       panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,90);
    //--- Display the Teeth line data from the specified bar on the panel in table 1
       string teeth_str=StringFormat("Teeth(%lu)",period_teeth);
       panel.DrawText(teeth_str, panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
       value_str=(value_teeth!=EMPTY_VALUE ? DoubleToString(value_teeth,ind_digits) : "");
       panel.DrawText(value_str,panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,90);
    //--- Display the Lips line data from the specified bar on the panel in table 1
       string lips_str=StringFormat("Lips(%lu)",period_jaws);
       panel.DrawText(lips_str, panel.CellX(1,2,0)+2, panel.CellY(1,2,0)+2);
       value_str=(value_lips!=EMPTY_VALUE ? DoubleToString(value_lips,ind_digits) : "");
       panel.DrawText(value_str,panel.CellX(1,2,1)+2,panel.CellY(1,2,1)+2,clrNONE,90);
       
    //--- Display a description of the Teeth line state relative to the Jaws line
       panel.DrawText("Teeth vs Jaws", panel.CellX(1,3,0)+2, panel.CellY(1,3,0)+2);
       ENUM_LINE_STATE state_tj=LineStateRelative(handle,index,1,value_jaws,IndicatorValue(handle,index+1,GATORJAW_LINE));
       string state_tj_str=
         (
          state_tj==LINE_STATE_ABOVE        ?  "Teeth > Jaws"  : 
          state_tj==LINE_STATE_UNDER        ?  "Teeth < Jaws"  : 
          state_tj==LINE_STATE_TOUCH_ABOVE  || 
          state_tj==LINE_STATE_TOUCH_BELOW  ?  "Touch"     :
          LineStateDescription(state_tj)
         );
    //--- The label color changes depending on the value of the line relative to the level
       color clr=(state_tj==LINE_STATE_CROSS_UP || state_tj==LINE_STATE_ABOVE  ? clrBlue : state_tj==LINE_STATE_CROSS_DOWN || state_tj==LINE_STATE_UNDER ? clrRed : clrNONE);
       panel.DrawText(state_tj_str,panel.CellX(1,3,1)+2,panel.CellY(1,3,1)+2,clr,90);
       
    //--- Display a description of the Lips line state relative to the Teeth line
       panel.DrawText("Lips vs Teeth", panel.CellX(1,4,0)+2, panel.CellY(1,4,0)+2);
       ENUM_LINE_STATE state_lt=LineStateRelative(handle,index,2,value_teeth,IndicatorValue(handle,index+1,GATORTEETH_LINE));
       string state_lt_str=
         (
          state_lt==LINE_STATE_ABOVE        ?  "Lips > Teeth"  : 
          state_lt==LINE_STATE_UNDER        ?  "Lips < Teeth"  : 
          state_lt==LINE_STATE_TOUCH_ABOVE  || 
          state_lt==LINE_STATE_TOUCH_BELOW  ?  "Touch"     :
          LineStateDescription(state_lt)
         );
    //--- The label color changes depending on the value of the line relative to the level
       clr=(state_lt==LINE_STATE_CROSS_UP || state_lt==LINE_STATE_ABOVE  ? clrBlue : state_lt==LINE_STATE_CROSS_DOWN || state_lt==LINE_STATE_UNDER ? clrRed : clrNONE);
       panel.DrawText(state_lt_str,panel.CellX(1,4,1)+2,panel.CellY(1,4,1)+2,clr,90);
       
    //--- Redraw the chart to immediately display all changes on the panel
       ChartRedraw(ChartID());
      }
    
    

    Oltre ai valori delle linee dell’indicatore sulle barre situate sotto il cursore, il pannello visualizza gli stati dei rapporti delle linee Denti - Mascelle e Labbra - Denti. Le loro relazioni sono visualizzate come testo e le loro posizioni relative sono indicate dal colore del testo visualizzato.

    Inoltre, quando si utilizza il pannello, il gestore degli eventi del pannello viene richiamato nel gestore degli eventi dell’EA OnChartEvent(), così come vengono gestiti gli eventi per la ricezione dell'indice della barra sotto il cursore:

    //+------------------------------------------------------------------+
    //| 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);
         }
      }
    
    

    Dopo aver compilato l'EA e averlo lanciato sul grafico, possiamo monitorare lo stato del valore e della linea dell'indicatore sul pannello:


    È possibile visualizzare l’EA di test TestWilliamsAlligator.mq5 nei file allegati all'articolo.


    Awesome Oscillator

    L'Indicatore Tecnico Awesome Oscillator di Bill Williams (AO) è una media mobile semplice a 34 periodi, tracciata attraverso i punti centrali delle barre (H+L)/2, che viene sottratta alla media mobile semplice a 5 periodi, costruita attraverso i punti centrali delle barre (H+L)/2. Ci mostra chiaramente cosa sta succedendo alla forza trainante del mercato al momento attuale.

    Segnali di Acquisto Validi

    Il "piattino" è l'unico segnale di acquisto che arriva quando l'istogramma è al di sopra della linea dello zero. Non dimenticate quanto segue:
    • il segnale del piattino viene generato quando l'istogramma inverte la sua direzione dal basso verso l'alto. La seconda colonna è inferiore alla prima ed è colorata di rosso. La terza colonna è superiore alla seconda ed è colorata di verde;
    • per generare il segnale del piattino, l'istogramma deve avere almeno tre barre.

    Tenete a mente, che tutte le colonne dell'Awesome Oscillator dovrebbero essere sopra la linea dello zero per utilizzare il segnale del piattino.

    La "Zero Line Cross" è un segnale Buy che si forma quando l'istogramma passa da valori negativi a valori positivi. Tenete presente che:

    • per generare questo segnale, sono necessarie solo due colonne;
    • la prima barra deve essere al di sotto della linea dello zero, la seconda deve attraversarla (transizione da un valore negativo a uno positivo);
    • La generazione simultanea di segnali di acquisto e di vendita è impossibile.

    I "picchi gemelli" sono l'unico segnale Buy che può essere generato quando i valori dell'istogramma sono inferiori a zero. Ricordate quanto segue:

    • il segnale viene generato quando si ha un picco che punta verso il basso (il minimo più basso) che si trova al di sotto della linea dello zero ed è seguito da un altro picco che punta verso il basso e che è leggermente più alto (una cifra negativa con un valore assoluto minore, che è quindi più vicino alla linea dello zero), rispetto al picco precedente che punta verso il basso;
    • l'istogramma deve essere al di sotto della linea dello zero tra i due picchi. Se il grafico a barre attraversa la linea dello zero nel tratto compreso tra i due picchi, il segnale di acquisto non funziona. Tuttavia, verrà generato un altro segnale di acquisto - Zero Line Cross;
    • ciascun nuovo picco del grafico a barre deve essere superiore (un numero negativo di minor valore assoluto che è più vicino alla linea dello zero) rispetto al picco precedente;
    • se si forma un picco più alto (che è più vicino alla linea dello zero) ed il grafico a barre non ha varcato la linea dello zero, un ulteriore segnale di acquisto verrà generato.

      Segnali di Vendita

      I segnali di vendita dell'Awesome Oscillator sono identici a quelli di acquisto. Il segnale di origine è invertito e si trova sotto lo zero. La Zero Line Cross è in calo - la prima barra è sopra lo zero, la seconda è sotto. Il segnale dei due picchi è superiore alla linea dello zero ed è anche invertito.



      Parametri

      La funzione iAO() viene utilizzata per creare l'handle dell'indicatore:

      Restituisce l’handle dell'indicatore Awesome Oscillator. Solo un buffer.

      int  iAO(
         string           symbol,     // symbol name
         ENUM_TIMEFRAMES  period      // period
         );
      

      symbol

      [in] Il nome del simbolo dello strumento finanziario i cui i dati devono essere utilizzati per calcolare l'indicatore. NULL indica il simbolo corrente.

      period

      [in] Il valore del periodo, può essere uno dei valori dell'enumerazione ENUM_TIMEFRAMES, 0 indica il timeframe corrente.

      Restituisce l'handle dell'indicatore tecnico specificato. Se fallisce, restituisce INVALID_HANDLE. Per liberare la memoria del computer da un indicatore inutilizzato, utilizzare IndicatorRelease() a cui viene passato l'handle dell'indicatore.


      Dichiarare le variabili di input e globali nell'EA per creare l'indicatore:

      //+------------------------------------------------------------------+
      //|                                               TestWilliamsAO.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
        };
      //--- global variables
      int      handle=INVALID_HANDLE;  // Indicator handle
      int      ind_digits=0;           // Number of decimal places in the indicator values
      string   ind_title;              // Indicator description
      
      

      Quando si utilizza il pannello nell'EA, dichiarare le variabili globali e includere il file della classe del pannello:

      //+------------------------------------------------------------------+
      //|                                               TestWilliamsAO.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
        };
      //--- global variables
      int      handle=INVALID_HANDLE;  // Indicator handle
      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
      
      


      Inizializzazione

      Impostazione dei valori delle variabili globali dell'indicatore e creazione del suo handle:

      //+------------------------------------------------------------------+
      //| Expert initialization function                                   |
      //+------------------------------------------------------------------+
      int OnInit()
        {
      //--- create timer
         EventSetTimer(60);
      
      //--- Indicator
      //--- Set the indicator name and the number of decimal places
         ind_title="AO";
         ind_digits=Digits()+1;
      //--- Create indicator handle
         ResetLastError();
         handle=iAO(Symbol(),PERIOD_CURRENT);
         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);
        }
      
      

      Se l'EA prevede l'utilizzo del pannello, crearlo qui:

      //+------------------------------------------------------------------+
      //| Expert initialization function                                   |
      //+------------------------------------------------------------------+
      int OnInit()
        {
      //--- create timer
         EventSetTimer(60);
      
      //--- Indicator
      //--- Set the indicator name and the number of decimal places
         ind_title="AO";
         ind_digits=Digits()+1;
      //--- Create indicator handle
         ResetLastError();
         handle=iAO(Symbol(),PERIOD_CURRENT);
         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,199,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,97);
      
      //--- 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,97);
         
      //--- 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);
        }
      
      


      Deinizializzazione

      Rilasciare l'handle dell'indicatore nel gestore OnDeinit() dell’EA:

      //+------------------------------------------------------------------+
      //| 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("");
        }
      
      

      L'oggetto dashboard creato viene rimosso quando si utilizza il pannello:

      //+------------------------------------------------------------------+
      //| 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;
        }
      


      Recupero dei dati

      Di seguito sono riportate le funzioni generali per ottenere i dati tramite l'handle dell'indicatore. Le funzioni sono state esaminate nell'articolo sul collegamento degli oscillatori agli EA. Le funzioni presentate possono essere utilizzate "così come sono" in programmi personalizzati:

      //+------------------------------------------------------------------+
      //| 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";
           }
        }
      
      

      Quando si utilizza il pannello, i dati vengono visualizzati utilizzando la funzione:

      //+------------------------------------------------------------------+
      //| 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 value0=IndicatorValue(handle,index,  0);
         double value1=IndicatorValue(handle,index+1,0);
         string value_str=(value0!=EMPTY_VALUE ? DoubleToString(value0,ind_digits) : "");
         color clr=(value0>value1 ? clrGreen : value0<value1 ? clrRed : clrNONE);
         panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,90);
         
      //--- 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,clr,90);
         
      //--- Display a description of the indicator line state relative to zero
         panel.DrawText("AO vs Zero", panel.CellX(1,2,0)+2, panel.CellY(1,2,0)+2);
         ENUM_LINE_STATE state_zero=LineStateRelative(handle,index,0,0);
         string state_zero_str=
           (
            state_zero==LINE_STATE_ABOVE        ?  "AO > 0"  : 
            state_zero==LINE_STATE_UNDER        ?  "AO < 0"  : 
            state_zero==LINE_STATE_TOUCH_ABOVE  || 
            state_zero==LINE_STATE_TOUCH_BELOW  ?  "Touch"     :
            LineStateDescription(state_zero)
           );
      //--- The label color changes depending on the value of the line relative to the level
         clr=(state_zero==LINE_STATE_CROSS_UP ? clrGreen : state_zero==LINE_STATE_CROSS_DOWN ? clrRed : clrNONE);
         panel.DrawText(state_zero_str,panel.CellX(1,2,1)+2,panel.CellY(1,2,1)+2,clr,90);
         
      //--- Redraw the chart to immediately display all changes on the panel
         ChartRedraw(ChartID());
        }
      
      

      Oltre a descrivere lo stato della linea dell'indicatore, che ha il colore della colonna dell'istogramma situata sotto il cursore, il pannello visualizza lo stato della sua posizione rispetto allo zero. Quando la linea dell'indicatore attraversa la linea dello zero verso l'alto, è contrassegnata da un testo verde, mentre la direzione verso il basso è contrassegnata da un testo rosso.

      Inoltre, quando si utilizza il pannello, il gestore degli eventi del pannello viene richiamato nel gestore degli eventi dell’EA OnChartEvent(), così come vengono gestiti gli eventi per la ricezione dell'indice della barra sotto il cursore:

      //+------------------------------------------------------------------+
      //| 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);
           }
        }
      
      

      Dopo aver compilato l'EA e averlo lanciato sul grafico, possiamo monitorare lo stato del valore e della linea dell'indicatore sul pannello:


      È possibile visualizzare l’EA di test TestWilliamsAO.mq5 nei file allegati all'articolo.


      Fractals

      Tutti i mercati sono caratterizzati dal fatto che, per la maggior parte del tempo, i prezzi non oscillano molto e solo per un breve periodo (15-30 percento) si possono osservare cambiamenti di tendenza. I periodi più lucrativi sono di solito i casi in cui i prezzi di mercato cambiano secondo una certa tendenza.

      Il Frattale è uno dei cinque indicatori del sistema di trading di Bill Williams, che permette di individuare il fondo o la cima. La definizione tecnica di frattale ascendente è una serie di almeno cinque barre successive, in cui vi sono due barre, prima e dopo il massimo più alto, che presentano massimi inferiori. Il set di inversione è una serie di almeno cinque barre successive, con il LOW più basso al centro e due LOW più alti da entrambi i lati, correlati al frattale di vendita. Su un grafico i frattali hanno i valori di High e Low e sono indicati da frecce verso l'alto o verso il basso.

      I segnali dell'indicatore tecnicoindicatore tecnico Fractals devono essere filtrati, utilizzando l'indicatore tecnico Alligator. In altre parole, non dovresti chiudere una transazione di acquisto, se il frattale è inferiore ai Denti dell’Alligator e non dovrai chiudere una transazione di vendita, se il frattale è superiore ai Denti dell’Alligator. Dopo che il segnale frattale è stato creato ed è in atto, determinato dalla sua posizione al di là delle Mascelle dell’Alligator, rimane un segnale fino a quando non viene preso o fino a quando emerge un segnale frattale più recente.



      Parametri

      La funzione iFractals() viene utilizzata per creare l’handle dell'indicatore:

      Restituisce l’handle dell'indicatore Fractals.

      int  iFractals(
         string           symbol,     // symbol name
         ENUM_TIMEFRAMES  period      // period
         );
      

      symbol

      [in] Il nome del simbolo dello strumento finanziario i cui i dati devono essere utilizzati per calcolare l'indicatore. NULL indica il simbolo corrente.

      period

      [in] Il valore del periodo, può essere uno dei valori dell'enumerazione ENUM_TIMEFRAMES, 0 indica il timeframe corrente.

      Valore restituito

      Restituisce l'handle dell'indicatore tecnico specificato. Se fallisce, restituisce INVALID_HANDLE. Per liberare la memoria del computer da un indicatore inutilizzato, utilizzare IndicatorRelease() a cui viene passato l'handle dell'indicatore.

      Indici dei buffer: 0 — UPPER_LINE, 1 — LOWER_LINE.


      Dichiarare le variabili di input e globali nell'EA per creare l'indicatore:

      //+------------------------------------------------------------------+
      //|                                         TestWilliamsFractals.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"
      //--- global variables
      int      handle=INVALID_HANDLE;  // Indicator handle
      int      ind_digits=0;           // Number of decimal places in the indicator values
      string   ind_title;              // Indicator description
      
      

      Quando si utilizza il pannello nell'EA, dichiarare le variabili globali e includere il file della classe del pannello:

      //+------------------------------------------------------------------+
      //|                                         TestWilliamsFractals.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>
      //--- global variables
      int      handle=INVALID_HANDLE;  // Indicator handle
      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
      
      


      Inizializzazione

      Impostazione dei valori delle variabili globali dell'indicatore e creazione del suo handle:

      //+------------------------------------------------------------------+
      //| Expert initialization function                                   |
      //+------------------------------------------------------------------+
      int OnInit()
        {
      //--- create timer
         EventSetTimer(60);
      
      //--- Indicator
      //--- Set the indicator name and the number of decimal places
         ind_title="Fractals";
         ind_digits=Digits();
      //--- Create indicator handle
         ResetLastError();
         handle=iFractals(Symbol(),PERIOD_CURRENT);
         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);
        }
      
      

      Se l'EA prevede l'utilizzo del pannello, crearlo qui:

      //+------------------------------------------------------------------+
      //| Expert initialization function                                   |
      //+------------------------------------------------------------------+
      int OnInit()
        {
      //--- create timer
         EventSetTimer(60);
      
      //--- Indicator
      //--- Set the indicator name and the number of decimal places
         ind_title="Fractals";
         ind_digits=Digits();
      //--- Create indicator handle
         ResetLastError();
         handle=iFractals(Symbol(),PERIOD_CURRENT);
         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,199,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,97);
      
      //--- 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,97);
         
      //--- 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);
        }
      
      


      Deinizializzazione

      Rilasciare l'handle dell'indicatore nel gestore OnDeinit() dell’EA:

      //+------------------------------------------------------------------+
      //| 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("");
        }
      
      

      L'oggetto dashboard creato viene rimosso quando si utilizza il pannello:

      //+------------------------------------------------------------------+
      //| 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;
        }
      


      Recupero dei dati

      Di seguito sono riportate le funzioni generali per ottenere i dati tramite l'handle dell'indicatore. Le funzioni sono state esaminate nell'articolo sul collegamento degli oscillatori agli EA. Le funzioni presentate possono essere utilizzate "così come sono" in programmi personalizzati:

      //+------------------------------------------------------------------+
      //| 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";
           }
        }
      
      

      Quando si utilizza il pannello, i dati vengono visualizzati utilizzando la funzione:

      //+------------------------------------------------------------------+
      //| 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 (upper fractal)
         panel.DrawText(ind_title+" Up", panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
         double value0=IndicatorValue(handle,index,UPPER_LINE);
         string value_str0=(value0!=EMPTY_VALUE ? DoubleToString(value0,ind_digits) : " ");
         panel.DrawText(value_str0,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,90);
      
      //--- Display the indicator data from the specified bar on the panel in table 1 (lower fractal)
         panel.DrawText(ind_title+" Down", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
         double value1=IndicatorValue(handle,index,LOWER_LINE);
         string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,ind_digits) : " ");
         panel.DrawText(value_str1,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());
        }
      
      

      Inoltre, quando si utilizza il pannello, il gestore degli eventi del pannello viene richiamato nel gestore degli eventi dell’EA OnChartEvent(), così come vengono gestiti gli eventi per la ricezione dell'indice della barra sotto il cursore:

      //+------------------------------------------------------------------+
      //| 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);
           }
        }
      
      

      Dopo aver compilato e lanciato l'EA sul grafico, possiamo controllare i valori del buffer dell'indicatore sul pannello:


      È possibile visualizzare l’EA di test TestWilliamsFractals.mq5 nei file allegati all'articolo.


      Gator Oscillator

      Gator Oscillator si basa sull’Alligator e mostra il grado di convergenza/divergenza delle sue linee di equilibrio (Smoothed Moving Average). L'istogramma superiore è la differenza assoluta tra i valori delle linee blu e rosse. L'istogramma inferiore è la differenza assoluta tra i valori della linea rossa e della linea verde, ma con il segno meno, poiché l'istogramma è disegnato dall'alto verso il basso.


      Parametri

      La funzione iGator() viene utilizzata per creare l’handle dell'indicatore:

      Restituisce l'handle dell'indicatore Gator. L'oscillatore mostra la differenza tra le linee blu e rosse dell’Alligator (istogramma superiore) e la differenza tra le linee rosse e verdi (istogramma inferiore).

      int  iGator(
         string              symbol,            // symbol name
         ENUM_TIMEFRAMES     period,            // period
         int                 jaw_period,        // period for calculating jaws
         int                 jaw_shift,         // horizontal shift of jaws
         int                 teeth_period,      // period for calculating teeth
         int                 teeth_shift,       // teeth shift
         int                 lips_period,       // period for calculating lips
         int                 lips_shift,        // horizontal shift of lips 
         ENUM_MA_METHOD      ma_method,         // smoothing type
         ENUM_APPLIED_PRICE  applied_price      // price type or handle
         );
      

      symbol

      [in] Il nome del simbolo dello strumento finanziario i cui i dati devono essere utilizzati per calcolare l'indicatore. NULL indica il simbolo corrente.

      period

      [in] Il valore del periodo, può essere uno dei valori dell'enumerazione ENUM_TIMEFRAMES, 0 indica il timeframe corrente.

      jaw_period

      [in] Periodo di mediazione della linea blu (Mascella dell’Alligatore).

      jaw_shift

      [in] Spostamento della linea blu dell'alligator rispetto al grafico dei prezzi. Non si riferisce direttamente allo spostamento visivo dell'istogramma dell'indicatore.

      teeth_period

      [in] Periodo di mediazione della linea rossa (Denti dell’Alligatore).

      teeth_shift

      [in] Spostamento della linea rossa dell'alligator rispetto al grafico dei prezzi. Non si riferisce direttamente allo spostamento visivo dell'istogramma dell'indicatore.

      lips_period

      [in] Periodo di mediazione della linea verde (Labbra dell’Alligatore).

      lips_shift

      [in] Spostamento della linea verde dell'alligator rispetto al grafico dei prezzi. Non si riferisce direttamente allo spostamento visivo dell'istogramma dell'indicatore.

      ma_method

      [in] Metodo di mediazione. Può avere qualsiasi valore dell'enumerazione ENUM_MA_METHOD.

      applied_price

      [in] Prezzo applicato. Una qualsiasi delle costanti di prezzo ENUM_APPLIED_PRICE o un altro handle di un indicatore.

      Restituisce l'handle dell'indicatore tecnico specificato. Se fallisce, restituisce INVALID_HANDLE. Per liberare la memoria del computer da un indicatore inutilizzato, utilizzare IndicatorRelease() a cui viene passato l'handle dell'indicatore.

      Indici del buffer: 0 - UPPER_HISTOGRAM, 1 - colore del buffer dell'istogramma superiore, 2 - LOWER_HISTOGRAM, 3 - colore del buffer dell'istogramma inferiore.


      Dichiarare le variabili di input e globali nell'EA per creare l'indicatore:

      //+------------------------------------------------------------------+
      //|                                            TestWilliamsGator.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                 InpPeriodJaws  =  13;            /* Jaws Period    */
      input int                  InpShiftJaws   =  8;             /* Jaws Shift     */
      input uint                 InpPeriodTeeth =  8;             /* Teeth Period   */
      input int                  InpShiftTeeth  =  5;             /* Teeth Shift    */
      input uint                 InpPeriodLips  =  5;             /* Lips Period    */
      input int                  InpShiftLips   =  3;             /* Lips Shift     */
      input ENUM_MA_METHOD       InpMethod      =  MODE_SMMA;     /* Smoothed       */
      input ENUM_APPLIED_PRICE   InpAppliedPrice=  PRICE_MEDIAN;  /* Applied Price  */
      //--- global variables
      int      handle=INVALID_HANDLE;  // Indicator handle
      int      period_jaws=0;          // Jaws line calculation period
      int      period_teeth=0;         // Teeth line calculation period
      int      period_lips=0;          // Lips line calculation period
      int      ind_digits=0;           // Number of decimal places in the indicator values
      string   ind_title;              // Indicator description
      
      

      Quando si utilizza il pannello nell'EA, dichiarare le variabili globali e includere il file della classe del pannello:

      //+------------------------------------------------------------------+
      //|                                            TestWilliamsGator.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                 InpPeriodJaws  =  13;            /* Jaws Period    */
      input int                  InpShiftJaws   =  8;             /* Jaws Shift     */
      input uint                 InpPeriodTeeth =  8;             /* Teeth Period   */
      input int                  InpShiftTeeth  =  5;             /* Teeth Shift    */
      input uint                 InpPeriodLips  =  5;             /* Lips Period    */
      input int                  InpShiftLips   =  3;             /* Lips Shift     */
      input ENUM_MA_METHOD       InpMethod      =  MODE_SMMA;     /* Smoothed       */
      input ENUM_APPLIED_PRICE   InpAppliedPrice=  PRICE_MEDIAN;  /* Applied Price  */
      //--- global variables
      int      handle=INVALID_HANDLE;  // Indicator handle
      int      period_jaws=0;          // Jaws line calculation period
      int      period_teeth=0;         // Teeth line calculation period
      int      period_lips=0;          // Lips 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
      
      


      Inizializzazione

      Impostazione dei valori delle variabili globali dell'indicatore e creazione del suo handle:

      //+------------------------------------------------------------------+
      //| Expert initialization function                                   |
      //+------------------------------------------------------------------+
      int OnInit()
        {
      //--- create timer
         EventSetTimer(60);
      
      //--- Indicator
      //--- Set and adjust the calculation period if necessary
         period_jaws=int(InpPeriodJaws<1 ? 13 : InpPeriodJaws);
         period_teeth=int(InpPeriodTeeth<1 ? 8 : InpPeriodTeeth);
         period_lips=int(InpPeriodLips<1 ? 5 : InpPeriodLips);
      //--- Set the indicator name and the number of decimal places
         ind_title=StringFormat("Gator(%lu,%lu,%lu)",period_jaws,period_teeth,period_lips);
         ind_digits=Digits()+1;
      //--- Create indicator handle
         ResetLastError();
         handle=iGator(Symbol(),PERIOD_CURRENT,period_jaws,InpShiftJaws,period_teeth,InpShiftTeeth,period_lips,InpShiftLips,InpMethod,InpAppliedPrice);
         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);
        }
      
      

      Se l'EA prevede l'utilizzo del pannello, crearlo qui:

      //+------------------------------------------------------------------+
      //| Expert initialization function                                   |
      //+------------------------------------------------------------------+
      int OnInit()
        {
      //--- create timer
         EventSetTimer(60);
      
      //--- Indicator
      //--- Set and adjust the calculation period if necessary
         period_jaws=int(InpPeriodJaws<1 ? 13 : InpPeriodJaws);
         period_teeth=int(InpPeriodTeeth<1 ? 8 : InpPeriodTeeth);
         period_lips=int(InpPeriodLips<1 ? 5 : InpPeriodLips);
      //--- Set the indicator name and the number of decimal places
         ind_title=StringFormat("Gator(%lu,%lu,%lu)",period_jaws,period_teeth,period_lips);
         ind_digits=Digits()+1;
      //--- Create indicator handle
         ResetLastError();
         handle=iGator(Symbol(),PERIOD_CURRENT,period_jaws,InpShiftJaws,period_teeth,InpShiftTeeth,period_lips,InpShiftLips,InpMethod,InpAppliedPrice);
         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,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,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,3,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);
        }
      
      


      Deinizializzazione

      Rilasciare l'handle dell'indicatore nel gestore OnDeinit() dell’EA:

      //+------------------------------------------------------------------+
      //| 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("");
        }
      
      

      L'oggetto dashboard creato viene rimosso quando si utilizza il pannello:

      //+------------------------------------------------------------------+
      //| 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;
        }
      


      Recupero dei dati

      Di seguito sono riportate le funzioni generali per ottenere i dati tramite l'handle dell'indicatore. Le funzioni sono state esaminate nell'articolo sul collegamento degli oscillatori agli EA. Le funzioni presentate possono essere utilizzate "così come sono" in programmi personalizzati:

      //+------------------------------------------------------------------+
      //| 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";
           }
        }
      
      

      Quando si utilizza il pannello, i dati vengono visualizzati utilizzando la funzione:

      //+------------------------------------------------------------------+
      //| 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 value0=IndicatorValue(handle,index,UPPER_HISTOGRAM); // Upper histogram
         double value1=IndicatorValue(handle,index,1);               // Upper histogram color buffer
         double value2=IndicatorValue(handle,index,LOWER_HISTOGRAM); // Lower histogram
         double value3=IndicatorValue(handle,index,3);               // Lower histogram color buffer
         color clr=clrNONE;
      //--- Display the upper histogram data from the specified bar on the panel in table 1
         panel.DrawText(ind_title+" Up", panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
         string value_str=(value0!=EMPTY_VALUE ? DoubleToString(value0,ind_digits) : "");
         clr=(value1>0 ? clrRed : clrGreen);
         panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clr,100);
         
      //--- Display the lower histogram data from the specified bar on the panel in table 1
         panel.DrawText(ind_title+" Down", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
         value_str=(value2!=EMPTY_VALUE ? DoubleToString(value2,ind_digits) : "");
         clr=(value3>0 ? clrRed : clrGreen);
         panel.DrawText(value_str,panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clr,100);
         
      //--- Redraw the chart to immediately display all changes on the panel
         ChartRedraw(ChartID());
        }
      
      

      Il colore del testo che descrive i valori dei buffer dell'indicatore ha un colore uguale alla colonna corrispondente dell'istogramma dell'indicatore.

      Inoltre, quando si utilizza il pannello, il gestore degli eventi del pannello viene richiamato nel gestore degli eventi dell’EA OnChartEvent(), così come vengono gestiti gli eventi per la ricezione dell'indice della barra sotto il cursore:

      //+------------------------------------------------------------------+
      //| 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);
           }
        }
      
      

      Dopo aver compilato l'EA e averlo lanciato sul grafico, possiamo monitorare lo stato del valore e della linea dell'indicatore sul pannello:



      È possibile visualizzare l’EA di test TestWilliamsGator.mq5 nei file allegati all'articolo.


      Market Facilitation Index

      Market Facilitation Index (BW MFI) è l'indicatore che mostra la variazione di prezzo per un tick. I valori assoluti dell'indicatore non hanno alcun significato così come sono, solo le variazioni dell'indicatore hanno un senso. Bill Williams sottolinea l'intercambiabilità di MFI e del volume:

      • Market Facilitation Index aumenta ed aumenta anche il volume — evidenzia che: a) aumenta il numero di giocatori che arrivano sul mercato (aumenti di volume); b) i nuovi giocatori in arrivo aprono posizioni in direzione dello sviluppo della barra, cioè il movimento è iniziato e raccoglie velocità.
      • Market Facilitation Index scende e scende anche il volume. Significa che i partecipanti al mercato non sono più interessati.
      • Market Facilitation Index aumenta, ma il volume diminuisce. È molto probabile che il mercato non sia supportato dal volume dei traders ed il prezzo cambia a causa di speculazioni dei floor traders (agenti broker e dealers).
      • Market Facilitation Index scende, ma il volume aumenta. C'è una battaglia tra tori e orsi, caratterizzata da un grande volume di vendita ed acquisto, ma il prezzo non cambia notevolmente poiché le forze sono uguali. Una delle parti contendenti (acquirenti e venditori) alla fine vincerà la battaglia. Di solito, la rottura di tale barra consente di sapere se questa barra determina la continuazione del trend o annulla il trend. Bill Williams chiama tale barra "curtsying".



      Parametri

      La funzione iBWMFI() viene utilizzata per creare l’handle dell'indicatore:

      Restituisce l'handle dell'indicatore Market Facilitation Index. Solo un buffer.

      int  iBWMFI(
         string               symbol,             // symbol name
         ENUM_TIMEFRAMES      period,             // period
         ENUM_APPLIED_VOLUME  applied_volume      // type of volume used for calculations
         );
      

      symbol

      [in] Il nome del simbolo dello strumento finanziario i cui i dati devono essere utilizzati per calcolare l'indicatore. NULL indica il simbolo corrente.

      period

      [in] Il valore del periodo, può essere uno dei valori dell'enumerazione ENUM_TIMEFRAMES, 0 indica il timeframe corrente.

      applied_volume

      [in] Volume utilizzato. Uno qualsiasi dei valori dell'enumerazione ENUM_APPLIED_VOLUME.

      Restituisce l'handle dell'indicatore tecnico specificato. Se fallisce, restituisce INVALID_HANDLE. Per liberare la memoria del computer da un indicatore inutilizzato, utilizzare IndicatorRelease() a cui viene passato l'handle dell'indicatore.


      Dichiarare le variabili di input e globali nell'EA per creare l'indicatore:

      //+------------------------------------------------------------------+
      //|                                            TestWilliamsBWMFI.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 ENUM_APPLIED_VOLUME  InpVolume   =  VOLUME_TICK;   /* Applied Volume */
      //--- global variables
      int      handle=INVALID_HANDLE;  // Indicator handle
      int      ind_digits=0;           // Number of decimal places in the indicator values
      string   ind_title;              // Indicator description
      
      

      Quando si utilizza il pannello nell'EA, dichiarare le variabili globali e includere il file della classe del pannello:

      //+------------------------------------------------------------------+
      //|                                            TestWilliamsBWMFI.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 ENUM_APPLIED_VOLUME  InpVolume   =  VOLUME_TICK;   /* Applied Volume */
      //--- global variables
      int      handle=INVALID_HANDLE;  // Indicator handle
      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
      
      


      Inizializzazione

      Impostazione dei valori delle variabili globali dell'indicatore e creazione del suo handle:

      //+------------------------------------------------------------------+
      //| Expert initialization function                                   |
      //+------------------------------------------------------------------+
      int OnInit()
        {
      //--- create timer
         EventSetTimer(60);
      
      //--- Indicator
      //--- Set the indicator name and the number of decimal places
         ind_title="BW MFI";
         ind_digits=Digits();
      //--- Create indicator handle
         ResetLastError();
         handle=iBWMFI(Symbol(),PERIOD_CURRENT,InpVolume);
         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);
        }
      
      

      Se l'EA prevede l'utilizzo del pannello, crearlo qui:

      //+------------------------------------------------------------------+
      //| Expert initialization function                                   |
      //+------------------------------------------------------------------+
      int OnInit()
        {
      //--- create timer
         EventSetTimer(60);
      
      //--- Indicator
      //--- Set the indicator name and the number of decimal places
         ind_title="BW MFI";
         ind_digits=Digits();
      //--- Create indicator handle
         ResetLastError();
         handle=iBWMFI(Symbol(),PERIOD_CURRENT,InpVolume);
         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,199,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,97);
      
      //--- 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,97);
         
      //--- 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);
        }
      
      


      Deinizializzazione

      Rilasciare l'handle dell'indicatore nel gestore OnDeinit() dell’EA:

      //+------------------------------------------------------------------+
      //| 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("");
        }
      
      

      L'oggetto dashboard creato viene rimosso quando si utilizza il pannello:

      //+------------------------------------------------------------------+
      //| 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;
        }
      


      Recupero dei dati

      Di seguito sono riportate le funzioni generali per ottenere i dati tramite l'handle dell'indicatore. Le funzioni sono state esaminate nell'articolo sul collegamento degli oscillatori agli EA. Le funzioni presentate possono essere utilizzate "così come sono" in programmi personalizzati:

      //+------------------------------------------------------------------+
      //| 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";
           }
        }
      
      

      Quando si utilizza il pannello, i dati vengono visualizzati utilizzando la funzione:

      //+------------------------------------------------------------------+
      //| 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,90);
         
      //--- Create Volumes indicator handle
         static bool create=false;
         static int hv=INVALID_HANDLE;
         if(!create)
           {
            ResetLastError();
            hv=iVolumes(Symbol(),PERIOD_CURRENT,InpVolume);
            if(hv==INVALID_HANDLE)
              {
               PrintFormat("%s: Failed to create indicator handle Volumes. Error %ld",__FUNCTION__,GetLastError());
               return;
              }
            create=true;
           }
      
      //--- Get Volumes indicator status
         ENUM_LINE_STATE state_vol=LineState(hv,index,0);
      //--- Display a description of the indicator line state
         panel.DrawText("BW MFI State", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
         ENUM_LINE_STATE state=LineState(handle,index,0);
         color clr=clrNONE;
         string state_str=LineStateDescription(state);
         if((state==LINE_STATE_UP || state==LINE_STATE_TURN_UP) && (state_vol==LINE_STATE_UP || state_vol==LINE_STATE_TURN_UP))
           {
            state_str="MFI Up, Vol Up";
            clr=clrGreen;
           }
         if((state==LINE_STATE_DOWN || state==LINE_STATE_TURN_DOWN) && (state_vol==LINE_STATE_DOWN || state_vol==LINE_STATE_TURN_DOWN))
           {
            state_str="MFI Dn, Vol Dn";
            clr=clrSaddleBrown;
           }
         if((state==LINE_STATE_UP || state==LINE_STATE_TURN_UP) && (state_vol==LINE_STATE_DOWN || state_vol==LINE_STATE_TURN_DOWN))
           {
            state_str="MFI Up, Vol Dn";
            clr=clrBlue;
           }
         if((state==LINE_STATE_DOWN || state==LINE_STATE_TURN_DOWN) && (state_vol==LINE_STATE_UP || state_vol==LINE_STATE_TURN_UP))
           {
            state_str="MFI Dn, Vol Up";
            clr=clrLightCoral;
           }
      
      //--- Set font parameters for indicator state data (bold font)
         name=panel.FontParams(size,flags,angle);
         panel.SetFontParams(name,9,FW_BOLD);
         panel.DrawText(state_str,panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clr,90);
      //--- Restore the normal thickness of the panel font
         panel.SetFontParams(name,9);
         
      //--- Redraw the chart to immediately display all changes on the panel
         ChartRedraw(ChartID());
        }
      
      

      È possibile ottenere i dati dell’indicatore BW MFI nel modo consueto, attraverso le funzioni universali qui fornite. Ma per interpretare le letture delle colonne dell'indicatore, abbiamo bisogno di un altro indicatore, l'indicatore Volumes, poiché per colorare le colonne dell'istogramma vengono confrontati due indicatori - il valore della colonna dell'istogramma e il valore del volume rispetto ai loro valori precedenti. Per ottenere il volume nella funzione, creare l'handle dell'indicatore Volumes (una volta al primo accesso) e confrontare gli stati delle linee BW MFI e Volumes. Una descrizione della loro relazione reciproca viene visualizzata sul pannello sotto forma di testo.

      Inoltre, quando si utilizza il pannello, il gestore degli eventi del pannello viene richiamato nel gestore degli eventi dell’EA OnChartEvent(), così come vengono gestiti gli eventi per la ricezione dell'indice della barra sotto il cursore:

      //+------------------------------------------------------------------+
      //| 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);
           }
        }
      
      

      Dopo aver compilato l'EA e averlo lanciato sul grafico, possiamo monitorare lo stato del valore e della linea dell'indicatore sul pannello:

      È possibile visualizzare l’EA di test TestWilliamsBWMFI.mq5 nei file allegati all'articolo.


      Perfezionamento delle classi del pannello. Navigazione

      Negli EA di prova di questa serie, utilizziamo il pannello creato nel primo articolo. È stato possibile creare una tabella nel pannello. Le coordinate della tabella possono essere utilizzate per visualizzare i dati sul pannello. Ora le classi del pannello sono state completate: è possibile creare un numero qualsiasi di tabelle per inserirvi i dati. Ho anche risolto il problema della scomparsa temporanea dei dati del pannello dopo la riduzione della dashboard, cambiando un timeframe ed espandendola nuovamente. Esaminiamo brevemente i cambiamenti effettuati per non tornare sull'argomento delle modifiche apportate alle classi del pannello.

      Ora ogni tabella creata sul pannello può restituire le sue coordinate: X1, Y1 - angolo superiore sinistro, X2 e Y2 - angolo inferiore destro. A ogni targa vengono assegnati un ID e un nome propri, con i quali è possibile accedervi per ottenere i dati.

      La classe dei dati tabulari CTableData presenta ora variabili private e metodi pubblici per scrivere e restituire questi valori:

      //+------------------------------------------------------------------+
      //| Table data class                                                 |
      //+------------------------------------------------------------------+
      class CTableData : public CObject
        {
      private:
         CArrayObj         m_list_rows;               // List of rows
         uint              m_id;                      // Table ID
         int               m_x1;                      // X1 coordinate
         int               m_y1;                      // Y1 coordinate
         int               m_x2;                      // X2 coordinate
         int               m_y2;                      // Y2 coordinate
         int               m_w;                       // Width
         int               m_h;                       // Height
         string            m_name;                    // Table name
      public:
      //--- Set table name
         void              SetName(const string name) { this.m_name=name;              }
      //--- Return table (1) ID and (2) name
         uint              ID(void)             const { return this.m_id;              }
         string            Name(void)           const { return this.m_name;            }
         
      //--- Set coordinate (1) X1, (2) X2
         void              SetX1(const uint x1)       { this.m_x1=(int)x1;             }
         void              SetX2(const uint x2)       { this.m_x2=(int)x2;             }
      //--- Set coordinate (1) Y1, (2) Y2
         void              SetY1(const uint y1)       { this.m_y1=(int)y1;             }
         void              SetY2(const uint y2)       { this.m_y2=(int)y2;             }
      //--- Set table coordinates
         void              SetCoords(const int x1,const int y1,const int x2,const int y2)
                             {
                              this.SetX1(x1);
                              this.SetY1(y1);
                              this.SetX2(x2);
                              this.SetY2(y2);
                             }
         
      //--- Return coordinate (1) X1, (2) X2
         int               X1(void)             const { return this.m_x1;              }
         int               X2(void)             const { return this.m_x2;              }
      //--- Return coordinate (1) Y1, (2) Y2
         int               Y1(void)             const { return this.m_y1;              }
         int               Y2(void)             const { return this.m_y2;              }
         
      //--- Return (1) width and (2) height
         int               Width(void)          const { return this.m_x2-this.m_x1+1;  }
         int               Height(void)         const { return this.m_y2-this.m_y1+1;  }
         
      //--- Return the list of table rows
      

      È stato aggiunto un metodo pubblico che restituisce il numero di celle nella riga specificata:

         int               ColumnsInRow(const int row_index)
                             {
                              //--- If there is no row in the list, return 0
                              if(this.RowsTotal()==0)
                                 return 0;
                              //--- Get a pointer to the specified row and return the number of cells in it
                              CTableRow *row=this.GetRow(row_index);
                              return(row!=NULL ? row.CellsTotal() : 0);
                             }
      //--- Return the total number of cells in the table
      

      Aggiunto un metodo pubblico che restituisce il numero totale di celle della tabella:

      //--- Return the total number of cells in the table
         int               CellsTotal(void)
                             {
                              //--- If there is no row in the list, return 0
                              if(this.RowsTotal()==0)
                                 return 0;
                              //---
                              int num=0;
                              int total=this.RowsTotal();
                              for(int i=0;i<total;i++)
                                 num+=this.ColumnsInRow(i);
                              return num;
                             }
      //--- Clear lists of rows and table cells
      

      In precedenza, si restituiva semplicemente il numero di colonne nella prima riga della tabella, sperando che il loro numero fosse lo stesso in ogni riga. Ora possiamo ottenere il numero totale di celle della tabella - dal numero di celle posizionate in ogni riga della tabella. È anche possibile ottenere un numero di celle in una riga specifica. È quindi possibile creare tabelle non reticolari. La creazione di tabelle con un numero di celle differenti nelle righe non è stata testata per mancanza di richieste nelle attività correnti. Molto probabilmente saranno necessari ulteriori miglioramenti. Ma per ora non c'è bisogno di queste tabelle.

      La classe dispone del metodo virtuale Compare, che consente di confrontare le tabelle in base agli ID (modalità = 0) o ai nomi (modalità != 0):

      //--- Virtual method for comparing two objects
         virtual int       Compare(const CObject *node,const int mode=0) const
                             {
                              const CTableData *compared=node;
                              if(mode==0)
                                 return(this.ID()>compared.ID() ? 1 : this.ID()<compared.ID() ? -1 : 0);
                              else
                                 return(this.Name()==compared.Name() ? 0 : this.Name()>compared.Name() ? 1 : -1);
                             }
      

      L'ID della tabella creata viene ora passato al costruttore parametrico della classe:

      //--- Constructor/destructor
                           CTableData(const uint id) : m_id(id){ this.m_list_rows.Clear(); this.m_name="";  }
                          ~CTableData(void)                    { this.m_list_rows.Clear();                  }
      


      Mentre l'istanza dell'oggetto dati tabulari era dichiarata in precedenza nella classe del pannello, ora dichiariamo l'elenco che contiene i puntatori alle tabelle create nel pannello.

      //+------------------------------------------------------------------+
      //| Dashboard class                                                  |
      //+------------------------------------------------------------------+
      class CDashboard : public CObject
        {
      private:
         CCanvas           m_canvas;                  // Canvas
         CCanvas           m_workspace;               // Work space
         CArrayObj         m_list_table;              // List of tables
         ENUM_PROGRAM_TYPE m_program_type;            // Program type
         ENUM_MOUSE_STATE  m_mouse_state;             // Mouse button status
      

      Dichiarare le variabili per creare i nomi dei file per salvare i pixel dello sfondo e dell'area di lavoro in un file nella sezione privata:

         string            m_name_gv_m;               // Name of the global terminal variable storing the collapsed panel flag 
      
         string            m_name_gv_u;               // Name of the global terminal variable storing the flag of the pinned panel 
         string            m_filename_bg;             // File name to save background pixels 
         string            m_filename_ws;             // File name for saving work space pixels
         
         uint              m_array_wpx[];             // Array of pixels to save/restore the workspace 
         uint              m_array_ppx[];             // Array of pixels to save/restore the panel background 
      

      Sono stati aggiunti e migliorati i metodi per lavorare con i font del pannello e per creare e ottenere le tabelle e le loro coordinate:

      //--- Set default panel font parameters
         void              SetFontParams(const string name,const int size,const uint flags=0,const uint angle=0);
      //--- Return the specified dashboard font parameters
         string            FontParams(int &size,uint &flags,uint &angle);
      //--- Return the specified panel (1) font, (2) size and font flags
         string            FontName(void)    const { return this.m_workspace.FontNameGet();  }
         int               FontSize(void)    const { return this.m_workspace.FontSizeGet();  }
         uint              FontFlags(void)   const { return this.m_workspace.FontFlagsGet(); }
      //--- Display a text message at the specified coordinates
         void              DrawText(const string text,const int x,const int y,const color clr=clrNONE,const int width=WRONG_VALUE,const int height=WRONG_VALUE);
      //--- Create a new table
         bool              CreateNewTable(const int id=WRONG_VALUE);
      //--- Return tabular data object by (1) ID and (2) name
         CTableData       *GetTable(const uint id);
         CTableData       *GetTable(const string name);
      //--- Draw a (1) background grid (2) with automatic cell size
         void              DrawGrid(const uint table_id,const uint x,const uint y,const uint rows,const uint columns,const uint row_size,const uint col_size,const color line_color=clrNONE,bool alternating_color=true);
         void              DrawGridAutoFill(const uint table_id,const uint border,const uint rows,const uint columns,const color line_color=clrNONE,bool alternating_color=true);
      //--- Print grid data (line intersection coordinates)
         void              GridPrint(const uint table_id,const uint indent=0)
                             {
                              CTableData *table=this.GetTable(table_id);
                              if(table==NULL)
                                {
                                 ::PrintFormat("%s: Error. Failed to get table object with id %lu",__FUNCTION__,table_id);
                                 return;
                                }
                              table.Print(indent);
                             }
      //--- Write the X and Y coordinate values of the specified table cell to variables
         void              CellXY(const uint table_id,const uint row,const uint column, int &x, int &y)
                             {
                              CTableData *table=this.GetTable(table_id);
                              if(table==NULL)
                                {
                                 ::PrintFormat("%s: Error. Failed to get table object with id %lu",__FUNCTION__,table_id);
                                 return;
                                }
                              table.CellXY(row,column,x,y);
                             }
      //--- Return the (1) X and (2) Y coordinate of the specified table cell
         int               CellX(const uint table_id,const uint row,const uint column)
                             {
                              CTableData *table=this.GetTable(table_id);
                              if(table==NULL)
                                {
                                 ::PrintFormat("%s: Error. Failed to get table object with id %lu",__FUNCTION__,table_id);
                                 return WRONG_VALUE;
                                }
                              return table.CellX(row,column);
                             }
         int               CellY(const uint table_id,const uint row,const uint column)
                             {
                              CTableData *table=this.GetTable(table_id);
                              if(table==NULL)
                                {
                                 ::PrintFormat("%s: Error. Failed to get table object with id %lu",__FUNCTION__,table_id);
                                 return WRONG_VALUE;
                                }
                              return table.CellY(row,column);
                             }
      //--- Write X1 and Y1, X2 and Y2 coordinate values of the specified table to the variables
         void              TableCoords(const uint table_id,int &x1,int &y1,int &x2,int &y2)
                             {
                              x1=y1=x2=y2=WRONG_VALUE;
                              CTableData *table=this.GetTable(table_id);
                              if(table==NULL)
                                 return;
                              x1=table.X1();
                              y1=table.Y1();
                              x2=table.X2();
                              y2=table.Y2();
                             }
      
      //--- Return the (1) X1, (2) Y1, (3) X2 and (4) Y2 coordinate of the specified table
         int               TableX1(const uint table_id)
                             {
                              CTableData *table=this.GetTable(table_id);
                              return(table!=NULL ? table.X1() : WRONG_VALUE);
                             }
         int               TableY1(const uint table_id)
                             {
                              CTableData *table=this.GetTable(table_id);
                              return(table!=NULL ? table.Y1() : WRONG_VALUE);
                             }
         int               TableX2(const uint table_id)
                             {
                              CTableData *table=this.GetTable(table_id);
                              return(table!=NULL ? table.X2() : WRONG_VALUE);
                             }
         int               TableY2(const uint table_id)
                             {
                              CTableData *table=this.GetTable(table_id);
                              return(table!=NULL ? table.Y2() : WRONG_VALUE);
                             }
      
      //--- Event handler
         void              OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
      //--- Constructor/destructor
                           CDashboard(const uint id,const int x,const int y, const int w,const int h,const int wnd=-1);
                          ~CDashboard();
      


      Nel costruttore della classe, creare i nomi dei file per salvare lo sfondo e lo spazio di lavoro:

      //--- Set the names of global terminal variables to store panel coordinates, collapsed/expanded state and pinning
         this.m_name_gv_x=this.m_program_name+"_id_"+(string)this.m_id+"_"+(string)this.m_chart_id+"_X";
         this.m_name_gv_y=this.m_program_name+"_id_"+(string)this.m_id+"_"+(string)this.m_chart_id+"_Y";
         this.m_name_gv_m=this.m_program_name+"_id_"+(string)this.m_id+"_"+(string)this.m_chart_id+"_Minimize";
         this.m_name_gv_u=this.m_program_name+"_id_"+(string)this.m_id+"_"+(string)this.m_chart_id+"_Unpin";
      
      //--- Set file names for saving background and work space pixels
         this.m_filename_bg=this.m_program_name+"\\Dashboard"+(string)this.m_id+"\\background.bin";
         this.m_filename_ws=this.m_program_name+"\\Dashboard"+(string)this.m_id+"\\workspace.bin";
         
      

      Alla fine del costruttore, se il pannello viene ridotto, i dati dei file vengono caricati negli array dei pixel dello sfondo e dello spazio di lavoro:

      //--- If the panel collapse flag is set, load the background and work space pixels from the files into arrays
         if(this.m_minimized)
           {
            if(::FileIsExist(this.m_filename_bg))
               this.FileLoadBackground();
            if(::FileIsExist(this.m_filename_ws))
               this.FileLoadWorkspace();
           }
        }
      

      Pertanto, se i pixel sono stati precedentemente salvati in file e il pannello viene creato in forma ridotta, l'aspetto del pannello viene caricato dai file e il pannello viene disegnato in forma ridotta. Quando viene espanso, il suo aspetto sarà ottenuto dagli array di pixel riempiti dai file.

      Nel distruttore, se il pannello è ridotto, prima di eliminare gli oggetti del pannello è necessario espanderlo, scrivere i dati dei pixel nei file e ridurlo di nuovo. Dopo di che, è possibile eliminare gli oggetti del pannello - il suo aspetto è già salvato nei file per essere ripristinato durante la successiva creazione nel costruttore:

      //+------------------------------------------------------------------+
      //| Destructor                                                       |
      //+------------------------------------------------------------------+
      CDashboard::~CDashboard()
        {
      //--- Write the current values to global terminal variables
         ::GlobalVariableSet(this.m_name_gv_x,this.m_x);
         ::GlobalVariableSet(this.m_name_gv_y,this.m_y);
         ::GlobalVariableSet(this.m_name_gv_m,this.m_minimized);
         ::GlobalVariableSet(this.m_name_gv_u,this.m_movable);
      
      //--- If the panel is collapsed,
      //--- expand the panel, save the appearance into pixel arrays and collapse the panel
         if(this.m_minimized)
           {
            this.Expand();
            this.SaveBackground();
            this.SaveWorkspace();
            this.Collapse();
           }
      //--- otherwise, if the panel is expanded,
      //--- save the appearance into pixel arrays
         else
           {
            this.SaveBackground();
            this.SaveWorkspace();
           }
      //--- Save pixel arrays to files
         this.FileSaveBackground();
         this.FileSaveWorkspace();
      
      //--- Delete panel objects
         this.m_canvas.Destroy();
         this.m_workspace.Destroy();
        }
      


      Nel blocco per la gestione dei clic sul pulsante di riduzione/espansione del pannello, selezionare il flag e salvare lo sfondo e lo spazio di lavoro nell’array dei pixel se il pannello viene espanso:

            //--- If the panel collapse/expand button is pressed 
            else if(state==MOUSE_STATE_PRESSED_INSIDE_MINIMIZE)
              {
               //--- Disable chart scrolling, right-click menu and crosshair
               this.SetChartsTool(false);
               //--- If the panel is not collapsed, save the background and work space into pixel arrays 
               if(!this.m_minimized)
                 {
                  this.SaveWorkspace();
                  this.SaveBackground();
                 }
               //--- "flip" the panel collapse flag,
               this.m_minimized=!this.m_minimized;
               //--- redraw the panel taking into account the new state of the flag,
               this.Draw(this.m_title);
               //--- redraw the panel header area 
               this.RedrawHeaderArea();
               //--- If the panel is pinned and expanded, move it to the stored location coordinates
               if(this.m_minimized && !this.m_movable)
                  this.Move(this.m_x_dock,this.m_y_dock);
               //--- Update the canvas with chart redrawing and
               this.m_canvas.Update();
               //--- write the state of the panel expand flag to the global terminal variable
               ::GlobalVariableSet(this.m_name_gv_m,this.m_minimized);
              }
      


      Le stringhe per il salvataggio dell'array dei pixel sono state rimosse dal metodo di riduzione del pannello. Ora il salvataggio dei pixel avviene solo quando si preme il pulsante di minimizzazione/espansione:

      //+------------------------------------------------------------------+
      //| Collapse the panel                                               |
      //+------------------------------------------------------------------+
      void CDashboard::Collapse(void)
        {
      //--- Save the pixels of the working space and the panel background into arrays
         this.SaveWorkspace();
         this.SaveBackground();
      //--- Remember the current height of the panel
         int h=this.m_h;
      //--- Change the dimensions (height) of the canvas and working space
         if(!this.SetSizes(this.m_canvas.Width(),this.m_header_h))
            return;
      //--- Draw the header area
         this.DrawHeaderArea(this.m_title);
      //--- Return the saved panel height to the variable
         this.m_h=h;
        }
      


      Implementazione di un metodo che restituisce i parametri di impostazione del font del pannello:

      //+------------------------------------------------------------------+
      //| Return the specified dashboard font parameters                   |
      //+------------------------------------------------------------------+
      string CDashboard::FontParams(int &size,uint &flags,uint &angle)
        {
         size=this.m_workspace.FontSizeGet();
         flags=this.m_workspace.FontFlagsGet();
         angle=this.m_workspace.FontAngleGet();
         return this.m_workspace.FontNameGet();
        }
      

      Il metodo restituisce il nome del font. La dimensione del carattere, i suoi flag e l'angolo vengono scritti nelle variabili passate dal collegamento.

      Il colore del testo viene ora passato anche al metodo di disegno. L'impostazione predefinita è clrNONE, ovvero il colore del testo impostato in precedenza :

      //+------------------------------------------------------------------+
      //| Display a text message at the specified coordinates              |
      //+------------------------------------------------------------------+
      void CDashboard::DrawText(const string text,const int x,const int y,const color clr=clrNONE,const int width=WRONG_VALUE,const int height=WRONG_VALUE)
        {
      //--- Declare variables to record the text width and height in them
         int w=width;
         int h=height;
      //--- If the width and height of the text passed to the method have zero values,
      //--- then the entire working space is completely cleared using the transparent color
         if(width==0 && height==0)
            this.m_workspace.Erase(0x00FFFFFF);
      //--- Otherwise
         else
           {
            //--- If the passed width and height have default values (-1), we get its width and height from the text 
            if(width==WRONG_VALUE && height==WRONG_VALUE)
               this.m_workspace.TextSize(text,w,h);
            //--- otherwise,
            else
              {
               //--- if the width passed to the method has the default value (-1) - get the width from the text, or
               //--- if the width passed to the method has a value greater than zero, use the width passed to the method, or
               //--- if the width passed to the method has a zero value, use the value 1 for the width
               w=(width ==WRONG_VALUE ? this.m_workspace.TextWidth(text)  : width>0  ? width  : 1);
               //--- if the height passed to the method has a default value (-1), get the height from the text, or
               //--- if the height passed to the method has a value greater than zero, use the height passed to the method, or
               //--- if the height passed to the method has a zero value, use value 1 for the height
               h=(height==WRONG_VALUE ? this.m_workspace.TextHeight(text) : height>0 ? height : 1);
              }
            //--- Fill the space according to the specified coordinates and the resulting width and height with a transparent color (erase the previous entry)
            this.m_workspace.FillRectangle(x,y,x+w,y+h,0x00FFFFFF);
           }
      //--- Display the text to the space cleared of previous text and update the working space without redrawing the screen
         this.m_workspace.TextOut(x,y,text,::ColorToARGB(clr==clrNONE ? this.m_fore_color : clr));
         this.m_workspace.Update(false);
        }
      


      Implementazione dei metodi per la creazione di una nuova tabella e per l'ottenimento di dati tabellari in base all'ID e al nome della tabella:

      //+------------------------------------------------------------------+
      //| Create a new table                                               |
      //+------------------------------------------------------------------+
      bool CDashboard::CreateNewTable(const int id=WRONG_VALUE)
        {
         uint num=(id>WRONG_VALUE ? id : this.m_list_table.Total());
         CTableData *table=new CTableData(num);
         this.m_list_table.Sort();
         if(this.m_list_table.Search(table)!=WRONG_VALUE)
           {
            PrintFormat("%s: Error. Table with id %lu already exists in the list",__FUNCTION__,num);
            delete table;
            return false;
           }
         if(!this.m_list_table.Add(table))
           {
            PrintFormat("%s: Error. Failed to add table with id %lu to the list",__FUNCTION__,num);
            delete table;
            return false;
           }
         return true;
        }
      //+------------------------------------------------------------------+
      //| Return tabular data object by ID                                 |
      //+------------------------------------------------------------------+
      CTableData *CDashboard::GetTable(const uint id)
        {
         if(this.m_list_table.Total()==0)
           {
            PrintFormat("%s: Error. The list of tables is empty. First you need to create a table using CreateNewTable",__FUNCTION__);
            .return NULL;
           }
         CTableData *table=new CTableData(id);
         if(table==NULL)
           {
            ::PrintFormat("%s: Error. Failed to create table object with id %lu",__FUNCTION__,id);
            .return NULL;
           }
         this.m_list_table.Sort();
         int index=this.m_list_table.Search(table);
         delete table;
         return this.m_list_table.At(index);
        }
      //+------------------------------------------------------------------+
      //| Return tabular data object by name                               |
      //+------------------------------------------------------------------+
      CTableData *CDashboard::GetTable(const string name)
        {
         if(this.m_list_table.Total()==0)
           {
            PrintFormat("%s: Error. The list of tables is empty. First you need to create a table using CreateNewTable",__FUNCTION__);
            .return NULL;
           }
         CTableData *table=new CTableData(0);
         if(table==NULL)
           {
            ::PrintFormat("%s: Error. Failed to create table object");
            .return NULL;
           }
         table.SetName(name);
         this.m_list_table.Sort(1);
         int index=this.m_list_table.Search(table);
         delete table;
         return this.m_list_table.At(index);
        }
      


      Modifiche ai metodi di disegno delle tabelle:

      //+------------------------------------------------------------------+
      //| Draw the background grid                                         |
      //+------------------------------------------------------------------+
      void CDashboard::DrawGrid(const uint table_id,
                                const uint x,const uint y,const uint rows,const uint columns,const uint row_size,const uint col_size,
                                const color line_color=clrNONE,bool alternating_color=true)
        {
      //--- Get a table object by ID
         CTableData *table=this.GetTable(table_id);
         if(table==NULL)
           {
            PrintFormat("%s: Error. Failed to get table object with id %lu",__FUNCTION__,table_id);
            return;
           }
      //--- Clear all lists of the tabular data object (remove cells from rows and all rows)
         table.Clear();
      //--- Line height cannot be less than 2
         int row_h=int(row_size<2 ? 2 : row_size);
      //--- Column width cannot be less than 2
         int col_w=int(col_size<2 ? 2 : col_size);
         
      //--- The X1 (left) coordinate of the table cannot be less than 1 (to leave one pixel around the perimeter of the panel for the frame)
         int x1=int(x<1 ? 1 : x);
      //--- Calculate the X2 coordinate (right) depending on the number of columns and their width
         int x2=x1+col_w*int(columns>0 ? columns : 1);
      //--- The Y1 coordinate is located under the panel title area
         int y1=this.m_header_h+(int)y;
      //--- Calculate the Y2 coordinate (bottom) depending on the number of lines and their height
         int y2=y1+row_h*int(rows>0 ? rows : 1);
      //--- Set table coordinates
         table.SetCoords(x1,y1-this.m_header_h,x2,y2-this.m_header_h);
         
      //--- Get the color of the table grid lines, either by default or passed to the method
         color clr=(line_color==clrNONE ? C'200,200,200' : line_color);
      //--- If the initial X coordinate is greater than 1, draw a table frame
      //--- (in case of the coordinate 1, the table frame is the panel frame)
         if(x1>1)
            this.m_canvas.Rectangle(x1,y1,x2,y2,::ColorToARGB(clr,this.m_alpha));
      //--- In the loop by table rows,
         for(int i=0;i<(int)rows;i++)
           {
            //--- calculate the Y coordinate of the next horizontal grid line (Y coordinate of the next table row)
            int row_y=y1+row_h*i;
            //--- if the flag of "alternating" line colors is passed and the line is even
            if(alternating_color && i%2==0)
              {
               //--- lighten the table background color and draw a background rectangle
               color new_color=this.NewColor(clr,45,45,45);
               this.m_canvas.FillRectangle(x1+1,row_y+1,x2-1,row_y+row_h-1,::ColorToARGB(new_color,this.m_alpha));
              }
            //--- Draw a table grid horizontal line
            this.m_canvas.Line(x1,row_y,x2,row_y,::ColorToARGB(clr,this.m_alpha));
            
            //--- Create a new table row object
            CTableRow *row_obj=new CTableRow(i);
            if(row_obj==NULL)
              {
               ::PrintFormat("%s: Failed to create table row object at index %lu",(string)__FUNCTION__,i);
               continue;
              }
            //--- Add it to the list of rows of the tabular data object
            //--- (if adding an object failed, delete the created object)
            if(!table.AddRow(row_obj))
               delete row_obj;
            //--- Set its Y coordinate in the created row object taking into account the offset from the panel title
            row_obj.SetY(row_y-this.m_header_h);
           }
           
      //--- In the loop by table columns,
         for(int i=0;i<(int)columns;i++)
           {
            //--- calculate the X coordinate of the next vertical grid line (X coordinate of the next table row)
            int col_x=x1+col_w*i;
            //--- If the grid line goes beyond the panel, interrupt the loop
            if(x1==1 && col_x>=x1+m_canvas.Width()-2)
               break;
            //--- Draw a vertical line of the table grid
            this.m_canvas.Line(col_x,y1,col_x,y2,::ColorToARGB(clr,this.m_alpha));
            
            //--- Get the number of created rows from the table data object 
            int total=table.RowsTotal();
            //--- In the loop by table rows
            for(int j=0;j<total;j++)
              {
               //--- get the next row
               CTableRow *row=table.GetRow(j);
               if(row==NULL)
                  continue;
               //--- Create a new table cell 
               CTableCell *cell=new CTableCell(row.Row(),i);
               if(cell==NULL)
                 {
                  ::PrintFormat("%s: Failed to create table cell object at index %lu",(string)__FUNCTION__,i);
                  continue;
                 }
               //--- Add the created cell to the row
               //--- (if adding an object failed, delete the created object)
               if(!row.AddCell(cell))
                 {
                  delete cell;
                  continue;
                 }
               //--- In the created cell object, set its X coordinate and the Y coordinate from the row object 
               cell.SetXY(col_x,row.Y());
              }
           }
      //--- Update the canvas without redrawing the chart
         this.m_canvas.Update(false);
        }
      //+------------------------------------------------------------------+
      //| Draws the background grid with automatic cell sizing             |
      //+------------------------------------------------------------------+
      void CDashboard::DrawGridAutoFill(const uint table_id,const uint border,const uint rows,const uint columns,const color line_color=clrNONE,bool alternating_color=true)
        {
      //--- Get a table object by ID
         CTableData *table=this.GetTable(table_id);
         if(table==NULL)
           {
            PrintFormat("%s: Error. Failed to get table object with id %lu",__FUNCTION__,table_id);
            return;
           }
      //--- X1 (left) table coordinate
         int x1=(int)border;
      //--- X2 (right) table coordinate
         int x2=this.m_canvas.Width()-(int)border-1;
      //--- Y1 (upper) table coordinate
         int y1=this.m_header_h+(int)border;
      //--- Y2 (lower) table coordinate
         int y2=this.m_canvas.Height()-(int)border-1;
      //--- Set table coordinates
         table.SetCoords(x1,y1,x2,y2);
      
      //--- Get the color of the table grid lines, either by default or passed to the method
         color clr=(line_color==clrNONE ? C'200,200,200' : line_color);
      //--- If the offset from the edge of the panel is greater than zero, draw a table border,
      //--- otherwise, the panel border is used as the table border
         if(border>0)
            this.m_canvas.Rectangle(x1,y1,x2,y2,::ColorToARGB(clr,this.m_alpha));
      
      //--- Height of the entire table grid
         int greed_h=y2-y1;
      //--- Calculate the row height depending on the table height and the number of rows
         int row_h=(int)::round((double)greed_h/(double)rows);
      //--- In the loop based on the number of rows
         for(int i=0;i<(int)rows;i++)
           {
            //--- calculate the Y coordinate of the next horizontal grid line (Y coordinate of the next table row)
            int row_y=y1+row_h*i;
            //--- if the flag of "alternating" line colors is passed and the line is even
            if(alternating_color && i%2==0)
              {
               //--- lighten the table background color and draw a background rectangle
               color new_color=this.NewColor(clr,45,45,45);
               this.m_canvas.FillRectangle(x1+1,row_y+1,x2-1,row_y+row_h-1,::ColorToARGB(new_color,this.m_alpha));
              }
            //--- Draw a table grid horizontal line
            this.m_canvas.Line(x1,row_y,x2,row_y,::ColorToARGB(clr,this.m_alpha));
            
            //--- Create a new table row object
            CTableRow *row_obj=new CTableRow(i);
            if(row_obj==NULL)
              {
               ::PrintFormat("%s: Failed to create table row object at index %lu",(string)__FUNCTION__,i);
               continue;
              }
            //--- Add it to the list of rows of the tabular data object
            //--- (if adding an object failed, delete the created object)
            if(!table.AddRow(row_obj))
               delete row_obj;
            //--- Set its Y coordinate in the created row object taking into account the offset from the panel title
            row_obj.SetY(row_y-this.m_header_h);
           }
           
      //--- Table grid width
         int greed_w=x2-x1;
      //--- Calculate the column width depending on the table width and the number of columns
         int col_w=(int)::round((double)greed_w/(double)columns);
      //--- In the loop by table columns,
         for(int i=0;i<(int)columns;i++)
           {
            //--- calculate the X coordinate of the next vertical grid line (X coordinate of the next table row)
            int col_x=x1+col_w*i;
            //--- If this is not the very first vertical line, draw it
            //--- (the first vertical line is either the table frame or the panel frame)
            if(i>0)
               this.m_canvas.Line(col_x,y1,col_x,y2,::ColorToARGB(clr,this.m_alpha));
            
            //--- Get the number of created rows from the table data object 
            int total=table.RowsTotal();
            //--- In the loop by table rows
            for(int j=0;j<total;j++)
              {
               //--- get the next row
               CTableRow *row=table.GetRow(j);
               if(row==NULL)
                  continue;
               //--- Create a new table cell 
               CTableCell *cell=new CTableCell(row.Row(),i);
               if(cell==NULL)
                 {
                  ::PrintFormat("%s: Failed to create table cell object at index %lu",(string)__FUNCTION__,i);
                  continue;
                 }
               //--- Add the created cell to the row
               //--- (if adding an object failed, delete the created object)
               if(!row.AddCell(cell))
                 {
                  delete cell;
                  continue;
                 }
               //--- In the created cell object, set its X coordinate and the Y coordinate from the row object 
               cell.SetXY(col_x,row.Y());
              }
           }
      //--- Update the canvas without redrawing the chart
         this.m_canvas.Update(false);
        }
      


      I metodi per salvare/caricare i pixel nel/dal file ora applicano i nomi dei file precedentemente creati nel costruttore:

      //+------------------------------------------------------------------+
      //| Save the pixel array of the working space to a file              |
      //+------------------------------------------------------------------+
      bool CDashboard::FileSaveWorkspace(void)
        {
      //--- If the saved array is empty, inform of that and return 'false'
         if(this.m_array_wpx.Size()==0)
           {
            ::PrintFormat("%s: Error. The workspace pixel array is empty.",__FUNCTION__);
            return false;
           }
      //--- If the array could not be saved to a file, report this and return 'false'
         if(!::FileSave(this.m_filename_ws,this.m_array_wpx))
           {
            ::PrintFormat("%s: FileSave '%s' failed. Error %lu",__FUNCTION__,this.m_filename_ws,::GetLastError());
            return false;
           }
      //--- Successful, return 'true'
         return true;
        }
      //+------------------------------------------------------------------+
      //| Save the pixel array of the panel background to a file           |
      //+------------------------------------------------------------------+
      bool CDashboard::FileSaveBackground(void)
        {
      //--- If the saved array is empty, inform of that and return 'false'
         if(this.m_array_ppx.Size()==0)
           {
            ::PrintFormat("%s: Error. The background pixel array is empty.",__FUNCTION__);
            return false;
           }
      //--- If the array could not be saved to a file, report this and return 'false'
         if(!::FileSave(this.m_filename_bg,this.m_array_ppx))
           {
            ::PrintFormat("%s: FileSave '%s' failed. Error %lu",__FUNCTION__,this.m_filename_bg,::GetLastError());
            return false;
           }
      //--- Successful, return 'true'
         return true;
        }
      //+------------------------------------------------------------------+
      //| Upload the array of working space pixels from a file             |
      //+------------------------------------------------------------------+
      bool CDashboard::FileLoadWorkspace(void)
        {
      //--- If failed to upload data from the file into the array, report this and return 'false'
         if(::FileLoad(this.m_filename_ws,this.m_array_wpx)==WRONG_VALUE)
           {
            ::PrintFormat("%s: FileLoad '%s' failed. Error %lu",__FUNCTION__,this.m_filename_ws,::GetLastError());
            return false;
           }
      //--- Successful, return 'true'
         return true;
        }
      //+------------------------------------------------------------------+
      //| Upload the array of panel background pixels from a file          |
      //+------------------------------------------------------------------+
      bool CDashboard::FileLoadBackground(void)
        {
         if(::FileLoad(this.m_filename_bg,this.m_array_ppx)==WRONG_VALUE)
           {
            ::PrintFormat("%s: FileLoad '%s' failed. Error %lu",__FUNCTION__,this.m_filename_bg,::GetLastError());
            return false;
           }
      //--- Successful, return 'true'
         return true;
        }
      


      Conclusioni

      In questo articolo abbiamo analizzato la connessione di Volumes e degli indicatori di Bill Williams agli EA. Tutti i codici forniti nell'articolo possono essere utilizzati "così come sono" per inserirli nel codice personalizzato. Successivamente, prenderemo in considerazione l'ultima categoria di indicatori - gli indicatori di trend - in termini di connessione e utilizzo negli EA.

      Tutti i file (EA di prova e classi del pannello) possono essere scaricati dall'elenco di file allegato di seguito. La classe del pannello deve trovarsi in \MQL5\Include\Dashboard\Dashboard.mqh.


      Tradotto dal russo da MetaQuotes Ltd.
      Articolo originale: https://www.mql5.com/ru/articles/13277

      Arriva il Nuovo MetaTrader 5 e MQL5 Arriva il Nuovo MetaTrader 5 e MQL5
      Questa è solo una panoramica di MetaTrader 5. Non posso descrivere tutte le nuove funzionalità del sistema per un periodo di tempo così breve: i test sono iniziati il 09.09.2009. Questa è una data simbolica e sono sicuro che sarà un numero fortunato. Sono passati alcuni giorni da quando ho ricevuto la versione beta del terminale MetaTrader 5 e MQL5. Non sono riuscito a provare tutte le sue funzionalità, ma sono già sorpreso.
      Template pronti per includere indicatori negli Expert Advisor (Parte 1): Oscillatori Template pronti per includere indicatori negli Expert Advisor (Parte 1): Oscillatori
      L'articolo prende in considerazione gli indicatori standard della categoria degli oscillatori. Creeremo template pronti all'uso per il loro utilizzo negli EA - dichiarazione e impostazione dei parametri, inizializzazione e deinizializzazione degli indicatori, nonché ricezione dei dati e segnali dai buffer degli indicatori negli EA.
      Utilizza i canali MQL5.community e le chat di gruppo Utilizza i canali MQL5.community e le chat di gruppo
      Il sito web MQL5.com riunisce trader di tutto il mondo. Gli utenti pubblicano articoli, condividono codici gratuiti, vendono prodotti nel Market, offrono servizi da freelance e copiano segnali di trading. Puoi comunicare con loro sul Forum, nelle chat dei trader e nei canali MetaTrader.
      Algoritmi di ottimizzazione della popolazione: Algoritmo come-l’Elettromagnetismo (ЕМ) Algoritmi di ottimizzazione della popolazione: Algoritmo come-l’Elettromagnetismo (ЕМ)
      L'articolo descrive i principi, i metodi e le possibilità di utilizzo dell'Algoritmo Elettromagnetico in vari problemi di ottimizzazione. L'algoritmo EM è un efficiente strumento di ottimizzazione in grado di lavorare con grandi quantità di dati e funzioni multidimensionali.