English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Italiano
Zigzag ve ATR örneklerini Kullanarak Göstergelerin Sınıflara Uyarlanması

Zigzag ve ATR örneklerini Kullanarak Göstergelerin Sınıflara Uyarlanması

MetaTrader 5Göstergeler | 16 Aralık 2021, 15:01
66 0
Aleksandr Chugunov
Aleksandr Chugunov

Bu bize neden gerekli?

MetaQuotes Software Corp. MetaTrader 5 işlem platformunun yeni, 5. Sürümünde özel göstergeler ile çalışma konseptini gözden geçirdi. Artık çok daha hızlı çalışıyorlar; eşsiz girdi parametrelerine sahip her göstergenin tek örneği var, yani kopyaları bir sembolün on grafiğinde bile kullanılsa sadece bir kere hesaplanır.

Ancak bir algoritmanın işletilmesinde değişiklik yapılmadı. Sunucularından birini ya da önemli boyuttaki geçmiş senkronizasyonunu kaybetmesi durumunda prev_calculated değeri (ya da MetaTrader 4 için IndicatorCounted()) sıfırlanır ve bu da bütün geçmişin göstergelerinin tekrar hesaplanmasına yol açar (geliştiriciler bunu herhangi bir durumda göstergelerin değerlerinin doğruluğunu garantilemek için kasıtlı olarak yapmışlardır). Göstergelerin hesaplanma hızlarını etkileyebilecek birkaç şey vardır:

  • Büyük dönem: fiyatlar_toplamı;
  • Karmaşık, kaynak tüketen hesaplamalar;
  • Birden fazla sembol ve nokta kullanmak;
  • Zayıf kişisel bilgisayar;

Durumunuzda geçerli olan maddelerin sayısı arttıkça bütün işlem geçmişinin göstergelerinin tekrar hesaplanması sorunu da sizin için daha gerçek hale gelecektir. Ek olarak, durum bilgiyi aktarmak için kötü bir kanal kullanılması ile de kötüleşir.

Tabii ki, gösterge hesaplamalarının derinliğini girdi parametrelerini kullanarak sınırlayabilirsiniz ancak iCustom göstergelerini kullanırken burada ufak bir fark var. Herhangi bir grafik ya da özel göstergeler tarafından kullanılabilecek görev çubuğu sayısı bütün platform için küresel kapsamda ayarlanmıştır. Hafızada her özel göstergenin arabelleği için yer ayrılmıştır ve sadece TERMINAL_MAXBARSile sınırlıdır.

Ancak büyük bir ekleme yapıldı- eğer hesaplanan görev çubuğu sayısının sınırını göstergenin algoritmasında değiştirirseniz (örneğin bir girdi kullanarak ya da direkt olarak kodun içinde), o zaman hafıza her yeni görev çubuğu geldiğinde duruma göre tahsis edilecektir (adım adım belirlenen TERMINAL_MAXBARS limitine kadar yükselir ( ya da bu limitin üstüne çıkar - bu algoritma tamamen geliştiricilere bağlıdır, sonraki sürümlerde değiştirebilirler)).


Bütün İşlem Geçmişinin Göstergelerinin Tekrar Hesaplanmasını Önlemenin Yolları

Şu anda bu sorunu çözmek için aşağıdaki yolları görebiliyorum;
  1. MetaQuotes’tan bu sorunu platform seviyesinde çözmesini istemek
  2. Örneksel bir prev_calculated’in uygulanması için ayrı bir sınıf oluşturmak

Göstergede prev_calculated hesaplaması için bir algoritma oluşturulabileceğine dair bir varsayıma dayanan bir değişken de vardı ancak görünüşe göre MetaTrader 5’in, MetaTrader 4’ten farklı olarak, prev_calculated’i sıfırlarken tüm gösterge arabelleklerini temizliyor ( yani tüm gösterge dizilerinin silinmesini zorla gerçekleştiriyor, bu da platform tabanlı bir özellik olduğundan kontrolünüzün dışında).

Her değişkeni ayrı olarak inceleyelim.

  • İlk değişken sadece geliştiricilere bağlı. Belki bu makalenin yayınlanmasından sonra yapmayı düşünürler. Ve belki de tam donanımlı bir mekanizmanın uygulanması özel göstergelerin bloklar halimde hesaplanmasını büyük ölçüde etkiler (ancak bu mekanizma bir seçenek olarak da eklenebilir) ve bu nedenle her şeyi olduğu gibi bırakırlar.
  • İkinci değişken. Örneksel bir prev_calculated’in uygulanmasından sorumlu özel bir sınıfın yaratılması. Hem özel bir göstergede (sadece prev_calculated değerleri almak için) hem de Expert Advisor’da kullanılacak bir veri sağlayıcısında ( ya da komut dizisinde) gerekli özel göstergenin hesaplanması için özel olarak geliştirilmiş bir sınıfla beraber kullanabiliriz.


Sorunu Çözmede İkinci Değişkenin Avantajları ve Dezavantajları

Avantajlar:
  • dizin unsurlarına halka erişiminin düzenlenmesi ile hareketli dizinde tek bir hafıza tahsisi yapılarak gerekli hafıza hacminin sabitlenmesi;
  • talep üzerine hesaplanması için ayrı bir sınıf kullanırken göstergenin senkronizasyonu ve hesaplanması (semaforlar, bayraklar, olay işlemleri vb. kullanmadan);
  • göstergenin hesaplanması için ayrı bir komut kullanıldığında, yeniden hesaplamanın sonucu uzatılmış bir biçimde döner (örneğin: değişiklik yapılmadı, sadece son ışın değiştirildi, yeni ışın eklendi, vb.).
Dezavantajlar:
  • gösterge değerlerinin hesaplanmasında kullanılan fiyat geçmişinin bir kopyasını bulundurma gerekliliği;
  • verileri karşılaştıran mantık işlemlerini kullanarak geçmişin platformdaki geçmişle olan senkronizasyonunu manuel olarak yapma gerekliliği.


Örneksel bir prev_calculated’in uygulanması için CCustPrevCalculated sınıfını oluşturmak

Sınıfın uygulanmasının kendisi bizi ilgilendiren, anlatılacak pek bir şey içermiyor. Algoritma tarihi hem geçmişi iki tarafa genişletmeyi hem de sol taraftan olabilecek olası “kesilmeyi” dikkate alır. Ayrıca bu algoritma geçmişin hesaplanmış verilerin içine yerleştirilmesini de işleyebilir (aslında bu MetaTrader 4’te mevcut, henüz MetaTrader 5’te bununla karşılaşmadım) Bu sınıfın kaynak kodu CustPrevCalculated.mqh dosyasındadır.

Size önemli kısımlardan bahsedeyim.


Dizin Unsurlarına Halka Erişimi Oluşturmak

Bu sınıfı oluşturmak için alışılagelmedik bir yöntem kullanacağız - hafızanın dizine tek seferlik tahsis edilmesi için ve dizinleri kopyalamadaki gereksiz işlemlerden kaçınmak için dizin unsurlarına halka erişimi. 5 unsur üzerinden düşünelim:


Dizin unsurlarına halka erişimi


 
İlk olarak numaralandırmanın 0 ile başladığı dizinle çalışıyoruz. Peki bir sonraki değeri dizin boyutunu koruyarak eklememiz gerekiyorsa (örneğin yeni bir görev çubuğu eklememiz) ne yapmalıyız? İki yolu var:
  • 2-5 numaralı hafıza hücrelerini sırasıyla 1-4 numaralı hücrelere kopyalamak ve bu şekilde 5 numaralı hafıza hücresini boşaltmak;
  • içindeki bilgileri değiştirmeden dizin sıralamasını değiştirmek (sarım adreslemesi).

İkinci değişkeni uygulamak için bir değişkene ihtiyacımız var, buna DataStartInd diyelim; bu dizinin sıfır diziliminin pozisyonunu depolayacak. Sonraki hesaplamaları kolaylaştırmak için numaralandırılması dizinin normal sıralamasıyla uyumlu olacak (yani sıfırdan başlayacak). BarsLimit değişkeninde bu dizindeki unsurların sayısını depolayacağız. Böylelikle sanal ‘I’ sıralamasının dizin unsurlarının gerçek adresi aşağıdaki basit formülü kullanarak hesaplanacak:

  • Normal numaralandırma için - (DataStartInd+I) % BarsLimit
  • Zaman serilerinde olduğu şekilde adresleme için (DataStartInd+DataBarsCount-1-I) % BarsLimit
DataBarsCount değişkeni gerçekten kullanılan hafıza hücrelerinin sayısını depolar (örneğin, 5 hücrenin sadece 3’ünü kullanabiliriz)


Geçmiş Senkronizasyonu Algoritmaları

Ben kendim için bir geçmiş kopyasının(yerel geçmiş) istemci platformundaki geçmiş ile senkronizasyonunda kullanılan algoritmalarla çalışmanın üç modunu seçtim:
  • CPCHSM_NotSynch - yerel geçmişinizin senkronizasyonu halihazırda oluşturulmuş görev çubuklarına uygulanmaz ( kendi risk ve sorumluluğunuzda) Aslında bu mod fiyat değerlerindeki önemsiz sapmaların hesaplamaların kesinliğini fazla etkileyemediği durumlarda, bir gösterge için de rahatça kullanılabilir (MA, ADX, vb.) Bu mod, örneğin bir yükselişin üzerine gelen bir diğer yükseliş fazlalığının önemli olduğu durumlarda, ZigZag için ölümcül olabilir.
  • CPCHSM_Normal - yerel geçmiş aşağıda verilen algoritma tarafından her yeni görev çubuğunda senkronize edilir.
  • CPCHSM_Paranoid - yerel geçmiş aşağıda verilen her veri senkronizasyonu işlevi kullanıldığında senkronize edilir.

Senkronizasyon mekanizmasının kendisi de bir programcı tarafından ayarlanan başla bir parametreye bağlıdır - HSMinute (HistorySynchSecond’da depolanır). Bir Borsa Merkezinin geçmişin sadece son HSMinute dakikalarını düzeltebileceğini düşünüyoruz. Eğer o dönemin senkronizasyonunda bir fark bulunmazsa geçmiş özdeş sayılır ve karşılaşma durdurulur. Eğer bir fark bulunursa o zaman bütün geçmiş kontrol edilir ve düzeltilir.

Buna ek olarak algoritma, fiyatlar/ alış-satış farkları/ hacimleri sadece başlatma sırasında belirlenen MqlRates yapısından kontrol etmeye izin verir. Örneğin bir ZigZag çizmek için sadece Yüksek ve Düşük fiyatlara ihtiyacımız var.


CCustPrevCalculated Sınıfının İşlevsel Kullanımı

CCustPrevCalculated sınıfını başlatmak için, başarılı olması durumunda ‘doğru’ dönüşünü verecek olan, InitData() işlevini kullanmamız gerekir.
CCustPrevCalculated CustPrevCalculated;
CustPrevCalculated.InitData(_Symbol, _Period, 150, CPCHSM_Normal, CPCH_high|CPCH_low, 15);
Geçmişi senkronize etmek için PrepareData() işlevini kullanmamız gerekir:
CPCPrepareDataResultCode resData;
resData = CustPrevCalculated.PrepareData();

PrepareData() işlevi tarafından dönüt olarak verilebilecek değerlerin değişkenleri:

enum CPCPrepareDataResultCode
  {
   CPCPDRC_NoData,                     // Returned when there is no data for calculation (not prepared by the server)
   CPCPDRC_FullInitialization,         // Full initialization of the array has been performed
   CPCPDRC_Synch,                      // Synchronization with adding new bars has been performed
   CPCPDRC_SynchOnlyLastBar,           // Synchronization of only the last bar has been performed (possible cutting of the history)
   CPCPDRC_NoRecountNotRequired        // Recalculation has not been performed, since the data was not changed
  };


CCustPrevCalculated Sınıfının Veri Erişimi İşlevleri

Not: hesaplamaları hızlandırmak için, dizin taşmaları için yapılan kontroller hariç tutulmuştur. Daha net olmak gerekirse, sıralama yanlış olursa yanlış değerler dönüt olarak verilecektir.

Adı
Amacı
 uint GetDataBarsCount()
 Mevcut görev çubuklarının sayısını dönüt verir
 uint GetDataBarsCalculated()
 Değişiklik yapılmamış görev çubuklarının sayısını dönüt verir
 uint GetDataStartInd()
 Sarım erişiminin sıralamasını dönüt verir (özel göstergeler için)
 bool GetDataBarsCuttingLeft()
 Soldan görev çubukları kesilmesinin sonuçlarını dönüt verir
 double GetDataOpen(int shift, bool AsSeries)
 Değişim çubuğu için “Açık” dönütü verir
 double GetDataHigh(int shift, bool AsSeries)
 Değişim çubuğu için “Yüksek” dönütü verir
 double GetDataLow(int shift, bool AsSeries)
 Değişim çubuğu için “Düşük” dönütü verir
 double GetDataClose(int shift, bool AsSeries)
 Değişim çubuğu için “Kapalı” dönütü verir
 datetime GetDataTime(int shift, bool AsSeries)
 Değişim çubuğu için “Zaman” dönütü verir
 long GetDataTick_volume(int shift, bool AsSeries)
 Değişim çubuğu için “Tick_volume” (Tik hacmi) dönütü verir
 long GetDataReal_volume(int shift, bool AsSeries)
 Değişim çubuğu için “Real_Volume” (Gerçek Hacim) dönütü verir
 int GetDataSpread(int shift, bool AsSeries)
 Değişim çubuğu için “Alım Satım Marjı” dönütü verir


CCustPrevCalculated Sınıfının Daha Çok Optimize Edilmesinin Örnekleri

  • Birden fazla dizine (belli bir amaçla belirlenen) geçerek MqlRates’ten kaçınmak (hafıza gereksinimlerini azaltır ancak dizin kopyalama taleplerinin sayısı bakımından yükleri artırır).
  • Erişimin her işlevini belirli bir dizin sıralaması türü ile kesin kullanım için iki ayrı bağımsız işleve bölmek ( <<bool AsSeries>> parametresinden kaçınarak). Avantaj sadece «if (AsSeries)» mantık koşulunda vardır.


Özel Gösterge ZigZag’ının Hesaplanması için CCustPrevCalculated Sınıfının Verilerine dayanarak CCustZigZagPPC’yi Oluşturmak

Bu algoritma Profesyonel ZigZag isimli özel göstergeye dayanır. Bu sınıfın kaynak kodu ZigZags.mqh dosyasında bulunur; ek olarak OutsideBar.mqh kütüphanesi de dış görev çubuklarıyla çalışmak için kullanılır.

Göstergemizdeki bir görev çubuğunun tanımı için ayrı bir yapı oluşturalım:

struct ZZBar
  {
   double UP, DN;                      // Buffers of the ZigZag indicator
   OrderFormationBarHighLow OB;       // Buffer for caching of an external bar
  };

Ayrıca, bu sınıfın hesaplamalarının dönüt sonuçlarını da belirleyelim:

enum CPCZZResultCode
  {
   CPCZZRC_NotInitialized,             // Class is no initialized
   CPCZZRC_NoData,                     // Faield to receive data (including the external bar)
   CPCZZRC_NotChanged,                 // No changes of ZZ rays
   CPCZZRC_Changed                     // ZZ rays changed
  };

CCustZigZagPPC sınıfını başlatmak için Init() işlevini bir kez daha kullanmamız gerekecek; başarılı olması durumunda ‘doğru’ dönüşünü verecek:

CCustZigZagPPC ZZ1;
ZZ1.Init(CustPrevCalculated, _Symbol, _Period, 150, CPCHSM_Normal, CPCH_high|CPCH_low, 15, 0, true, 12, 10);

Göstergenin hesaplamaları için CCustPrevCalculated sınıfının önceden hesaplanmış verilerine dayanarak verileri güncellemeye başlamamız gerek:

CPCPrepareDataResultCode resZZ1;
resZZ1 = ZZ1.PrepareData(resData);

Sonra da Calculate() (Hesapla) işlevini kullanmamız gerek:

if ( (resZZ1 != CPCPDRC_NoData) && (resZZ1 != CPCPDRC_NoRecountNotRequired) )
   ZZ1.Calculate();

Bir CCustPrevCalculated sınıfını birden fazla CCustZigZagPPC sınıfıyla beraber kullanma örneğinin tamamını ScriptSample_CustZigZagPPC.mq5 dosyasında bulabilirsiniz.


CCustZigZagPPC Sınıfının Veri Erişimi İşlevi

Adı
Amacı
 uint GetBarsCount()
 Mevcut görev çubuklarının sayısını dönüt verir
 uint GetBarsCalculated()  Hesaplanan görev çubuklarının sayısını dönüt verir
 double GetUP(uint shift, bool AsSeries)
 Bir görev çubuğunun ZigZag’ının pik değerini dönüt verir
 double GetDN(uint shift, bool AsSeries)
 Bir görev çubuğunun ZigZag’ının en düşük değerini dönüt verir
 OrderFormationBarHighLow GetOB(uint shift, bool AsSeries)  Bir görev çubuğunun ‘Dış’ değerini dönüt verir


Görsel ve Program Kontrolü

Görsel kontrol için öncelikle asıl göstergeyi bir grafiğe ekleyelim ve üzerine de ,eşdeğer girdi parametreleri bulunduran, yazdığımız test göstergesi Indicator_CustZigZag.mq5’i ekleyelim; bu çalışmanın sonucu aşağıdaki gibidir:

Kırmızı- orijinal gösterge, mavi - kendi göstergemiz, son 100 görev çubuklarının hesabı.

Expert Advisor’da karşılaştırabileceğimiz şekilde; bir fark olacak mı ? iCustom("AlexSTAL_ZigZagProf)’dan ve CCustZigZagPPC’den elde edilen sonuçlar her tik için Expert_CustZigZagPPC_test.mq5 test Expert Advisor’da karşılaştırılır. Hesaplamalarla ilgili bilgiler işlem kayıt defterinde görüntülenir (algoritmada geçmiş bulunmadığından dolayı ilk birkaç görev çubuğu için hesaplama yapılmayabilir):

(EURUSD,M1)                1.35797; 1.35644; 1.35844; 1.35761; 1.35901; 1.35760; 1.35959; 1.35791; 1.36038; 1.35806; 1.36042; 1.35976; 1.36116; 1.35971; // it is normal
(EURUSD,M1) Tick processed: 1.35797; 1.35644; 1.35844; 1.35761; 1.35901; 1.35760; 1.35959; 1.35791; 1.36038; 1.35806; 1.36042; 1.35976; 1.36116; 
(EURUSD,M1) Divergence on the bar: 7 

Bu Expert Advisor’ı detaylı olarak inceleyelim. Çalışmasının küresel değişkenlerini belirleyin:

#include <ZigZags.mqh>

CCustPrevCalculated CustPrevCalculated;
CCustZigZagPPC ZZ1;
int HandleZZ;

Değişkenleri başlatın:

int OnInit()
  {
   // Creating new class and initializing it
   CustPrevCalculated.InitData(_Symbol, _Period, 150, CPCHSM_Normal, CPCH_high|CPCH_low, 15);
   
   // Initializing the class ZZ
   ZZ1.Init(GetPointer(CustPrevCalculated), _Symbol, _Period, 150, CPCHSM_Normal, CPCH_high|CPCH_low, 15, 0, true, 12, 10);
   
   // Receiving handle for the custom indicator
   HandleZZ = iCustom(_Symbol, _Period, "AlexSTAL_ZigZagProf", 12, 10, 0 , true);
   Print("ZZ_handle = ", HandleZZ, "  error = ", GetLastError());

   return(0);
  }
Expert Advisor’daki Tikleri İşlemek:
void OnTick()
  {
   // Calculation of data
   CPCPrepareDataResultCode resData, resZZ1;
   resData = CustPrevCalculated.PrepareData();
   
   // Start recalculation for each indicator! PrepareData obligatory!
   resZZ1 = ZZ1.PrepareData(resData);
   
   // Расчет данных ZZ1
   if ( !((resZZ1 != CPCPDRC_NoData) && (resZZ1 != CPCPDRC_NoRecountNotRequired)) )
      return;

   // Получим результаты расчета
   ZZ1.Calculate();

Elimizde CCustZigZagPPC tarafından hesaplanan zZ1.GetBarsCalculated var. Buna iCustom("AlexSTAL_ZigZagProf)’ın ve CCustZigZagPPC sınıfının verilerini karşılaştırma kodunu ekleyelim:

   int tmpBars = (int)ZZ1.GetBarsCalculated();
   double zzUP[], zzDN[];
   CopyBuffer(HandleZZ, 0, 0, tmpBars, zzUP);
   CopyBuffer(HandleZZ, 1, 0, tmpBars, zzDN);
   
   // Perform comparison
   string tmpSt1 = "", tmpSt2 = "";
   for (int i = (tmpBars-1); i >= 0; i--)
     {
      double tmpUP = ZZ1.GetUP(i, false);
      double tmpDN = ZZ1.GetDN(i, false);
      if (tmpUP != zzUP[i])
         Print("Divergence on the bar: ", i);
      if (tmpDN != zzDN[i])
         Print("Divergence on the bar: ", i);
      if (tmpUP != EMPTY_VALUE)
         tmpSt1 = tmpSt1 + DoubleToString(tmpUP, _Digits) + "; ";
      if (tmpDN != EMPTY_VALUE)
         tmpSt1 = tmpSt1 + DoubleToString(tmpDN, _Digits) + "; ";

      if (zzUP[i] != EMPTY_VALUE)
         tmpSt2 = tmpSt2 + DoubleToString(zzUP[i], _Digits) + "; ";
      if (zzDN[i] != EMPTY_VALUE)
         tmpSt2 = tmpSt2 + DoubleToString(zzDN[i], _Digits) + "; ";
     }
  Print("Tick processed: ", tmpSt1);
  Print("                              ", tmpSt2);
  }

CCustZigZagPPC sınıfının bir Expert Advisor ya da bir komut dizisinde basit ve işlevsel kullanımının örneği bu şekildedir. Doğrudan erişimin işlevleri CopyBuffer() yerine GetUP(), GetDN(), GetOB().


Göstergemizi Farklı bir Sınıfa Taşımak (iATR örneğiyle)

ZigZags.mqh dosyasına dayanarak özel göstergelerin hızlı geliştirilmesi için yukarıdaki ilkelere uygun olarak MyIndicator.mqh taslağını oluşturdum.

Genel plan:

1. Hazırlık Aşaması.

  • MyIndicator.mqh’yi başka bir isimli bir dosya olarak kopyalayın (benim örneğimde ATRsample.mqh) ve son versiyonunu MetaEditor 5’te açın.
  • “MyInd” metnini kendi göstergenizin ismiyle değiştirin (benim örneğimde “ATR”).

2. İlk (orijinal) göstergeden sınıfa alınacak dış parametreleri seçin, bunları beyan edin ve başlatın.

Benim örneğimde ATR göstergesinin bir tane dış parametresi var:
input int InpAtrPeriod=14;  // ATR period
  • bu parametreyi sınıfımıza ve sınıfın başlatılması işlevine ekleyelim:
class CCustATR
  {
protected:
   ...
   uchar iAtrPeriod;
   ...
public:
   ...
   bool Init(CCustPrevCalculated *CPC, string Instr, ENUM_TIMEFRAMES TF, int Limit, CPCHistorySynchMode HSM, uchar HS, uint HSMinute, uchar AtrPeriod);
  • Init işlevinin gövde başlığını değiştirin ve değişken parametresini girdi değeriyle başlatın:
bool CCustATR::Init(CCustPrevCalculated *CPC, string Instr, ENUM_TIMEFRAMES TF, int Limit, CPCHistorySynchMode HSM, uchar HS, uint HSMinute, uchar AtrPeriod)
{
      ...
      BarsLimit = Limit;
      iAtrPeriod = AtrPeriod;
      ...

3. İlk göstergedeki gerekli arabellek sayısını belirleyin ve bunları sınıfımızda beyan edin. Ayrıca INDICATOR_DATA arabelleklerinin dönüt işlevlerini de beyan edin.

  • Yapıyı kendi
struct ATRBar
  {
   double Val;                          // Indicator buffers
  };

yapımıza değiştirin:

struct ATRBar
  {
   double ATR;
   double TR;
  };
  • Sıfır değerlerini belirleyin:
CPCPrepareDataResultCode CCustATR::PrepareData(CPCPrepareDataResultCode resData)
{
   ...
   for (uint i = (DataBarsCalculated == 0)?0:(DataBarsCalculated+1); i < DataBarsCount; i++)
     {
      Buf[PInd(i, false)].ATR = EMPTY_VALUE;
      Buf[PInd(i, false)].TR = EMPTY_VALUE;
     }
   ...
  • INDICATOR_DATA arabelleklerinin dönüt işlevlerini değiştirin ve ekleme yapın:

aşağıdakini

class CCustATR
  {
   ...
   double GetVal(uint shift, bool AsSeries);                      // returns the Val value of the buffer for a bar
   ...

bununla değiştirin (sadece bir adet arabellek var ise değiştirme kısmını atlayabilirsiniz)

class CCustATR
  {
   ...
   double GetATR(uint shift, bool AsSeries);                      // Возвращает значение буфера ATR для бара
   ...

ve ilgili işlevin kodunu değiştirin:

double CCustATR::GetATR(uint shift, bool AsSeries)
{
   if ( shift > (DataBarsCount-1) )
      return(EMPTY_VALUE);
   return(Buf[PInd(shift, AsSeries)].ATR);
}
Not: arabellek dönüt değerlerinin birden fazla işlevi yerine, arabelleğin sayısı ya da adını içeren ek bir parametreye sahip tek bir işlev kullanabilirsiniz.


4. İlk göstergenin OnCalculate() işlevinin mantığını sınıfın ilgili işlevine kopyalayın

  • İlk kontroller
CPCATRResultCode CCustATR::Calculate()
{
   ...
   // Check if there are enough bars for the calculation
   if (DataBarsCount <= iAtrPeriod)
      return(CPCATRRC_NoData);
   ...
  • Hesaplamalar: ilk tikte ve sonraki tiklerde hesaplamalar için görev çubuğu sayısı:
   if ( DataBarsCalculated != 0 )
      BarsForRecalculation = DataBarsCount - ATRDataBarsCalculated - 1;
   else
     {
      Buf[PInd(0, false)].TR = 0.0;
      Buf[PInd(0, false)].ATR = 0.0;
      //--- filling out the array of True Range values for each period
      for (uint i = 1; i < DataBarsCount; i++)
         Buf[PInd(i, false)].TR = MathMax(CustPrevCalculated.GetDataHigh(i, false), CustPrevCalculated.GetDataClose(i-1, false)) - 
                                  MathMin(CustPrevCalculated.GetDataLow(i, false), CustPrevCalculated.GetDataClose(i-1, false));
      //--- first AtrPeriod values of the indicator are not calculated
      double firstValue = 0.0;
      for (uint i = 1; i <= iAtrPeriod; i++)
        {
         Buf[PInd(i, false)].ATR = 0;
         firstValue += Buf[PInd(i, false)].TR;
        }
      //--- calculating the first value of the indicator
      firstValue /= iAtrPeriod;
      Buf[PInd(iAtrPeriod, false)].ATR = firstValue;
      
      BarsForRecalculation = DataBarsCount - iAtrPeriod - 2;
     }
  • Her tikte hesaplama:
   for (uint i = (DataBarsCount - BarsForRecalculation - 1); i < DataBarsCount; i++)
     {
      Buf[PInd(i, false)].TR = MathMax(CustPrevCalculated.GetDataHigh(i, false), CustPrevCalculated.GetDataClose(i-1, false)) - 
                               MathMin(CustPrevCalculated.GetDataLow(i, false), CustPrevCalculated.GetDataClose(i-1, false));
      Buf[PInd(i, false)].ATR = Buf[PInd(i-1, false)].ATR + (Buf[PInd(i, false)].TR-Buf[PInd(i-iAtrPeriod, false)].TR) / iAtrPeriod;
      ...

Bu kadar. Sınıfımız oluşturuldu. Görsel kontrol için test göstergesi oluşturabilirsiniz (benim örneğimde Indicator_ATRsample.mq5)



Makaleyi düzeltirken CCustPrevCalculated sınıfını sadece tek bir özel gösterge ile kullanırsanız bu sınıfın özel göstergedeki oluşturma, başlatma ve senkronizasyon işlemlerini birleştirebileceğiniz fikri aklıma geldi (benim örneğimde bunlar CCustZigZagPPC ve CCustATR) Bu amaç için özel göstergenin başlatma işlevini kullanırken nesnenin sıfır noktasını kullanmanız gerekir:

   ATR.Init(NULL, _Symbol, _Period, iBars, CPCHSM_Normal, 0, 30, InpAtrPeriod);

Bu şekilde genel yapı

#include <CustPrevCalculated.mqh>
#include <ATRsample.mqh>
CCustPrevCalculated CustPrevCalculated;
CCustATR ATR;

int OnInit()
  {
   CustPrevCalculated.InitData(_Symbol, _Period, iBars, CPCHSM_Normal, 0, 30);
   ATR.Init(GetPointer(CustPrevCalculated), _Symbol, _Period, iBars, CPCHSM_Normal, 0, 30, InpAtrPeriod);
  }

int OnCalculate(...)
  {
   CPCPrepareDataResultCode resData = CustPrevCalculated.PrepareData();
   CPCPrepareDataResultCode resATR = ATR.PrepareData(resData);
   if ( (resATR != CPCPDRC_NoData) && (resATR != CPCPDRC_NoRecountNotRequired) )
      ATR.Calculate();
  }

bu şekilde basite indirgenecek:

#include <ATRsample.mqh>
CCustATR ATR;

int OnInit()
  {
   ATR.Init(NULL, _Symbol, _Period, iBars, CPCHSM_Normal, 0, 30, InpAtrPeriod);
  }

int OnCalculate(...)
  {
   ATR.Calculate();
  }
Bunun işlevsel bir örneği Indicator_ATRsample2.mq5 dosyasında verilmiştir.

Bahsedilen Teknolojinin Strateji Test Edicisinin Performansına olan Etkisi

Kontrol etmek için sıfır görev çubuğu göstergesinin her tikte üç değişkenden birine bağlı olarak değerini alan bir test Expert Advisor( TestSpeed_IndPrevCalculated.mq5) oluşturdum:

enum eTestVariant
  {
   BuiltIn,    // Built-in indicator iATR
   Custom,     // Custom indicator iCustom("ATR")
   IndClass    // Calculation in the class
  };

Bu Expert Advisor 1 faktörün üzerinde aşağıdaki optimizasyon parametreleri ile 10 defa kullanıldı:

  • Sembol: EURUSD
  • Dönem: geçmişin tamamı [1993..2001]
  • Ticaret modu: her tikte
  • Dış parametre: FalseParameter [0..9]

Göstergenin üç değişkeninden her biri için optimizasyon süresini ölçtüm. Bu kontrolün sonucu doğrusal bir sütun grafiği veriyor.

 ATR göstergesinin üç çeşit uygulamasının optimizasyon zamanları

    Optimizasyon zamanını ölçmek için kullanılan Expert Advisor’ın kaynak kodu:

    //+------------------------------------------------------------------+
    //|                                  TestSpeed_IndPrevCalculated.mq5 |
    //|                                         Copyright 2011, AlexSTAL |
    //|                                           http://www.alexstal.ru |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2011, AlexSTAL"
    #property link      "http://www.alexstal.ru"
    #property version   "1.00"
    //--- connect the include file with the CustATR class
    #include <ATRsample.mqh>
    //--- set the selection of the parameter as an enumeration
    enum eTestVariant
      {
       BuiltIn,    // Built-in indicator iATR
       Custom,     // Custom indicator iCustom("ATR")
       IndClass    // Calculation withing the class
      };
    //--- input variables
    input eTestVariant TestVariant;
    input int          FalseParameter = 0;
    //--- period of the ATR indicator
    const uchar        InpAtrPeriod = 14;
    //--- handle of the built-in or custom indicator
    int                Handle;
    //--- indicator based on the class 
    CCustATR           *ATR;
    
    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
       //---
       switch(TestVariant)
         {
          case BuiltIn:
             Handle = iATR(_Symbol, _Period, InpAtrPeriod);
             break;
          case Custom:
             Handle = iCustom(_Symbol, _Period, "Examples\ATR", InpAtrPeriod);
             break;
          case IndClass:
             ATR = new CCustATR;
             ATR.Init(NULL, _Symbol, _Period, 100, CPCHSM_Normal, 0, 30, InpAtrPeriod);
             break;
         };
       //---
       return(0);
      }
    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
      {
       switch(TestVariant)
         {
          case IndClass:
             delete ATR;
             break;
         };
      }
    //+------------------------------------------------------------------+
    //| Expert tick function                                             |
    //+------------------------------------------------------------------+
    void OnTick()
      {
       double tmpValue[1];
       switch(TestVariant)
         {
          case BuiltIn:
             CopyBuffer(Handle, 0, 0, 1, tmpValue);
             break;
          case Custom:
             CopyBuffer(Handle, 0, 0, 1, tmpValue);
             break;
          case IndClass:
             ATR.Calculate();
             tmpValue[0] = ATR.GetATR(0, true);
             break;
         };
      }
    //+------------------------------------------------------------------+

    Görüldüğü üzere bu teknoloji strateji test edicisinin performansını normal bir özel gösterge kullanmaya kıyasla dikkate değer ölçüde azaltmıyor.


    Bu Teknolojinin İşlevsel Kullanımına İlişkin Notlar

    • Strateji test edicisinde bir Expert Advisor test edilirken özel bir göstergedeki prev_calculated değeri sıfırlanamaz, bu yüzden bu modda senkronizasyon geçmişi devre dışı bırakılmıştır;
    • göstergenin hesaplanması sadece sınıfların başlatılma aşamasında ayarlanan son ‘n’ çubuklarında yapılır;
    • hesaplama belirli bir sembole ve başlatılan sınıfın belirli bir döneme kesin olarak bağlı kalmayı gerektirir. Diğer semboller ve dönemlerin hesaplamalarını yapmak için sınıfların yeni örneklerini oluşturmamız gerekir.


    Sonuç

    Bir programcı her durum için görevin uygulanmasının farklı değişkenlerinin bütün artı ve eksilerini göz önünde bulundurmalıdır. Makalede önerilen uygulama sadece kendi avantaj ve dezavantajlarına sahip yollardan biri.

    Not: Hata yapmayan kişi hiçbir şey yapmaz! Hata bulduysanız lütfen bana bilgi verin.

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

    HTML'de grafikler ve şemalar HTML'de grafikler ve şemalar
    Günümüzde içinde ağ tarayıcısı yüklü olmayan bir bilgisayar bulmak zordur. Ağ tarayıcıları uzun zamandır evrimleşip iyileşiyorlar. Bu makale, grafik ve şemaların tarayıcılarda görüntülenmesini sağlayan bir işlem platformu olan MetaTrader 5’ten alınan bilgilere dayanarak güvenli bir şekilde nasıl grafik ve şema oluşturulabileceğini anlatır.
    C++ Şablonlarına Alternatif Olarak Sahte Şablonları Kullanma C++ Şablonlarına Alternatif Olarak Sahte Şablonları Kullanma
    Makale, şablonları kullanmadan, ancak şablonlarda iherenet programlama stilini koruyarak programlamanın bir yolunu açıklar. Özel yöntemler kullanarak şablonların uygulanmasını anlatır ve belirtilen şablonlar temelinde bir kod oluşturmak için eklenmiş hazır bir komut dosyasına sahiptir.
    MQL5 Sihirbazı: Açık Pozisyonların İzlenme Modülü Nasıl Oluşturulur MQL5 Sihirbazı: Açık Pozisyonların İzlenme Modülü Nasıl Oluşturulur
    MQL5 Sihirbazının alım satım stratejileri üreticisi, alım satım fikirlerinin test edilmesini büyük ölçüde basitleştirir. Bu makale MQL5 Sihirbazının alım satım stratejileri üreticisine nasıl bağlanılacağını ve alım satım yaparken kârınızı korumaya izin veren ve düşüşleri azaltan, fiyat pozisyon yönünde gittiğinde Zarar Durdur seviyesini kayıpsız bir bölgeye taşıyarak açık pozisyonları yöneten sınıfınızı nasıl yazacağınızı ele alır. Ayrıca MQL5 Sihirbazı için oluşturulan sınıfın tanımının yapısı ve formatı hakkında bilgi verir.
    Yönetilmeyen dışa aktarmaları kullanarak C# kodunu MQL5'e gösterme Yönetilmeyen dışa aktarmaları kullanarak C# kodunu MQL5'e gösterme
    Bu yazıda MQL5 kodu ile yönetilen C# kodu arasındaki etkileşimin farklı yöntemlerini sundum. Ayrıca MQL5 yapılarının C#'a karşı nasıl sıralanacağı ve MQL5 betiklerinde dışa aktarılan DLL işlevlerinin nasıl çağrılacağı konusunda da birkaç örnek sağladım. Sağlanan örneklerin, yönetilen kodda DLL yazma konusunda gelecekteki araştırmalar için bir temel oluşturabileceğine inanıyorum. Bu makale aynı zamanda MetaTrader'ın C#'da halihazırda uygulanmış olan birçok kütüphaneyi kullanmasına da kapı aralamaktadır.