
Template pronti per includere indicatori negli Expert Advisor (Parte 2): Indicatori di Volume e Bill Williams
Contenuto
- Introduzione
- Indicatori di Volume
- Indicatori di Bill Williams
- Accelerator Oscillator
- Alligator
- Awesome Oscillator
- Fractals
- Gator Oscillator
- Market Facilitation Index
- Perfezionamento delle classi del pannello. Panoramica
- Conclusioni
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.
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 );
[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 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





- 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