Crea il tuo Market Watch usando le Classi Standard della Libreria

Dmitriy Skub | 16 dicembre, 2021


Introduzione

L'obiettivo principale del compito era sviluppare uno strumento facile da usare ed estensibile per l'output di informazioni testuali arbitrarie sul grafico del terminale client MetaTrader 5. Ad esempio, le impostazioni correnti dell'Expert Advisor, o i risultati del suo lavoro durante un intervallo specificato, presentati come una tabella, i valori dei prezzi o una tabella dei valori degli indicatori, utilizzati dal trader, o un registro dell'Expert Advisor di trading . Tutte queste informazioni possono essere visualizzate dinamicamente sul grafico durante il lavoro di EA o indicatore, che utilizza questa libreria.

Come base per lo sviluppo della libreria, è stata utilizzata una raccolta di classi della Libreria Standard del terminale client MetaTrader 5. Per prima cosa considereremo queste classi e poi le estenderemo per adempiere al nostro compito.


Classi Standard per Oggetti Grafici

L'insieme delle classi, che ci interessa, si trova nelle cartelle Include e Include\ChartObjects.

Questi sono i seguenti file:

Consideriamo queste classi in modo più dettagliato.


CObject: la Classe Base

La descrizione della classe è breve, quindi mostriamola qui:

class CObject
{
protected:
   CObject          *m_prev;
   CObject          *m_next;

public:
                     CObject();

   CObject          *Prev()                { return(m_prev); }
   void              Prev(CObject *node)   { m_prev=node;    }
   CObject          *Next()                { return(m_next); }
   void              Next(CObject *node)   { m_next=node;    }

   virtual bool      Save(int file_handle) { return(true);   }
   virtual bool      Load(int file_handle) { return(true);   }

   virtual int       Type() const          { return(0);      }

protected:
   virtual int       Compare(const CObject *node,int mode=0) const { return(0); }
};

//+------------------------------------------------------------------+
void CObject::CObject()
{
   m_prev=NULL;
   m_next=NULL;
}

Come puoi vedere, questa classe contiene solo dati e metodi di uso generale, non sono correlati direttamente con l'output sul grafico.

Tuttavia, ha una proprietà molto importante: può essere utilizzata per creare elenchi semplicemente collegati e doppiamente collegati. Queste le funzionalità fornite da CObject::m_prev e CObject::m_next campi dati di tipo CObject* e metodi di lettura/scrittura. Il campo CObject::m_prev si riferisce all'elemento della lista precedente, mentre CObject::m_next - si riferisce a quello successivo. Maggiori dettagli sulla costruzione delle liste verranno forniti ulteriormente.

Inoltre, esiste un metodo per confrontare due oggetti di tipo CObject*, il metodo CObject::Compare che può essere utilizzato durante l'ordinamento degli elementi dell'elenco. Esistono altri due metodi utili, che consentono di salvare/contare i campi dati nei file: questi sono i metodi CObject::Save e CObject::Load. Per ottenere la funzionalità desiderata questi metodi dovrebbero essere sovraccaricati nelle classi discendenti.

CObject::Type è il metodo di identificazione del tipo di oggetto. Questo metodo è utile quando si manipola un elenco che contiene oggetti di tipo diverso.

La classe CObject (e le sue istanze) ha le seguenti caratteristiche:

La maggior parte dei metodi descritti sopra sono virtuali e non implementati nella classe base. La classe base non ha proprietà reali con significato fisico. Come di consueto in OOP, la funzionalità dovrebbe essere implementata nelle classi discendenti.


CChartObject: la classe base per gli oggetti grafici

Il CChartObject è un discendente della classe CObject.

Nel suo nome, si può vedere che questa è una classe per descrivere alcuni oggetti grafici astratti. Tuttavia, questo oggetto astratto contiene già alcune proprietà fisiche e metodi per lavorare con queste proprietà. Queste proprietà sono comuni a tutti gli oggetti grafici in MetaTrader 5, quindi è logico inserirli in questa classe.

Consideriamoli in modo più dettagliato. Utilizzeremo i seguenti dati per collegare l'oggetto grafico alla finestra del grafico:

protected:
  long       m_chart_id;    // identifier of the chart, which contains 
                               // specified graphic object (0 - current chart)
  int        m_window;      // number of chart sub-window (0 - main window)
  string     m_name;        // unique name of the object on the chart
  int        m_num_points;  // number of points for anchoring the object

Prima di specificare o leggere le proprietà dell'oggetto grafico reale, deve essere attaccato con l'oggetto (istanza di classe). Questa operazione viene eseguita dal metodo CChartObject::Attach. Nelle classi discendenti, viene chiamato immediatamente dopo la creazione dell'oggetto grafico sul grafico.

bool CChartObject::Attach(long chart_id,string name,int window,int points)
{
  if(ObjectFind(chart_id,name)<0)
  {
    return(false);
  }

  if(chart_id==0) chart_id=ChartID();

  m_chart_id  =chart_id;
  m_window    =window;
  m_name      =name;
  m_num_points=points;

  return(true);
}

Innanzitutto verifichiamo l'esistenza di un oggetto grafico reale. Se esiste, le sue proprietà vengono memorizzate nei campi interni dell'oggetto classe CChartObject. Successivamente, possiamo leggere e modificare le proprietà dell'oggetto grafico (colore, posizione, ecc.).

Le modalità di salvataggio/lettura delle proprietà dell'oggetto grafico sono già implementate nei metodi CChartObject::Save e CChartObject::Load. Il metodo genitore di save/read dovrebbe essere chiamato prima nella classe discendente prima del proprio metodo.

La classe CChartObject (e le sue istanze) ha le seguenti nuove proprietà rispetto a quelle di base:


CChartObjectText: una Classe per oggetti Text-Graphic

Passiamo ora al file ChartObjectsTxtControls.mqh. Qui troveremo la descrizione delle classi sviluppate per l'output dei vari oggetti grafici, contenenti il testo, sul grafico. Consideriamo le loro caratteristiche di base.

La classe di base per loro è la classe CChartObjectText. Incapsula le proprietà e i metodi, associati all'output di testo sul grafico.

Ecco la descrizione della classe:

class CChartObjectText : public CChartObject
{
public:
   double            Angle() const;
   bool              Angle(double angle);
   string            Font() const;
   bool              Font(string font);
   int               FontSize() const;
   bool              FontSize(int size);
   ENUM_ANCHOR_POINT  Anchor() const;
   bool              Anchor(ENUM_ANCHOR_POINT anchor);

   bool              Create(long chart_id,string name,int window,datetime time,double price);

   virtual int       Type() const { return(OBJ_TEXT); }

   virtual bool      Save(int file_handle);
   virtual bool      Load(int file_handle);
};

Rispetto a CChartObject, contiene metodi per leggere e modificare le proprietà degli oggetti grafici testuali: l'angolo di orientamento del testo sul grafico, il nome del carattere del testo, la dimensione del carattere e le coordinate dell'oggetto grafico. È apparso un nuovo metodo CChartObjectText :: Create che ci permette di creare un vero e proprio oggetto grafico di tipo OBJ_TEXT sul grafico.

La sua implementazione:

bool CChartObjectText::Create(long chart_id,string name,int window,datetime time,double price)
{
  bool result = ObjectCreate( chart_id, name, OBJ_TEXT, window, time, price );
  if(result)
  {
    result &= Attach(chart_id, name, window, 1 );
  }

  return(result);
}

Nel caso di una creazione riuscita dell'oggetto grafico (il metodo ObjectCreate ha restituito true), il metodo CChartObject :: Attachviene chiamato, che abbiamo considerato in precedenza.

Pertanto, rispetto alla classe genitore, CChartObjectText include nuove funzionalità:


CChartObjectLabel: una classe per oggetti grafici "Etichetta di testo"

La classe successiva nella gerarchia delle classi standard è la classe CChartObjectLabel. Consente di creare oggetti grafici di tipo OBJ_LABEL (Etichetta di testo) sul grafico.

Ecco una descrizione di questa classe:

class CChartObjectLabel : public CChartObjectText
{
public:
   int               X_Distance() const;
   bool              X_Distance(int X);
   int               Y_Distance() const;
   bool              Y_Distance(int Y);
   int               X_Size() const;
   int               Y_Size() const;
   ENUM_BASE_CORNER  Corner() const;
   bool              Corner(ENUM_BASE_CORNER corner);

   bool              Time(datetime time) { return(false);  }
   bool              Price(double price) { return(false);  }

   bool              Create(long chart_id,string name,int window,int X,int Y);

   virtual int       Type() const        { return(OBJ_LABEL); }

   virtual bool      Save(int file_handle);
   virtual bool      Load(int file_handle);
};

Qui dobbiamo notare la differenza tra l'oggetto grafico di tipo OBJ_TEXT e l'oggetto di tipo OBJ_LABEL.

Il primo è vincolante sul grafico del prezzo (coordinate prezzo-tempo) o sul grafico nella sotto-finestra. Il secondo è vincolante per le coordinate cartografiche della finestra o sotto-finestra (pixel) del grafico.

Pertanto, gli oggetti di tipo OBJ_TEXT vengono spostati insieme al grafico durante lo scorrimento, mentre gli oggetti di tipo OBJ_LABEL rimangono fissi durante lo scorrimento. Pertanto, a seconda delle attività, dobbiamo scegliere un particolare tipo di oggetto testuale grafico.

Rispetto alla classe genitore, la classe CChartObjectLabel include le seguenti caratteristiche distintive:


CChartObjectEdit: una classe per oggetti grafici "Input Field"

La classe successiva nella gerarchia è la classe CChartObjectEdit. Questa è una classe per creare un oggetto grafico di tipo OBJ_EDIT (campo di input).

L'oggetto di questo tipo è vincolante allo stesso modo dell'oggetto di tipo OBJ_LABEL utilizzando le coordinate del grafico (pixel), è logico derivarlo dalla classe CChartObjectLabel allo stesso modo delle classi standard:

class CChartObjectEdit : public CChartObjectLabel
{
public:
   bool              X_Size(int X);
   bool              Y_Size(int Y);
   color             BackColor() const;
   bool              BackColor(color new_color);
   bool              ReadOnly() const;
   bool              ReadOnly(bool flag);

   bool              Angle(double angle) { return(false);    }

   bool              Create(long chart_id,string name,int window,int X,int Y,int sizeX,int sizeY);

   virtual int       Type() const        { return(OBJ_EDIT); }

   virtual bool      Save(int file_handle);
   virtual bool      Load(int file_handle);
};

La differenza tra i campi di input di tipo OBJ_EDIT dall'etichetta di testo è la seguente:


CChartObjectButton: una classe per l'oggetto grafico "Button"

Un'altra classe nella gerarchia degli oggetti grafici testuali è la classe CChartObjectButton. Questo oggetto è chiamato pulsante, è sviluppato per creare un elemento di controllo sul grafico, sotto forma di un pulsante premuto. Questa classe è un discendente della classe CChartObjectEdit e ne eredita le funzionalità:

class CChartObjectButton : public CChartObjectEdit
{
public:
  bool             State() const;
  bool             State(bool state);

  virtual int       Type() const { return(OBJ_BUTTON); }

  virtual bool      Save(int file_handle);
  virtual bool      Load(int file_handle);
};

La differenza del pulsante di tipo OBJ_BUTTON dal campo di input è la seguente:


La struttura complessiva delle Classi Standard per oggetti testuali grafici

La struttura (gerarchia) delle classi della Libreria Standard, può essere riassunta come segue:

La struttura complessiva delle classi standard

Figura 1. La struttura complessiva delle classi standard

La classe CObject è una classe base per altre classi standard, ad esempio la classe CList, che gestisce l'elenco così come le altre.


Estensione della Funzionalità delle Classi della Libreria Standard

Abbiamo brevemente discusso la gerarchia delle classi standard, che sono sviluppate per generare oggetti grafici testuali sul grafico. Ora estendiamo questa gerarchia con nuove classi. Prima di tutto, dobbiamo decidere la funzionalità necessaria per l'implementazione. Formuliamo i requisiti. Poiché si tratta dell'output di informazioni testuali, è logico fornire queste informazioni nella seguente forma strutturata:

TITOLO: TESTO INFORMATIVO


Questa struttura della rappresentazione delle informazioni testuali è adatta alla maggior parte dei casi semplici.

Ad esempio, l'indicatore dei parametri dei simboli potrebbe avere il seguente aspetto:

Un esempio di visualizzazione strutturata di informazioni testuali

Figura 2. Un esempio di visualizzazione strutturata di informazioni testuali

Utilizza sei campi di informazione della struttura proposta sopra. Alcuni elementi della struttura potrebbero essere assenti. Ad esempio, nella Figura 2, il riquadro del campo superiore non è mostrato. Gli altri campi contengono il titolo e il testo. Questo indicatore si trova nel file PricelInfo.mq5, allegato a questo articolo.


Posizionamento degli oggetti grafici

Il secondo punto che dobbiamo considerare è il modo di posizionare gli oggetti testuali grafici sul grafico. Il metodo adottato per specificare le coordinate in pixel dello schermo consente il posizionamento dell'oggetto in una posizione arbitraria della carta.

In pratica è scomodo quando è necessario sistemare più oggetti testuali in punti diversi della carta, poiché bisogna calcolare tutte le coordinate dello schermo. Inoltre, se modifichi le dimensioni del grafico, tutte le coordinate dei pixel devono essere ricalcolate per assicurarti che la posizione relativa degli oggetti sullo schermo non sia stata modificata.

Se tracciamo la finestra del grafico in rettangoli (campi) della stessa dimensione e assegniamo a ciascuno di questi rettangoli una coordinata orizzontale e verticale, otterremo un sistema di posizionamento universale, indipendente dalla risoluzione dello schermo.

Quando si crea l'oggetto di testo grafico, l'utente può impostare il numero massimo di campi nelle direzioni verticale e orizzontale e le coordinate dell'oggetto in tali campi come i parametri. La funzionalità, incorporata nella classe appropriata, regolerà automaticamente le coordinate degli oggetti grafici quando si modifica la risoluzione dello schermo. In questo modo le coordinate vengono specificate una volta e non necessitano di ulteriori aggiustamenti.


Generazione Automatica di Nomi di Oggetti Univoci

La domanda successiva che deve essere affrontata è la generazione automatica del nome dell'oggetto di testo grafico. Il requisito principale per il metodo di generazione è l'ottenimento di un nome univoco all'interno della finestra data. Ciò consentirà di individuare sullo schermo un numero sufficiente di oggetti, senza preoccuparsi della necessità di inventare nomi non duplicati.

Proponiamo il seguente metodo per generare un nome (una stringa, composta da campi):

Date Time
Numero di millisecondi


Per ottenere la parte della stringa, contenente la data e l'ora, utilizzare la seguente chiamata:

TimeToString(TimeGMT(), TIME_DATE|TIME_MINUTES|TIME_SECONDS);

Per ottenere il numero di millisecondi, utilizzare la seguente chiamata:

DoubleToString(GetTickCount(), 0);

Ma anche se misuriamo il tempo entro millisecondi, è del tutto possibile che quando chiamiamo consecutivamente la funzione GetTickCount() due o più volte, otterremo lo stesso valore. Ciò è dovuto alla limitazione della discrezione dei timer interni del sistema operativo e del processore. Pertanto, è necessario adottare misure aggiuntive per rilevare una situazione del genere.

Il metodo proposto è implementato nelle seguenti funzioni:

string GetUniqName()
{
  static uint prev_count = 0;

  uint count = GetTickCount();
  while(1)
  {
    if(prev_count == UINT_MAX)
    {
      prev_count = 0;
    }
    if(count <= prev_count)
    {
      prev_count++;
      count = prev_count;
    }
    else
    {
      prev_count = count;
    }

//  Verify that there is no object already existing with such a name:
    string name = TimeToString(TimeGMT(), TIME_DATE|TIME_MINUTES|TIME_SECONDS)+" "+DoubleToString(count, 0);
    if(ObjectFind(0, name) < 0)
    {
      return(name);
    }
  }

  return(NULL);
}

La limitazione di questo metodo: la generazione di non più di 4294967295 (UINT_MAX) nomi univoci al secondo. Apparentemente, questo dovrebbe essere sufficiente nella pratica. Per ogni evenienza, c'è un'ulteriore verifica dell'esistenza di un oggetto grafico con lo stesso nome.


La visualizzazione del titolo della struttura informativa - classe TTitleDisplay

Di seguito è illustrata la classe per la visualizzazione del titolo sullo schermo del terminale:

class TTitleDisplay : public CChartObjectLabel
{
protected:
  long    chart_id;
  int     sub_window;
  long    chart_width;       // width of the chart in pixels
  long    chart_height;      // height of the chart graph in pixels
  long    chart_width_step;  // step of the coordinates grid in the horizontal direction
  long    chart_height_step; // step of the coordinates grid in the vertical direction
  int     columns_number;    // number of columns
  int     lines_number;      // number of lines
  int     curr_column;
  int     curr_row;

protected:
  void    SetParams(long chart_id, int window, int cols, int lines);// specify the object's parameters

protected:
  string  GetUniqName();    // get a unique name
  bool    Create(long chart_id, int window, int cols, int lines, int col, int row);
  void    RecalcAndRedraw();// recount the coordinates and re-draw

        
public:
  void    TTitleDisplay();  // constructor
  void    ~TTitleDisplay(); // destructor
};

Il metodo di base, che crea un oggetto testuale grafico:

bool Create(long chart_id, int window, int _cols, int _lines, int _col, int _row);

L'assegnazione dei parametri di ingresso è la seguente:

Il metodo TTitleDisplay:: SetParams calcola i parametri dell'oggetto, associati al posizionamento sullo schermo.

L'implementazione è la seguente:

void TTitleDisplay::SetParams(long _chart_id, int _window, int _cols, int _lines)
{
  this.chart_id = _chart_id;
  this.sub_window = _window;
  this.columns_number = _cols;
  this.lines_number = _lines;

//  Specify the size of the window in pixels:
  this.chart_width = GetSystemMetrics(SM_CXFULLSCREEN);
  this.chart_height = GetSystemMetrics(SM_CYFULLSCREEN);

//  Calculate the step of the coordinates grid:
  this.chart_width_step = this.chart_width/_cols;
  this.chart_height_step = this.chart_height/_lines;
}

Qui abbiamo usato la chiamata alla funzione WinAPI GetSystemMetrics per ottenere le impostazioni di visualizzazione correnti. Questa funzione viene importata dalla libreria di sistema di Windows user32.dll.

Una classe per la creazione del testo informativo TFieldDisplay è costruita in modo simile. I dettagli possono essere trovati nella libreria TextDisplay.mqh, allegata a questo articolo.


Classe CList: Manipolazione di oggetti nell'elenco

Consideriamo ora un'altra classe standard, di cui avremo bisogno per la realizzazione del nostro piano. Questa classe consente di raggruppare gli oggetti in elenchi. Si trova nel file Include\Arrays\List.mqh.  Questo file fornisce la descrizione e l'implementazione di una classe CList standard, che è il discendente della classe base CObject, considerata in precedenza in questo articolo. Contiene un insieme di metodi per manipolare gli oggetti nell'elenco (aggiunta all'elenco, rimozione dall'elenco, accesso a un elemento arbitrario dell'elenco e cancellazione dell'elenco).

Consideriamo i metodi di base:

   int Add(CObject *new_node);
   int Insert(CObject *new_node,int index);

Questi sono due metodi per aggiungere un nuovo elemento all'elenco. Il primo CList::Add ti permette di aggiungere un nuovo elemento new_node alla fine della lista. Il secondo CList::Insert consente di inserire un nuovo elemento new_node in una posizione arbitraria (specificata dall'indice) nell'elenco.

   bool  Delete(int index);

Il metodo CList::Delete consente di rimuovere un elemento con un indice specificato dall'elenco. Nel frattempo, oltre a rimuovere l'elemento dalla lista, viene rilasciata la memoria, occupata dall'elemento di tipo CObject, che era occupata dall'elemento di tipo CObject. In altre parole, l'oggetto verrà eliminato "fisicamente".

   int       IndexOf(CObject* node);
   CObject*  GetNodeAtIndex(int index);
   CObject*  GetFirstNode();
   CObject*  GetPrevNode();
   CObject*  GetNextNode();
   CObject*  GetLastNode();

IlCList:: IndexOf restituisce l'indice di un elemento specifico nell'elenco. La numerazione degli indici parte da zero: il primo elemento ha indice zero. Questo metodo esegue l'operazione inversa restituisce l'indice per puntatore dell'elemento. Se l'elemento non è nell'elenco, restituisce -1.

Altri quattro metodi per navigare nell'elenco CList:: GetFirstNode restituisce l'elemento precedente, il CList:: GetNextNode restituisce il successivo, il CList:: GetLastNode restituisce l'ultimo elemento nell'elenco.

   void  Clear();

Il metodo CList::Clear permette di rimuovere tutti gli elementi dalla lista, e inoltre libera la memoria, occupata dagli oggetti.

Questi sono i metodi di base della classe CList, il resto è descritto nella MQL5 Reference.


Classe TableDisplay: Creazione di una tabella per visualizzare il testo sul Grafico

Quindi, abbiamo tutto ciò di cui abbiamo bisogno per la realizzazione del piano. Ecco una semplice classe che permette di organizzare gli oggetti grafici testuali in una tabella di dimensioni arbitrarie:

class TableDisplay : public CList
{
protected:
  long  chart_id;
  int   sub_window;

public:
  void  SetParams(long _chart_id, int _window, ENUM_BASE_CORNER _corner = CORNER_LEFT_UPPER);
  int   AddTitleObject(int _cols, int _lines, int _col, int _row, 
                      string _title, color _color, string _fontname = "Arial", int _fontsize = 8);
  int   AddFieldObject(int _cols, int _lines, int _col, int _row, 
                          color _color, string _fontname = "Arial", int _fontsize = 8);
  bool  SetColor(int _index, color _color);
  bool  SetFont(int _index, string _fontname, int _fontsize);
  bool  SetText(int _index, string _text);

public:
  void  TableDisplay();
  void  ~TableDisplay();
};

Prima di aggiungere gli oggetti grafici alla tabella, è necessario impostare i parametri: l'identificatore del carattere, l'indice della sottofinestra e il punto di ancoraggio. Questo viene fatto chiamando il metodoTableDisplay:: SetParams. Successivamente è possibile aggiungere alla tabella un numero qualsiasi di titoli e campi di testo.

Il testo completo della libreria, che abbiamo sviluppato, è reperibile nel file TextDisplay.mqh, allegato a questo articolo.


3. Un esempio di Creazione del Market Watch

Considera l'esempio della creazione di una tabella per visualizzare i valori di più simboli, sotto forma di un indicatore.

Dovrebbe assomigliare più o meno a questo:

Un esempio di tabella dello schermo

Рисунок 3. Un esempio di tabella dello schermo

#include  <TextDisplay.mqh>
#define  NUMBER  8
//---------------------------------------------------------------------
string  names[NUMBER]   = {"EURUSD", "GBPUSD", "AUDUSD", "NZDUSD", "USDCHF", "USDCAD", "USDJPY", "EURJPY"};
int     coord_y[NUMBER] = {2,        3,        4,        5,        6,      7,        8,       9};
Le coordinate sono ordinate a partire da zero.
TableDisplay  Table1;
int OnInit()
{
//  Creating a table
  Table1.SetParams(0, 0);

  for(int i=0; i<NUMBER; i++)
  {
    Table1.AddFieldObject(40, 40, 3, coord_y[i], Yellow);
  }

  for(int i=0; i<NUMBER; i++)
  {
    Table1.AddTitleObject(40, 40, 1, coord_y[i], names[i]+":", White);
  }

  ChartRedraw(0);
  EventSetTimer(1);

  return(0);
}

È ottimale farlo nell’handler dell'evento OnInit. Innanzitutto, aggiungi i campi delle informazioni, utilizzando il metodoTableDisplay:: AddFieldObject. Quindi aggiungi loro i titoli, usando il metodo TableDisplay:: AddTitleObject.

L'aggiunta avviene in cicli separati per due motivi: in primo luogo, il numero di titoli, in generale, potrebbe non coincidere con il numero di campi di informazione, e in secondo luogo, l'accesso ai campi di informazione aggiornati è più facile da organizzare quando sono indicizzati in un riga (in questo caso, da zero al valore NUMBER - 1).

In questo caso l'aggiornamento delle informazioni dinamiche è organizzato dall’handler dell'evento Timer. L’handler OnTimer per questo evento dovrebbe ricevere i valori dei prezzi per gli strumenti dati e visualizzare questi valori sul grafico.

Il testo di questo è mostrato di seguito:

//---------------------------------------------------------------------
double    rates[NUMBER];
datetime  times[NUMBER];
MqlTick   tick;
//---------------------------------------------------------------------
// OnTimer event handler
//---------------------------------------------------------------------
void OnTimer()
{
  for(int i=0; i<NUMBER; i++)
  {
//  Obtain the price values:
    ResetLastError();
    if(SymbolInfoTick(names[i], tick) != true)
    {
      Table1.SetText(i,"Err "+DoubleToString(GetLastError(),0));
      Table1.SetColor(i,Yellow);
      continue;
    }

    if(tick.time>times[i])
    {
       Table1.SetText(i, DoubleToString(tick.bid, (int)(SymbolInfoInteger(names[i], SYMBOL_DIGITS))));

       if(tick.bid>rates[i])
       {
         Table1.SetColor(i, Lime);
       }
       else if(tick.bid<rates[i])
       {
         Table1.SetColor(i, Red);
       }
       else
       {
         Table1.SetColor(i, Yellow);
       }
       rates[i] = tick.bid;
       times[i] = tick.time;
    }
  }

  ChartRedraw(0);
}

All'inizio del ciclo vengono letti i dati di tick per lo strumento specificato, quindi viene verificata la rilevanza dei dati - se il tempo del tick è cambiato rispetto al precedente, consideriamo i dati irrilevanti. Inoltre analizziamo il valore della quotazione rispetto al tick precedente.

Se il prezzo corrente è maggiore di quello precedente, allora specifichiamo un colore verde per il campo delle informazioni. Se è inferiore, di un colore rosso e se sono uguali - un colore giallo. Alla fine del ciclo, il valore corrente del prezzo e il tempo di scadenza vengono memorizzati per l'analisi nella chiamata del gestore di eventi OnTimer.

In generale, il codice per l'aggiornamento delle informazioni dinamiche dipende dall'attività. Fondamentalmente, l'utente deve solo implementare questa parte del codice. Supponiamo di aver deciso di aggiungere uno spread a destra del prezzo in Figura 2. Vediamo come questo può essere fatto.

Tutto ciò che serve è aggiungere ulteriori campi dati alla tabella:

  for(int i=0; i<NUMBER; i++)
  {
    Table1.AddFieldObject(40, 40, 5, coord_y[i], Yellow);
  }

e includi il codice che aggiorna il valore dello spread nel gestore dell'evento OnTimer:

  Table1.SetText(i+NUMBER, DoubleToString((tick.ask-tick.bid)/SymbolInfoDouble(names[i], SYMBOL_POINT), 0));

Di conseguenza, otteniamo la seguente immagine:

Prezzi con uno spread

Figure 4. Prezzi con uno spread

Si noti che i campi indice per lo spread, a partire dal valore NUMBER (se i è uguale a zero).

void OnDeinit(const int _reason)
{
  EventKillTimer();

//  Removing the elements of display:
  Table1.Clear();
}

Qui il timer e la tabella vengono cancellati. Nel frattempo, anche tutti gli oggetti vengono rilasciati dalla memoria.

Il testo completo di questo indicatore è reperibile nel file PriceList.mq5, allegato a questo articolo. L'allegato contiene una versione "migliorata" dell'indicatore, che visualizza lo spread. Ha una serie di parametri esterni per la specifica del colore del titolo e la posizione di una tabella all'interno del grafico.


Conclusione

L'allegato MarketWatch.mq5 (e l'allegato MarketWatch.mqh) contiene l'indicatore per la visualizzazione dei parametri base degli strumenti di negoziazione, sotto forma di tabella riepilogativa. Per ogni simbolo viene mostrata l'informazione, simile alla Figura 2.

Inoltre, mostra la percentuale di variazione del prezzo per gli intervalli di tempo specificati. L'insieme di simboli (non più di 16) e gli intervalli di tempo sono specificati come una stringa con elementi, separati da un punto e virgola.  I risultati del lavoro di questo indicatore sono mostrati nella Figura 5:

Indicatore della revisione del mercato

Figura 5. Indicatore Market Watch

Abbiamo considerato un modo per visualizzare le informazioni di testo sul grafico del terminale client MetaTrader 5.

Utilizzando le classi della Libreria Standard, fornite con il terminale client, siamo stati in grado di sviluppare in modo abbastanza semplice e veloce la nuova funzionalità per la rappresentazione di informazioni testuali sotto forma di tabella bidimensionale. L'approccio orientato agli oggetti del linguaggio MQL5 è molto potente.