English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
Manuale MQL5: Sviluppo di un indicatore multi-simbolo per l’analisi della divergenza dei prezzi

Manuale MQL5: Sviluppo di un indicatore multi-simbolo per l’analisi della divergenza dei prezzi

MetaTrader 5Esempi | 12 gennaio 2022, 09:58
185 0
Anatoli Kazharski
Anatoli Kazharski

Introduzione

In questo articolo, considereremo lo sviluppo di un indicatore multi-simbolo per analizzare la divergenza dei prezzi in un determinato periodo di tempo. Gli argomenti principali sono già stati discussi nel precedente articolo sulla programmazione di indicatori multi-valuta Manuale MQL5: Sviluppo di un indicatore di volatilità multi-simbolo in MQL5. Quindi questa volta ci soffermeremo solo su quelle nuove caratteristiche e funzioni che sono state cambiate radicalmente. Se sei un neofita della programmazione di indicatori multi-valuta, ti consiglio di leggere prima l'articolo precedente.

In questo articolo prenderemo in considerazione le seguenti questioni:

  • Modifica delle proprietà del grafico.
  • Gestione degli eventi CHARTEVENT_OBJECT_DRAG (trascinamento di un oggetto grafico) e CHARTEVENT_CHART_CHANGE (ridimensionamento del grafico o modifica delle proprietà del grafico mediante la finestra di dialogo delle proprietà).
  • Rendering dei buffer degli indicatori utilizzando più di un colore.
  • Definizione di alti e bassi nei buffer degli indicatori all'interno dell'area di visibilità per impostare un grafico alto/basso.
  • Inversione di una serie.

La quantità di codice risultante per il nostro indicatore è piuttosto grande, circa 1500 righe. Pertanto, distribuiremo tutte le funzioni in file separati e le collegheremo al file di progetto principale. Ci saranno tre categorie di funzioni per i file esterni:

  • Checks.mqh - Funzioni per eseguire vari controlli e per scaricare i dati disponibili.
  • Objects.mqh - Funzioni per la gestione di oggetti grafici.
  • Chart.mqh - Funzioni per la gestione delle proprietà del grafico.

Tutte le funzioni che non appartengono alle categorie di cui sopra verranno lasciate nel file principale.


Sviluppo dell'indicatore

Passiamo alla programmazione dell'indicatore. Per prima cosa dobbiamo creare un nuovo progetto. Per fare ciò, nella directory Metatrader5\MQL5 \Indicators creare una cartella con lo stesso nome del nostro indicatore e in essa la cartella Includi in cui posizioneremo i file di include. Quindi, creare il file principale nella cartella degli indicatori. Questo può essere fatto manualmente creando un file di testo con l'estensione *.mq5 o utilizzando la procedura guidata MQL5 per modello. Oltre alle funzioni principali del programma OnInit(), OnDeinit() e OnCalculate(), useremo anche OnChartEvent() e OnTimer().

Proprio come nell'articolo precedente, oltre al simbolo corrente mostreremo i dati per 5 simboli specificati nei parametri esterni. Ma questa volta, invece di valori calcolati da qualche formula, produrremo dati grezzi dei prezzi sul grafico. L'utente è libero di scegliere il tipo di rappresentazione dei dati nei parametri esterni dall'elenco a discesa: Linea, Barre o Candele Giapponesi.

Se dovessimo visualizzare i dati solo come linee monocolore, allora sarebbe sufficiente specificare il numero di buffer pari al numero di simboli nelle proprietà dell'indicatore (#property). Ma poiché ci sono due modalità per disegnare serie come barre e candele giapponesi, abbiamo bisogno di più buffer per la modalità a due colori: quattro buffer per eseguire il rendering di ogni serie e un buffer per impostare il colore (a seconda delle condizioni) per ogni elemento di una serie grafica.

Per ogni serie è necessario specificare i colori nella sezione delle proprietà del programma. Per fare ciò, è sufficiente elencarli separati da virgole. Prima viene il colore utilizzato in modalità monocolore. In modalità a due colori, viene utilizzato per le barre / candele giapponesi. Il secondo colore verrà utilizzato solo nella modalità a due colori per le barre giù / candele giapponesi.

I codici di tutti questi parametri sono forniti di seguito:

#property indicator_chart_window // Indicator is in the main window
#property indicator_buffers 25   // Number of buffers for indicator calculation
#property indicator_plots   5    // Number of plotting series
//--- Indicator buffers colors
#property indicator_color1  clrDodgerBlue,C'0,50,100'
#property indicator_color2  clrLimeGreen,C'20,80,20'
#property indicator_color3  clrGold,C'160,140,0'
#property indicator_color4  clrAqua,C'0,140,140'
#property indicator_color5  clrMagenta,C'130,0,130'

Con la direttiva #define dichiariamo le costanti e usando la riga di comando #include includiamo i file con funzioni che sono già state descritte sopra e la classe dalla libreria Standard per lavorare con il canvas:

//--- Constants 
#define RESET           0 // Returning the indicator recalculation command to the terminal
#define SYMBOLS_COUNT   5 // Number of symbols
//--- Include the class for working with the canvas
#include <Canvas\Canvas.mqh>
//--- Include the class for working with the canvas
#include "Include/Checks.mqh"
#include "Include/Chart.mqh"
#include "Include/Objects.mqh"

Aggiungere le enumerazioni ENUM_DRAWTYPE e ENUM_START_POINT per creare elenchi a discesa che consentano di selezionare il tipo di disegno dei dati di prezzo e la modalità del punto di partenza della divergenza di prezzo nei parametri esterni:

//--- Drawing type of the price data
enum ENUM_DRAWTYPE
  {
   LINE   =0,  // Line
   BARS   =1,  // Bars
   CANDLES=2   // Candlesticks
  };
//--- Mode of the price divergence starting point
enum ENUM_START_POINT
  {
   VERTICAL_LINE=0,  // Vertical line
   MONTH        =1,  // Month
   WEEK         =2,  // Week
   DAY          =3,  // Day
   HOUR         =4   // Hour
  };

I tipi di rendering dei dati sono già descritti sopra, ora parliamo un po 'di più di cosa significa il punto di partenza della divergenza di prezzo.

Complessivamente ci saranno cinque modalità: Linea verticale, Mese,Settimana,, Giorno e Ora. Per la modalità Linea verticale, verrà aggiunta una linea verticale durante il caricamento dell'indicatore sul grafico. Trascinando questa linea si specifica la barra in cui i prezzi di tutti i simboli si incontreranno in un unico punto. Il prezzo di apertura della barra specificata per il simbolo corrente sarà considerato come un punto di riferimento di questo incontro. Qualsiasi altra modalità dirà al programma che ogni volta i prezzi dovrebbero soddisfare all'inizio del periodo specificato. Cioè all'inizio di ogni mese, all'inizio di ogni settimana, all'inizio di ogni giorno o all'inizio di ogni ora.

Di seguito è possibile trovare l'elenco dei parametri di input dell'indicatore:

//--- External parameters
input  ENUM_DRAWTYPE    DrawType             =CANDLES;       // Drawing type
input  ENUM_START_POINT StartPriceDivergence =VERTICAL_LINE; // Start of price divergence
input  bool             TwoColor             =false;         // Two-color bars/candlesticks
sinput string dlm01=""; //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
input  string           Symbol02             ="GBPUSD";      // Symbol 2
input  bool             Inverse02            =false;         // Inverse symbol 2
input  string           Symbol03             ="AUDUSD";      // Symbol 3
input  bool             Inverse03            =false;         // Inverse symbol 3
input  string           Symbol04             ="NZDUSD";      // Symbol 4
input  bool             Inverse04            =false;         // Inverse symbol 4
input  string           Symbol05             ="USDCAD";      // Symbol 5
input  bool             Inverse05            =false;         // Inverse symbol 5
input  string           Symbol06             ="USDCHF";      // Symbol 6
input  bool             Inverse06            =false;         // Inverse symbol 6

I simboli sono numerato a partire da 2, poiché 1 è il simbolo corrente sul grafico.

L'inversione può essere applicata per ogni simbolo incluso. L'inversione significa che i dati del simbolo verranno visualizzati a testa in giù. Questo può essere utile, quando l'elenco dei simboli analizzati include coppie di valute in cui la stessa valuta (ad esempio, dollaro USA) può essere sia quella base che quella contatore. Ad esempio, nella coppia di valute EURUSD, il dollaro USA è la valuta del contatore e nella coppia di valute USDCHF è quella di base. Se il simbolo corrente sul grafico è EURUSD, allora puoi attivare l'inversione per USDCHF, ciò che renderà la rappresentazione dei prezzi più conveniente per l'analisi.

Di seguito è riportato l'elenco delle variabili e delle matrici globali:

//--- Structure of the indicator buffers arrays
struct buffers
  {
   double            open[];   // Open prices buffer
   double            high[];   // High prices buffer
   double            low[];    // Low prices buffer
   double            close[];  // Close prices buffer
   double            icolor[]; // Buffer to determine the color of element
  };
buffers           buffer_data[SYMBOLS_COUNT];
//--- Load the class
CCanvas           canvas;
//--- Variables/arrays for copying data from OnCalculate()
int               OC_rates_total     =0; // Size of input time series
int               OC_prev_calculated =0; // Bars processed at the previous call
datetime          OC_time[];             // Opening time
double            OC_open[];             // Open prices
double            OC_high[];             // High prices
double            OC_low[];              // Low prices
double            OC_close[];            // Close prices
long              OC_tick_volume[];      // Tick volumes
long              OC_volume[];           // Real volumes
int               OC_spread[];           // Spread

//--- For the purpose of storing and checking the time of the first bar in the terminal
datetime          series_first_date[SYMBOLS_COUNT];
datetime          series_first_date_last[SYMBOLS_COUNT];
//--- Time array of the bar from which we will start drawing
datetime          limit_time[SYMBOLS_COUNT];
//--- Symbol names array
string            symbol_names[SYMBOLS_COUNT];
//--- Array of symbol inverse flags
bool              inverse[SYMBOLS_COUNT];
//--- Colors of indicator lines
color             line_colors[SYMBOLS_COUNT]={clrDodgerBlue,clrLimeGreen,clrGold,clrAqua,clrMagenta};
//--- String representing the lack of the symbol
string            empty_symbol="EMPTY";
//--- Chart properties
int               window_number              =WRONG_VALUE;               // Indicator window number
int               chart_width                =0;                         // Chart width
int               chart_height               =0;                         // Chart height
int               last_chart_width           =0;                         // Last saved chart width
int               last_chart_height          =0;                         // Last saved chart height
int               chart_center_x             =0;                         // Horizontal center of chart
int               chart_center_y             =0;                         // Vertical center of chart
color             color_bar_up               =clrRed;                    // Up bar color
color             color_bar_down             =C'100,0,0';                // Down bar color
string            indicator_shortname        ="MS_PriceDivergence";      // Short name of the indicator
string            prefix                     =indicator_shortname+"_";   // Prefix for objects
//--- Name of vertical line of the price divergence starting point
string            start_price_divergence=prefix+"start_price_divergence";
//--- Canvas properties
string            canvas_name             =prefix+"canvas";          // Canvas name
color             canvas_background       =clrBlack;                 // Canvas background color
uchar             canvas_opacity          =190;                      // Opacity
int               font_size               =16;                       // Font size
string            font_name               ="Calibri";                // Font
ENUM_COLOR_FORMAT clr_format              =COLOR_FORMAT_ARGB_RAW;    // Color components should be correctly set by the user
//--- Canvas messages
string            msg_prepare_data        ="Preparing data! Please wait...";
string            msg_not_synchronized    ="Unsynchronized data! Please wait...";
string            msg_load_data           ="";
string            msg_sync_update         ="";
string            msg_last                ="";
//---
ENUM_TIMEFRAMES   timeframe_start_point  =Period();    // Timeframe for the price divergence starting point
datetime          first_period_time      =NULL;        // Time of the first specified period on chart
double            divergence_price       =0.0;         // Price of the price divergence starting point
datetime          divergence_time        =NULL;        // Time of the price divergence starting point
double            symbol_difference[SYMBOLS_COUNT];    // Difference in price relative to the current symbol
double            inverse_difference[SYMBOLS_COUNT];   // Difference that is formed when calculating inversion

Successivamente, considereremo le funzioni utilizzate durante l'inizializzazione dell'indicatore. In generale, non ci sono cambiamenti importanti rispetto alla funzione OnInit() dell'articolo precedente.

Aggiungiamo il controllo su dove viene utilizzato l'indicatore. Il punto è che attualmente gli sviluppatori del terminale non hanno implementato tutte le funzionalità di controllo delle proprietà del grafico in Strategy Tester, quindi limitiamo il nostro indicatore ad essere utilizzato solo da Strategy Tester. Per implementare questo, scriveremo una semplice funzione - CheckTesterMode(). Si troverà nel file Checks.mqh:

//+------------------------------------------------------------------+
//| Checks if indicator is used in Strategy Tester                   |
//+------------------------------------------------------------------+
bool CheckTesterMode()
  {
//--- Report that indicator is not intended to be used in Strategy Tester
   if(MQLInfoInteger(MQL_TESTER) || 
      MQLInfoInteger(MQL_VISUAL_MODE) || 
      MQLInfoInteger(MQL_OPTIMIZATION))
     {
      Comment("Currently, the <- "+MQLInfoString(MQL_PROGRAM_NAME)+" -> indicator is not intended to be used in Strategy Tester!");
      return(false);
     }
//---
   return(true);
  }

Un'altra nuova funzione SetBarsColors() ha lo scopo di impostare i colori per barre / candele giapponesi del simbolo corrente. Si trova nel file Chart.mqh.

//+------------------------------------------------------------------+
//| Sets colors for the current symbol bars                          |
//+------------------------------------------------------------------+
void SetBarsColors()
  {
//--- Color for the up bar, shadows and body borders of bull candlesticks
   ChartSetInteger(0,CHART_COLOR_CHART_UP,color_bar_up);
//--- Body color of a bull candlestick
   ChartSetInteger(0,CHART_COLOR_CANDLE_BULL,color_bar_up);
//--- Line chart color and color of "Doji" Japanese candlesticks
   ChartSetInteger(0,CHART_COLOR_CHART_LINE,color_bar_up);
//--- For two-color mode
   if(TwoColor)
     {
      //--- Color for the down bar, shadows and body borders of bear candlesticks
      ChartSetInteger(0,CHART_COLOR_CHART_DOWN,color_bar_down);
      //--- Body color of a bear candlestick
      ChartSetInteger(0,CHART_COLOR_CANDLE_BEAR,color_bar_down);
     }
//--- If two-color mode is turned off
   else
     {
      //--- Color for the down bar, shadows and body borders of bear candlesticks
      ChartSetInteger(0,CHART_COLOR_CHART_DOWN,color_bar_up);
      //--- Body color of a bear candlestick
      ChartSetInteger(0,CHART_COLOR_CANDLE_BEAR,color_bar_up);
     }
  }

Durante l'inizializzazione, è necessario determinare quale modalità è selezionata nel parametro esterno StartPriceDivergence. Se è selezionata la linea Verticale, alla variabile globale timeframe_start_point verrà assegnato un valore predefinito, ovvero l'intervallo di tempo corrente. In caso contrario, verrà applicato l'intervallo di tempo selezionato. A tale scopo, scriviamo la funzione InitStartPointTF():

//+------------------------------------------------------------------+
//| Identifies timeframe for the price starting point mode           |
//+------------------------------------------------------------------+
void InitStartPointTF()
  {
//--- Exit if vertical line mode is selected
   if(StartPriceDivergence==VERTICAL_LINE)
      return;
//--- Otherwise define the timeframe
   switch(StartPriceDivergence)
     {
      case MONTH : timeframe_start_point=PERIOD_MN1; break;
      case WEEK  : timeframe_start_point=PERIOD_W1;  break;
      case DAY   : timeframe_start_point=PERIOD_D1;  break;
      case HOUR  : timeframe_start_point=PERIOD_H1;  break;
     }
  }

La funzione CheckInputParameters() a differenza di quella dell'articolo precedente ora è simile alla 1:

//+------------------------------------------------------------------+
//| Checks input parameters for correctness                          |
//+------------------------------------------------------------------+
bool CheckInputParameters()
  {
//--- For all other modes except the 'Vertical Line'
   if(StartPriceDivergence!=VERTICAL_LINE)
     {
      //--- If the current period is greater than or equal to the specified period of the price divergence starting point, report of it and exit
      if(PeriodSeconds()>=PeriodSeconds(timeframe_start_point))
        {
         Print("Current timeframe should be less than one specified in the Start Price Divergence parameter!");
         Comment("Current timeframe should be less than one specified in the Start Price Divergence parameter!");
         return(false);
        }
     }
//---
   return(true);
  }

Gli array vengono inizializzati proprio come nell'articolo precedente. Sono stati modificati solo i nomi e il numero di matrici.

//+------------------------------------------------------------------+
//| First initialization of arrays                                   |
//+------------------------------------------------------------------+
void InitArrays()
  {
   ArrayInitialize(limit_time,NULL);
   ArrayInitialize(symbol_difference,0.0);
   ArrayInitialize(inverse_difference,0.0);
   ArrayInitialize(series_first_date,NULL);
   ArrayInitialize(series_first_date_last,NULL);
//---
   for(int s=0; s<SYMBOLS_COUNT; s++)
     {
      ArrayInitialize(buffer_data[s].open,EMPTY_VALUE);
      ArrayInitialize(buffer_data[s].high,EMPTY_VALUE);
      ArrayInitialize(buffer_data[s].low,EMPTY_VALUE);
      ArrayInitialize(buffer_data[s].close,EMPTY_VALUE);
      ArrayInitialize(buffer_data[s].icolor,EMPTY_VALUE);
     }
  }
//+------------------------------------------------------------------+
//| Initializes array of symbols                                     |
//+------------------------------------------------------------------+
void InitSymbolNames()
  {
   symbol_names[0]=AddSymbolToMarketWatch(Symbol02);
   symbol_names[1]=AddSymbolToMarketWatch(Symbol03);
   symbol_names[2]=AddSymbolToMarketWatch(Symbol04);
   symbol_names[3]=AddSymbolToMarketWatch(Symbol05);
   symbol_names[4]=AddSymbolToMarketWatch(Symbol06);
  }
//+------------------------------------------------------------------+
//| Initializes array of inversions                                  |
//+------------------------------------------------------------------+
void InitInverse()
  {
   inverse[0]=Inverse02;
   inverse[1]=Inverse03;
   inverse[2]=Inverse04;
   inverse[3]=Inverse05;
   inverse[4]=Inverse06;
  }

Sono state apportate modifiche significative alla funzione SetIndicatorProperties(). In realtà, questa è una funzione completamente nuova. Ora, a seconda della modalità di rendering dei dati selezionata, le proprietà corrispondenti vengono impostate durante l'inizializzazione.

//+------------------------------------------------------------------+
//| Sets indicator properties                                        |
//+------------------------------------------------------------------+
void SetIndicatorProperties()
  {
//--- Set the short name
   IndicatorSetString(INDICATOR_SHORTNAME,indicator_shortname);
//--- Set the number of decimal digits
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
//---  In the 'Line' mode we need only one buffers that displays the open price
   if(DrawType==LINE)
     {
      for(int s=0; s<SYMBOLS_COUNT; s++)
         SetIndexBuffer(s,buffer_data[s].close,INDICATOR_DATA);
     }
//--- In other modes we use all prices for drawing 
//    bars/candlesticks and additional buffer for the two-color mode
   else if(DrawType==BARS || DrawType==CANDLES)
     {
      for(int s=0; s<SYMBOLS_COUNT; s++)
        {
         static int buffer_number=0;
         SetIndexBuffer(buffer_number,buffer_data[s].open,INDICATOR_DATA);
         buffer_number++;
         SetIndexBuffer(buffer_number,buffer_data[s].high,INDICATOR_DATA);
         buffer_number++;
         SetIndexBuffer(buffer_number,buffer_data[s].low,INDICATOR_DATA);
         buffer_number++;
         SetIndexBuffer(buffer_number,buffer_data[s].close,INDICATOR_DATA);
         buffer_number++;
         SetIndexBuffer(buffer_number,buffer_data[s].icolor,INDICATOR_COLOR_INDEX);
         buffer_number++;
        }
     }
//--- Set labels for the current timeframe
//    In the 'Line' mode only opening price is used
   if(DrawType==LINE)
     {
      for(int s=0; s<SYMBOLS_COUNT; s++)
         PlotIndexSetString(s,PLOT_LABEL,symbol_names[s]+",Close");
     }
//--- In other modes all prices of bars/candlesticks
//    ";" is used as a separator
   else if(DrawType==BARS || DrawType==CANDLES)
     {
      for(int s=0; s<SYMBOLS_COUNT; s++)
        {
         PlotIndexSetString(s,PLOT_LABEL,
                            symbol_names[s]+",Open;"+
                            symbol_names[s]+",High;"+
                            symbol_names[s]+",Low;"+
                            symbol_names[s]+",Close");
        }
     }
//--- Set the type of lines for indicator buffers
//--- Line
   if(DrawType==LINE)
      for(int s=0; s<SYMBOLS_COUNT; s++)
         PlotIndexSetInteger(s,PLOT_DRAW_TYPE,DRAW_LINE);
//--- Bars
   if(DrawType==BARS)
      for(int s=0; s<SYMBOLS_COUNT; s++)
         PlotIndexSetInteger(s,PLOT_DRAW_TYPE,DRAW_COLOR_BARS);
//--- Candlesticks
   if(DrawType==CANDLES)
      for(int s=0; s<SYMBOLS_COUNT; s++)
         PlotIndexSetInteger(s,PLOT_DRAW_TYPE,DRAW_COLOR_CANDLES);

//--- Set the type of lines for data of current symbol
//--- Line
   if(DrawType==LINE)
      ChartSetInteger(0,CHART_MODE,CHART_LINE);
//--- Bars
   if(DrawType==BARS)
      ChartSetInteger(0,CHART_MODE,CHART_BARS);
//--- Candlesticks
   if(DrawType==CANDLES)
      ChartSetInteger(0,CHART_MODE,CHART_CANDLES);

//--- Set the line width
   for(int s=0; s<SYMBOLS_COUNT; s++)
      PlotIndexSetInteger(s,PLOT_LINE_WIDTH,1);
//--- Set the line color for the 'Line' mode
   if(DrawType==LINE)
      for(int s=0; s<SYMBOLS_COUNT; s++)
         PlotIndexSetInteger(s,PLOT_LINE_COLOR,line_colors[s]);
//--- Display data in Data Window only for existing symbols
   for(int s=0; s<SYMBOLS_COUNT; s++)
     {
      if(symbol_names[s]!=empty_symbol)
         PlotIndexSetInteger(s,PLOT_SHOW_DATA,true);
      else
         PlotIndexSetInteger(s,PLOT_SHOW_DATA,false);
     }
//--- Empty value for plotting where nothing will be drawn
   for(int s=0; s<SYMBOLS_COUNT; s++)
      PlotIndexSetDouble(s,PLOT_EMPTY_VALUE,EMPTY_VALUE);
  }

E infine, un'altra nuova funzione SetDivergenceLine() da utilizzare in OnInit(). Imposta la linea verde verticale per manipolare il punto iniziale della divergenza di prezzo nella modalità Linea verticale.

//+------------------------------------------------------------------+
//| Sets vertical line for price divergence starting point           |
//+------------------------------------------------------------------+
void SetDivergenceLine()
  {
//--- If there is no vertical line yet, set it
   if(StartPriceDivergence==VERTICAL_LINE && ObjectFind(0,start_price_divergence)<0)
      //--- Place a vertical line on the true bar
      CreateVerticalLine(0,0,TimeCurrent()+PeriodSeconds(),start_price_divergence,
                         2,STYLE_SOLID,clrGreenYellow,true,true,false,"","\n");
//--- For all other modes except the 'Vertical Line'
   if(StartPriceDivergence!=VERTICAL_LINE)
      DeleteObjectByName(start_price_divergence);
  }

Di seguito è riportata la rappresentazione di tutto ciò che è descritto sopra all'interno della funzione OnInit(). Quando tutto è diviso in funzioni e file separati, diventa molto comodo leggere il codice del programma.

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Check if indicator is currently being used in Strategy Tester
   if(!CheckTesterMode())
      return(INIT_FAILED);
//--- Set the color for bars/candlesticks
   SetBarsColors();
//--- Define the timeframe for the price divergence starting point
   InitStartPointTF();
//--- Check input parameters for correctness
   if(!CheckInputParameters())
      return(INIT_PARAMETERS_INCORRECT);
//--- Set the timer at 1-second intervals
   EventSetMillisecondTimer(1000);
//--- Set the font to be displayed on the canvas
   canvas.FontSet(font_name,font_size,FW_NORMAL);
//--- Initialization of arrays
   InitArrays();
//--- Initialize the array of symbols 
   InitSymbolNames();
//--- Initialize the array of inversions
   InitInverse();
//--- Set indicator properties
   SetIndicatorProperties();
//--- Set vertical line of the price divergence start
   SetDivergenceLine();
//--- Clear the comment
   Comment("");
//--- Refresh the chart
   ChartRedraw();
//--- Initialization completed successfully
   return(INIT_SUCCEEDED);
  }

Nella funzione OnCalculate(), il codice del programma è rimasto praticamente invariato. Nell'articolo precedente, dopo aver effettuato tutti i controlli sulla disponibilità dei dati, il programma ha prima riempito gli array ausiliari e solo dopo ha riempito i buffer degli indicatori con i dati preparati. Questa volta cercheremo di organizzare tutto in un unico ciclo.

Ho reso più rigorose le funzioni di convalida e caricamento dei dati. Ora ogni valore che si desidera ottenere passa attraverso il numero specificato di tentativi. Se si ottiene un valore, il ciclo viene interrotto. E poiché ora abbiamo modalità in cui dobbiamo determinare l'inizio di un periodo (mese, settimana, giorno, ora), quindi otterremo l'ora di inizio del periodo attraverso l'intervallo di tempo più alto. Pertanto, ho creato una funzione aggiuntiva simile a LoadAndFormData() che ha un nome simile a LoadAndFormDataHighTF(). Il suo codice è molto simile a quello originale, quindi non lo pubblicherò qui.

La verifica della disponibilità dei dati per i tempi correnti e superiori è stata implementata in un'unica funzione CheckAvailableData():

//+------------------------------------------------------------------+
//| Checks the amount of available data for all symbols              |
//+------------------------------------------------------------------+
bool CheckAvailableData()
  {
   int attempts=100;
   
//---
   for(int s=0; s<SYMBOLS_COUNT; s++)
     {
      //--- If this symbol is available
      if(symbol_names[s]!=empty_symbol)
        {
datetime time[];                    // Array for checking the number of bars
   int      total_period_bars   =0;    // Number of bars of the current period
   datetime terminal_first_date =NULL; // First date of the current time frame data available in the terminal
         //--- Get the first date of the current time frame data in the terminal
         terminal_first_date=(datetime)SeriesInfoInteger(symbol_names[s],Period(),SERIES_TERMINAL_FIRSTDATE);
         //--- Get the number of available bars from the date specified
         total_period_bars=Bars(symbol_names[s],Period(),terminal_first_date,TimeCurrent());
         //--- Check the readiness of bar data
         for(int i=0; i<attempts; i++)
           {
            //--- Copy the specified amount of data
            if(CopyTime(symbol_names[s],Period(),0,total_period_bars,time))
              {
               //--- If the required amount has been copied, terminate the loop
               if(ArraySize(time)>=total_period_bars)
                  break;
              }
           }
         //--- If the amount of data copied is not sufficient, one more attempt is required
         if(ArraySize(time)==0 || ArraySize(time)<total_period_bars)
           {
            msg_last=msg_prepare_data;
            ShowCanvasMessage(msg_prepare_data);
            OC_prev_calculated=0;
            return(false);
           }
        }
     }
//--- Exit if current mode is vertical line of the price divergence starting point
   if(StartPriceDivergence==VERTICAL_LINE)
      return(true);
   else
     {
      datetime time[];                    // Array for checking the number of bars
      int      total_period_bars   =0;    // Number of bars of the current period
      datetime terminal_first_date =NULL; // First date of the current time frame data available in the terminal
      //--- Get the first date of the current time frame data in the terminal
      for(int i=0; i<attempts; i++)
         if((terminal_first_date=(datetime)SeriesInfoInteger(Symbol(),Period(),SERIES_FIRSTDATE))>0)
            break;
      //--- Get the number of available bars from the date specified
      for(int i=0; i<attempts; i++)
         if((total_period_bars=(int)SeriesInfoInteger(Symbol(),timeframe_start_point,SERIES_BARS_COUNT))>0)
            break;
      //--- Check the readiness of bar data
      for(int i=0; i<attempts; i++)
         //--- Copy the specified amount of data
         if(CopyTime(Symbol(),timeframe_start_point,
            terminal_first_date+PeriodSeconds(timeframe_start_point),TimeCurrent(),time)>0)
            break;
      //--- If the amount of data copied is not sufficient, one more attempt is required
      if(ArraySize(time)<=0 || total_period_bars<=0)
        {
         msg_last=msg_prepare_data;
         ShowCanvasMessage(msg_prepare_data);
         OC_prev_calculated=0;
         return(false);
        }
     }
//---
   return(true);
  }

La funzione FillIndicatorBuffers() è stata notevolmente complicata per l'attività corrente. Ciò è dovuto al fatto che ora ci sono diverse modalità e ognuna di esse richiede le proprie azioni. In effetti, tutto può essere diviso in quattro passaggi.

  • Ottenere i dati per il simbolo specificato.
  • Ottenere dati per un intervallo di tempo più elevato e determinare il tempo e il livello dei prezzi, in cui i prezzi di tutti i simboli si incontrano.
  • Calcolo dei valori e riempimento del buffer dell'indicatore.
  • Verifica dei valori calcolati.

Il codice funzione viene fornito con commenti dettagliati per la vostra considerazione:

//+------------------------------------------------------------------+
//| Fills indicator buffers                                          |
//+------------------------------------------------------------------+
void FillIndicatorBuffers(int i,int s,datetime const &time[])
  {
   MqlRates    rates[];             // Data structure
   double      period_open[];       // Opening price for bar at the price divergence starting point
   datetime    period_time[];       // Time of the price divergence starting point
   int         attempts=100;        // Number of copying attempts
   datetime    high_tf_time=NULL;   // Time of higher timeframe's bar

//--- Exit if we are out of "true" bars zone
   if(time[i]<limit_time[s])
      return;
//--- Reset the last error
   ResetLastError();
//--- Get data of current bar for the specified symbol
   for(int j=0; j<attempts; j++)
      if(CopyRates(symbol_names[s],Period(),time[i],1,rates)==1)
        { ResetLastError(); break; }
//--- Exit if failed to get data
   if(ArraySize(rates)<1 || GetLastError()!=0)
      return;
//--- If the current time is before the first timeframe's time or
//    bar time is not equal to the bar time of the current symbol or
//    empty values are fetched
   if(rates[0].time==NULL || 
      time[i]!=rates[0].time || 
      time[i]<first_period_time || 
      rates[0].low==EMPTY_VALUE || 
      rates[0].open==EMPTY_VALUE ||
      rates[0].high==EMPTY_VALUE ||
      rates[0].close==EMPTY_VALUE)
     {
      //--- Write empty value
      if(DrawType!=LINE)
        {
         buffer_data[s].low[i]   =EMPTY_VALUE;
         buffer_data[s].open[i]  =EMPTY_VALUE;
         buffer_data[s].high[i]  =EMPTY_VALUE;
        }
      buffer_data[s].close[i]=EMPTY_VALUE;
      return;
     }
//--- If current mode is vertical line of the price divergence starting point
   if(StartPriceDivergence==VERTICAL_LINE)
     {
      //--- Get the time of the line
      divergence_time=(datetime)ObjectGetInteger(0,start_price_divergence,OBJPROP_TIME);
      //--- Get the time of the first bar
      first_period_time=time[0];
     }
//--- For all other modes, we will keep track the beginning of period
   else
     {
      //--- If we are here for the first time, store data of the first bar of higher timeframe
      if(divergence_time==NULL)
        {
         ResetLastError();
         //--- Get opening time of the first bar of higher timeframe
         for(int j=0; j<attempts; j++)
            if(CopyTime(Symbol(),timeframe_start_point,time[0]+PeriodSeconds(timeframe_start_point),1,period_time)==1)
              { ResetLastError(); break; }
         //--- Exit if failed to get price/time
         if(ArraySize(period_time)<1 || GetLastError()!=0)
            return;
         //--- Otherwise store time of the first bar of higher timeframe
         else
            first_period_time=period_time[0];
        }
      //--- If current bar's time on the current timeframe is before the first bar's time on higher timeframe
      if(time[i]<first_period_time)
         high_tf_time=first_period_time;
      //--- Otherwise we will receive data of the last bar of the higher timeframe with respect to the current bar on the current timeframe
      else
         high_tf_time=time[i];
      //--- Reset the last error
      ResetLastError();
      //--- Get the opening price of the first bar of the higher timeframe
      for(int j=0; j<attempts; j++)
         if(CopyOpen(Symbol(),timeframe_start_point,high_tf_time,1,period_open)==1)
           { ResetLastError(); break; }
      //--- Get opening time of the first bar of higher timeframe
      for(int j=0; j<attempts; j++)
         if(CopyTime(Symbol(),timeframe_start_point,high_tf_time,1,period_time)==1)
           { ResetLastError(); break; }
      //--- Exit if failed to get price/time
      if(ArraySize(period_open)<1 || ArraySize(period_time)<1 || GetLastError()!=0)
         return;
      //--- If the current timeframe's time is before the first period's time or
      //    time of specified period is not equal to the one in memory
      if(time[i]<first_period_time || divergence_time!=period_time[0])
        {
         symbol_difference[s]  =0.0; // Zero out difference in symbol prices
         inverse_difference[s] =0.0; // Zero our difference of inversion
         //--- Store time of the price divergence starting point
         divergence_time=period_time[0];
         //--- Store price of the price divergence starting point
         divergence_price=period_open[0];
         //--- Set vertical line in the beginning of the price divergence start
         CreateVerticalLine(0,0,period_time[0],start_price_divergence+"_"+TimeToString(divergence_time),
                            2,STYLE_SOLID,clrWhite,false,false,true,TimeToString(divergence_time),"\n");
        }
     }
//--- If current mode is 'Vertical Line' and bar's time is less than line's time
   if(StartPriceDivergence==VERTICAL_LINE && time[i]<divergence_time)
     {
      //--- Keep zero values of difference
      symbol_difference[s]  =0.0;
      inverse_difference[s] =0.0;
      //--- For the 'Line' drawing mode only opening price is used
      if(DrawType==LINE)
         buffer_data[s].close[i]=rates[0].close-symbol_difference[s];
      //--- For all other modes all prices are used
      else
        {
         buffer_data[s].low[i]   =rates[0].low-symbol_difference[s];
         buffer_data[s].open[i]  =rates[0].open-symbol_difference[s];
         buffer_data[s].high[i]  =rates[0].high-symbol_difference[s];
         buffer_data[s].close[i] =rates[0].close-symbol_difference[s];
         //--- Set color for the current element of indicator buffer
         SetBufferColorIndex(i,s,rates[0].close,rates[0].open);
        }
     }
//--- For all other modes
   else
     {
      //--- If inversion of symbol data is required
      if(inverse[s])
        {
         //--- If new period has started, recalculate variables
         if(symbol_difference[s]==0.0)
           {
            //--- For the 'Vertical Line' mode
            if(StartPriceDivergence==VERTICAL_LINE)
              {
               //--- Calculate the difference
               symbol_difference[s]  =rates[0].open-OC_open[i];
               inverse_difference[s] =OC_open[i]-(-OC_open[i]);
              }
            //--- For all other modes
            else
              {
               //--- Calculate the difference
               symbol_difference[s]  =rates[0].open-divergence_price;
               inverse_difference[s] =divergence_price-(-divergence_price);
              }
           }
         //--- In the 'Line' mode only opening price is used
         if(DrawType==LINE)
            buffer_data[s].close[i]=-(rates[0].close-symbol_difference[s])+inverse_difference[s];
         //--- For all other modes all prices are used
         else
           {
            buffer_data[s].low[i]   =-(rates[0].low-symbol_difference[s])+inverse_difference[s];
            buffer_data[s].open[i]  =-(rates[0].open-symbol_difference[s])+inverse_difference[s];
            buffer_data[s].high[i]  =-(rates[0].high-symbol_difference[s])+inverse_difference[s];
            buffer_data[s].close[i] =-(rates[0].close-symbol_difference[s])+inverse_difference[s];
            //--- Set color for the current element of indicator buffer
            SetBufferColorIndex(i,s,rates[0].close,rates[0].open);
           }
        }
      //--- If inversion is not used, then we need to calculate only the difference between symbol prices at the beginning of period
      else
        {
         //--- If new period has started
         if(symbol_difference[s]==0.0)
           {
            //--- For the 'Vertical Line' mode
            if(StartPriceDivergence==VERTICAL_LINE)
               symbol_difference[s]=rates[0].open-OC_open[i];
            //--- For all other modes
            else
               symbol_difference[s]=rates[0].open-divergence_price;
           }
         //--- For the 'Line' drawing mode only opening price is used
         if(DrawType==LINE)
            buffer_data[s].close[i]=rates[0].close-symbol_difference[s];
         //--- For all other modes all prices are used
         else
           {
            buffer_data[s].low[i]   =rates[0].low-symbol_difference[s];
            buffer_data[s].open[i]  =rates[0].open-symbol_difference[s];
            buffer_data[s].high[i]  =rates[0].high-symbol_difference[s];
            buffer_data[s].close[i] =rates[0].close-symbol_difference[s];
            //--- Set color for the current element of indicator buffer
            SetBufferColorIndex(i,s,rates[0].close,rates[0].open);
           }
        }
     }
//--- Verification of the calculated values
//    In the 'Line' mode only opening price is used
   if(DrawType==LINE)
     {
      //--- If the current time is before the first timeframe's time or
      //    bar time is not equal to the bar time, write empty value
      if(time[i]!=rates[0].time || time[i]<first_period_time)
         buffer_data[s].close[i]=EMPTY_VALUE;
     }
//--- For all other modes all prices are used
   else
     {
      //--- If the current time is before the first timeframe's time or
      //    bar time is not equal to the bar time of the current symbol or
      //    empty values are fetched
      if(rates[0].time==NULL || 
         time[i]!=rates[0].time || 
         time[i]<first_period_time || 
         rates[0].low==EMPTY_VALUE || 
         rates[0].open==EMPTY_VALUE ||
         rates[0].high==EMPTY_VALUE ||
         rates[0].close==EMPTY_VALUE)
        {
         //--- Write empty value
         buffer_data[s].low[i]   =EMPTY_VALUE;
         buffer_data[s].open[i]  =EMPTY_VALUE;
         buffer_data[s].high[i]  =EMPTY_VALUE;
         buffer_data[s].close[i] =EMPTY_VALUE;
        }
     }
  }

Quando si studia la funzione sopra si dovrebbe notare un'altra funzione personalizzata SetBufferColorIndex(). Questa funzione imposta il colore nel buffer di colore dell'indicatore.

//+------------------------------------------------------------------+
//| Sets the color for buffer element by condition                   |
//+------------------------------------------------------------------+
void SetBufferColorIndex(int i,int symbol_number,double close,double open)
  {
//--- For two-color mode, check condition
   if(TwoColor)
     {
      //--- If the closing price is more than the opening price, this is up bar, so we use the first color
      if(close>open)
         buffer_data[symbol_number].icolor[i]=0;
      //--- otherwise it is down bar, so we use the second color
      else
         buffer_data[symbol_number].icolor[i]=1;
     }
//--- For one-color mode we use the first color for all bars/candlesticks
   else
      buffer_data[symbol_number].icolor[i]=0;
  }

Una volta riempiti i buffer dell'indicatore, è necessario determinare il massimo e il minimo da tutti i valori attualmente visibili nella finestra del grafico. MQL5 permette di ottenere la prima barra visibile in una finestra del grafico e il numero di barre visibili. Trarremo vantaggio da queste funzionalità in un'altra funzione personalizzata CorrectChartMaxMin(). Il flusso di codice nella funzione può essere suddiviso in diversi passaggi:

  • Determinazione dei numeri della prima e dell'ultima barre visibili.
  • Determinazione del massimo e del minimo delle barre visibili per il simbolo corrente.
  • Determinazione del massimo e del minimo tra tutte le matrici di simboli.
  • Impostazione del massimo e del minimo nelle proprietà del grafico.

Di seguito è riportato il codice della funzione CorrectChartMaxMin() che si trova nel file Chart.mqh.

//+------------------------------------------------------------------+
//| Corrects chart's high/low with respect to all buffers            |
//+------------------------------------------------------------------+
void CorrectChartMaxMin()
  {
   double low[];                  // Array of lows
   double high[];                 // Array of highs
   int    attempts          =10;  // Number of attempts
   int    array_size        =0;   // Array size for drawing
   int    visible_bars      =0;   // Number of visible bars
   int    first_visible_bar =0;   // Number of the first visible bar
   int    last_visible_bar  =0;   // Number of the last visible bar
   double max_price         =0.0; // Highest price
   double min_price         =0.0; // Lowest price
   double offset_max_min    =0.0; // Offset from chart's high/low
//--- Reset the last error
   ResetLastError();
//--- Number of visible bars
   visible_bars=(int)ChartGetInteger(0,CHART_VISIBLE_BARS);
//--- Number of the first visible bar
   first_visible_bar=(int)ChartGetInteger(0,CHART_FIRST_VISIBLE_BAR);
//--- Number of the last visible bar
   last_visible_bar=first_visible_bar-visible_bars;
//--- Exit in case of error
   if(GetLastError()!=0)
      return;
//--- Fix incorrect value
   if(last_visible_bar<0)
      last_visible_bar=0;
//--- Get the current symbol high/low in visible area of chart
   for(int i=0; i<attempts; i++)
      if(CopyHigh(Symbol(),Period(),last_visible_bar,visible_bars,high)==visible_bars)
         break;
   for(int i=0; i<attempts; i++)
      if(CopyLow(Symbol(),Period(),last_visible_bar,visible_bars,low)==visible_bars)
         break;
//--- Exit if failed to get data
   if(ArraySize(high)<=0 || ArraySize(low)<=0)
      return;
//--- If succeeded to get data, identify high and low in the current symbol arrays
   else
     {
      min_price=low[ArrayMinimum(low)];
      max_price=high[ArrayMaximum(high)];
     }
//--- Get high and low prices in all price arrays
   for(int s=0; s<SYMBOLS_COUNT; s++)
     {
      //--- If current symbol is not present, go to the next one
      if(symbol_names[s]==empty_symbol)
         continue;
      //---
      datetime time[];         // Time array
      int      bars_count=0; // Number of bars for calculation
      //--- Set zero size for arrays
      ArrayResize(high,0);
      ArrayResize(low,0);
      //--- Get the time of the first bar visible on chart
      for(int i=0; i<attempts; i++)
         if(CopyTime(Symbol(),Period(),last_visible_bar,visible_bars,time)==visible_bars)
            break;
      //--- Exit if the amount of data is less than number of visible bars on chart
      if(ArraySize(time)<visible_bars)
         return;
      //--- If time of the first "true" bar is greater than
      //    time of the first visible bar on the chart, then
      //    get available number of bars of the current symbol in loop
      if(limit_time[s]>time[0])
        {
         //--- Get the array size
         array_size=ArraySize(time);
         //--- Get the number of bars from the first "true" one
         if((bars_count=Bars(Symbol(),Period(),limit_time[s],time[array_size-1]))<=0)
            return;
        }
      //--- Else get number of visible bars on chart
      else
         bars_count=visible_bars;
      //--- Index elements in indicator buffers as timeseries
      ArraySetAsSeries(low,true);
      ArraySetAsSeries(high,true);
      //--- Copy data from the indicator buffer
      //    All modes except 'Line'
      if(DrawType!=LINE)
        {
         ArrayCopy(low,buffer_data[s].low);
         ArrayCopy(high,buffer_data[s].high);
        }
      //--- For the 'Line' mode
      else
        {
         ArrayCopy(low,buffer_data[s].close);
         ArrayCopy(high,buffer_data[s].close);
        }
      //--- Get the array size
      array_size=ArraySize(high);
      //--- Fill empty values,
      //    so they are not considered when calculating high/low
      for(int i=0; i<array_size; i++)
        {
         if(high[i]==EMPTY_VALUE)
            high[i]=max_price;
         if(low[i]==EMPTY_VALUE)
            low[i]=min_price;
        }
      //--- Get high/low with respect to inversion
      if(inverse[s])
        {
         //--- If no errors occur, store values
         if(ArrayMaximum(high,last_visible_bar,bars_count)>=0 && 
            ArrayMinimum(low,last_visible_bar,bars_count)>=0)
           {
            max_price=fmax(max_price,low[ArrayMaximum(low,last_visible_bar,bars_count)]);
            min_price=fmin(min_price,high[ArrayMinimum(high,last_visible_bar,bars_count)]);
           }
        }
      else
        {
         //--- If no errors occur, store values
         if(ArrayMinimum(low,last_visible_bar,bars_count)>=0 && 
            ArrayMaximum(high,last_visible_bar,bars_count)>=0)
           {
            min_price=fmin(min_price,low[ArrayMinimum(low,last_visible_bar,bars_count)]);
            max_price=fmax(max_price,high[ArrayMaximum(high,last_visible_bar,bars_count)]);
           }
        }
     }
//--- Calculate offset (3%) form chart's top and bottom
   offset_max_min=((max_price-min_price)*3)/100;
//--- Turn on the fixed chart scale mode.
   ChartSetInteger(0,CHART_SCALEFIX,true);
//--- Set high/low
   ChartSetDouble(0,CHART_FIXED_MAX,max_price+offset_max_min);
   ChartSetDouble(0,CHART_FIXED_MIN,min_price-offset_max_min);
//--- Refresh the chart
   ChartRedraw();
  }

La funzione sopra descritta verrà utilizzata durante l'elaborazione dell'evento di trascinamento della linea verticale (e quando si calcolano i valori dell'indicatore in OnCalculate, ovviamente):

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Event of dragging a graphical object
   if(id==CHARTEVENT_OBJECT_DRAG)
     {
      //--- If current mode is vertical line for the price divergence starting point, then update indicator buffers
      if(StartPriceDivergence==VERTICAL_LINE)
         OnCalculate(OC_rates_total,
                     0,
                     OC_time,
                     OC_open,
                     OC_high,
                     OC_low,
                     OC_close,
                     OC_tick_volume,
                     OC_volume,
                     OC_spread);
     }
//--- Event of resizing the chart or modifying the chart properties using the properties dialog window.
   if(id==CHARTEVENT_CHART_CHANGE)
      //--- Correct the maximum and minimum of chart with respect to the indicator buffers' values
      CorrectChartMaxMin();
  }

Tutte le funzioni sono pronte. Puoi studiare il codice completamente commentato allegato a questo articolo.

Dimostriamo cosa abbiamo alla fine ottenuto. I simboli predefiniti GBPUSD,, AUDUSD, NZDUSD, USDCAD, USDCHF sono specificati in parametri esterni. Nello screenshot qui sotto puoi vedere il grafico settimanale per EURUSD in modalità Linea verticale con inversione disabilitata:

Intervallo di tempo settimanale nella modalità "Linea verticale"

Fig. 1 - Intervallo di tempo settimanale in modalità "Linea verticale"

Nello screenshot qui sotto puoi vedere l'intervallo di tempo M30 in modalità Giorno, ma questa volta l'inversione è abilitata per i simboli con USD come valuta di base. Nel nostro caso si tratta di USDCAD (candele azzurre) e USDCHF (candele viola).

Intervallo di tempo M30 nella modalità "Giorno"

Fig. 2 - Intervallo di tempo M30 in modalità "Giorno"


Conclusione

Penso che abbiamo creato uno strumento piuttosto interessante e informativo per l'analisi multi-valuta della divergenza dei prezzi. Questo indicatore può essere migliorato all'infinito.

Grazie per il vostro tempo!

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

MQL5 Cookbook - Consulente esperto multi-valuta e il lavoro con ordini in sospeso in MQL5 MQL5 Cookbook - Consulente esperto multi-valuta e il lavoro con ordini in sospeso in MQL5
Questa volta creeremo un Expert Advisor multi-valuta con un algoritmo di trading basato sul lavoro con gli ordini in sospeso Buy Stop e Sell Stop. Questo articolo considera le seguenti questioni: fare trading in un intervallo di tempo specificato, inserire/modificare/eliminare ordini in sospeso, verificare se l'ultima posizione è stata chiusa a Take Profit o Stop Loss e controllo della cronologia delle operazioni per ciascun simbolo.
Forum sulla programmazione MQL5 Liste Forum sulla programmazione MQL5 Liste
La nuova versione del linguaggio di programmazione per lo sviluppo di strategie di trading, MQL [MQL5], fornisce funzionalità più potenti ed efficaci rispetto alla versione precedente [MQL4]. Il vantaggio risiede essenzialmente nelle funzionalità di programmazione orientata agli oggetti. Questo articolo esamina la possibilità di utilizzare tipi di dati personalizzati complessi, come nodi ed elenchi. Fornisce inoltre un esempio di utilizzo delle liste nella programmazione pratica in MQL5.
Indicatore per la creazione di grafici Renko Indicatore per la creazione di grafici Renko
L'articolo descrive un esempio di creazione di grafici Renko e la sua implementazione in MQL5 come indicatore. Le modifiche di questo indicatore lo distinguono da un grafico classico. Può essere costruito sia nella finestra dell'indicatore che sul grafico principale. Inoltre, c'è l'indicatore ZigZag. Puoi trovare alcuni esempi dell'implementazione del grafico.
Econometrics Previsioni EURUSD One-Step-Ahead Econometrics Previsioni EURUSD One-Step-Ahead
L'articolo si concentra sulla previsione anticipata per EURUSD utilizzando il software EViews e un'ulteriore valutazione dei risultati delle previsioni utilizzando i programmi in EViews. La previsione prevede modelli di regressione e viene valutata tramite un Expert Advisor sviluppato per MetaTrader 4.