English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
L'implementazione di una modalità multivaluta su MetaTrader 5

L'implementazione di una modalità multivaluta su MetaTrader 5

MetaTrader 5Esempi | 17 dicembre 2021, 15:06
102 0
Konstantin Gruzdev
Konstantin Gruzdev

Introduzione

Attualmente esiste un gran numero di sistemi di trading multivaluta sviluppati, indicatori ed Expert Advisor. Tuttavia, gli sviluppatori continuano ad affrontare i problemi specifici dello sviluppo di sistemi multivaluta.

Con il rilascio del terminal client MetaTrader 5 e del linguaggio di programmazione MQL5 abbiamo acquisito una nuova opportunità per implementare una modalità multivaluta completa e, di conseguenza, robot e indicatori multivaluta più efficienti. Queste nuove opportunità saranno l'argomento dell’articolo.


Panoramica dell'approccio convenzionale

Nel nostro caso l'approccio convenzionale è il tentativo di implementare un sistema multivaluta basato sulle funzioni standard OnTick() e OnCalculate() che ha sostituito la funzione start() di MQL4. In poche parole, con la comparsa di un nuovo tick o una nuova barra sul grafico corrente, tutte le coppie di valute (che partecipano al sistema multivaluta) vengono richieste in sequenza per l'analisi e il processo decisionale successivi.

I problemi di questo approccio sono:

  1. La dipendenza dell'intero sistema dall'arrivo di tick su un simbolo di trading del grafico corrente.

    Per un mercato veloce, quando i tick per il simbolo del grafico attuale sono frequenti, non ci sono proprio problemi. Ma con un mercato lento, ad esempio durante la notte, i tick possono arrivare molto raramente: una volta ogni mezzo minuto o anche più rare. Durante gli intervalli tra l'arrivo di tick rari, l'intero sistema multivaluta è "dormiente", anche se i cambiamenti che si verificano sugli altri simboli possono essere piuttosto drastici.

    Questa assenza non è molto significativa se il sistema è configurato per lavorare su tempi lunghi. Ma minore è il lasso di tempo, maggiore è l'effetto. Ogni giorno il mondo aumenta la velocità, i computer sono in grado di elaborare sempre più informazioni per unità di tempo e, di conseguenza, sempre più persone sono disposte a lavorare su piccoli periodi e persino su tick.

  2. La complessità della sincronizzazione dei dati storici su tutti i simboli utilizzati in un sistema multivaluta.

    "Su MetaTrader 4 vengono disegnate solo le barre all'interno delle quali è avvenuta almeno una variazione di prezzo. Se non si verifica alcuna variazione di prezzo entro un minuto, si verificherà un gap di una barra nel grafico con un periodo di un minuto". Così si afferma all'inizio dell'articolo "Free-of-Holes" Charts.

    Tale approccio alla costruzione di un grafico è mantenuto su MetaTrader 5. Ovvero, avere lo stesso numero di barre sul grafico per ogni simbolo non significa che siano sincronizzate nel tempo. Ad esempio, la centesima barra può avere un tempo di apertura diverso per ogni simbolo. Pertanto, durante la costruzione e il ricalcolo di un indicatore multivaluta è importante assicurarsi che tutte le barre siano coerenti tra loro per ciascun simbolo.

    Anche questo non è molto significativo se il sistema è configurato per lavorare su un arco di tempo ampio, poiché con l'aumento del periodo la probabilità di perdere una barra diminuisce notevolmente. Anche se, come si suol dire, non si è mai troppo prudenti. E non puoi mai saperlo, a meno che l’altra persona non faccia nulla.

    Dovremmo annotare separatamente il tempo di sincronizzazione della barra incompleta corrente. Quando appare una nuova barra sul grafico corrente, non significa che si siano formate nuove barre anche su altri simboli. Pertanto, un tentativo di chiedere il prezzo di una nuova barra attraverso un altro simbolo utilizzando le funzioni CopyXXXX() può portare all’ottenimento del prezzo della barra precedente per il simbolo o semplicemente a un errore di copiatura. A proposito, su un altro simbolo potrebbe formarsi una nuova barra molto prima di quella attuale. Questo può anche influenzare l'accuratezza della valutazione della situazione.

    L'articolo "Creazione di un Indicatore Multivaluta, Utilizzando un Numero di Buffer di Indicatori Intermedi" descrive le opzioni che più o meno risolvono il problema della sincronizzazione dei dati storici.

  3. Altro punto importante, relativo alla sincronizzazione dei dati: come facciamo a sapere che c'è stato un aggiornamento storico per qualche simbolo di trading?

    Ad esempio, quando costruiamo un indicatore di valuta unica non ci sono problemi. Se la variabile di input prev_calculated della funzione OnCalculate() si è azzerata, allora ricalcoliamo l'indicatore. Ma cosa facciamo se c'è stato un aggiornamento nella cronologia per il simbolo, e non sul grafico corrente? Ciò può verificarsi in qualsiasi momento e potrebbe essere necessario ricalcolare l'indicatore multivaluta. La risposta a questa domanda è abbastanza pertinente.

Senza nemmeno entrare nelle altre funzionalità, possiamo vedere che queste tre istanze sono sufficienti per causare così tanti problemi che il codice dell'EA multivaluta o dell'indicatore diventa troppo grande. Ma l'intera portata del problema non è stata risolta...


Nuove speranze con la funzione OnTimer()

La nuova capacità del programma MQL di generare l'evento Timer e il gestore di eventi standard OnTimer() lascia sperare nell’emersione di nuovi tipi di sistemi multivaluta. Ciò è dovuto, da un lato, al fatto che ora l'Expert Advisor/indicatore multivaluta potrebbe diventare indipendente dalla ricezione di tick dal simbolo del grafico corrente e, dall'altro, dal fatto che potremmo monitorare il lavoro dell'EA in base al tempo. Ma...

Ciò risolve parzialmente i problemi descritti nel paragrafo 1 della sezione precedente e, ovviamente, offre alcuni vantaggi. Ma proprio come quando si riceve l'evento NewTick, con la ricezione dell'evento Timer è necessario interrogare in sequenza tutte le coppie di valute nel tentativo di tenere traccia dei cambiamenti. A volte questo deve essere fatto abbastanza spesso, il che può aumentare significativamente l'uso delle risorse dei programmi MQL5.

Le problematiche individuate nei paragrafi 2 e 3 restano allo stesso livello di soluzione. Oltre a ciò, è necessario risolvere i problemi specifici nella funzione OnTimer(). Ad esempio, i problemi di inizializzazione/deinizializzazione del timer, il suo lavoro durante i fine settimana, ecc.

Senza sminuire gli ovvi vantaggi del gestore di eventi OnTimer(), va notato che non consente ancora di implementare una modalità multivaluta completa.


Nuove possibilità con la funzione OnChartEvent()

Le limitazioni delle funzioni standard di cui sopra sono dovute alla loro ristretta specializzazione: sono destinate a gestire eventi specifici e predefiniti e non sono destinate a sistemi multivaluta. 

Un elemento di salvataggio per lo sviluppatore di un sistema multivaluta può essere il gestore di eventi personalizzato standard OnChartEvent(). Consente al programmatore di generare i propri eventi a sua discrezione.

Ad esempio, nessuno può impedirci di utilizzare questa funzione per ottenere tick per qualsiasi simbolo sul grafico corrente. Tutto quello che dobbiamo fare è inviare una "spia" al grafico con il simbolo appropriato.

Con questa simpatica parola, "spia", mi riferisco al seguente indicatore:

//+------------------------------------------------------------------+
//|                                                         iSpy.mq5 |
//|                                            Copyright 2010, Lizar |
//|                                               lizar-2010@mail.ru |
//+------------------------------------------------------------------+
#define VERSION         "1.00 Build 2 (26 Dec 2010)"

#property copyright   "Copyright 2010, Lizar"
#property link        "https://www.mql5.com/ru/users/Lizar"
#property version     VERSION
#property description "iSpy agent-indicator. If you want to get ticks, attach it to the chart"
#property indicator_chart_window

input long            chart_id=0;        // chart id
input ushort          custom_event_id=0; // event id

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate (const int rates_total,        // size of price[] array
                 const int prev_calculated,  // bars, calculated at the previous call
                 const int begin,            // starting index of data
                 const double& price[]       // array for the calculation
   )
  {
   double price_current=price[rates_total-1];

   //--- Initialization:
   if(prev_calculated==0) 
     { // Generate and send "Initialization" event
      EventChartCustom(chart_id,0,(long)_Period,price_current,_Symbol);
      return(rates_total);
     }
   
   // When the new tick, let's generate the "New tick" custom event
   // that can be processed by Expert Advisor or indicator
   EventChartCustom(chart_id,custom_event_id+1,(long)_Period,price_current,_Symbol);
   
   //--- return value of prev_calculated for next call
   return(rates_total);
  }

Possiamo lanciare questa "spia" nel grafico del simbolo desiderato e quindi gestire i suoi messaggi nell'EA o nell'indicatore utilizzando la funzione OnChartEvent(). Per decodificare correttamente i messaggi della "spia", dobbiamo interpretare i parametri di questa funzione nel modo seguente:

  • id, identificatore dell'evento. Se id-CHARTEVENT_CUSTOM = 0, la nostra ""spia" segnala che la variabile prev_calculated è stata azzerata e che devono essere intraprese le azioni appropriate;
  • lparam, in questo caso indica il periodo del grafico su cui viene lanciata la "spia";
  • dparam, prezzo del tick. Per impostazione predefinita, questo è l'ultimo prezzo di chiusura. Sebbene durante il lancio di "spy" questo può essere impostato su qualsiasi valore dall'enumerazione ENUM_APPLIED_PRICE;
  • sparam, il nome del simbolo di trading su cui è stato ricevuto l'evento.

Per dimostrare il lavoro simultaneo di più "spie", scriveremo un semplice EA exSpy.mq5 (la versione completa è disponibile nell'archivio):

//+------------------------------------------------------------------+
//|                                                        exSpy.mq5 |
//|                                            Copyright 2010, Lizar |
//|                            https://www.mql5.com/ru/users/Lizar |
//+------------------------------------------------------------------+
#define VERSION       "1.00 Build 1 (26 Dec 2010)"

#property copyright   "Copyright 2010, Lizar"
#property link        "https://www.mql5.com/ru/users/Lizar"
#property version     VERSION
#property description "The Expert Advisor shows the work of iSPY indicator"

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {   
   if(iCustom("GBPUSD",PERIOD_M1,"iSpy",ChartID(),0)==INVALID_HANDLE) 
      { Print("Error in setting of spy on GBPUSD"); return(true);}
   if(iCustom("EURUSD",PERIOD_M1,"iSpy",ChartID(),1)==INVALID_HANDLE) 
      { Print("Error in setting of spy on EURUSD"); return(true);}
   if(iCustom("USDJPY",PERIOD_M1,"iSpy",ChartID(),2)==INVALID_HANDLE) 
      { Print("Error in setting of spy on USDJPY"); return(true);}
      
   Print("Spys ok, waiting for events...");
   //---
   return(0);
  }
  
//+------------------------------------------------------------------+
//| The Standard event handler.                                      |
//| See MQL5 Reference for the details.                              |
//|                                                                  |
//| In this case it is used for decoding of spy messages, sent by    |
//| iSPY spy indicator                                               |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // event id:
                                     // if id-CHARTEVENT_CUSTOM=0-"initialization" event
                const long&   lparam, // chart period
                const double& dparam, // price
                const string& sparam  // symbol
               )
  {
   if(id>=CHARTEVENT_CUSTOM)      
     {
      Print(TimeToString(TimeCurrent(),TIME_SECONDS)," -> id=",
            id-CHARTEVENT_CUSTOM,":  ",sparam," ",
            EnumToString((ENUM_TIMEFRAMES)lparam)," price=",dparam);
     }
  }
//+------------------------------------------------------------------+

Avvia l'Expert Advisor su qualsiasi grafico.

Ecco i risultati:


 

Come si evince dal log, otteniamo tutti i tick per il simbolo desiderato, nonché l'evento "initialization" che, in particolare, verrà ricevuto se c'è un aggiornamento o un caricamento della cronologia.

Riassumiamo i risultati intermedi:

  • Non dipendiamo dai segni di spunta del simbolo particolare come abbiamo fatto quando abbiamo usato le funzioni standard OnTick() e OnCalculate().
  • Non siamo limitati a intervalli di tempo strettamente definiti come quando si utilizza OnTimer().
  • Non è necessario richiedere tutti i simboli in una sequenza o in un ciclo per tenere traccia delle loro modifiche. Una volta che si sono verificati cambiamenti sul simbolo, otteniamo l'evento corrispondente. Possiamo identificare il simbolo su cui si è verificato l'evento grazie all'identificatore dell'evento id o dal parametro sparam.
  • Abbiamo risolto il problema del tracciamento della sincronizzazione dei dati storici con il server per ogni singolo simbolo.
  • Una piccola parte del lavoro è assegnata a programmi aggiuntivi. Ciò è rilevante per la parallelizzazione dell'elaborazione dei dati.
  • Nei parametri della funzione OnChartEvent() otteniamo informazioni aggiuntive: il periodo, il prezzo e il nome del simbolo. Ciò può semplificare notevolmente il codice dell'EA o dell'indicatore, poiché non è necessario richiedere ulteriori informazioni su questi dati.

A questo punto possiamo sostanzialmente concludere questo articolo, dal momento che abbiamo implementato con successo una modalità multivaluta tramite il terminale MetaTrader 5 e il linguaggio di programmazione MQL5. Ma questa è un'implementazione "grezza". Pertanto, andremo oltre.


L'implementazione di una modalità multivaluta

Se usi l'idea precedente di implementare un regime multivaluta nella sua forma pura, a un certo punto inizierai ad affrontare delle difficoltà. Il problema è che tale approccio ci permette di ottenere tutti i tick per ogni simbolo di trading su cui sta girando la "spia".

In un secondo, con un mercato veloce, potrebbe esserci un numero di tick su ciascun simbolo. Ciò può portare a un "blocco" dell'ordine dell'evento. Ecco un avviso dalla sezione Aiuto:

Il terminal client aggiunge la comparsa di eventi nella coda degli eventi. Quindi gli eventi vengono elaborati uno dopo l'altro a seconda dell'ordine in cui sono stati ricevuti. C'è un'eccezione per l'evento NewTick. Se la coda ha già un evento simile o questo evento è in elaborazione, il nuovo evento NewTick non viene accodato.

La coda degli eventi è limitata nella dimensione. All'overflow della coda i vecchi eventi vengono rimossi senza essere elaborati al fine di consentire la ricezione di nuovi eventi. Pertanto, è consigliato per scrivere gestori di eventi efficienti e non è raccomandato per usare loop infiniti (c'è un'eccezione per gli script, che gestiscono solo l'evento Start).

L'overflow della coda può portare alla perdita di eventi importanti per l'indicatore multivaluta o per l’EA. Almeno da una parte. Dall'altra, non sempre abbiamo bisogno di segni di spunta per tutti i simboli. A volte abbiamo bisogno di ottenere solo l'evento "new bar" su qualsiasi intervallo di tempo. O una serie di eventi "new bar" per diversi intervalli di tempo. Fondamentalmente, la nostra "spia" non è adatta a tali esigenze e il suo utilizzo non è molto conveniente.

Rendiamolo universale, così da non dover mai tornare alla questione di come ottenere un evento basato sul simbolo di un EA o di un indicatore multivaluta. A tal fine, prendiamo come esempio l'enumerazione di eventi ENUM_CHART_EVENT_SYMBOL dal "Pannello di controllo MCM" per la descrizione di Expert Advisor e indicatori multivaluta:

enum ENUM_CHART_EVENT_SYMBOL
  {
   CHARTEVENT_INIT      =0,         // "Initialization" event
   CHARTEVENT_NO        =0,         // No events

   CHARTEVENT_NEWBAR_M1 =0x00000001, // "New bar" event on M1 chart
   CHARTEVENT_NEWBAR_M2 =0x00000002, // "New bar" event on M2 chart
   CHARTEVENT_NEWBAR_M3 =0x00000004, // "New bar" event on M3 chart
   CHARTEVENT_NEWBAR_M4 =0x00000008, // "New bar" event on M4 chart
   
   CHARTEVENT_NEWBAR_M5 =0x00000010, // "New bar" event on M5 chart
   CHARTEVENT_NEWBAR_M6 =0x00000020, // "New bar" event on M6 chart
   CHARTEVENT_NEWBAR_M10=0x00000040, // "New bar" event on M10 chart
   CHARTEVENT_NEWBAR_M12=0x00000080, // "New bar" event on M12 chart
   
   CHARTEVENT_NEWBAR_M15=0x00000100, // "New bar" event on M15 chart
   CHARTEVENT_NEWBAR_M20=0x00000200, // "New bar" event on M20 chart
   CHARTEVENT_NEWBAR_M30=0x00000400, // "New bar" event on M30 chart
   CHARTEVENT_NEWBAR_H1 =0x00000800, // "New bar" event on H1 chart
   
   CHARTEVENT_NEWBAR_H2 =0x00001000, // "New bar" event on H2 chart
   CHARTEVENT_NEWBAR_H3 =0x00002000, // "New bar" event on H3 chart
   CHARTEVENT_NEWBAR_H4 =0x00004000, // "New bar" event on H4 chart
   CHARTEVENT_NEWBAR_H6 =0x00008000, // "New bar" event on H6 chart
   
   CHARTEVENT_NEWBAR_H8 =0x00010000, // "New bar" event on H8 chart
   CHARTEVENT_NEWBAR_H12=0x00020000, // "New bar" event on H12 chart
   CHARTEVENT_NEWBAR_D1 =0x00040000, // "New bar" event on D1 chart
   CHARTEVENT_NEWBAR_W1 =0x00080000, // "New bar" event on W1 chart
     
   CHARTEVENT_NEWBAR_MN1=0x00100000, // "New bar" event on MN1 chart
   CHARTEVENT_TICK      =0x00200000, // "New tick" event
   
   CHARTEVENT_ALL       =0xFFFFFFFF, // All events
  };

In effetti, questa enumerazione è un flag degli eventi del grafico personalizzato. È il set minimo che potrebbe essere richiesto per la modalità multivaluta. Certo, può essere integrato. La combinazione di flag determinerà gli eventi che invieremo dalla "spia".

I flag possono essere combinati utilizzando l'operazione bit a bit "OR". Ad esempio, il CHARTEVENT_NEWBAR_M1 | La combinazione CHARTEVENT_NEWBAR_H1 indica che invieremo gli eventi "new bar" dall’intervallo di tempo al minuto e orario con l'aiuto della "spia". Questi flag saranno il parametro di input del nostro indicatore-spia. In seguito,lo chiameremo "agente-indicatore".

L'indicatore stesso, conformemente alle nuove idee, si presenta così:

//+------------------------------------------------------------------+
//|                                        Spy Control panel MCM.mq5 |
//|                                            Copyright 2010, Lizar |
//|                            https://www.mql5.com/en/users/Lizar |
//+------------------------------------------------------------------+
#define VERSION       "1.00 Build 3 (26 Dec 2010)"

#property copyright   "Copyright 2010, Lizar"
#property link        "https://www.mql5.com/en/users/Lizar"
#property version     VERSION
#property description "This is the MCM Control Panel agent-indicator."
#property description "Is launched on the required symbol on any time-frame"
#property description "and generates the custom NewBar event and/or NewTick"
#property description "for the chart which receives the event"

#property indicator_chart_window
  
input long                    chart_id;                 // identifier of the chart which receives the event
input ushort                  custom_event_id;          // event identifier  
input ENUM_CHART_EVENT_SYMBOL flag_event=CHARTEVENT_NO;// indicator, which determines the event type.

MqlDateTime time, prev_time;

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate (const int rates_total,       // size of the price[] array
                 const int prev_calculated, // bars processed at the previous call
                 const int begin,           // where the data begins
                 const double& price[]      // calculations array
   )
  {  
   double price_current=price[rates_total-1];

   TimeCurrent(time);
   
   if(prev_calculated==0)
     {
      EventCustom(CHARTEVENT_INIT,price_current);
      prev_time=time; 
      return(rates_total);
     }
   
//--- new tick
   if((flag_event & CHARTEVENT_TICK)!=0) EventCustom(CHARTEVENT_TICK,price_current);       

//--- check change time
   if(time.min==prev_time.min && 
      time.hour==prev_time.hour && 
      time.day==prev_time.day &&
      time.mon==prev_time.mon) return(rates_total);

//--- new minute
   if((flag_event & CHARTEVENT_NEWBAR_M1)!=0) EventCustom(CHARTEVENT_NEWBAR_M1,price_current);     
   if(time.min%2 ==0 && (flag_event & CHARTEVENT_NEWBAR_M2)!=0)  EventCustom(CHARTEVENT_NEWBAR_M2,price_current);
   if(time.min%3 ==0 && (flag_event & CHARTEVENT_NEWBAR_M3)!=0)  EventCustom(CHARTEVENT_NEWBAR_M3,price_current); 
   if(time.min%4 ==0 && (flag_event & CHARTEVENT_NEWBAR_M4)!=0)  EventCustom(CHARTEVENT_NEWBAR_M4,price_current);      
   if(time.min%5 ==0 && (flag_event & CHARTEVENT_NEWBAR_M5)!=0)  EventCustom(CHARTEVENT_NEWBAR_M5,price_current);     
   if(time.min%6 ==0 && (flag_event & CHARTEVENT_NEWBAR_M6)!=0)  EventCustom(CHARTEVENT_NEWBAR_M6,price_current);     
   if(time.min%10==0 && (flag_event & CHARTEVENT_NEWBAR_M10)!=0) EventCustom(CHARTEVENT_NEWBAR_M10,price_current);      
   if(time.min%12==0 && (flag_event & CHARTEVENT_NEWBAR_M12)!=0) EventCustom(CHARTEVENT_NEWBAR_M12,price_current);      
   if(time.min%15==0 && (flag_event & CHARTEVENT_NEWBAR_M15)!=0) EventCustom(CHARTEVENT_NEWBAR_M15,price_current);      
   if(time.min%20==0 && (flag_event & CHARTEVENT_NEWBAR_M20)!=0) EventCustom(CHARTEVENT_NEWBAR_M20,price_current);      
   if(time.min%30==0 && (flag_event & CHARTEVENT_NEWBAR_M30)!=0) EventCustom(CHARTEVENT_NEWBAR_M30,price_current);      
   if(time.min!=0) {prev_time=time; return(rates_total);}
//--- new hour
   if((flag_event & CHARTEVENT_NEWBAR_H1)!=0) EventCustom(CHARTEVENT_NEWBAR_H1,price_current);
   if(time.hour%2 ==0 && (flag_event & CHARTEVENT_NEWBAR_H2)!=0)  EventCustom(CHARTEVENT_NEWBAR_H2,price_current);
   if(time.hour%3 ==0 && (flag_event & CHARTEVENT_NEWBAR_H3)!=0)  EventCustom(CHARTEVENT_NEWBAR_H3,price_current);      
   if(time.hour%4 ==0 && (flag_event & CHARTEVENT_NEWBAR_H4)!=0)  EventCustom(CHARTEVENT_NEWBAR_H4,price_current);      
   if(time.hour%6 ==0 && (flag_event & CHARTEVENT_NEWBAR_H6)!=0)  EventCustom(CHARTEVENT_NEWBAR_H6,price_current);      
   if(time.hour%8 ==0 && (flag_event & CHARTEVENT_NEWBAR_H8)!=0)  EventCustom(CHARTEVENT_NEWBAR_H8,price_current);      
   if(time.hour%12==0 && (flag_event & CHARTEVENT_NEWBAR_H12)!=0) EventCustom(CHARTEVENT_NEWBAR_H12,price_current);      
   if(time.hour!=0) {prev_time=time; return(rates_total);}
//--- new day
   if((flag_event & CHARTEVENT_NEWBAR_D1)!=0) EventCustom(CHARTEVENT_NEWBAR_D1,price_current);      
//--- new week
   if(time.day_of_week==1 && (flag_event & CHARTEVENT_NEWBAR_W1)!=0) EventCustom(CHARTEVENT_NEWBAR_W1,price_current);      
//--- new month
   if(time.day==1 && (flag_event & CHARTEVENT_NEWBAR_MN1)!=0) EventCustom(CHARTEVENT_NEWBAR_MN1,price_current);      
   prev_time=time;
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

void EventCustom(ENUM_CHART_EVENT_SYMBOL event,double price)
  {
   EventChartCustom(chart_id,custom_event_id,(long)event,price,_Symbol);
   return;
  } 

Questo indicatore che fa parte del pannello di controllo di MCM non è stato rinominato, gli allegati ne contengono semplicemente una versione aggiornata (vedi "Pannello di controllo spia MCM.mq5"). Ma questo non significa che non possa essere utilizzato separatamente dal pannello.

Questo agente-indicatore genera eventi utente personalizzati e li trasmette al destinatario del grafico per l'ulteriore elaborazione di questi eventi nell'EA o nell'indicatore, utilizzando la funzione OnChartEvent(). Ora i parametri di input di questa funzione devono essere interpretati come segue:

  • id, identificatore dell'evento;
  • lparam, indicatore dell'evento ricevuto dall'agente del pannello. Gli indicatori corrispondenti all'enumerazione ENUM_CHART_EVENT_SYMBOL;
  • dparam, il prezzo del tick o il prezzo di apertura per una nuova barra per un periodo di tempo specifico;
  • sparam, il nome del simbolo di trading su cui si è verificato l'evento.

 La demo EA non sembra più complicata della precedente (la versione completa è disponibile nell'archivio):

//+------------------------------------------------------------------+
//|                                      exSpy Control panel MCM.mq5 |
//|                                            Copyright 2010, Lizar |
//|                            https://www.mql5.com/en/users/Lizar |
//+------------------------------------------------------------------+
#define VERSION       "1.00 Build 1 (28 Dec 2010)"

#property copyright   "Copyright 2010, Lizar"
#property link        "https://www.mql5.com/en/users/Lizar"
#property version     VERSION
#property description "The EA demonstrates the work of the MCM Spy Control panel"

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {   
   if(iCustom("GBPUSD",PERIOD_M1,"Spy Control panel MCM",ChartID(),0,
             CHARTEVENT_NEWBAR_M1|CHARTEVENT_NEWBAR_M5)==INVALID_HANDLE) 
      { Print("Error in setting of spy on GBPUSD"); return(true);}
   if(iCustom("EURUSD",PERIOD_M1,"Spy Control panel MCM",ChartID(),1,
             CHARTEVENT_NEWBAR_M2)==INVALID_HANDLE) 
      { Print("Error in setting of spy on EURUSD"); return(true);}
   if(iCustom("USDJPY",PERIOD_M1,"Spy Control panel MCM",ChartID(),2,
             CHARTEVENT_NEWBAR_M6)==INVALID_HANDLE) 
      { Print("Error in setting of spy on USDJPY"); return(true);}
      
   Print("Spys ok, waiting for events...");
   //---
   return(0);
  }
  
//+------------------------------------------------------------------+
//| The Standard event handler.                                      |
//| See MQL5 Reference for the details.                              |
//|                                                                  |
//| In this case it is used for decoding of spy messages, sent by    |
//| iSPY Control panel MCM indicator.                                |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,           // event identifier
                  const long&   lparam, // the event flag.
                                       // event flag is a bit mask of ENUM_CHART_EVENT_SYMBOL enumeration.
                  const double& dparam, // price
                  const string& sparam  // symbol 
                 )
  {
   if(id>=CHARTEVENT_CUSTOM)      
     {
      Print(TimeToString(TimeCurrent(),TIME_SECONDS)," -> id=",id-CHARTEVENT_CUSTOM,
           ":  ",sparam," ",EnumToString((ENUM_CHART_EVENT_SYMBOL)lparam)," price=",dparam);
     }
  }

I risultati del lavoro dell’ex spia pannello di controllo MCM:


 

Come puoi vedere, riceviamo regolarmente tutti gli eventi richiesti.

Riassumiamo ancora una volta il risultato intermedio:

  • Grazie alla nuova versione dell'indicatore-agente, abbiamo mantenuto tutti i nostri risultati precedenti.
  • Ora, dai nostri programmi MQL multivaluta, possiamo specificare almeno 23 eventi che includono gli eventi "new tick", "new bar" e "initialization".
  • L'EA/indicatore dà ancora più lavoro agli agenti, al fine di scaricarli. 

Bene, come si è visto, non è così difficile implementare una modalità multivaluta completa su MetaTrader 5.

Inoltre, voglio evidenziare alcuni dettagli.

Primo. Tutti gli eventi generati dagli "agenti" per il nostro EA/indicatore multivaluta sono esterni. In relazione a ciò, sorge la domanda: "È necessario eseguire gli agenti direttamente dall'EA/indicatore?". La risposta è "no."

Secondo. Nella funzione OnChartEvent(), l'id dell'evento sembra essere di riserva poiché possiamo scoprire per quale simbolo l'evento è stato ricevuto dal parametro sparam, cioè il nome del simbolo di scambio. Quindi, possiamo forse usarlo per qualche altro scopo? La risposta è: "sì". 

Tali argomenti hanno portato alla nascita del "pannello di controllo MCM" per Expert Advisor e Indicatori multivaluta. È una sorta di "intercalare" tra il terminale e un EA/indicatore. Questo ci ha fornito ancora più vantaggi e flessibilità nella configurazione di un ambiente multivaluta:

  • Il pannello può essere installato come indicatore separato sul grafico e, quindi, assegnare gli indicatori multivaluta compatibili con il pannello.
  • Il pannello può includere indicatori e EA come unità componenti. Verrà caricato insieme a loro.  
  • Possiamo attivare/disattivare il simbolo dalla finestra "Market Watch" per il trading o l'analisi utilizzando il menu "Events". Nella funzione OnChartEvent() non possiamo trovare il numero di serie del simbolo tramite l'identificatore dell'evento id, nella finestra "Market Watch".
  • Possiamo impostare la modalità di trading per tick o per evento "new bar" per qualsiasi periodo e per il simbolo selezionato nel "Market Watch". Tutto questo viene fatto usando il menu normale.
  • Possiamo modificare tutte le configurazioni di cui sopra senza scaricare, arrestare o accedere alla finestra delle proprietà dell'EA o dell'indicatore.
  • Tutto ciò non limita il potenziale di creatività nella creazione di indicatori multivaluta e EA. Inoltre, adesso non dobbiamo integrare nel nostro codice gli output di questo pannello. La gestione degli indicatori-agente è ora implementata in esso.
  • La struttura del sistema multivaluta è ancora più semplice.


L'indicatore multivaluta RSI per l'indice del dollaro USDx

Per sperimentare tutti i vantaggi del metodo di cui sopra, propongo di implementare la variante multivaluta dell'indicatore RSI per l'indice del dollaro USDx utilizzando il pannello di controllo MCM.

Per cominciare, parlerò di alcune caratteristiche speciali. Spesso, quando proviamo ad analizzare l'indice del dollaro, contiamo semplicemente gli indicatori delle letture dell'indice. Dal mio punto di vista, questo non è del tutto corretto poiché ogni simbolo del paniere dell'indice delle coppie di valute apporta i propri contributi. Quindi, come esempio, calcoliamo l'RSI per l'indice del dollaro con la formula simile a quella del calcolo dell'indice:

Cioè, prima calcoleremo l'RSI per una particolare coppia di valute e poi leggeremo l'RSI per l'indice, tenendo conto del peso dei coefficienti.

Il lettore noterà che c'è un problema con la sincronizzazione dei dati storici di tutti i simboli utilizzati nel sistema multivaluta. (Vedi paragrafo 2 della sezione "Overview of the conventional approach").

Questo problema è stato risolto nell'indicatore utilizzando le funzioni di classe per la costruzione dei buffer sincronizzati RSI (file SynchronizedBufferRSI.mqh). Non ha senso fornire l'intero codice della classe, ma solo i momenti rilevanti presentati di seguito. 

Innanzitutto, il buffer dell'indicatore è definito all'interno della classe con il modificatore di accesso pubblico:

public:
   double   buffer[];   // indicator buffer

In secondo luogo, l'inizializzazione dell'indicatore viene eseguita utilizzando il metodo della classe:

//--- Initialization methods:
bool Init(int n,string symbol,int rsi_count,int rsi_period);

E terzo, per ogni barra il valore del buffer dell'indicatore è sincronizzato con l'intervallo di tempo corrente, utilizzando il metodo di aggiornamento della classe:

//+------------------------------------------------------------------+
//| The method of receiving/updating indicator data for one bar      |
//| of the indicator buffer.                                         |
//| INPUT:  bar   - bar number                                       |
//| OUTPUT: no.                                                      |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
void CSynchronizedBufferRSI::Refresh(int bar=0)
  {
   buffer[bar]=EMPTY_VALUE; // Initialization of the bar of the indicator buffer.
     
   //--- Inquire the time of the bar for the current graph:
   datetime time[1];      
   if(CopyTime(_Symbol,_Period,bar,1,time)!=1) return; // In case of an error, we wait for the next tick/bar...

   //--- Request the value of the indicator for the symbol for the time,
   //--- consistent with that of the bar of the current graph:
   double value[1];
   if(CopyBuffer(m_handle,0,time[0],time[0],value)!=1) return; // In case of an error, wait for the next tick/bar...

   buffer[bar]=value[0];
   return;
  }

Per la sincronizzazione completa di tutti i buffer indicatori è necessario utilizzare un intervallo di tempo intero di un minuto senza "buchi", come descritto nell'articolo seguente. Ma per questo metodo di sincronizzazione dei buffer dell'indicatore abbiamo appositamente selezionato l'intervallo di tempo del grafico corrente, poiché la visualizzazione dell'indicatore avviene su di esso.

Parlando per esperienza personale, posso dire che ha senso utilizzare un tale metodo di sincronizzazione per brevi periodi, per qualsiasi serie temporale o buffer indicatore se il loro simbolo è diverso dal simbolo sul grafico corrente.

Il grafico mostra chiaramente perché vale la pena farlo:


 

Per periodi di tempo più lunghi questo non viene in genere osservato.

Ultimo ma non meno importante. Di seguito è riportato il codice di un gestore di eventi utente standard, utilizzato nell'indicatore:

//+------------------------------------------------------------------+
//| The Standard event handler.                                      |
//| See MQL5 Reference for the details.                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // event identifier or position symbol in the "Market Match"+CHARTEVENT_CUSTOM  
                const long& lparam,   // event indicator
                const double& dparam, // price
                const string& sparam  // symbol
                )
  {
   int custom_id=id-CHARTEVENT_CUSTOM-1;
   
   if(custom_id>=0)      
     {
      if(lparam!=CHARTEVENT_NEWBAR_NO)
        { 
         //--- Recalculation of the last uncompleted bar:
         if(EventToPeriod(lparam)==_Period && sparam==_Symbol)
           { // Recalculation of the indicator, if a new bar on the current chart
            iRSIUSDx_Ind[0]=EMPTY_VALUE;
            //--- Updating the value of the RSI for all of the currency pairs for the new bar
            for(int i=0;i<symbol_total;i++) buffer[i].Refresh();
            iRSIUSDx(symbol_total);   // calculation of the current incomplete bar RSI for the index
            return;
           }
         
         buffer[custom_id].Refresh(); // The value of RSI for the custom_id of the currency pair for the current bar
         iRSIUSDx(symbol_total);      // calculation of the RSI for the current(uncompleted) bar RSIx
         return;
        }
      else 
        { 
         //--- Recalculation of the indicator for the "Initialization" event 
         buffer[custom_id].RefreshBuffer();     // Update of the RSI buffer for the custom_id of the currency pair
         Init_iRSIUSDx(symbol_total,calculate); // Update of the RSI buffer for the index
         return;
        }
     }
  }

Particolarità del codice:

  • L'uso dell'identificatore dell'evento id per fare riferimento all'array, il quale contiene i puntatori alle istanze di classe che hanno lo scopo di calcolare i buffer dell'indicatore RSI, sincronizzati con l'intervallo di tempo corrente. Questo approccio semplifica notevolmente la struttura del codice.
  • L'evento "Initializing" viene utilizzato per ricalcolare il buffer dell'indicatore RSI solo per la coppia di valute da cui è stato ricevuto e non per tutti e sei i simboli. Come accennato in precedenza, ciò consente di sincronizzare l'indicatore, ad esempio, durante l'aggiornamento della cronologia per un simbolo.
  • L'evento "new bar" viene utilizzato per sincronizzare tutti i buffer dell'indicatore RSI per una nuova barra sul grafico corrente.
  • L'evento "new tick" viene utilizzato da tutte le coppie di valute per aggiornare gli indicatori sull'ultima barra incompleta. Inoltre, il riconteggio della barra viene effettuato solo per la coppia, per la quale è stato ricevuto il "nuovo tick". 

Dopo aver esplorato il codice completo dell'indicatore RSI per l'indice del dollaro USDx, il funzionamento diventerà più chiaro.

Caratteristiche di installazione:

  • Scarica il "pannello di controllo MCM" per Expert Advisor e Indicatori multivaluta e compila i file "iControl panel MCM.mq5" e "Spy Control panel MCM.mq5".
  • Specifica il seguente ordine di simboli nella finestra "Market Match":
    1. EURUSD
    2. USDJPY
    3. GBPUSD
    4. USDCAD
    5. USDSEK
    6. USDCHF
    Questo è necessario perché non ho inserito un segno di spunta appropriato nell'indicatore e questa sequenza è necessaria per il corretto calcolo dell'indicatore.
  • Decomprimi l'archivio iRSIUSDx.zip nella cartella /MQL5. Collega iRSIUSDx.ex5 dalla cartella /MQL5/Indicators/iRSIUSDx/ al grafico EURUSD con il periodo M1.
  • In sequenza, per tutti e sei i simboli presenti nel menu "Event" del “pannello di controllo MCM", imposta l'evento "New tick", come descritto qui. Dovresti ottenere un'immagine simile a quella sopra.
  • Inoltre, per il simbolo EURUSD, imposta l'evento "new bar" sul grafico dei minuti. Nell'indicatore, questo evento viene utilizzato per la sincronizzazione quando la nuova barra si trova sull'intervallo di tempo corrente, che è uguale a M1.
  • Se desideri un esempio più visivo, imposta l'indice del dollaro come descritto qui.

Conclusione

L'implementazione discussa di una modalità multivaluta completa su MetaTrader 5 dimostra pienamente i vantaggi della piattaforma e del linguaggio di programmazione MQL5 nel risolvere questo problema. Ciò che in precedenza causava la maggior parte delle difficoltà, ora è disponibile.

Chiaramente, questo è solo l'inizio di un movimento in questa direzione. Sicuramente ci saranno più opzioni per un modo ancora migliore di sincronizzazione dei dati, gestione delle modalità multivaluta, ecc.  Ma spero che ora sia chiaro che abbiamo tutti gli strumenti necessari per farlo.


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

File allegati |
irsiusdx_en.zip (7.54 KB)
Collegare NeuroSolutions Neuronets Collegare NeuroSolutions Neuronets
Oltre alla creazione di neuronet, la suite software NeuroSolutions consente di esportarli come DLL. Questo articolo descrive il processo di creazione di un neuronet, la generazione di un DLL e la connessione a un Expert Advisor per il trading su MetaTrader 5.
Le tabelle elettroniche in MQL5 Le tabelle elettroniche in MQL5
L'articolo descrive una classe di array bidimensionali e dinamici contenente dati di tipo diverso nella sua prima dimensione. La memorizzazione dei dati sotto forma di tabella è utile per risolvere un'ampia gamma di problemi di disposizione, memorizzazione e funzionamento con informazioni associate di diverso tipo. Il codice sorgente della classe che implementa la funzionalità di lavorare con le tabelle è allegato all'articolo.
Moving mini-max: un nuovo indicatore per l'analisi tecnica e la sua implementazione in MQL5 Moving mini-max: un nuovo indicatore per l'analisi tecnica e la sua implementazione in MQL5
Nel seguente articolo descriverò un processo di implementazione dell'indicatore Moving Mini-Max basato su un documento di Z.G.Silagadze "Moving Mini-max: a new indicator for technical analysis". L'idea dell'indicatore si basa sulla simulazione di fenomeni di tunneling quantistico proposta da G. Gamov nella teoria del decadimento alfa.
Approccio econometrico all'analisi dei grafici Approccio econometrico all'analisi dei grafici
Questo articolo descrive in particolare i metodi econometrici di analisi, l'analisi di autocorrelazione e l'analisi della varianza condizionale. Qual è il vantaggio dell'approccio qui descritto? L'uso dei modelli GARCH non lineari consente di rappresentare formalmente la serie analizzata dal punto di vista matematico e di creare una previsione per un numero specificato di passaggi.