English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
Manuale MQL5: Gestione di eventi grafici personalizzati

Manuale MQL5: Gestione di eventi grafici personalizzati

MetaTrader 5Esempi | 12 gennaio 2022, 11:36
97 0
Denis Kirichenko
Denis Kirichenko

Introduzione

Questo articolo è una continuazione logica dell'articolo Manuale MQL5: Gestione di eventi grafici tipici. Copre i metodi di lavoro con eventi grafici personalizzati. Qui il lettore può trovare esempi di sviluppo e gestione di eventi personalizzati. Tutte le idee discusse in questo articolo sono state implementate con strumenti orientati agli oggetti.

Poiché il tema degli eventi personalizzati è piuttosto ampio, è il caso in cui un programmatore e uno sviluppatore possono introdurre la creatività nel loro lavoro.


1. Evento grafico personalizzato

È chiaro che questo evento è definito dall'utente. Spetta a un programmatore decidere cosa esattamente e quale compito o blocco di programma potrebbe assumere la forma di un evento. Gli sviluppatori MQL5 possono creare i propri eventi, che espandono le capacità del linguaggio per l'implementazione di algoritmi complessi.

Un evento personalizzato è il secondo tipo possibile di evento grafico. Il primo è un evento tipico. Sebbene non esista un termine come "evento grafico tipico" nella documentazione, suggerisco comunque di utilizzarlo per i primi dieci tipi di eventi grafici.

Gli sviluppatori suggeriscono solo un'enumerazione per tutti gli eventi del grafico: ENUM_CHART_EVENT.

Secondo la documentazione, ci sono 65535 identificatori di eventi personalizzati. Il primo e l'ultimo identificatore degli eventi personalizzati sono impostati dai valori espliciti di CHARTEVENT_CUSTOM e CHARTEVENT_CUSTOM_LAST, che è numericamente uguale a 1000 e 66534 di conseguenza (Fig.1).

Fig.1 Il primo e l'ultimo identificatore di eventi personalizzati

Fig.1 Il primo e l'ultimo identificatore di eventi personalizzati

Semplici calcoli considerando il primo e l'ultimo identificatore produrranno: 66534-1000+1=65535.

Prima di utilizzare gli eventi personalizzati, devono essere prima progettati. In questo senso uno sviluppatore diventa una mente e l'autore del concept dell'evento, che viene poi implementato come algoritmo per il futuro Expert. Sarebbe utile avere una classificazione degli eventi personalizzati. Questo metodo conoscitivo non permetterà di eliminare l'ambiguità ma certamente la ridurrà e organizzerà la linea di ragionamento.

Consideriamo un tale criterio di un evento personalizzato come fonte. Ad esempio, lo sviluppatore sergeev ha suggerito l'idea di un prototipo di robot commerciale. Divide tutti gli eventi in tre gruppi (Fig.2).

Fig.2 Gruppi di sorgenti di eventi personalizzate

Fig.2 Gruppi di origini eventi personalizzate

Quindi, secondo questa idea principale, gli eventi personalizzati devono essere sviluppati in base alla loro appartenenza al gruppo.

Cerchiamo di fare qualcosa di semplice per cominciare. Dapprima prenderemo il primo gruppo, che comprende eventi indicatori. Gli eventi che possono appartenere a questo gruppo sono: creazione e cancellazione di un indicatore, ricezione di un segnale di apertura e chiusura di una posizione. Il secondo gruppo include eventi di modifica dello stato di ordini e posizioni. Nel nostro esempio, l'apertura e la chiusura delle posizioni sarà in questo gruppo. È tutto molto semplice. E, infine, il gruppo più complesso per la formalizzazione è un gruppo di eventi esterni.

Prendiamo due eventi: abilitare e disabilitare il trading manuale.

Fig.3 Fonti di eventi personalizzati

Fig.3 Fonti di eventi personalizzati

Lo schema primario può essere stabilito attraverso il metodo deduttivo (dal generale allo speciale) (Fig.3). Questo è lo schema che utilizzeremo in seguito per creare tipi di eventi nella classe corrispondente (Tabella 1).

Tabella 1 Eventi personalizzati

Tabella 1 Eventi personalizzati

Questa tabella non può ancora essere definita un "concetto di evento", ma è un inizio. Ecco un altro approccio. È risaputo che un modello di un sistema di trading astratto consiste di tre sottosistemi: moduli di base (Fig.4).

Fig.4 Modello di un sistema di trading astratto

Fig.4 Modello di un sistema di trading astratto

Gli eventi personalizzati basati sul criterio "sorgente" possono essere classificati come eventi generati in:

  1. il sottosistema del segnale;
  2. sottosistema delle posizioni aperte finali;
  3. sottosistema di gestione del denaro.

Quest'ultimo, ad esempio, può includere eventi come il raggiungimento del livello di drawdown consentito, l'aumento del volume degli scambi di un valore prestabilito, l'aumento della percentuale di un limite di perdita, ecc.


2. Gestore e generatore di ChartEvent

Le seguenti poche righe saranno dedicate al gestore e generatore di eventi grafici. Per quanto riguarda la gestione di un evento grafico personalizzato, il suo principio è simile a quello della gestione di un evento grafico tipico.

Un gestore, la funzione OnChartEvent(), accetta quattro costanti come parametri. Apparentemente, gli sviluppatori hanno utilizzato questo meccanismo per implementare l'idea di identificare un evento e ottenere ulteriori informazioni su di esso. A mio parere è un meccanismo di programma molto compatto e conveniente.

La funzione EventChartCustom() genera un evento grafico personalizzato. Sorprendentemente, è possibile creare un evento grafico personalizzato per il grafico "proprio" e per uno "alieno". Penso che l'articolo più interessante sul significato dei grafici propri e alieni sia L'implementazione di una modalità multivaluta in MetaTrader 5.

A mio avviso c'è una discordia nel fatto che l'identificatore dell'evento è di tipo ushort nel generatore, mentre nell'handler è di tipo int. Sarebbe logico usare anche il tipo di dati ushort nel gestore.


3. Classe di evento personalizzato

Come ho detto prima, il concetto dell'evento spetta allo sviluppatore Expert. Ora lavoreremo con gli eventi della Tabella 1. Per prima cosa ordineremo la classe dell'evento personalizzato CEventBase e le sue derivate (Fig.5).

Fig.5 Gerarchia delle classi di eventi

Fig.5 Gerarchia delle classi di eventi

La classe base si presenta come segue:

//+------------------------------------------------------------------+
//| Class CEventBase.                                                |
//| Purpose: base class for a custom event                           |
//| Derives from class CObject.                                      |
//+------------------------------------------------------------------+
class CEventBase : public CObject
  {
protected:
   ENUM_EVENT_TYPE   m_type;
   ushort            m_id;
   SEventData        m_data;

public:
   void              CEventBase(void)
     {
      this.m_id=0;
      this.m_type=EVENT_TYPE_NULL;
     };
   void             ~CEventBase(void){};
   //--
   bool              Generate(const ushort _event_id,const SEventData &_data,
                              const bool _is_custom=true);
   ushort            GetId(void) {return this.m_id;};

private:
   virtual bool      Validate(void) {return true;};
  };

Il tipo di evento è impostato dall'enumerazione ENUM_EVENT_TYPE:

//+------------------------------------------------------------------+
//| A custom event type enumeration                                  |
//+------------------------------------------------------------------+
enum ENUM_EVENT_TYPE
  {
   EVENT_TYPE_NULL=0,      // no event
   //---
   EVENT_TYPE_INDICATOR=1, // indicator event
   EVENT_TYPE_ORDER=2,     // order event
   EVENT_TYPE_EXTERNAL=3,  // external event
  };

I membri dei dati comprendono l'identificatore dell'evento e la struttura dei dati.

Il metodo Generate() della classe base CEventBase si occupa della generazione di un evento. Il metodo GetId() restituisce l'id dell'evento e il metodo virtuale Validate() controlla il valore dell'identificatore dell'evento. All'inizio ho incluso il metodo di gestione degli eventi nella classe, ma in seguito mi sono reso conto che ogni evento è unico e un metodo astratto non è sufficiente qui. Ho finito per delegare questa attività alla classe CEventProcessor che gestisce gli eventi personalizzati.


4. Classe gestore eventi personalizzato

La classe CEventProcessor dovrebbe generare e gestire otto eventi presentati. I membri dati della classe hanno il seguente aspetto:

//+------------------------------------------------------------------+
//| Class CEventProcessor.                                           |
//| Purpose: base class for an event processor EA                    |
//+------------------------------------------------------------------+
class CEventProcessor
  {
//+----------------------------Data members--------------------------+
protected:
   ulong             m_magic;
   //--- flags
   bool              m_is_init;
   bool              m_is_trade;
   //---
   CEventBase       *m_ptr_event;
   //---
   CTrade            m_trade;
   //---
   CiMA              m_fast_ema;
   CiMA              m_slow_ema;
   //---
   CButton           m_button;
   bool              m_button_state;
//+------------------------------------------------------------------+
  };

Tra l'elenco degli attributi ci sono i flag di inizializzazione e commercio. Il primo non consentirà all'EA di negoziare se non si avvia correttamente. Il secondo controlla il permesso per il trading.

C'è anche il puntatore all'oggetto del tipo CEventBase, che lavora con eventi di diverso tipo usando il polimorfismo. Un'istanza della classe CTrade fornisce l'accesso alle operazioni di trading.

Gli oggetti di tipo CiMA facilitano la gestione dei dati ricevuti dagli indicatori. Per semplificare l'esempio, ho preso due medie mobili che riceveranno un segnale di trading. Esiste anche un'istanza della classe "CButton" che verrà utilizzata per l'abilitazione/disabilitazione manuale dell'EA.

I metodi della classe sono stati divisi dal principio "moduli – procedure – funzioni – macro":

//+------------------------------------------------------------------+
//| Class CEventProcessor.                                           |
//| Purpose: base class for an event processor EA                    |
//+------------------------------------------------------------------+
class CEventProcessor
  {
//+-------------------------------Methods----------------------------+
public:
   //--- constructor/destructor
   void              CEventProcessor(const ulong _magic);
   void             ~CEventProcessor(void);

   //--- Modules
   //--- event generating
   bool              Start(void);
   void              Finish(void);
   void              Main(void);
   //--- event processing
   void              ProcessEvent(const ushort _event_id,const SEventData &_data);

private:
   //--- Procedures
   void              Close(void);
   void              Open(void);

   //--- Functions
   ENUM_ORDER_TYPE   CheckCloseSignal(const ENUM_ORDER_TYPE _close_sig);
   ENUM_ORDER_TYPE   CheckOpenSignal(const ENUM_ORDER_TYPE _open_sig);
   bool              GetIndicatorData(double &_fast_vals[],double &_slow_vals[]);

   //--- Macros
   void              ResetEvent(void);
   bool              ButtonStop(void);
   bool              ButtonResume(void);
  };

Tra i moduli ce ne sono tre che generano solo eventi: quello iniziale—Start(), quello finale—Finish() e quello principale—Main(). Il quarto modulo ProcessEvent() è sia un gestore di eventi che un generatore.


4.1 Modulo iniziale

Questo modulo è progettato per essere chiamato nel gestore OnInit().

//+------------------------------------------------------------------+
//| Start module                                                     |
//+------------------------------------------------------------------+
bool CEventProcessor::Start(void)
  {
//--- create an indicator event object
   this.m_ptr_event=new CIndicatorEvent();
   if(CheckPointer(this.m_ptr_event)==POINTER_DYNAMIC)
     {
      SEventData data;
      data.lparam=(long)this.m_magic;
      //--- generate CHARTEVENT_CUSTOM+1 event
      if(this.m_ptr_event.Generate(1,data))
         //--- create a button
         if(this.m_button.Create(0,"Start_stop_btn",0,25,25,150,50))
            if(this.ButtonStop())
              {
               this.m_button_state=false;
               return true;
              }
     }

//---
   return false;
  }

In questo modulo viene creato un puntatore all'oggetto evento indicatore. Quindi viene generato l'evento "Creazione indicatore". Un pulsante è l'ultimo ad essere creato. Si passa alla modalità "Stop". Significa che se si preme il pulsante, l'Esperto smette di funzionare.

Anche la struttura EventData è coinvolta in questa definizione del metodo. È un semplice contenitore per i parametri passati al generatore dell'evento personalizzato. Qui verrà compilato solo un campo della struttura: è il campo di tipo lungo. Conterrà il numero magico di EA.


4.2 Modulo di finitura

Questo modulo dovrebbe essere chiamato nel gestore OnDeinit().

//+------------------------------------------------------------------+
//| Finish  module                                                   |
//+------------------------------------------------------------------+
void CEventProcessor::Finish(void)
  {
//--- reset the event object
   this.ResetEvent();
//--- create an indicator event object
   this.m_ptr_event=new CIndicatorEvent();
   if(CheckPointer(this.m_ptr_event)==POINTER_DYNAMIC)
     {
      SEventData data;
      data.lparam=(long)this.m_magic;
      //--- generate CHARTEVENT_CUSTOM+2 event
      bool is_generated=this.m_ptr_event.Generate(2,data,false);
      //--- process CHARTEVENT_CUSTOM+2 event
      if(is_generated)
         this.ProcessEvent(CHARTEVENT_CUSTOM+2,data);
     }
  }

Qui il puntatore dell'evento precedente viene cancellato e viene generato l'evento "Cancellazione indicatore". Devo notare che se un evento personalizzato viene generato nel gestore OnDeinit(), otterrai un errore di runtime 4001 (errore esterno imprevisto). Pertanto, la generazione e la gestione degli eventi vengono eseguite all'interno di questo metodo senza chiamare OnChartEvent().

Anche in questo caso, il numero magico di EA verrà archiviato utilizzando la struttura SEventData.


4.3 Modulo principale

Questo modulo dovrebbe essere chiamato nel gestore OnTick().

//+------------------------------------------------------------------+
//| Main  module                                                     |
//+------------------------------------------------------------------+
void CEventProcessor::Main(void)
  {
//--- a new bar object
   static CisNewBar newBar;

//--- if initialized     
   if(this.m_is_init)
      //--- if not paused   
      if(this.m_is_trade)
         //--- if a new bar
         if(newBar.isNewBar())
           {
            //--- close module
            this.Close();
            //--- open module
            this.Open();
           }
  }

In questo modulo vengono chiamate le procedure Open() e Close(). La prima procedura può generare l'evento "Ricezione segnale di apertura" e la seconda - "Ricezione segnale di chiusura". L'attuale versione del modulo è completamente funzionante con un nuovo aspetto della barra. Una classe per rilevare un La nuova barra è stata descritta da Konstantin Gruzdev.


4.4 Modulo di gestione degli eventi

Questo modulo dovrebbe essere chiamato nel gestore OnChartEvent(). Questo modulo è il più grande in termini di dimensioni e funzionalità.

//+------------------------------------------------------------------+
//| Process event module                                             |
//+------------------------------------------------------------------+
void CEventProcessor::ProcessEvent(const ushort _event_id,const SEventData &_data)
  {
//--- check event id
   if(_event_id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- button click
      if(StringCompare(_data.sparam,this.m_button.Name())==0)
        {
         //--- button state
         bool button_curr_state=this.m_button.Pressed();
         //--- to stop
         if(button_curr_state && !this.m_button_state)
           {
            if(this.ButtonResume())
              {
               this.m_button_state=true;
               //--- reset the event object
               this.ResetEvent();
               //--- create an external event object
               this.m_ptr_event=new CExternalEvent();
               //---
               if(CheckPointer(this.m_ptr_event)==POINTER_DYNAMIC)
                 {
                  SEventData data;
                  data.lparam=(long)this.m_magic;
                  data.dparam=(double)TimeCurrent();
                  //--- generate CHARTEVENT_CUSTOM+7 event
                  ushort curr_id=7;
                  if(!this.m_ptr_event.Generate(curr_id,data))
                     PrintFormat("Failed to generate an event: %d",curr_id);
                 }
              }
           }
         //--- to resume
         else if(!button_curr_state && this.m_button_state)
           {
            if(this.ButtonStop())
              {
               this.m_button_state=false;
               //--- reset the event object
               this.ResetEvent();
               //--- create an external event object
               this.m_ptr_event=new CExternalEvent();
               //---
               if(CheckPointer(this.m_ptr_event)==POINTER_DYNAMIC)
                 {
                  SEventData data;
                  data.lparam=(long)this.m_magic;
                  data.dparam=(double)TimeCurrent();
                  //--- generate CHARTEVENT_CUSTOM+8 event
                  ushort curr_id=8;
                  if(!this.m_ptr_event.Generate(curr_id,data))
                     PrintFormat("Failed to generate an event: %d",curr_id);
                 }
              }
           }
        }
     }
//--- user event 
   else if(_event_id>CHARTEVENT_CUSTOM)
     {
      long magic=_data.lparam;
      ushort curr_event_id=this.m_ptr_event.GetId();
      //--- check magic
      if(magic==this.m_magic)
         //--- check id
         if(curr_event_id==_event_id)
           {
            //--- process the definite user event 
            switch(_event_id)
              {
               //--- 1) indicator creation
               case CHARTEVENT_CUSTOM+1:
                 {
                  //--- create a fast ema
                  if(this.m_fast_ema.Create(_Symbol,_Period,21,0,MODE_EMA,PRICE_CLOSE))
                     if(this.m_slow_ema.Create(_Symbol,_Period,55,0,MODE_EMA,PRICE_CLOSE))
                        if(this.m_fast_ema.Handle()!=INVALID_HANDLE)
                           if(this.m_slow_ema.Handle()!=INVALID_HANDLE)
                             {
                              this.m_trade.SetExpertMagicNumber(this.m_magic);
                              this.m_trade.SetDeviationInPoints(InpSlippage);
                              //---
                              this.m_is_init=true;
                             }
                  //---
                  break;
                 }
               //--- 2) indicator deletion
               case CHARTEVENT_CUSTOM+2:
                 {
                  //---release indicators
                  bool is_slow_released=IndicatorRelease(this.m_fast_ema.Handle());
                  bool is_fast_released=IndicatorRelease(this.m_slow_ema.Handle());
                  if(!(is_slow_released && is_fast_released))
                    {
                     //--- to log?
                     if(InpIsLogging)
                        Print("Failed to release the indicators!");
                    }
                  //--- reset the event object
                  this.ResetEvent();
                  //---
                  break;
                 }
               //--- 3) check open signal
               case CHARTEVENT_CUSTOM+3:
                 {
                  MqlTick last_tick;
                  if(SymbolInfoTick(_Symbol,last_tick))
                    {
                     //--- signal type
                     ENUM_ORDER_TYPE open_ord_type=(ENUM_ORDER_TYPE)_data.dparam;
                     //---
                     double open_pr,sl_pr,tp_pr,coeff;
                     open_pr=sl_pr=tp_pr=coeff=0.;
                     //---
                     if(open_ord_type==ORDER_TYPE_BUY)
                       {
                        open_pr=last_tick.ask;
                        coeff=1.;
                       }
                     else if(open_ord_type==ORDER_TYPE_SELL)
                       {
                        open_pr=last_tick.bid;
                        coeff=-1.;
                       }
                     sl_pr=open_pr-coeff*InpStopLoss*_Point;
                     tp_pr=open_pr+coeff*InpStopLoss*_Point;

                     //--- to normalize prices
                     open_pr=NormalizeDouble(open_pr,_Digits);
                     sl_pr=NormalizeDouble(sl_pr,_Digits);
                     tp_pr=NormalizeDouble(tp_pr,_Digits);
                     //--- open the position
                     if(!this.m_trade.PositionOpen(_Symbol,open_ord_type,InpTradeLot,open_pr,
                        sl_pr,tp_pr))
                       {
                        //--- to log?
                        if(InpIsLogging)
                           Print("Failed to open the position: "+_Symbol);
                       }
                     else
                       {
                        //--- pause
                        Sleep(InpTradePause);
                        //--- reset the event object
                        this.ResetEvent();
                        //--- create an order event object
                        this.m_ptr_event=new COrderEvent();
                        if(CheckPointer(this.m_ptr_event)==POINTER_DYNAMIC)
                          {
                           SEventData data;
                           data.lparam=(long)this.m_magic;
                           data.dparam=(double)this.m_trade.ResultDeal();
                           //--- generate CHARTEVENT_CUSTOM+5 event
                           ushort curr_id=5;
                           if(!this.m_ptr_event.Generate(curr_id,data))
                              PrintFormat("Failed to generate an event: %d",curr_id);
                          }
                       }
                    }
                  //---
                  break;
                 }
               //--- 4) check close signal
               case CHARTEVENT_CUSTOM+4:
                 {
                  if(!this.m_trade.PositionClose(_Symbol))
                    {
                     //--- to log?
                     if(InpIsLogging)
                        Print("Failed to close the position: "+_Symbol);
                    }
                  else
                    {
                     //--- pause
                     Sleep(InpTradePause);
                     //--- reset the event object
                     this.ResetEvent();
                     //--- create an order event object
                     this.m_ptr_event=new COrderEvent();
                     if(CheckPointer(this.m_ptr_event)==POINTER_DYNAMIC)
                       {
                        SEventData data;
                        data.lparam=(long)this.m_magic;
                        data.dparam=(double)this.m_trade.ResultDeal();
                        //--- generate CHARTEVENT_CUSTOM+6 event
                        ushort curr_id=6;
                        if(!this.m_ptr_event.Generate(curr_id,data))
                           PrintFormat("Failed to generate an event: %d",curr_id);
                       }
                    }
                  //---
                  break;
                 }
               //--- 5) position opening
               case CHARTEVENT_CUSTOM+5:
                 {
                  ulong ticket=(ulong)_data.dparam;
                  ulong deal=(ulong)_data.dparam;
                  //---
                  datetime now=TimeCurrent();
                  //--- check the deals & orders history
                  if(HistorySelect(now-PeriodSeconds(PERIOD_H1),now))
                     if(HistoryDealSelect(deal))
                       {
                        double deal_vol=HistoryDealGetDouble(deal,DEAL_VOLUME);
                        ENUM_DEAL_ENTRY deal_entry=(ENUM_DEAL_ENTRY)HistoryDealGetInteger(deal,DEAL_ENTRY);
                        //---
                        if(deal_entry==DEAL_ENTRY_IN)
                          {
                           //--- to log?
                           if(InpIsLogging)
                             {
                              Print("\nNew position for: "+_Symbol);
                              PrintFormat("Volume: %0.2f",deal_vol);
                             }
                          }
                       }
                  //---
                  break;
                 }
               //--- 6) position closing
               case CHARTEVENT_CUSTOM+6:
                 {
                  ulong ticket=(ulong)_data.dparam;
                  ulong deal=(ulong)_data.dparam;
                  //---
                  datetime now=TimeCurrent();
                  //--- check the deals & orders history
                  if(HistorySelect(now-PeriodSeconds(PERIOD_H1),now))
                     if(HistoryDealSelect(deal))
                       {
                        double deal_vol=HistoryDealGetDouble(deal,DEAL_VOLUME);
                        ENUM_DEAL_ENTRY deal_entry=(ENUM_DEAL_ENTRY)HistoryDealGetInteger(deal,DEAL_ENTRY);
                        //---
                        if(deal_entry==DEAL_ENTRY_OUT)
                          {
                           //--- to log?
                           if(InpIsLogging)
                             {
                              Print("\nClosed position for: "+_Symbol);
                              PrintFormat("Volume: %0.2f",deal_vol);
                             }
                          }
                       }
                  //---
                  break;
                 }
               //--- 7) stop trading
               case CHARTEVENT_CUSTOM+7:
                 {
                  datetime stop_time=(datetime)_data.dparam;
                  //---
                  this.m_is_trade=false;                  
                  //--- to log?                  
                  if(InpIsLogging)
                     PrintFormat("Expert trading is stopped at: %s",
                                 TimeToString(stop_time,TIME_DATE|TIME_MINUTES|TIME_SECONDS));
                  //---
                  break;
                 }
               //--- 8) resume trading 
               case CHARTEVENT_CUSTOM+8:
                 {
                  datetime resume_time=(datetime)_data.dparam;
                  this.m_is_trade=true;                  
                  //--- to log?                  
                  if(InpIsLogging)                     
                     PrintFormat("Expert trading is resumed at: %s",
                                 TimeToString(resume_time,TIME_DATE|TIME_MINUTES|TIME_SECONDS));
                  //---
                  break;
                 }
              }
           }
     }
  }

Si compone di due parti. Il primo è la gestione degli eventi collegati con un clic sull'oggetto "Pulsante". Questo clic genererà un evento personalizzato esterno, che verrà gestito dal gestore in un secondo momento.

La seconda parte è progettata per l'elaborazione degli eventi personalizzati generati. Contiene due blocchi, dove dopo che un evento rilevante è stato gestito, ne viene generato uno nuovo. Nel primo blocco viene elaborato l'evento "Ricezione segnale di apertura". La sua corretta gestione genera un nuovo evento di ordine "Apertura di una posizione". Nel secondo blocco viene elaborato l'evento "Ricezione segnale di chiusura". Se il segnale viene gestito, si verifica l'evento "Chiusura di una posizione".

L'Expert CustomEventProcessor.mq5 è un buon esempio dell'utilizzo della classe CEventProcessor. L'EA è stato progettato per creare eventi e rispondere in modo appropriato. Con il paradigma OPP, siamo stati in grado di ridurre al minimo il codice sorgente a un numero inferiore di righe. Il codice sorgente di EA può essere trovato in allegato a questo articolo.

A mio avviso, non è necessario fare riferimento ogni volta al meccanismo di un evento personalizzato. Ci sono molte cose minori, insignificanti e senza eventi in termini di strategia che possono avere una forma diversa.


Conclusione

In questo articolo ho cercato di illustrare i principi per lavorare con eventi personalizzati nell'ambiente MQL5. Spero che le idee trattate in questo articolo siano di interesse per i programmatori con esperienze diverse, non solo per i principianti.

Sono contento che il linguaggio MQL5 si stia sviluppando. Probabilmente, nel prossimo futuro ci saranno modelli di classe e potrebbero essere puntatori a funzioni. Quindi saremo in grado di scrivere un delegato a tutti gli effetti che punta a un metodo di un oggetto arbitrario.

I file sorgente dall'archivio possono essere inseriti in una cartella del progetto. Nel mio caso, è la cartella MQL5\Projects\ChartUserEvent.

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

File allegati |
Le foreste casuali prevedono le tendenze Le foreste casuali prevedono le tendenze
Questo articolo considera l'utilizzo del pacchetto Rattle per la ricerca automatica di modelli per prevedere le posizioni lunghe e corte di coppie di valute sul Forex. Questo articolo può essere utile sia per i trader principianti che per quelli esperti.
Manuale MQL5: Elaborazione dell'evento TradeTransaction Manuale MQL5: Elaborazione dell'evento TradeTransaction
Questo articolo considera le capacità del linguaggio MQL5 dal punto di vista della programmazione event-driven. Il più grande vantaggio di questo approccio è che il programma può ricevere informazioni sull'attuazione graduale di un'operazione di trading. L'articolo contiene anche un esempio di ricezione ed elaborazione di informazioni sull'operazione di trading in corso utilizzando il gestore dell'evento TradeTransaction. A mio parere, un tale approccio può essere utilizzato per copiare le offerte da un terminale all'altro.
Perché l'hosting virtuale su MetaTrader 4 e MetaTrader 5 è migliore del solito VPS? Perché l'hosting virtuale su MetaTrader 4 e MetaTrader 5 è migliore del solito VPS?
La rete Virtual Hosting Cloud è stata sviluppata appositamente per MetaTrader 4 e MetaTrader 5 e presenta tutti i vantaggi di una soluzione nativa. Approfitta della nostra offerta gratuita 24 ore su 24: prova subito un server virtuale.
Reti neurali di terza generazione: Reti profonde Reti neurali di terza generazione: Reti profonde
Questo articolo è dedicato a una nuova direzione nell'apprendimento automatico: deep learning o, per essere precisi, reti neurali profonde. Questa è una breve rassegna delle reti neurali di seconda generazione, l'architettura delle loro connessioni e dei principali tipi, metodi e regole di apprendimento e i loro principali svantaggi. Segue la storia dello sviluppo della rete neurale di terza generazione, i loro principali tipi, peculiarità e metodi di allenamento. Sono condotti esperimenti pratici sulla costruzione e l'addestramento di una rete neurale profonda avviata dai pesi di un autoencoder impilato con dati reali. Tutte le fasi, dalla selezione dei dati di input alla derivazione metrica sono discusse in dettaglio. L'ultima parte dell'articolo contiene un'implementazione software di una rete neurale profonda in un Expert Advisor con un indicatore integrato basato su MQL4 / R.