MQL5 Cookbook: Özel Grafik Olaylarını İşleme

Denis Kirichenko | 14 Ocak, 2022

Giriş

Bu makale, MQL5 Cookbook: makalesinin mantıklı bir devamıdır: Tipik Grafik Olaylarının İşlenmesi. Bu özel grafik olayları ile çalışma yöntemlerini kapsar. Burada okuyucu, özel olayların geliştirilmesi ve ele alınmasına ilişkin örnekler bulabilir. Bu makalede tartışılan tüm fikirler, nesne yönelimli araçlarla uygulanmıştır.

Özel olay teması oldukça geniş olduğu için, programcı ve geliştiricinin çalışmalarına yaratıcılık katabilmesi mümkündür.


1. Özel Grafik Olayı

Bu olayın kullanıcı tarafından tanımlandığı açıktır. Tam olarak neyin ve hangi görevin veya program bloğunun olay şeklini alabileceğine karar vermek bir programcıya kalmıştır. MQL5 geliştiricileri, karmaşık algoritmaların uygulanması için dil imkanlarını genişleten kendine ait olaylar oluşturabilir.

Özel olay, bir grafik olayının olası ikinci türüdür. Birincisi tipik olaydır. Belgeler'de "tipik grafik olayı" diye bir terim olmamasına rağmen ilk on grafik olay türü için bunu kullanmanızı öneririm.

Geliştiriciler, tüm grafik olayları için yalnızca bir numaralandırma önerir— ENUM_CHART_EVENT.

Belgelere göre, özel olayların 65535 tanımlayıcısı vardır. Özel olayların ilk ve son tanımlayıcıları, sayısal olarak benzer şekilde 1000 ve 66534'e eşit olan CHARTEVENT_CUSTOM ve CHARTEVENT_CUSTOM_LAST açık değerleri tarafından belirlenir (Şekil 1).

Şek.1 Özel olayların ilk ve son tanımlayıcıları

Şek.1 Özel olayların ilk ve son tanımlayıcıları

İlk ve son tanımlayıcıları dikkate alan basit hesaplamalar şunları üretecektir: 66534-1000+1=65535.

Özel olaylar kullanılmadan önce tasarlanmaları gereklidir. Geliştirici bu anlamda, daha sonra gelecekteki Expert için bir algoritma olarak uygulanan olay kavramının beyni ve yazarı olur. Özel olayların sınıflandırmasına sahip olmak faydalı olacaktır. Bu bilişsel yöntem belirsizlikten kurtulmaya izin vermeyecektir ancak kesinlikle derecesini azaltacaktır ve akıl yürütme çizgisini düzenleyecektir.

Özel bir olaydaki bu şekilde bir kriteri kaynak olarak ele alalım. Örneğin, sergeev geliştiricisi bir alım satım robotu prototip fikri önerdi. Tüm olayları üç gruba ayırır (Şek.2).

Şek.2 Özel olay kaynağı grupları

Şek.2 Özel olay kaynağı grupları

Daha sonra bu ana fikre göre, özel olaylar grup üyeliğine göre geliştirilecektir.

Başlamak için basit bir şey deneyelim. İlk önce gösterge olaylarını içeren ilk grubu ele alacağız. Bu gruba ait olabilecek olaylar şunlardır: bir göstergenin oluşturulması ve silinmesi, bir pozisyonun açılması ve kapanması için sinyal alınması. İkinci grup, talimatalrın ve pozisyonların durumunu değiştirme olaylarını içerir. Örneğimizde, pozisyonların açılması ve kapanması bu grupta olacaktır. Tamamı çok basittir. Ve son olarak, resmileştirme için en karmaşık grup, bir grup dış olaydır.

İki olayı ele alalım: manuel alım satımı etkinleştirme ve devre dışı bırakma.

Şek.3 Özel olay kaynakları

Şek.3 Özel olay kaynakları

Birincil formasyon, tümdengelim yöntemiyle (genelden özele) kurulabilir (Şekil 3). Bu, daha sonra karşılık gelen sınıftaki olay türlerini yaratmak için kullanacağımız formasyonun ta kendisidir (Tablo 1).

Tablo 1 Özel olaylar

Tablo 1 Özel olaylar

Bu tablo henüz "olay kavramı" olarak adlandırılamaz, ancak bir başlangıçtır. İşte başka bir yaklaşım daha. Muğlak bir alım satım sistemi şeklinin üç alt sistemden oluştuğu yaygın bir bilgidir - temel modüller (Şekil 4).

Fig.4 Muğlak bir alım satım sisteminin modeli

Şek.4 Muğlak bir alım satım sisteminin modeli

"Kaynak" ölçütüne dayalı özel olaylar şurada oluşturulan olaylar olarak sınıflandırılabilir:

  1. sinyal alt sistemi;
  2. takip eden açık pozisyonların alt sistemi;
  3. para yönetiminin alt sistemi.

İkincisi, mesela izin verilen alçalma seviyesine ulaşma, işlem hacmini belirli bir değerle artırma, zarar limitinin yüzdesini artırma vb. gibi olayları içerebilir.


2. ChartEvent İşleyicisi ve Oluşturucusu

Aşağıdaki birkaç satır, grafik olaylarının işleyicisine ve oluşturucusuna ayrılacaktır. Özel bir grafik olayının işlenme prensibi tipik bir grafik olayının ele alınmasına benzer.

Bir işleyici, OnChartEvent() işlevi, parametre olarak dört sabit alır. Görünüşe göre geliştiriciler bu mekanizmayı bir olayı tanımlama ve onun hakkında ek bilgi edinme fikrini uygulamak için kullandılar. Benim görüşüme göre çok kompakt ve kullanışlı bir program mekanizması.

EventChartCustom() işlevi özel bir grafik olayı oluşturur. Fevkalade şekilde, özel bir grafik olayı "kendi" grafiği ve "yabancı" bir grafik için oluşturulabilir. Bence, kendi ve yabancı çizelgelerinin anlamı hakkında en ilginç makale MetaTrader 5'te Çoklu Para Birimi Modunun Uygulanması olabilir.

Benim düşünceme göre, olay tanımlayıcısının oluşturucuda ushort türünde olması, işleyicide ise int türünde olması olgusunda bir uyumsuzluk vardır. İşleyicide de ushort veri türünü kullanmak mantıklı olacaktır.


3. Özel Olay Sınıfı

Daha önce de belirttiğim gibi olay kavramı Expert geliştiriciye kalmıştır. Şimdi Tablo 1'deki olaylarla çalışacağız. İlk önce CEventBase özel olayının sınıfını ve türevlerini sıralayacağız (Şekil 5).

Şek.5 Olay sınıflarının hiyerarşisi

Şek.5 Olay sınıflarının hiyerarşisi

Temel sınıf aşağıdaki gibi görünür:

//+------------------------------------------------------------------+
//| 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;};
  };

Olay türü ENUM_EVENT_TYPE numaralandırması tarafından belirlenir:

//+------------------------------------------------------------------+
//| 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
  };

Veri üyeleri, olay tanımlayıcıdan ve veri yapısından oluşur.

CEventBase temel sınıfının Generate() yöntemi, bir olay oluşturmakla ilgilenir. GetId() yöntemi, olay kimliğini döndürür ve Validate() sanal yöntemi, olay tanımlayıcı değerini kontrol eder. İlk olarak sınıfa olay işleme yöntemini dahil ettim ancak daha sonra her olayın benzersiz olduğunu ve soyut bir yöntemin burada yeterli olmadığını anladım. Bu görevi, özel olayları işleyen CEventProcessor sınıfına devretmekle bitirdim.


4. Özel Olay İşleyici Sınıfı

CEventProcessor sınıfının, sunulan sekiz olayı oluşturması ve işlemesi beklenmektedir. Sınıfın veri üyeleri şöyle görünür:

//+------------------------------------------------------------------+
//| 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;
//+------------------------------------------------------------------+
  };

Öznitelikler listesi arasında başlatma ve alım satım bayrakları vardır. İlki doğru şekilde başlayamazsa EA'nın alım satım yapmasına izin vermeyecektir. İkincisi, alım satım iznini kontrol eder.

Ayrıca, polimorfizm kullanarak farklı türlerdeki olaylarla çalışan CEventBase türünün nesnesine yönelik işaretçi de vardır. CTrade sınıfının örneği, alım satım işlemlerine erişim sağlar.

CiMA türündeki nesneler, göstergelerden alınan verilerin işlenmesini kolaylaştırır. Örneği basitleştirmek için, alım satım sinyali alacak iki tane Hareketli Ortalama aldım. EA'nın manuel olarak etkinleştirilmesi/devre dışı bırakılması için kullanılacak "CButton" sınıfının da örneği vardır.

Sınıfın yöntemleri "modüller - prosedürler - işlevler - makrolar" ilkesine göre bölünmüştür:

//+------------------------------------------------------------------+
//| 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);
  };

Modüller arasında sadece olay oluşturan üç modül vardır: başlangıç modülü—Başlangıç(), bitiş modülü—Bitiş() ve ana modül—Ana (). Dördüncü modül ProcessEvent() olsa da hem bir olay işleyicisi hem de bir oluşturucudur.


4.1 Başlangıç Modülü

Bu modül, OnInit() işleyicisinde çağrılacak şekilde tasarlanmıştır.

//+------------------------------------------------------------------+
//| 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;
  }

Bu modülde gösterge olay nesnesi için bir işaretçi oluşturulur. Ardından "Gösterge oluşturma" olayı oluşturulur. Düğme, oluşturulacak son şeydir. "Dur" moduna geçilir. Bu, düğmeye basılması durumunda Expert'in çalışmayı durduracağı anlamına gelir.

SEventData yapısı da bu yöntem tanımında yer almaktadır. Bu özel olayın oluşturucusuna aktarılan parametreler için basit bir kapsayıcıdır. Burada yalnızca yapının bir alanı doldurulacaktır - bu, uzun türün alanıdır. EA'nın sihirli sayısını tutacaktır.


4.2 Bitiş Modülü

Bu modülün OnDeinit() işleyicisinde çağrılması gereklidir.

//+------------------------------------------------------------------+
//| 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);
     }
  }

Burada önceki olay işaretçisi temizlenir ve "Gösterge silme" olayı oluşturulur. OnDeinit() işleyicisinde özel bir olay oluşturulursa, 4001 çalışma zamanı hatası (beklenmeyen harici hata) alacağınızı belirtmeliyim. Bu nedenle, olay oluşturma ve işleme OnChartEvent() çağrılmadan bu yöntem içinde gerçekleştirilir.

Yine, EA'nın sihirli sayısı SEventData yapısı kullanılarak saklanacaktır.


4.3 Ana Modül

Bu modülün OnTick() işleyicisinde çağrılması gereklidir.

//+------------------------------------------------------------------+
//| 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();
           }
  }

Bu modülde Open() ve Close() prosedürleri çağrılır. İlk prosedür, "Açma için bir sinyal alma" olayını ve ikincisi - "Kapatma için bir sinyal alma" olayını oluşturabilir. Modülün mevcut sürümü, yeni bir çubuk görünümünde tamamen işlevseldir. yeni çubuk Konstantin Gruzdev tarafından tanımlanmıştır.


4.4 Olay İşleme Modülü

Bu modülün OnChartEvent() işleyicisinde çağrılması gereklidir. Bu modül, boyut ve işlevsellik açısından en büyüğüdür.

//+------------------------------------------------------------------+
//| 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;
                 }
              }
           }
     }
  }

İki bölümden oluşur. İlki, "Düğme" nesnesine tıklama ile bağlantılı olayları ele almaktır. Bu tıklama, daha sonra işleyici tarafından ele alınacak olan harici bir özel olay oluşturacaktır.

İkinci kısım, oluşturulan özel olayların işlenmesi için tasarlanmıştır. İlgili olay işlendikten sonra, yeni bir tane oluşturulan iki blok içerir. "Açma sinyali alma" olayı ilk blokta işlenir. Başarılı bir şekilde işlenmesi, "Pozisyonun açılması" adlı yeni bir talimat olayı oluşturur. "Kapama sinyali alma" olayı ikinci blokta işlenir. Sinyal işlenirse, "Pozisyonun kapanması" olayı gerçekleşir.

Expert CustomEventProcessor.mq5, CEventProcessor sınıfını kullanmaya iyi bir örnektir. EA, olaylar oluşturmak ve bunlara uygun şekilde yanıt vermek için tasarlanmıştır. Kaynak kodunu OPP paradigması ile daha az sayıda satıra indirebildik. EA kaynak kodu bu makalenin ekinde bulunabilir.

Bana göre her seferinde özel bir olay mekanizmasına başvurmaya gerek yoktur. Farklı biçimlerde meydana gelen stratejilerle ilgili küçük, önemsiz ve olağan pek çok sorun vardır.


Sonuç

Bu makalede, MQL5 ortamındaki özel olaylarla çalışma prensiplerini göstermeye çalıştım. Bu makalede ele alınan fikirlerin yalnızca acemi olanlar için değil, farklı deneyime sahip programcıların da ilgisini çekeceğini umuyorum.

MQL5 dilinin gelişmesinden memnunum. Muhtemelen, yakın gelecekte sınıf şablonları olacaktır ve bu işlevler için işaretçiler olabilir. O zaman rastgele bir nesnenin yöntemine işaret eden tam teşekküllü bir temsilci yazabileceğiz.

Arşivdeki kaynak dosyalar bir proje klasörüne yerleştirilebilir. Benim durumumda bu, MQL5\Projects\ChartUserEvent klasörüdür.