English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Italiano
"Yeni Çubuk" Olay İşleyicisi

"Yeni Çubuk" Olay İşleyicisi

MetaTrader 5Örnekler | 15 Aralık 2021, 11:01
193 0
Konstantin Gruzdev
Konstantin Gruzdev

Giriş

Gösterge ve expert yazarları, yürütme süresi açısından kompakt kodu yazmakla her zaman ilgilenmişlerdir. Bu soruna farklı açılardan yaklaşabilirsiniz. Bu makaledeki bu geniş konudan, görünüşe göre halihazırda çözülmüş olan bir sorunu ele alacağız: Yeni bir çubuğu kontrol etme. Bu, hesaplama döngülerini sınırlamanın oldukça popüler bir yoludur; zira tüm hesaplamalar ve alım satım işlemleri, grafikte yeni bir çubuk oluşturulması sırasında bir kez gerçekleştirilir. Peki, neler tartışılacak?  

  • Yeni çubukları tespit etme yolları.
  • Yeni çubuk tespitinin mevcut algoritmalarındaki kusurlar.
  • Evrensel yeni çubuk tespit yöntemi oluşturma.
  • İncelikler ve bu yöntemi uygulama yolları.
  • NewBar olayı ve bu olayın işleyicisi - OnNewBar().

Yeni Çubukları Tespit Etme Yolları

Yeni bir çubuğun nasıl tespit edileceğine dair kabul edilebilir bazı çözümler mevcuttur. Örneğin, bunlar, Expert Advisor'lardaki Sınırlamalar ve Doğrulamalar, Göstergelerin Ekonomik Hesaplama Prensipleri makalelerinde veya burada bulunabilir. Bu arada, bu materyalleri incelemenizi tavsiye ederim. Bu sayede neden söz ettiğimi anlamanız daha da kolay olacaktır.  

Bu materyaller, şu anda tamamlanmamış çubuğun açılış zamanını izleme prensibini kullanır. Bu, çok kolay ve güvenilir bir yoldur. Yeni bir çubuğu tespit etmenin başka yöntemleri de vardır.

Örneğin, özel göstergelerde bu amaç için OnCalculate() işlevinin iki giriş parametresini kullanabilirsiniz: Rates_total ve prev_calculated. Gerçek şu ki bu yöntemin sınırlaması esas olarak, yalnızca mevcut grafikte ve göstergelerde yeni bir çubuk tespit etmek için kullanılabilmesidir. Başka bir dönem veya sembol üzerinde yeni bir çubuk bulmak istiyorsanız ek teknikler kullanmanız gerekir.

Veya örneğin, Tick Hacmi = 1 olduğunda veya tüm çubuk fiyatları eşit olduğunda ilk tick'te yeni bir çubuk yakalamayı deneyebilirsiniz: Açık = Yüksek = Düşük = Kapalı. Bu yöntemler test için iyi bir şekilde kullanılabilir, ancak gerçek alım satımda genellikle hata verirler. Bunun nedeni, birinci ve ikinci tick arasındaki anın oluşturulan çubuğu yakalamak için bazen yeterli olmamasıdır. Bu, özellikle güçlü bir piyasa hareketinde veya İnternet bağlantısının kalitesi düşük olduğunda fark edilir.  

TimeCurrent() işlevine dayalı olarak yeni bir çubuğu tespit etmenin bir yolu vardır. Bu arada, mevcut grafik için yeni bir çubuk tespit etmeniz gerekiyorsa bu, iyi bir yoldur. Bu makalenin sonunda bunu kullanacağız.

Bir komşuya dahi sorabilirsiniz: "Hey, yeni bir çubuk var mı?" Yanıtının ne olacağını merak ediyorum. Yeni bir tane tespit etmek için mevcut tamamlanmamış çubuğun açılış zamanını izleme prensibindeki seçiminizi durduralım. Basitliği ve güvenilirliği gerçekten tecrübe ile sabittir. 

Başlangıç Noktası

Yukarıda belirtilen materyallerde, yeni bir çubuğun tespiti ile ilgili işler fena gitmiyor. Fakat...  

Bu "fakatın" ne olduğunu anlamak için bir başlangıç noktası (veya bir prototip) olarak Expert Advisor'lardaki Sınırlamalar ve Doğrulamalar adlı makaleden basit ve iyi çalışan bir işlevi ele alacağız. O halde başlayalım:

//+------------------------------------------------------------------+
//| Returns true if a new bar has appeared for a symbol/period pair  |
//+------------------------------------------------------------------+
bool isNewBar()
  {
//--- memorize the time of opening of the last bar in the static variable
   static datetime last_time=0;
//--- current time
   datetime lastbar_time=SeriesInfoInteger(Symbol(),Period(),SERIES_LASTBAR_DATE);

//--- if it is the first call of the function
   if(last_time==0)
     {
      //--- set the time and exit
      last_time=lastbar_time;
      return(false);
     }

//--- if the time differs
   if(last_time!=lastbar_time)
     {
      //--- memorize the time and return true
      last_time=lastbar_time;
      return(true);
     }
//--- if we passed to this line, then the bar is not new; return false
   return(false);
  }

Bu prototip işlevi aslında çalışıyor ve tam işleve sahip. Fakat... 

Prototip İşlevinin Analizi

Bu işlevi (elbette) en mükemmel ve en iyisinin de iyisi olan Expert Advisor'ın kaynak koduna kopyaladım. Bu, işe yaramadı. Araştırmaya başladım. Bu işlevle ilgili düşüncelerimi aşağıda paylaştım.

İşlev başlığı. Sonuç olarak, her şeye göz atalım. İşlev başlığıyla başlayalım:

bool isNewBar()

İşlev başlığını beğendim; çok basit, kolay anlaşılır ve iletilen parametrelerle uğraşmaya gerek yok. Bunu, ilerde bu formda kullanmak güzel olurdu.

Çağrı sayısı kısıtlaması. Başlığı takiben statik değişkeni başlatan ilk ifade gelir:

//---  memorize the time of opening of the last bar in the static variable
   static datetime last_time=0;

Her şey oldukça iyi görünüyor. Fakat...

Sorun şu ki, statik değişkenini kullanıyoruz. Yardım konularında bize söylenen şey şudur: Yardım konularında bize söylenen şey şudur:

Statik değişkenler, programın yürütüldüğü andan itibaren mevcuttur ve özel OnInit() işlevi çağrılmadan önce yalnızca bir kez başlatılır. Başlangıç değerleri belirtilmemişse statik depolama sınıfının değişkenleri sıfır başlangıç değerlerini alır. 

Statik anahtar sözcüğüyle bildirilen yerel değişkenler, değerlerini yaşam değeri işlevi boyunca korur. Sonraki her işlev çağrısında, bu tür yerel değişkenler önceki çağrı sırasında sahip oldukları değerleri içerir.

Bu prototip işlevini tek bir yerden çağırırsanız bu, ihtiyacımız olan şeye sahip olduğumuz anlamına gelir. Fakat bu işlevi örneğin yine aynı hesaplama döngüsünde başka bir yerde kullanmak istersek her zaman false değerini döndürür; bu da çubuk olmadığı anlamına gelir. Ve bu, her zaman doğru olmayacak. Bu durumda, statik değişken, prototip işlev çağrılarının sayısına yapay bir sınırlama getirir.

Evrensellik sorunu. Prototip işlevinde aşağıdaki ifade şu şekilde görünür:

//--- current time
   datetime lastbar_time=SeriesInfoInteger(Symbol(),Period(),SERIES_LASTBAR_DATE);

En son tamamlanmamış çubuğun açılış zamanını elde etmek için SeriesInfoInteger() işlevinin SERIES_LASTBAR_DATE değiştiricisiyle birlikte kullanılması mantıklıdır.

Prototip işlevimiz isNewBar(), başlangıçta basit olarak düşünülmüştü; bu, varsayılan olarak alım satım enstrümanını ve mevcut grafiğin dönemini kullanır. Yeni çubuğu yalnızca mevcut grafikte izlemek istiyorsanız bu, kabul edilebilir. Ancak dönem ve enstrümanı yalnızca mevcut grafik için kullanmıyorsam ne yapmalıyım? Ayrıca, ya karmaşık bir grafiğim varsa? Örneğin, Renko veya Kagi'yi çizmeye karar verirsem ne olur?

Eksikliği bizi ciddi şekilde sınırlayabilir. Daha sonra, bunun nasıl düzeltileceğini ele alacağız.  

Hata işleme. SeriesInfoInteger() işlevine bir göz atalım. Grafik henüz oluşturulmamışken çalışırsa neyi döndüreceğini düşünüyorsunuz? Böyle bir durum, örneğin, Expert Advisor'ınızı veya göstergenizi bir grafiğe eklediğiniz ve dönemi veya sembolü değiştirmeye karar verdiğiniz veya terminali yeniden başlattığınız durumlarda ortaya çıkabilir. Peki zaman serilerinin güncellemesi sırasında ne olacak? Bu arada, Yardım konularında bu tip bir uyarı var:

Veri Kullanılabilirliği

Verilerin HCC biçimindeki veya hatta kullanıma hazır HC biçimindeki varlığı, bu verilerin bir grafikte görüntülenmesi veya MQL5 programlarında kullanılması için her zaman mutlak mevcudiyeti anlamına gelmez.

Bir MQL5 programından fiyat verilerine veya gösterge değerlerine erişirken, bunların belirli bir zamanda veya belirli bir andan itibaren kullanılabilirliğinin garanti edilmediğini unutmayın. Bunun nedeni, sistem kaynaklarını saklamak için bir mql5 programı için gerekli verilerin tam kopyasının MetaTrader 5'te depolanmamasıdır; yalnızca terminal veri tabanına doğrudan erişim sağlanır.

Tüm zaman dilimleri için fiyat geçmişi, HCC biçimindeki ortak verilerden oluşturulur ve bir sunucudan herhangi bir veri güncellemesi, tüm zaman dilimleri için verilerin güncellenmesine ve göstergelerin yeniden hesaplanmasına yol açar. Bu nedenle, bu veriler az önce mevcut olsa dahi verilere erişim reddedilebilir.

Peki, bu işlev neyi döndürecek? Bu belirsizliği önlemek için, bir şekilde son tamamlanmamış çubuğun açılış zamanının sorgu hatalarını yakalamaya başlamanız gerekir.  

Başlatma olasılığı. Devam edelim. Prototip işlevimizin aşağıdaki ifadelerini göz önünde bulundurun:

//--- if it is the first call of the function
   if(last_time==0)
     {
      //--- set the time and exit
      last_time=lastbar_time;
      return(false);
     }

Kesinlikle, burada her şey yolunda. Ancak, bir nüans var. Yardım Konuları bölümünde yukarıdaki cümleyi fark ettiniz mi? "Statik değişkenler, programın yürütüldüğü andan itibaren mevcuttur ve özel OnInit() işlevinden önce yalnızca bir kez başlatılır" Peki ya last_time değişkenini başlatmak için daha fazla zamana ihtiyacımız olursa? Daha doğrusu, ilk çağrının durumunu yapay olarak oluşturmak istiyorsanız ne yapmalısınız? Veya başka bir durumun? Yanıtlarını bildiğiniz soruları sormak kolaydır. Ama bunun üzerinde daha sonra daha fazla duracağız.

Çubuk sayısı. Daha sonra, prototip işlevimiz aşağıdaki kodu içerecektir:

//--- if the time differs
   if(last_time!=lastbar_time)
     {
      //--- memorize the time and return true
      last_time=lastbar_time;
      return(true);
     }

Gördüğünüz gibi, benim gibi bir programcı eğer işleci istemci terminalini ve Strateji Test Cihazını "şaşırtacak" şekilde yapabilir. Gerçek şu ki, mantıksal olarak, geçmiş zaman her zaman şimdiki zamandan daha azdır. Bu, last_time < lastbar_time'dır. Rastlantısal bir program hatası nedeniyle zaman makinem oldu; daha doğrusu tersi oldu: lastbar_time < last_time. Bu ne sürpriz! Genel olarak, böyle bir zaman paradoksunun tespit edilmesi ve bir hata mesajının görüntülenmesi kolaydır.

Ama her işte bir hayır var. "Zaman makinemde" izlerken, isNewBar() çağrıları arasında yalnızca yeni bir çubuğun görünmediğini keşfettim. Grafik dönemi ne kadar küçükse, işlev çağrıları arasında birkaç çubuğun oluşma olasılığı o kadar yüksek olur. Bunun birçok nedeni olabilir: Uzun hesaplama süresinden başlayıp sunucu ile geçici bir bağlantı eksikliği ile bitmesi. Yalnızca yeni bir çubuğun sinyalini değil, aynı zamanda çubuk sayısını da alma imkanı kesinlikle faydalı olacaktır.

Prototip işlevimiz şu şekilde bitiyor:

//--- if we passed to this line, then the bar is not new; return false
   return(false);

Evet, bu satıra geçtiysek - çubuk yeni değil.

Yeni isNewBar() işlevi oluşturma 

Bu noktada ilginç bir şey başlıyor. Tespit edilen zayıf yönleri çözeceğiz. Anlayacağınız üzere, biraz fazla mütevazi davrandım, bölümü "Yeni isNewBar() işlevi oluşturma" olarak adlandırdım. Daha sağlam bir şey yapacağız.

İşe, işlev çağrılarının sayısındaki kısıtlamalardan kurtularak başlayacağız.

Akla gelen ilk şey, Göstergelerin Ekonomik Hesaplanmasına İlişkin Prensipler başlıklı makaleden veya buradan isNewBar isNewBar() ile aynı adı taşıyan işlevleri kullanabilmenizdir. Yani, işlev gövdesine birden çok last_time değeri depolayan dizileri dahil etmek için, isNewBar() işlev çağrılarının sayaçlarını farklı yerlerden koyun, vb. Elbette, bunların tamamı çalışan sürümlerdir ve uygulanabilirler. Ancak, 12 döviz çifti üzerinde çalışmak üzere çok para birimli bir Expert Advisor yazdığımızı hayal edin. Göz önünde bulundurulacak ve kafa karıştırmayacak çok fazla gerekli nüans olacak mı?

Ne yapmalıyız? Yanıt burada!

Nesne Yönelimli Programlamanın iyi yanı, bir nesnenin veya bir sınıfın bir örneğinin, aynı sınıfın diğer örneklerinden bağımsız olarak "kendi başlarına işlev görebilmesi"dir. Öyleyse bir CisNewBar sınıfı oluşturmaya başlayalım; böylece bu sınıfın örneklerini Expert Advisor'ımızın veya Göstergemizin herhangi bir yerinde herhangi bir sayıda üretebileceğiz. Ve her örneğin "kendi işlevini görmesine" izin verin.

Başlamamız gereken nokta şu:

class CisNewBar 
  {
   protected:
      datetime          m_lastbar_time;   // Time of opening last bar
      
   public:
      void              CisNewBar();      // CisNewBar constructor
      //--- Methods of detecting new bars:
      bool              isNewBar();       // First type of request for new bar
  };  

bool CisNewBar::isNewBar()
  {
   //--- here is the definition of static variable

   //--- here will be the rest of method's code   
   ...

   //--- if we've reached this line, then the bar is not new; return false
   return(false);
  }

İsNewBar() işlevinin önceki hali şimdi yöntem. Artık last_time statik değişkeninin olmadığını unutmayın - Bunun yerine artık m_lastbar_time korumalı sınıf değişkenimiz var. isNewBar() yönteminde statik değişken bırakmış olsaydık, daha önce isNewBar() işlevinde karşılaştığımız sorunlarla karşılaşacağımız için tüm çabalarımız boşa giderdi - Bunlar statik değişkenlerin özellikleridir.

Ve şimdi son çubuğun zamanı, sınıfın m_lastbar_time korumalı değişkeninde saklanacak ve her sınıf örneğinde bellek bu değişken için ayrılacaktır. Bu şekilde, prototip işlevindeki çağrı sayısı kısıtlamasını kaldırabildik. isNewBar() yöntemini MQL programımızda farklı yerlerde istediğimiz kadar çağırabilir ve her yer için sınıf örneği oluşturabiliriz.

Bu, başardığımız bir şey. Şimdi evrensellik üzerinde çalışalım. Yeni sınıfımıza bir şey eklemeden önce, sizi eğlenceli bir fikir yöneltmek istiyorum.

Mantık yürütelim. Ne istiyoruz? Yeni çubuk ile ilgili sinyal almak istiyoruz. Bunu nasıl yapmak istiyoruz? Bu nedenle, son tick'te (veya son anda) mevcut tamamlanmamış çubuğun açılış zamanı, önceki tick'te (veya önceki anda) mevcut tamamlanmamış çubuğun açılış zamanından daha fazlaysa bu durumda yeni çubuk oluşturulur. Bu, karmaşık ama doğru bir ifade. Uzun lafın kısası, zamanı karşılaştırmamız gerekiyor. Bu nedenle, mevcut newbar_time tamamlanmamış çubuğunun açılış zamanını isNewBar() yöntemine geçirmenin mantıklı olacağına karar verdim. Bu durumda, yöntem başlığı aşağıdaki gibi olacaktır:

bool isNewBar(datetime newbar_time)

Henüz newbar_time'ı nereye götüreceğimizi sormayın; bunun zaten bilindiğini varsayın. Bunu daha sonra inceleyeceğiz.  

Bu arada, zamanı isNewBar() yöntemine geçirerek, yeni bir çubuğu tespit etmek için çok esnek bir araç elde ediyoruz. Her türlü alım satım aracıyla tüm standart grafik dönemlerini kapsayabileceğiz. Bu, artık sembol adına ve dönemin boyutuna bağlı kalmamamız için gerçekleşti.  

Ayrıca standart olmayan grafikleri de kullanabiliriz. Örneğin, tick mum grafikleri veya Renko veya Kagi grafikleri çiziyorsanız çubuk açılış zamanları pratik olarak hiçbir zaman standart grafik dönemlerinin zamanıyla çakışmaz. Bu durumda, işlevimiz vazgeçilmez olacaktır.

Çok yönlülük sorun değil. Fikrimize uygun olarak CisNewBar sınıfımızı tamamlayalım:

class CisNewBar 
  {
   protected:
      datetime          m_lastbar_time;   // Time of opening last bar
      uint              m_retcode;        // Result code of detecting new bar
      int               m_new_bars;       // Number of new bars
      string            m_comment;        // Comment of execution
      
   public:
      void              CisNewBar();      // CisNewBar constructor
      //--- Methods of detecting new bars:
      bool              isNewBar(datetime new_Time); // First type of request for new bar
  };
   
//+------------------------------------------------------------------+
//| First type of request for new bar                     |
//| INPUT:  newbar_time - time of opening (hypothetically) new bar   |
//| OUTPUT: true   - if new bar(s) has(ve) appeared                  |
//|         false  - if there is no new bar or in case of error      |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
bool CisNewBar::isNewBar(datetime newbar_time)
  {
   //--- Initialization of protected variables
   m_new_bars = 0;      // Number of new bars
   m_retcode  = 0;      // Result code of detecting new bar: 0 - no error
   m_comment  =__FUNCTION__+" Successful check for new bar";
   //---
   
   //--- Just to be sure, check: is the time of (hypothetically) new bar m_newbar_time less than time of last bar m_lastbar_time? 
   if(m_lastbar_time>newbar_time)
     { // If new bar is older than last bar, print error message
      m_comment=__FUNCTION__+" Synchronization error: time of previous bar "+TimeToString(m_lastbar_time)+
                                                  ", time of new bar request "+TimeToString(newbar_time);
      m_retcode=-1;     // Result code of detecting new bar: return -1 - synchronization error
      return(false);
     }
   //---
        
   //--- if it's the first call
   if(m_lastbar_time==0)
     {  
      m_lastbar_time=newbar_time; //--- set time of last bar and exit
      m_comment   =__FUNCTION__+" Initialization of lastbar_time = "+TimeToString(m_lastbar_time);
      return(false);
     }   
   //---

   //--- Check for new bar:
   if(m_lastbar_time<newbar_time)       
     { 
      m_new_bars=1;               // Number of new bars
      m_lastbar_time=newbar_time; // remember time of last bar
      return(true);
     }
   //---
   
   //--- if we've reached this line, then the bar is not new; return false
   return(false);
  }

Sınıfımızın kaynak koduna baktığınızda, muhtemelen çalışma zamanı hatalarının takibini göz önünde bulundurduğumuzu fark etmişsinizdir ve yeni çubukların sayısını depolayan değişkeni kullanıma sunduk.

Her şey yolunda, ancak evrensel yöntemimiz isNewBar(datetime newbar_time) büyük bir zorluk içeriyor. Bu zorluk, expert'imizin veya göstergemizin kaynak kodunda (varsayımsal olarak) newbar_time zamanını hesaplama konusunda her zaman endişelenmemiz gerektiğidir.  

Neyse ki, bazı durumlarda, bu işlevi sınıfımızın yeni ek yöntemine emanet ederek hayatınızı kolaylaştırabiliriz. Prototip işlevimizdeki standart dönemler ve semboller için bu, SeriesInfoInteger() işlevinin ikinci sürümünün SERIES_LASTBAR_DATE değiştiricisiyle kullanılmasıyla ve diğer tüm durumlarda genel yöntemin kullanılmasıyla yapılabilir. Sahip olduklarımıza bakalım:

//+------------------------------------------------------------------+
//| Second type of request for new bar                     |
//| INPUT:  no.                                                      |
//| OUTPUT: m_new_bars - Number of new bars                          |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
int CisNewBar::isNewBar()
  {
   datetime newbar_time;
   datetime lastbar_time=m_lastbar_time;
      
   //--- Request time of opening last bar:
   ResetLastError(); // Set value of predefined variable _LastError as 0
   if(!SeriesInfoInteger(m_symbol,m_period,SERIES_LASTBAR_DATE,newbar_time))
     { // If request has failed, print error message:
      m_retcode=GetLastError();  // Result code of detecting new bar: write value of variable _LastError
      m_comment=__FUNCTION__+" Error when getting time of last bar opening: "+IntegerToString(m_retcode);
      return(0);
     }
   //---
   
   //---Next use first type of request for new bar, to complete analysis:
   if(!isNewBar(newbar_time)) return(0);
   
   //---Correct number of new bars:
   m_new_bars=Bars(m_symbol,m_period,lastbar_time,newbar_time)-1;
   
   //--- If we've reached this line - then there is(are) new bar(s), return their number:
   return(m_new_bars);
  }

Elimizde şu anda ne var? Artık standart dönemler için son tamamlanmamış çubuğun açılış saatini belirlemekle ilgilenmemize gerek yok. Prototip işlevimize basit çağrısı ile ve sahip olduğu kusurlar olmadan yaklaştık. Hatta hata kodları, çalışma zamanı yorumları ve yeni çubuk sayısı gibi ek avantajlar da elde etti.   

Geriye bir şey kaldı mı? Evet. Son an var: Başlatma. Bunun için sınıf oluşturucusunu ve birkaç Set-yöntemini kullanacağız. Sınıf oluşturucumuz şu şekilde görünür:  

//+------------------------------------------------------------------+
//| CisNewBar constructor.                                           |
//| INPUT:  no.                                                      |
//| OUTPUT: no.                                                      |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
void CisNewBar::CisNewBar()
  {
   m_retcode=0;         // Result code of detecting new bar
   m_lastbar_time=0;    // Time of opening last bar
   m_new_bars=0;        // Number of new bars
   m_comment="";        // Comment of execution
   m_symbol=Symbol();   // Symbol name, by default - symbol of current chart
   m_period=Period();   // Chart period, by default - period of current chart
  }

Ve Set-yöntemleri şöyle görünür:

//--- Methods of initializing protected data:
void              SetLastBarTime(datetime lastbar_time){m_lastbar_time=lastbar_time;                            }
void              SetSymbol(string symbol)             {m_symbol=(symbol==NULL || symbol=="")?Symbol():symbol;  }
void              SetPeriod(ENUM_TIMEFRAMES period)    {m_period=(period==PERIOD_CURRENT)?Period():period;      }

Sınıf oluşturucusu sayesinde, mevcut grafiğin sembol ve döneminin başlatılmasına dikkat etmemize gerek yok. Bunlar, prototip işlevinde olduğu gibi varsayılan olarak kullanılacaktır. Ancak başka bir sembol veya grafik dönemi kullanmamız gerekirse bunun için, oluşturduğumuz Set-yöntemlerini kullanabiliriz. Ayrıca, SetLastBarTime(datetime lastbar_time) kullanarak "ilk çağrı"nın durumunu yeniden oluşturabilirsiniz.

Sonuç olarak, Expert Advisor ve Göstergelerdeki sınıfımızdan veri almak için birkaç Get-yöntemi oluşturalım: 

      //--- Methods of access to protected data:
uint              GetRetCode()     const  {return(m_retcode);     }  // Result code of detecting new bar 
datetime          GetLastBarTime() const  {return(m_lastbar_time);}  // Time of opening last bar
int               GetNewBars()     const  {return(m_new_bars);    }  // Number of new bars
string            GetComment()     const  {return(m_comment);     }  // Comment of execution
string            GetSymbol()      const  {return(m_symbol);      }  // Symbol name
ENUM_TIMEFRAMES   GetPeriod()      const  {return(m_period);      }  // Chart period

Artık mql5 programlarımızdaki gerekli tüm bilgileri elde edebiliriz. Şimdilik, CisNewBar sınıfını oluşturmaya son verebiliriz.

Sınıfımızın tam kaynak kodu Lib CisNewBar.mqh ekli dosyasındadır.

CisNewBar Sınıfı Kullanım Örnekleri

Ne oluşturduğumuza ilişkin tüm incelikleri öğrenmek için sınıf kullanımımızın örneklerini incelemenizi öneriyorum. Belki yalnızca avantajları değil dezavantajları da olabilir.

Örnek 1. Başlamak için, Expert Advisor'lardaki Sınırlamalar ve Doğrulamalar adlı makaleden isNewBar() işlevi için mutlak surette aynı olan bir Expert Advisor oluşturalım:

//+------------------------------------------------------------------+
//|                                               Example1NewBar.mq5 |
//|                                            Copyright 2010, Lizar |
//|                                               Lizar-2010@mail.ru |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, Lizar"
#property link      "Lizar-2010@mail.ru"
#property version   "1.00"

#include <Lib CisNewBar.mqh>

CisNewBar current_chart; // instance of the CisNewBar class: current chart

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   if(current_chart.isNewBar()>0)
     {     
      PrintFormat("New bar: %s",TimeToString(TimeCurrent(),TIME_SECONDS));
     }
  }

Her iki Expert Advisor'ı da aynı çift ve dönemi içeren grafiklerde çalıştıralım. Bakalım elimizde ne var:


İlk olarak, her iki Expert Advisor da yeni çubuk hakkında eşzamanlı olarak rapor verir. Ardından dururlar ve yalnızca dört dakika sonra yeni bir çubuk olduğunu bildirirler (bu kez, 1 olarak işaretlenir). Sorun değil - Birkaç dakikalığına internet bağlantımı kestim ve ne olacağına bakmaya karar verdim. Birkaç çubuk oluşturulmuş olmasına rağmen, bu bilgiyi almadık. Yeni Expert Advisor'da isNewBar() yöntemimiz böyle bir şeye izin verdiği için bu dezavantajı düzeltebiliriz.

Ardından, grafik dönemini M2 olarak değiştirdim. Expert Advisor'ların tepkisi farklıydı. CheckLastBar her 2 dakikada bir yeni çubuk hakkında rapor vermeye başladı ve Example1NewBar, dönem değişmemiş gibi (2 olarak işaretlenir) her dakika yeni çubuklar hakkında bilgi verdi.

Gerçek şu ki, current_chart örneğimiz, Expert Advisor grafiğe eklendiğinde sınıf oluşturucusu tarafından başlatıldı. Grafiğe halihazırda eklenmiş olan Expert Advisor'ın dönemini değiştirdiğinizde, sınıf oluşturucusu başlamaz ve Expert Advisor M1 dönemi ile çalışmaya devam eder. Bu, sınıf örneğimizin kendi işlevini gördüğü ve ortamdaki değişikliklerden etkilenmediği anlamına gelir. Bu, hem lehte hem de aleyhte olabilir - Tamamı görevlere bağlıdır.  

Expert Advisor'ımızın CheckLastBar olarak hareket edebilmesi için OnInit() işlevinde m_symbol ve m_period korumalı sınıf değişkenlerini başlatmamız gerekiyor. O halde, bunu yapmaya başlayalım.

Örnek 2. Expert Advisor'ımıza bazı eklemeleri uygulamaya koyalım ve performansını CheckLastBar ile tekrar karşılaştıralım. Expert Advisor'ın kaynak kodu Example2NewBar.mq5 dosyası olarak eklenir. Expert Advisor'ları aynı çifti ve dönemi içeren grafiklerde çalıştırın. Geçen seferki gibi onlar için aynı engelleri oluşturalım. Bakalım elimizde ne var:


Geçen sefer olduğu gibi, Expert Advisor'lar ilk önce yeni çubuk hakkında eşzamanlı olarak rapor verir. Sonra, internet bağlantısını birkaç dakika kestim... Sonra bağlantıyı tekrar açtım. Yeni Expert Advisor'ımız yalnızca yeni bir çubuk hakkında bilgi vermekle kalmadı, aynı zamanda kaç tanesinin göründüğünü de bildirdi (1 olarak işaretlenir). Çoğu gösterge ve expert için bu sayı, hesaplanmamış çubukların sayısı anlamına gelecektir. Böylece, uygun maliyetli yeniden hesaplama algoritmaları için iyi bir dayanağa sahip olduk.  

Sonra, grafikler dönemini M2 olarak değiştirdim. Örnek 1'den farklı olarak, Expert Advisor eşzamanlı olarak çalışır (2 olarak işaretlenir). OnInit() işlevinde m_symbol ve m_period korumalı sınıf değişkenlerinin başlatılması yardımcı oldu! Sembolü değiştirirken (3 olarak işaretlenir) Expert Advisor'lar da aynı şekilde çalışır. 

Örnek 3. CisNewBar sınıfımızda, hataları izleme olasılığını dahil ettik. Expert Advisor, hataları izlemeye gerek kalmayacak şekilde tasarlanmış olabilir. O zaman, bu olasılığı değerlendirmeyin. Hatanın mümkün olduğu bir durumu yapay olarak oluşturmaya ve onu yakalamaya çalışacağız. Bunun için, Expert Advisor'ın kaynak kodunu (Example3NewBar.mq5 dosyası) biraz destekleyeceğiz.

Ne yapacağım? Her zamanki gibi, Example3NewBar'ı dakika grafiklerinde çalıştıracağım. Ardından, terminalin Expert Advisor'ın talebinden önce zaman serisi oluşturmak için zamanının olmayacağı bir durumun ortaya çıkmasını umarak grafiğin enstrümanlarını değiştirmeye başlayacağım. Genel olarak, istemci terminalini meşgul edecek ve ne olacağını göreceğim...  

Birkaç denemeden sonra Expert Advisor'ımız bir hata yakaladı:

 

Artık çalışma zamanı hatalarını yakalayabildiğimizi güvenle söyleyebiliriz. Onlarla nasıl başa çıkılacağı zevk meselesi. Bu hatayı dört kez izlediğimizi unutmayın. İndirme tamamlandığında ve grafik oluşturulduğunda, Expert Advisor yalnızca 1 çubuğu kaçırdığımızı gösterdi.

Bu arada, Expert Advisor'ın kaynak koduna bakanlar, yalnızca isNewBar() yöntemi sıfırdan küçük veya sıfıra eşit bir değer döndürdüğünde hataları kontrol etmenin mantıklı olduğunu fark etmiş olabilir.

Uyarı: Bu deneme sırasında, grafik dönemini değiştirmeye başlayacaksanız, grafik dönemini küçükten büyüğe değiştirdiğinizde bir senkronizasyon hatası alırsınız. Bunun nedeni, 59 durumunda H1'in çubuk açılış zamanının (örneğin) M1'den daha erken olmasıdır. Grafik dönemini değiştirirken bu hatayı önlemek için OnInit() işlevindeki m_lastbar_time değişkenini SetLastBarTime (datetime lastbar_time) yöntemiyle düzgün şekilde başlatmanız gerekir.

Örnek 4. Bu örnekte Expert Advisor'ın görevini karmaşıklaştıralım. Üç döviz çifti alın: M1'de EURUSD ve M1'de GBPUSD ve M2'de USDJPY. İlk çifti içeren grafik güncel olacak ve üzerinde yalnızca yeni bir çubuk izleyeceğiz. İkinci çift ile Expert Advisor başladıktan sonra oluşan çubuk sayısını hesaplayacağız. İlk çift, yeni bir çubuk olduğunu bildirene kadar sayacağız. Ve üçüncü çiftte sürekli olarak (EURUSD'de bir çubuk göründüğünde) m_lastbar_time korumalı sınıf değişkeninin başlatılmasını gerçekleştireceğiz. Expert Advisor'ın kaynak kodu Example4NewBar.mq5 dosyası olarak eklenmiştir.

Bu örneği oluşturarak, CisNewBar sınıfımızın çoklu para birimi modunda nasıl çalışacağını öğrenmek istiyorum. O halde, başlıyorum... Elimde şunlar var:


Sonuçlar, soruları gündeme getiriyor. Yangına körükle gideceğim ve bu zaman aralığını Strateji Test Cihazı'nda çalıştıracağım. Strateji Test Cihazı sonuçları:


Bu durumda, "on fark bul" oyununu oynayabilirsiniz. Expert Advisor'ın demo hesap üzerinde çalışmasına ilişkin tuhaflıklara ek olarak, demo hesap ile Strateji Test Cihazı arasında farklılıklar olduğu açıktır ve bunlar net bir şekilde görülebilir. Doğru yaklaşımla benzer bir karşılaştırma, Expert Advisor'ın dezavantajlarını ortaya çıkarmakla kalmayacak, aynı zamanda ortadan kaldırılmasına da olanak tanıyacaktır. Belki de, bunun neden olduğunu, nasıl olduğunu ve nelerin düzeltilmesi gerektiğini Expert Advisor'da analiz etmeyeceğim.  

Örnek 5. Örneklerde, yeni bir çubuğu tespit etmenin en evrensel yöntemini hiçbir zaman açıkça kullanmadık - isNewBar(datetime newbar_time). Bunu yapmak için, MQL5'te Tick Göstergeleri Oluşturma adlı makaleden tick mum grafiğini alacağım ve çubuğun açılış zamanını saklamak için bir arabellek ekleyeceğim ( TickColorCandles v2.00.mq5 dosyası). Yeni tick mum grafiğinin zamanını anlatacak çok kısa bir Expert Advisor yazacağım (Example5NewBar.mq5 dosyası):

#property copyright "Copyright 2010, Lizar"
#property link      "Lizar-2010@mail.ru"
#property version   "1.00"

#include <Lib CisNewBar.mqh>

CisNewBar newbar_ind; // instance of the CisNewBar class: detect new tick candlestick
int HandleIndicator;  // indicator handle
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Get indicator handle:
   HandleIndicator=iCustom(_Symbol,_Period,"TickColorCandles v2.00",16,0,""); 
   if(HandleIndicator==INVALID_HANDLE)
     {
      Alert(" Error when creating indicator handle, error code: ",GetLastError());
      Print(" Incorrect initialization of Expert Advisor. Trade is not allowed.");
      return(1);
     }

//--- Attach indicator to chart:  
   if(!ChartIndicatorAdd(ChartID(),1,HandleIndicator))
     {
      Alert(" Error when attaching indicator to chart, error code: ",GetLastError());
      return(1);
     }
//--- If you passed until here, initialization was successful
   Print(" Successful initialization of Expert Advisor. Trade is allowed.");
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   double iTime[1];

//--- Get time of opening last unfinished tick candlestick:
   if(CopyBuffer(HandleIndicator,5,0,1,iTime)<=0)
     {
      Print(" Failed to get time value of indicator. "+
            "\nNext attempt to get indicator values will be made on the next tick.",GetLastError());
      return;
     }
//--- Detect the next tick candlestick:
   if(newbar_ind.isNewBar((datetime)iTime[0]))
     {
      PrintFormat("New bar. Opening time: %s  Time of last tick: %s",
                  TimeToString((datetime)iTime[0],TIME_SECONDS),
                  TimeToString(TimeCurrent(),TIME_SECONDS));
     }
  }

Elbette, tick mum grafiğinin açılış zamanını nasıl aldığımızı fark etmişsinizdir. Çok kolay, değil mi? Göstergeyi ve Expert Advisor'ı klasörlerine koyuyorum, Expert Advisor'ı derleyip çalıştırıyorum. Bu, işe yaradı; sonuçlar şu şekilde:  

 

"Yeni Çubuk" Olay İşleyicisi


Bu makalenin sonuna yaklaşırken, başka bir fikir paylaşmak istiyorum. Forum'da (Rusça) standart bir "yeni çubuk" olay işleyicisine sahip olmanın güzel olacağı fikri vardı. Belki de, geliştiriciler bu noktaya gelecek ama bu, olmayabilir de. Ancak MQL5'in iyi yanı, en çarpıcı fikirleri hassas ve basit bir şekilde uygulaması olasılığıdır.

Bir "yeni çubuk" olay işleyicisine (veya NewBar) sahip olmak istiyorsanız, o halde gelin onu oluşturalım! Çünkü özellikle, sınıfımızı kullanarak bu olayı artık kolaylıkla yakalayabiliriz. Expert'imiz (NewBar olay işleyicisi OnNewBar() ile) şu şekilde görünecek:

#property copyright "Copyright 2010, Lizar"
#property link      "Lizar-2010@mail.ru"
#property version   "1.00"

#include "OnNewBar.mqh" // Here is the secret of launching the "new bar" event handler

//+------------------------------------------------------------------+
//| New bar event handler function                                   |
//+------------------------------------------------------------------+
void OnNewBar()
  {
   PrintFormat("New bar: %s",TimeToString(TimeCurrent(),TIME_SECONDS));
  }

Oldukça iyi görünüyor. Expert Advisor'ımız çok basit görünüyor. Bu işleyici, yeni çubuk ile ilgili dize yazdırır. Hepsi bundan ibaret. NewBar olayının nasıl izleneceğini ve işleyicinin nasıl çalıştırılacağını anlamak için OnNewBar.mqh dosyasına bakmanız gerekir:

#property copyright "Copyright 2010, Lizar"
#property link      "Lizar@mail.ru"

#include <Lib CisNewBar.mqh>
CisNewBar current_chart; // instance of the CisNewBar class: current chart

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   int period_seconds=PeriodSeconds(_Period);                     // Number of seconds in current chart period
   datetime new_time=TimeCurrent()/period_seconds*period_seconds; // Time of bar opening on current chart
   if(current_chart.isNewBar(new_time)) OnNewBar();               // When new bar appears - launch the NewBar event handler
  }

Gördüğünüz gibi, burada da karmaşık bir şey yok. Ancak dikkatinizi çekmek istediğim birkaç nokta var:

Bunlardan ilki: Fark ettiğiniz üzere, çubuğun açılış zamanını hesaplamak için TimeCurrent() işlevini ve sınıfımızdan NewBar olayı için ilk kontrol yöntemini kullanıyorum. Bu güzel bir artı. Bu, SeriesInfoInteger() işlevini SERIES_LASTBAR_DATE değiştiricisiyle kullanırken olduğu gibi, bu tür bir yöntemin herhangi bir hata işleme gerektirmediği gerçeğinde yatmaktadır. Bu, bizim için önemlidir; zira OnNewBar() işleyicimiz mümkün olduğunca güvenilir olmalıdır.

İkincisi: Çubuğun açılış zamanını hesaplamak için TimeCurrent() işlevinin kullanılması en hızlı yoldur. SeriesInfoInteger() işlevinin, hata denetimi olmadan dahi aynı amaç için kullanılması daha yavaş bir yoldur.

İşleyicimizin sonucu:

   

Sonuç

  Materyal sunumu sırasında yeni bir çubuğu tespit etmenin yollarının iyi bir analizini yaptık. Mevcut yeni çubuğu tespit etme yöntemlerinin avantajlarını ve dezavantajlarını ortaya çıkardık. Sahip olduklarımıza dayanarak, programlamada ek maliyet olmadan neredeyse her görevde "yeni çubuk" olayı yakalamamıza olanak tanıyan CisNewBar sınıfını oluşturduk. Aynı zamanda, önceki çözümlerden kaynaklanan çoğu aksaklıktan kurtulduk.    

Bu örnekler, bizim tarafımızdan icat edilen yöntemlerin avantajlarını ve dezavantajlarını anlamamıza yardımcı oldu. Doğru çalışması açısından, çok para birimli mod özel dikkat gerektirir. Belirlenen verimsizliklerin kapsamlı bir analizini yapmalı ve bunları çözmenin yollarını geliştirmelisiniz.

  Oluşturulan "yeni çubuk" olay işleyicisi yalnızca tek para birimli Expert Advisor'lar için uygundur. Ancak bu amaç için en güvenilir ve en hızlı yolu kullanmayı öğrendik. Şimdi devam edebilir ve çok para birimli bir NewBar olay işleyicisi yapabilirsiniz. Ama bu başka bir makalenin konusu.  

MetaQuotes Ltd tarafından Rusçadan çevrilmiştir.
Orijinal makale: https://www.mql5.com/ru/articles/159

Ekli dosyalar |
example1newbar.mq5 (0.94 KB)
example2newbar.mq5 (2.61 KB)
example3newbar.mq5 (2.98 KB)
example4newbar.mq5 (6.41 KB)
example5newbar.mq5 (2.39 KB)
onnewbar.mq5 (0.8 KB)
onnewbar.mqh (1.12 KB)
Büyüyen Nöral Gaz: MQL5'te Uygulama Büyüyen Nöral Gaz: MQL5'te Uygulama
Makalede, Büyüyen nöral gaz (GNG) olarak adlandırılan uyarlanabilir kümeleme algoritmasını uygulayan bir MQL5 programının nasıl geliştirileceğine ilişkin bir örnek gösterilmektedir. Bu makale, dil belgelerini incelemiş ve nöroinformatik alanına ilişkin belirli programlama becerilerine ve temel bilgilere sahip kullanıcılara yöneliktir.
Simulink: Expert Advisor'ların Geliştiricileri için Bir Kılavuz Simulink: Expert Advisor'ların Geliştiricileri için Bir Kılavuz
Profesyonel bir programcı değilim. Ve bu nedenle, alım satım sistemi geliştirme üzerinde çalışırken "basitten karmaşığa gitmek" prensibi benim için birincil öneme sahiptir. Benim için basit olan tam olarak nedir? Her şeyden önce, sistemi oluşturma sürecinin ve çalışmasının mantığının görselleştirilmesidir. Ayrıca, minimum elle yazılmış koddur. Bu makalemde, bir Matlab paketine dayalı alım satım sistemini oluşturup test etmeye çalışacak ve ardından MetaTrader 5 için bir Expert Advisor yazacağım. MetaTrader 5'in geçmiş verileri, test süreci için kullanılacaktır.
MQL5 Sihirbazı: Programlamadan Expert Advisor'lar Oluşturma MQL5 Sihirbazı: Programlamadan Expert Advisor'lar Oluşturma
Programlama için zaman kaybetmeden bir alım satım stratejisi denemek ister misiniz? MQL5 Sihirbazı'nda alım satım sinyallerinin türünü seçebilir, takip eden pozisyonlar ve para yönetimi modülleri ekleyebilirsiniz; böylelikle işiniz biter! Kendi modül uygulamalarınızı oluşturun veya İşler hizmeti aracılığıyla talimat verin ve yeni modüllerinizi mevcut olanlarla birleştirin.
Hataları Bulma ve Günlüğe Kaydetme Hataları Bulma ve Günlüğe Kaydetme
MetaEditor 5 hata ayıklama özelliğine sahiptir. Ancak MQL5 programlarınızı yazarken, genellikle ayrı değerleri değil, test ve çevrimiçi çalışma sırasında görünen tüm mesajları görüntülemek istersiniz. Günlük dosyası içeriğinin boyutu büyük olduğunda, gerekli mesajın hızlı ve kolay alınmasını otomatikleştirmek aşikardır. Bu makalede MQL5 programlarında hata bulma yollarını ve günlüğe kaydetme yöntemlerini ele alacağız. Ayrıca, dosyalara günlük tutmayı basitleştireceğiz ve günlüklerin rahat bir şekilde görüntülenmesi için basit LogMon programı hakkında bilgi edineceğiz.