Crea il tuo Market Watch usando le Classi Standard della Libreria
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:
- Object.mqh - contiene la classe base CObject per la creazione di tutte le altre classi, relative agli oggetti grafici.
- ChartObject.mqh - contiene anche la classe CChartObject, derivata da CObject, che estende le funzionalità e l'incapsulamento dei dati e dei metodi generali degli oggetti grafici.
- ChartObjectsTxtControls.mqh - contiene una serie di classi, destinate a visualizzare i vari oggetti grafici sul grafico, contenenti il testo. (Classe base CChartObjectText e suoi discendenti: CChartObjectLabel, CChartObjectEdit e CChartObjectButton.
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:
- identificazione della sua posizione rispetto agli elementi vicini all'interno della lista.
- identificazione del tipo di oggetto.
- metodo per salvare e caricare i dati dell'oggetto.
- metodo per confrontare con gli oggetti specificati.
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:
- collegamento dell'oggetto grafico reale sul grafico con l'istanza della classe.
- lettura e modifica delle proprietà comuni di tutti gli oggetti grafici.
- cancellazione dell'oggetto grafico dal grafico.
- movimento dell'oggetto grafico sul grafico.
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à:
- lettura e modifica delle proprietà di oggetti grafici testuali.
- creando un vero oggetto grafico di tipo OBJ_TEXT sul grafico.
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:
- le coordinate della carta vengono utilizzate quando si individua l'oggetto grafico.
- permette di leggere e modificare l'angolo di ancoraggio. Si tratta infatti di assegnare l'inizio delle coordinate in uno dei quattro angoli della finestra del grafico.
- creando un vero e proprio oggetto grafico di tipo OBJ_LABEL sul grafico del terminale del cliente.
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:
- il campo di input ha le proprietà di larghezza e altezza (specificate in pixel dello schermo) che limitano la dimensione dell'oggetto grafico sul grafico. La dimensione dell'etichetta di testo viene regolata automaticamente in modo che l'intero testo possa essere visto.
- ci sono metodi per abilitare/disabilitare la modifica del testo - CChartObjectEdit:: ReadOnly.
- viene aggiunto un metodo per cambiare il colore di sfondo dell'area, occupata dall'oggetto grafico.
- non è possibile modificare l'angolo con cui viene visualizzato l'oggetto. Il campo di input può essere visualizzato solo in direzione orizzontale.
- permette di creare un vero e proprio oggetto grafico di tipo OBJ_EDIT sul grafico.
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:
- Sembra un pulsante premuto, simile a quelli utilizzati nelle finestre di dialogo di Windows.
- ha nuovi metodi per leggere/modificare lo stato del pulsante (premuto/non premuto) - CChartObjectButton::State.
- permette di creare un vero e proprio oggetto grafico di tipo OBJ_BUTTON sul grafico.
La struttura complessiva delle Classi Standard per oggetti testuali grafici
La struttura (gerarchia) delle classi della Libreria Standard, può essere riassunta come segue:
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:
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:
- chart_id - identificatore della finestra (0 - finestra principale);
- window - numero della sottofinestra (0 - principale);
- cols - il numero massimo di oggetti di testo grafico in orizzontale (il numero di colonne);
- lines - il numero massimo di oggetti di testo grafico in verticale (numero di righe);
- col - la coordinata orizzontale dell'oggetto grafico (varia da zero a cols - 1);
- row - la coordinata verticale dell'oggetto grafico (varia da zero a linee - 1);
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:
- Aggiungo alla lista:
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.
- Cancellazione dalla lista:
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".
- accesso all'elemento arbitrario della lista:
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.
- Cancellare l’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:
Рисунок 3. Un esempio di tabella dello schermo
- Passaggio 1 - Includi la libreria (nel codice sorgente dell'indicatore):
#include <TextDisplay.mqh>
- Fase 2 - Creazione di array con i nomi e le coordinate per i titoli:
#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.
- Passaggio 3: creare un oggetto tabella di tipo TableDisplay per memorizzare tutti gli oggetti di testo visualizzati:
TableDisplay Table1;
- Fase 4 - Aggiungi oggetti-titoli e campi di informazioni sull'oggetto nella tabella:
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).
- Passaggio 5: aggiunta del codice per aggiornare le informazioni dinamiche:
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:
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).
- Fase 6 - Rimozione degli oggetti creati:
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:
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.
Tradotto dal russo da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/ru/articles/179
- App di trading gratuite
- Oltre 8.000 segnali per il copy trading
- Notizie economiche per esplorare i mercati finanziari
Accetti la politica del sito e le condizioni d’uso