English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Italiano
Yardımcı Göstergelerle Bellek Tüketimini Azaltma

Yardımcı Göstergelerle Bellek Tüketimini Azaltma

MetaTrader 5Göstergeler | 22 Aralık 2021, 13:54
256 0
ds2
ds2

1. Sorun

Muhtemelen, çalışmaları için diğer yardımcı göstergeleri kullanan Expert Advisor'ları veya göstergeleri zaten kullanmış veya oluşturmuşsunuzdur.

Örneğin, ünlü gösterge MACD, değerleri arasındaki farkı hesaplayan EMA (Üstel Hareketli Ortalama) göstergesinin iki kopyasını kullanır:


Böyle bir bileşik gösterge, aslında birkaç basit göstergeye eşdeğerdir. Örneğin, daha önce bahsedilen MACD, ana göstergenin arabellekleri ve tüm yardımcı göstergelerinin arabellekleri için bellek ayırması gerektiği için, bellek ve işlemci zamanını tek EMA'dan üç kat daha fazla tüketir.

MACD'nin yanı sıra, ikiden fazla yardımcı gösterge kullanan daha karmaşık göstergeler vardır.

Ayrıca, bu bellek harcamaları aşağıdaki durumlarda büyük ölçüde artar:

  • Gösterge birden fazla zaman dilimi kullanırsa (örneğin, birkaç zaman diliminde dalgaların eşzamanlılığını izler), böylece her zaman dilimi için yardımcı göstergelerin ayrı kopyalarını oluşturur;
  • Gösterge çoklu para birimiyse;
  • Bir yatırımcı, birkaç para birimi çiftinde alım satım işlemi yapmak için göstergeyi kullanırsa (aynı anda iki on çiftten fazla işlem yapan insanlar tanıyorum).

Bu koşulların bir kombinasyonu, bir bilgisayarda basit bir bellek eksikliğine yol açabilir (bir istemci terminalinin bu tür göstergelerin kullanılması nedeniyle gigabaytlarca belleğe ihtiyaç duyduğu gerçek durumları biliyorum). MetaTrader 5 istemci terminalindeki bellek eksikliğine bir örnek şu şekildedir:


Böyle bir durumda, terminal göstergeyi bir grafiğe yerleştiremez veya doğru şekilde hesaplayamaz (göstergenin kodu bellek ayırma hatasını işlemiyorsa) veya hatta kapanabilir.

Neyse ki, terminal, daha fazla sanal bellek kullanarak, yani bilgilerin bir kısmını sabit diske depolayarak bellek eksikliğini telafi edebilir. Tüm programlar çalışacaktır, ancak çok yavaş...


2. Test Bileşik Göstergesi

Bu makale kapsamında incelememize devam etmek için bir bileşik gösterge oluşturalım; MACD'den daha karmaşık bir gösterge...

Bu, trendlerin başlangıcını izleyen bir gösterge olsun. 5 zaman diliminden gelen sinyalleri toplayacaktır, örneğin: H4, H1, M15, M5, M1. Tahminlerin güvenilirliğini arttırması gereken büyük ve küçük ortaya çıkan trendlerin rezonansını sabitlemeye olanak tanıyacaktır. Her zaman diliminde sinyal kaynağı olarak, MetaTrader 5 teslimatına dahil olan Ichimoku ve Price_Channel göstergelerini kullanacağız:

  • Ichimoku'nun Tenkan (kırmızı) çizgisi Kijun (mavi) çizgisinin üzerindeyse, trend yükselir ve altındaysa, trend düşer;


  • Fiyat, Price_Channel'ın orta çizgisinin üzerindeyse, trend yükselir; altındaysa trend düşer.


Toplamda, göstergemiz 10 yardımcı gösterge kullanacak: Her birinde 2 gösterge bulunan 5 zaman dilimi. Göstergemize Trender diyelim.

Tam kaynak kodu (makaleye de eklenmiştir) şu şekildedir:

#property indicator_separate_window
#property indicator_buffers 1
#property indicator_plots   1
#property indicator_minimum -1
#property indicator_maximum  1

#property indicator_type1   DRAW_HISTOGRAM
#property indicator_color1  DarkTurquoise

// The only buffer of the indicator
double ExtBuffer[];

// Timeframes of auxiliary indicators
ENUM_TIMEFRAMES TF[5] = {PERIOD_H4, PERIOD_H1, PERIOD_M15, PERIOD_M5, PERIOD_M1};

// Handles of auxiliary indicators for all timeframes
int h_Ichimoku[5], h_Channel[5];

//+------------------------------------------------------------------+
void OnInit()
  {
   SetIndexBuffer(0, ExtBuffer);
   ArraySetAsSeries(ExtBuffer, true);
   
   // Create auxiliary indicators
   for (int itf=0; itf<5; itf++)
     {
      h_Ichimoku[itf] = iCustom(Symbol(), TF[itf], 
                                "TestSlaveIndicators\\Ichimoku",
                                9, 26, 52
                               );
      h_Channel [itf] = iCustom(Symbol(), TF[itf],
                                "TestSlaveIndicators\\Price_Channel",
                                22
                               );
     }
  }
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime& time[],
                const double& open[],
                const double& high[],
                const double& low[],
                const double& close[],
                const long& tick_volume[],
                const long& volume[],
                const int& spread[])
  {
   ArraySetAsSeries(time, true);
  
   int limit = prev_calculated ? rates_total - prev_calculated : rates_total -1;

   for (int bar = limit; bar >= 0; bar--)
     {
      // Time of the current bar
      datetime Time  = time [bar];
      
      //--- Gather signals from all timeframes
      double Signal = 0; // total signal
      double bufPrice[1], bufTenkan[1], bufKijun [1], bufMid[1], bufSignal[1];
      for (int itf=0; itf<5; itf++)
        {
         //=== Bar price
         CopyClose(Symbol(), TF[itf], Time, 1, bufPrice);
         double Price = bufPrice[0];

         //=== The Ichimoku indicator         
         CopyBuffer(h_Ichimoku[itf], 0, Time, 1, bufTenkan);
         double Tenkan = bufTenkan[0];
         CopyBuffer(h_Ichimoku[itf], 1, Time, 1, bufKijun );    
         double Kijun  = bufKijun [0];
           
         if (Tenkan > Kijun) Signal++;
         if (Tenkan < Kijun) Signal--;
          
         //=== The channel indicator
         CopyBuffer(h_Channel [itf], 2, Time, 1, bufMid);
         double Mid = bufMid[0];

         if (Price > Mid) Signal++;
         if (Price < Mid) Signal--;
        }
        
      ExtBuffer[bar] = Signal/10;
     }

   return(rates_total);
  }
//+------------------------------------------------------------------+

Bu göstergeyi, sinyalleri topladığı göstergeler arasında en küçük zaman dilimine sahip bir grafik üzerinde kullanmalısınız; tüm küçük trendleri görmenin tek yolu budur. Bizim durumumuzda, bu, M1 zaman dilimidir. Gösterge şöyle görünür:


Ve şimdi en önemli kısma geçiyoruz: Bu gösterge tarafından tüketilen bellek miktarını hesaplayalım.

Ichimoku göstergesinin kaynak koduna bir göz atın (tam kod ektedir):

#property indicator_buffers 5

ve Price_Channel göstergesi (tam kod ektedir):

#property indicator_buffers 3

Bu çizgilerde 8 adet arabellek oluşturulduğunu görebilirsiniz. Bunu 5 zaman dilimi ile çarpın. Ve Trender göstergesinin kendisinin 1 arabelleğini ekleyin. Toplamda 41 arabelleğimiz var! Bu kadar etkileyici değerler, görünüşte basit (bir grafikte) göstergelerin arkasındadır.

İstemci terminalinin varsayılan özelliklerinde, bir arabellek, her biri 8 bayt tüketen double türünden yaklaşık 100.000 değer içerir. Böylece 41 arabellek yaklaşık 31 Mb bellek tüketir. Ve bunlar yalnızca değerlerin kendileridir; bunlara ek olarak arabelleklerde hangi hizmet bilgilerinin saklandığını bilmiyorum.

Şöyle dediğinizi duyar gibiyim: "31 Mb çok değil". Ancak bir yatırımcı çok sayıda para birimi çifti kullandığında, bu hacim bir sorun haline gelir. Göstergelere ek olarak, grafiğin kendisi de çok fazla bellek tüketir. Göstergelerin aksine, her çubuğun aynı anda birkaç değeri vardır: OHLC, zaman ve hacim. Bunu bir bilgisayara nasıl sığdırabiliriz?


3. Sorunu Çözmenin Yolları

Elbette bilgisayarınıza daha fazla bellek yükleyebilirsiniz. Ancak bu varyant teknik, finansal veya başka nedenlerle size uygun değilse veya yüklenebilecek bellek miktarını zaten tükettiyseniz ve yeterli değilse, tüketen göstergeleri incelemeli ve iştahlarını azaltmalısınız.

Bunu yapmak için... Okuldaki geometri dersini hatırlayın. Bileşik göstergelerimizin tüm arabelleklerinin dolu bir dikdörtgen olduğunu varsayalım:


Bu dikdörtgenin alanı tüketilen bellektir. Genişliği veya yüksekliği azaltarak alanı küçültebilirsiniz.

Bu durumda genişlik, göstergelerin çizildiği çubukların sayısıdır. Yükseklik, gösterge arabelleklerinin sayısıdır.


4. Çubukların Sayısını Azaltma

4.1. Basit Çözüm

MetaTrader ayarlarını düzenlemek için programcı olmanıza dahi gerek yok:



"Grafikteki maksimum çubuklar" değerini azaltarak, bu pencerelerdeki gösterge arabelleklerinin boyutunu küçültürsünüz. Bu, basit, etkili ve herkes tarafından kullanılabilirdir (bir yatırımcı alım satım yaparken derin bir fiyat geçmişine ihtiyaç duymuyorsa).

4.2. Başka Bir Çözüm Var mı?

MQL5 programcıları, gösterge arabelleklerinin göstergelerde önceden boyut ön ayarı olmayan dinamik diziler olarak bildirildiğini bilir. Örneğin, Ichimoku'nun 5 arabelleği:

double    ExtTenkanBuffer[];
double    ExtKijunBuffer[];
double    ExtSpanABuffer[];
double    ExtSpanBBuffer[];
double    ExtChinkouBuffer[];

Dizilerin boyutu belirtilmemiştir; zira MetaTrader 5 istemci terminalinin kendisi tarafından mevcut tüm geçmiş için ayarlanmıştır.

Aynısı OnCalculate işlevinde de vardır:

int OnCalculate (const int rates_total,      // size of the array price[]
               const int prev_calculated,  // number of bars processed at the previous call
               const int begin,            // the start of reliable data
               const double& price[]       // array for the calculation
   );

Bu işlevde fiyat arabelleği göstergeye iletilir. Bunun için bellek zaten terminal tarafından tahsis edilmiştir ve bir programcı onun boyutunu etkileyemez.

Ayrıca, MQL5, bir göstergenin arabelleğinin bir başkası için fiyat arabelleği olarak kullanılmasına olanak tanır ("başka bir göstergeye dayalı bir gösterge" çizin). Ancak burada dahi, bir programcı boyuta herhangi bir sınırlama getiremez; yalnızca gösterge tanıtıcısını iletir.

Dolayısıyla, MQL5'te gösterge arabelleklerinin boyutunu sınırlamanın hiçbir yolu yoktur.


5. Arabellek Sayısını Azaltma

Bir programcının burada çok çeşitli seçenekleri vardır. Bileşik bir göstergenin arabellek sayısını azaltmanın birkaç basit teorik yolunu buldum. Ancak, ana göstergede tüm arabellekler gerekli olduğu için, bunların tümü, yardımcı göstergelerin arabellek sayısının azalmasını gerektirir.

Bu araçlara ayrıntılı bir şekilde göz atalım ve gerçekten işe yarayıp yaramadıklarını ve ne gibi avantajları ve dezavantajları olduğunu kontrol edelim.

5.1. "İhtiyaç" Yöntemi

Bir yardımcı gösterge birçok arabellek içeriyorsa, ana gösterge için tümünün gerekli olmadığı görünebilir. Böylece, tükettikleri belleği boşaltmak için kullanılmayan göstergeleri devre dışı bırakabiliriz. Bunu yapmak için, o yardımcı göstergenin kaynak kodunda bazı değişiklikler yapmamız gerekir.

Bunu yardımcı göstergelerimizden biri olan Price_Channel ile yapalım. Bu, üç arabellek içerir ve Trender bunlardan yalnızca birini okur; bu nedenle kaldıracak gereksiz şeylerimiz var.

Price_Channel (ilk gösterge) ve Price_Channel-Need (tamamen yeniden yapılmış) göstergelerinin kodunun tamamı makaleye eklenmiştir. Ayrıca, yalnızca üzerinde yapılan değişiklikleri anlatacağım.

Her şeyden önce, arabellek sayacını 3'ten 1'e düşürün:

//#property indicator_buffers 3
  #property indicator_buffers 1
//#property indicator_plots   2
  #property indicator_plots   1

Ardından iki gereksiz arabellek dizisini kaldırın:

//--- indicator buffers
//double    ExtHighBuffer[];
//double    ExtLowBuffer[];
 double    ExtMiddBuffer[];

Şimdi, bu göstergeyi derlemeye çalışırsak, derleyici bu dizilerin çağrıldığı tüm satırı gösterecektir:


Bu yöntem, değiştirilmesi gereken şeylerin hızlı bir şekilde bulunmasına olanak tanır. Bu, gösterge kodu çok büyük olduğunda oldukça uygundur.

Bizim durumumuzda toplam 4 adet "bildirilmemiş tanımlayıcı" satırı var. Onları düzeltelim.

Beklediğimiz gibi, ikisi OnInit'te bulunuyor. Ama onlarla birlikte, gerekli ExtMiddBuffer'ı içeren satırı kaldırmak zorunda kaldık. Bunun yerine, arabelleğin başka bir indeksi ile benzer bir tane ekliyoruz. Gösterge artık indeks 2'yi içeren bir arabelleğe sahip olmadığı için, yalnızca 0 mümkündür:

//   SetIndexBuffer(0,ExtHighBuffer,INDICATOR_DATA);
//   SetIndexBuffer(1,ExtLowBuffer,INDICATOR_DATA);
//   SetIndexBuffer(2,ExtMiddBuffer,INDICATOR_DATA);
     SetIndexBuffer(0,ExtMiddBuffer,INDICATOR_DATA);

"Kes" göstergesini görsel modda kullanmayı planlıyorsanız, görünüm ayarlarının arabellek indeksi ile birlikte değiştirilmesi gerektiğini göz önünde bulundurun. Bizim durumumuzda:

//#property indicator_type1   DRAW_FILLING
  #property indicator_type1   DRAW_LINE

Görselleştirmeye ihtiyacınız yoksa görünümü değiştirmeyi atlayabilirsiniz; bu hatalara yol açmaz.

"Bildirilmemiş tanımlayıcı" listesiyle çalışmaya devam edelim. Son iki değişiklik (beklenen de budur), gösterge arabellek dizilerinin doldurulduğu OnCalculate'dedir. Gerekli ExtMiddBuffer, kaldırılan ExtHighBuffer ve ExtLowBuffer'ı çağırdığı için, bunların yerine ara değişkenler geçirilir:

   //--- the main loop of calculations
   for(i=limit;i<rates_total;i++)
     {
//      ExtHighBuffer[i]=Highest(High,InpChannelPeriod,i);
        double      high=Highest(High,InpChannelPeriod,i);
//      ExtLowBuffer[i]=Lowest(Low,InpChannelPeriod,i);
        double      low=Lowest(Low,InpChannelPeriod,i);
//      ExtMiddBuffer[i]=(ExtHighBuffer[i]+ExtLowBuffer[i])/2.0;;
        ExtMiddBuffer[i]=(   high         +   low         )/2.0;;
     }

Gördüğünüz gibi, tüm bu "cerrahi operasyon"da zor bir şey yok. Gerekli şeyler hızlı bir şekilde bulundu; birkaç "neşter kesisi" ve iki arabellek hariç tutulur. Tüm bileşik gösterge Trender içinde toplam tasarruf 10 arabellektir (2*5 zaman dilimi).

Price_Channel ve Price_Channel-Need'i alt alta açıp kaybolan arabellekleri görebilirsiniz:


Trender göstergesinde Price_Channel-Need'i kullanmak için Trender kodunda "Price_Channel" yardımcı göstergesinin adını "Price_Channel-Need" olarak düzeltmemiz gerekiyor. Ayrıca, gerekli arabelleğin indeksini 2'den 0'a değiştirmemiz gerekiyor. Hazır Trender-Need makaleye eklenmiştir.


5.2. "Toplama" Yöntemi

Bir ana gösterge, bir yardımcı göstergenin birden fazla ara belleğinin verilerini okursa ve ardından onunla bir toplama işlemi gerçekleştirirse (örneğin, ekleme veya karşılaştırma), bu işlemi ana göstergede gerçekleştirmeye gerek yoktur. Bunu yardımcı bir göstergede yapabilir ve ardından sonucu ana göstergeye iletebiliriz. Bu nedenle, birden fazla arabelleğe sahip olmak gerekli değildir; tamamı bir tanesiyle değiştirilir.

Bizim durumumuzda, böyle bir yöntem Ichimoku için geçerlidir. Trender, ondan 2 arabellek kullandığı için (0 - Tenkan ve 1 - Kijun):

         CopyBuffer(h_Ichimoku[itf], 0, Time, 1, bufTenkan);
         double Tenkan = bufTenkan[0];
         CopyBuffer(h_Ichimoku[itf], 1, Time, 1, bufKijun );    
         double Kijun  = bufKijun [0];
           
         if (Tenkan > Kijun) Signal++;
         if (Tenkan < Kijun) Signal--;

Ichimoku'nun 0 ve 1 arabelleğini bir sinyal arabelleğinde toplarsak, yukarıda gösterilen Trender parçası aşağıdakiyle değiştirilecektir:

         CopyBuffer(h_Ichimoku[itf], 0, Time, 1, bufSignal);
         
         Signal += bufSignal[0];

Trender-Aggregate'in tamamı makaleye eklenmiştir.

Şimdi, Ichimoku'da yapılması gereken temel değişikliklere bakalım.

Ayrıca, bu gösterge kullanılmayan arabellekleri içerir. Yani "Toplama" yöntemine ek olarak "İhtiyaç" yöntemini de uygulayabiliriz. Bu nedenle, Ichimoku'da yalnızca 5'lik bir arabellek kalır - bu, gerekli arabellekleri toplayan arabellektir:

//#property indicator_buffers 5
  #property indicator_buffers 1
//#property indicator_plots   4
  #property indicator_plots   1

Tek arabelleğe yeni bir ad verelim:

//--- indicator buffers
//double    ExtTenkanBuffer[];
//double    ExtKijunBuffer[];
//double    ExtSpanABuffer[];
//double    ExtSpanBBuffer[];
//double    ExtChinkouBuffer[];
  double    ExtSignalBuffer[];

Yeni adın pratik bir anlamı var; bu, daha önce kullanılan arabelleklerin tüm adlarının göstergeden silinmesine olanak tanır. Değiştirilmesi gereken tüm satırları hızlı bir şekilde bulmayı ("İhtiyaç" yönteminde açıklandığı gibi derlemeyi kullanarak) sağlar.

Göstergeyi bir grafik üzerinde görselleştirecekseniz, görünüm ayarlarını değiştirmeyi unutmayın. Ayrıca, bizim durumumuzda, toplama arabelleğinin, tükettiği iki arabellekle karşılaştırıldığında farklı bir değer aralığına sahip olduğunu da göz önünde bulundurmalısınız. Bu, şu anda bir fiyat türevi göstermiyor, ancak iki arabellekten hangisinin daha büyük olduğunu gösteriyor. Bu tür sonuçları bir grafiğin altında ayrı bir pencerede görüntülemek daha uygundur:

//#property indicator_chart_window
  #property indicator_separate_window

Bu nedenle, OnInit'te aşağıdaki değişiklikleri yapın:

//--- indicator buffers mapping
//   SetIndexBuffer(0,ExtTenkanBuffer,INDICATOR_DATA);
//   SetIndexBuffer(1,ExtKijunBuffer,INDICATOR_DATA);
//   SetIndexBuffer(2,ExtSpanABuffer,INDICATOR_DATA);
//   SetIndexBuffer(3,ExtSpanBBuffer,INDICATOR_DATA);
//   SetIndexBuffer(4,ExtChinkouBuffer,INDICATOR_DATA);
     SetIndexBuffer(0,ExtSignalBuffer,INDICATOR_DATA);

Ve en ilginç kısım OnCalculate'dedir. Not: Üç gereksiz arabellek basitçe silinir ("İhtiyaç" yöntemini kullandığımız için) ve gerekli ExtTenkanBuffer ve ExtKijunBuffer, Tenkan ve Kijun geçici değişkenleriyle değiştirilir. Bu değişkenler, toplama arabelleği ExtSignalBuffer'ın hesaplanması için döngü sonunda kullanılır:

   for(int i=limit;i<rates_total;i++)
     {
//     ExtChinkouBuffer[i]=Close[i];
      //--- tenkan sen
      double high=Highest(High,InpTenkan,i);
      double low=Lowest(Low,InpTenkan,i);
//     ExtTenkanBuffer[i]=(high+low)/2.0;
       double  Tenkan    =(high+low)/2.0;
      //--- kijun sen
      high=Highest(High,InpKijun,i);
      low=Lowest(Low,InpKijun,i);
//     ExtKijunBuffer[i]=(high+low)/2.0;
       double  Kijun    =(high+low)/2.0;
      //--- senkou span a
//     ExtSpanABuffer[i]=(ExtTenkanBuffer[i]+ExtKijunBuffer[i])/2.0;
      //--- senkou span b
      high=Highest(High,InpSenkou,i);
      low=Lowest(Low,InpSenkou,i);
//     ExtSpanBBuffer[i]=(high+low)/2.0;

       //--- SIGNAL
       double Signal = 0;
       if (Tenkan > Kijun) Signal++;
       if (Tenkan < Kijun) Signal--;
       ExtSignalBuffer[i] = Signal;
     }

Toplamda eksi 4 arabellek. Ve Ichimoku'ya yalnızca "Need" yöntemini uygularsak, yalnızca 3 daha az arabelleğimiz olurdu.

Trender'ın tamamında tasarruf toplamda 20 arabellektir (4 * 5 zaman dilimi).

Ichimoku-Aggregate kodunun tamamı makaleye eklenmiştir. Bu göstergeyi orijinal göstergeyle karşılaştırmak için ikisini de tek bir grafikte açın. Hatırladığınız gibi, değiştirilen gösterge artık grafiğin altında ayrı bir pencerede görüntüleniyor:


5.3. "Dahil Etme" Yöntemi

Arabellek sayısını azaltmanın en radikal yolu, tüm yardımcı göstergelerden kurtulmaktır. Bunu yaparsak, göstergemizde yalnızca bir arabellek kalır; bu, ana göstergeye ait olan arabellektir. Daha az arabellek olamaz.

Aynı sonuç, yardımcı göstergelerin kodunu ana göstergeye taşıyarak da elde edilebilir. Bazen bu, yapılması zaman alan bir şey gibi görünebilir, ancak nihai etki buna değer. En zoru, göstergelerden taşınan kodu uyarlamaktır. Başka bir göstergenin kodu içinde çalışması amaçlanmamıştır.

Bu sırada ortaya çıkan ana sorunlar şu şekildedir:

  • Ad çakışması. Değişken adları aynı, işlevler (özellikle OnCalculate gibi sistem işlevleri);
  • Arabelleklerin olmaması. Bazı göstergelerde, göstergelerin mantığı, verilerin arabelleklerde saklanması/işlenmesi ile yakından bağlantılıysa, bu, aşılmaz bir engel olabilir. Bizim durumumuzda, amacımız bellek tüketimini azaltmak olduğu için, arabelleklerin basit dizilerle değiştirilmesi her derde deva değildir. Devasa bir geçmiş verisinin bellekte saklanmasını reddetmek önemlidir.

Bu sorunların etkili bir şekilde çözülmesini sağlayan yöntemleri gösterelim.

Her yardımcı gösterge bir sınıf olarak yazılmalıdır. O zaman göstergelerin tüm değişkenleri ve işlevleri her zaman (sınıfları içinde) benzersiz adlara sahip olacak ve diğer göstergelerle çakışmayacaktır.

Birçok gösterge taşınırsa, bunları kullanırken karışıklığı önlemek için bu sınıfların standardizasyonunu düşünebilirsiniz. Bu amaçla, bir temel gösterge sınıfı oluşturun ve tüm yardımcı göstergelerin sınıflarını ondan devralın.

Ben aşağıdaki sınıfı yazdım:

class CIndicator
  {
protected:
   string symbol;             // currency pair
   ENUM_TIMEFRAMES timeframe;  // timeframe

   double Open[], High[], Low[], Close[]; // simulation of price buffers
   int BufLen; // necessary depth of filling of price buffers

public:
   //--- Analogs of standard functions of indicators
   void Create(string sym, ENUM_TIMEFRAMES tf) {symbol = sym; timeframe = tf;};
   void Init();
   void Calculate(datetime start_time); // start_time - address of bar that should be calculated
  };

Şimdi buna dayanarak Ichimoku göstergesi için bir sınıf oluşturalım. İlk olarak, özellikler şeklinde, giriş parametrelerini orijinal adlarıyla yazın. Daha sonra gösterge kodundaki hiçbir şeyi değiştirmemek için:

class CIchimoku: public CIndicator
  {
private:
   // Simulation of input parameters of the indicator
   int InpTenkan;
   int InpKijun;
   int InpSenkou;

Tüm arabelleklerin adlarını saklayın. Evet, yanlış duymadınız; bu göstergeye ait 5 arabelleğin tamamını bildiriyoruz. Ama bunlar sahte. Her biri yalnızca bir çubuktan oluşur:

public:   
   // Simulation of indicator buffers
   double ExtTenkanBuffer [1];
   double ExtKijunBuffer  [1];
   double ExtSpanABuffer  [1];
   double ExtSpanBBuffer  [1];
   double ExtChinkouBuffer[1];   

Peki bunu neden yaptık? Koddaki daha fazla değişiklik miktarını azaltmak için... Bunu anlayacaksınız. Devralınan CIchimoku.Calculate yöntemini, Ichimoku'dan alınan OnCalculate işlevinin koduyla doldurarak yeniden tanımlayın.

Bu işlevi taşırken döngünün geçmiş çubuklara göre kaldırıldığına dikkat edin. Şimdi burada yalnızca belirli bir zamana sahip bir çubuk hesaplanır. Ve ana hesaplama kodu aynı kalır. Bu nedenle, arabelleklerin tüm adlarını ve göstergenin parametrelerini çok dikkatli bir şekilde sakladık.

Calculate yönteminin en başında fiyat arabelleklerinin değerlerle dolu olmasına da dikkat etmelisiniz. Bir çubuğun hesaplanması için gereken sayıda değer vardır.

   void Calculate(datetime start_time)
     {
      CopyHigh (symbol,timeframe,start_time,BufLen,High);
      CopyLow  (symbol,timeframe,start_time,BufLen,Low );
      CopyClose(symbol,timeframe,start_time,1     ,Close);

//    int limit;
      //---
//    if(prev_calculated==0) limit=0;
//    else                   limit=prev_calculated-1;
      //---
//    for(int i=limit;i<rates_total;i++)
      int i=0;
        {
         ExtChinkouBuffer[i]=Close[i];
         //--- tenkan sen
         double high=Highest(High,InpTenkan,i);
         double low=Lowest(Low,InpTenkan,i);
         ExtTenkanBuffer[i]=(high+low)/2.0;
         //--- kijun sen
         high=Highest(High,InpKijun,i);
         low=Lowest(Low,InpKijun,i);
         ExtKijunBuffer[i]=(high+low)/2.0;
         //--- senkou span a
         ExtSpanABuffer[i]=(ExtTenkanBuffer[i]+ExtKijunBuffer[i])/2.0;
         //--- senkou span b
         high=Highest(High,InpSenkou,i);
         low=Lowest(Low,InpSenkou,i);
         ExtSpanBBuffer[i]=(high+low)/2.0;
        }
      //--- done
//    return(rates_total);     
     };

Elbette ki orijinal kodu saklamayı atlayabiliriz. Ancak bu durumda, işleyişinin mantığını anlamamızı gerektiren büyük bir bölümünü yeniden yazmamız gerekecektir. Bizim durumumuzda, gösterge basittir ve anlaşılması kolay olacaktır. Peki ya gösterge karmaşıksa? Böyle bir durumda yardımcı olabilecek yöntemi size gösterdim.

Şimdi CIchimoku.Init yöntemini dolduralım; burada her şey basit:

   void Init(int Tenkan = 9, int Kijun = 26, int Senkou = 52)
     {
      InpTenkan = Tenkan; InpKijun = Kijun; InpSenkou = Senkou;
      BufLen = MathMax(MathMax(InpTenkan, InpKijun), InpSenkou);
     };

Ichimoku, CIchimoku sınıfına alınması gereken diğer iki işlevi içerir: En Yüksek ve En Düşük. Fiyat arabelleklerinin belirli bir bölümünde maksimum ve minimum değerleri ararlar.

Fiyat arabelleklerimiz gerçek değil; bunların çok küçük boyutları var (yukarıdaki Calculate yönteminde doldurulduklarını gördünüz). Bu nedenle En Yüksek ve En Düşük işlevlerinin çalışma mantığını biraz değiştirmeliyiz.

Bu durumda da, minimum değişiklik yapma prensibini takip ettim. Tüm değişiklikler, arabellekteki çubukların indekslenmesini genel olandan (arabellek uzunluğu tüm mevcut geçmiş olduğunda) yerel olana (şimdi fiyat arabellekleri yalnızca bir gösterge çubuğunun hesaplanması için gerekli değerleri içerdiği için) değiştiren bir satırın eklenmesinden oluşur:

   double Highest(const double&array[],int range,int fromIndex)
     {
       fromIndex=MathMax(ArraySize(array)-1, 0);
      double res=0;
   //---
      res=array[fromIndex];
      for(int i=fromIndex;i>fromIndex-range && i>=0;i--)
        {
         if(res<array[i]) res=array[i];
        }
   //---
      return(res);
     }

En Düşük yöntemi de aynı şekilde değiştirilir.

Price_Channel göstergesinde de benzer değişiklikler yapılmıştır, ancak CChannel adlı sınıf olarak temsil edilecektir. Her iki sınıfın tam kodu, makaleye eklenmiş Trender-Include dosyasındadır.

Kodu taşımanın ana yönünü açıkladım. Bence bu yöntemler çoğu gösterge için yeterlidir.

Standart olmayan ayarlara sahip göstergeler ek zorluklara neden olabilir. Örneğin, Price_Channel göze çarpmayan satırlar içeriyor:

   PlotIndexSetInteger(0,PLOT_SHIFT,1);
   PlotIndexSetInteger(1,PLOT_SHIFT,1);

Bunlar, gösterge grafiğinin 1 çubukta kaydırıldığı anlamına gelir. Bizim durumumuzda, CopyBuffer ve CopyHigh işlevlerinin parametrelerinde aynı çubuk koordinatlarının (zaman) ayarlanmasına rağmen iki farklı çubuğun kullanılması durumuna yol açar.

Bu sorun Trender-Include'da çözülmüştür ("bunlar", sorunun olmadığı CIchimoku sınıfından farklı olarak CChannel sınıfının gerekli kısımlarına eklenmiştir). Yani böyle bir "kurnaz" göstergeye ihtiyacınız varsa, onu nerede arayacağınızı biliyorsunuz.

Evet, taşıma işlemini tamamladık; şimdi her iki gösterge de Trender-Include göstergesinin içinde iki sınıf olarak yazılıyor. Geriye bu göstergeleri çağırma şeklini değiştirmek kaldı. Trender'da tanıtıcı dizileri vardı; Trender-Include'da bunlar nesne dizileriyle değiştirilir:

// Handles of auxiliary indicator for all timeframes
//int h_Ichimoku[5], h_Channel[5];
// Instances of embedded auxiliary indicators
CIchimoku o_Ichimoku[5]; CChannel o_Channel[5];

OnInit'te yardımcı göstergelerin oluşturulması artık aşağıdaki gibi görünür:

   for (int itf=0; itf<5; itf++)
     {
      o_Ichimoku[itf].Create(Symbol(), TF[itf]);
      o_Ichimoku[itf].Init(9, 26, 52);
      o_Channel [itf].Create(Symbol(), TF[itf]);
      o_Channel [itf].Init(22);
     }

Ve OnCalculate'deki CopyBuffer, nesnelerin özelliklerine doğrudan çağrı ile değiştirilir:

         //=== The Ichimoku indicator
         o_Ichimoku[itf].Calculate(Time);

         //CopyBuffer(h_Ichimoku[itf], 0, Time, 1, bufTenkan);
         //double Tenkan = bufTenkan[0];
         double Tenkan = o_Ichimoku[itf].ExtTenkanBuffer[0];

         //CopyBuffer(h_Ichimoku[itf], 1, Time, 1, bufKijun );    
         //double Kijun  = bufKijun [0];
         double Kijun  = o_Ichimoku[itf].ExtKijunBuffer [0];
           
         if (Tenkan > Kijun) Signal++;
         if (Tenkan < Kijun) Signal--;
          
         //=== The Channel indicator
         o_Channel[itf].Calculate(Time);

         //CopyBuffer(h_Channel [itf], 2, Time, 1, bufMid);
         //double Mid = bufMid[0];
         double Mid = o_Channel[itf].ExtMiddBuffer[0];

         if (Price > Mid) Signal++;
         if (Price < Mid) Signal--;

Eksi 40 arabellek. Bu çabaya değer.

Trender'ın daha önce açıklanan "İhtiyaç" ve "Toplama" yöntemlerine göre her değişikliğinden sonra, elde edilen göstergeleri görsel modda test ettim.

Şimdi böyle bir test yapalım: Bir grafikte ilk göstergeyi (Trender) ve yeniden yapılanı (Trender-Include) açın. Her iki göstergenin çizgileri birbiriyle doğru bir şekilde kesiştiği için her şeyin doğru yapıldığını söyleyebiliriz:


5.4. Peki Bunu Tek Tek Yapabilir miyiz?

Yardımcı göstergelerin arabellek sayısını azaltmanın 3 yolunu zaten göz önünde bulundurduk. Ama peki ya yaklaşımı kökten değiştirmeye çalışırsak? - Toplam sayılarını azaltmak yerine aynı anda bellekte tutulan arabelleklerin sayısını azaltmaya çalışırsak ne olur? Yani, tüm göstergeleri tek seferde yüklemek yerine göstergeleri belleğe tek tek yükleyeceğiz. Bir "dolambaçlı yol" düzenlememiz gerekiyor: Bir yardımcı gösterge oluşturun, verilerini okuyun, silin, bir sonrakini oluşturun vb. (tüm zaman dilimlerini gözden geçirene kadar). Ichimoku göstergesi en fazla arabelleğe sahiptir - 5. Böylece, teorik olarak aynı anda maksimum 5 arabellek bellekte tutulabilir (ve ayrıca ana göstergenin 1 arabelleği) ve toplam tasarruf 35 arabellektir!

Peki bu mümkün mü? MQL5'te göstergeleri silmek için özel bir işlev vardır - IndicatorRelease.

Ancak, bu, göründüğü kadar kolay değil. MetaTrader 5, MQL5 programlarının yüksek hızda çalışmasına önem verir; bu nedenle, başka bir EA, gösterge veya script dosyasının ihtiyaç duyması halinde, çağrılan tüm zaman serileri önbellekte tutulur. Ve yalnızca uzun bir süre çağrılmazlarsa, belleği boşaltmak için kaldırılırlar. Bu zaman aşımı 30 dakikaya kadar uzayabilir.

Bu nedenle, göstergelerin sürekli olarak oluşturulması ve silinmesi, anında büyük bir bellek tasarrufu elde edilmesine izin vermeyecektir. Ancak, bu, bilgisayarı önemli ölçüde yavaşlatabilir; zira her oluşturulduğunda tüm fiyat geçmişi için bir gösterge hesaplanır. Ana göstergenin her bir çubuğunda böyle bir işlemi gerçekleştirmenin ne kadar mantıklı olduğunu bir düşünün...

Yine de, "gösterge dolambaçlı yolu" fikri bir "beyin fırtınası" için oldukça ilginçti. Gösterge belleğinin optimizasyonu hakkında başka bir orijinal fikir bulduysanız, yorumlarınızı makaleye yazın. Bu konu ile ilgili sonraki makalelerden birinde teorik veya pratik bir şekilde kullanıma sunulabilirler.


6. Gerçek Bellek Tüketimini Ölçme

Evet, önceki bölümlerde, yardımcı göstergelerin arabelleklerinin sayısını azaltmak için 3 çalışma yöntemi uyguladık. Şimdi, gerçek bellek tüketimini nasıl azalttığını analiz edelim.

MS Windows'ta "Görev Yöneticisi"ni kullanarak terminal tarafından tüketilen bellek boyutunu ölçeceğiz. "İşlemler" sekmesinde, istemci terminali tarafından tüketilen RAM ve sanal belleğin boyutunu görebilirsiniz. Örneğin:


Ölçümler, terminal tarafından minimum bellek tüketimini (göstergelere göre bellek tüketimine yakın olacak) görmeyi sağlayan aşağıdaki algoritmaya göre yapılır:

  1. MetaQuotes-Demo sunucusundan derin bir fiyat geçmişi indirin (geçmişinin otomatik olarak indirilmesi için bir sembol üzerinde bir test yapmak yeterlidir);
  2. Terminali bir sonraki ölçüm için ayarlayın (gerekli grafikleri ve göstergeleri açın) ve belleği gereksiz bilgilerden temizlemek için yeniden başlatın;
  3. Yeniden başlatılan terminalin tüm göstergelerin hesaplamasını tamamlamasını bekleyin. Bunu, işlemcinin sıfır yüklemesiyle göreceksiniz;
  4. Görev çubuğunda terminali simge durumuna küçültün (terminalin sağ üst köşesindeki standart "Simge Durumuna Küçült" düğmesine tıklayarak). Şu anda hesaplamalar için kullanılmayan belleği boşaltacaktır (yukarıdaki ekran görüntüsünde simge durumuna küçültülmüş bellek tüketimi örneğini görebilirsiniz - sanal bellekten çok daha az RAM tüketildiğini görebilirsiniz);
  5. "Görev Yöneticisi"nde "Bellek Kullanımı" (RAM) ve "VM boyutu" (sanal bellek) sütunlarını toplayın. Windows XP'de bu şekilde adlandırılırlar, işletim sisteminin diğer sürümlerinde adlar biraz farklı olabilir.

Ölçüm parametreleri:

  • Ölçümlerin daha doğru olması için, bir fiyat grafiği, yani 22 M1 grafiği yerine MetaQuotes demo hesabında bulunan tüm para birimi çiftlerini kullanacağız. Ardından ortalama değerleri hesaplayacağız;
  • "Grafikteki maksimum çubuklar" seçeneği (bölüm 4.1'de açıklanmıştır) standart değere sahiptir - 100000;
  • İşletim Sistemi - Windows XP, 32 bit.

Ölçümlerin sonucundan ne beklenir? İki not vardır:

  1. Trender göstergesi 41 arabellek kullansa da, bu 41*100000 çubuk tükettiği anlamına gelmez. Bunun nedeni, arabelleklerin beş zaman dilimi arasında dağıtılması ve daha büyük olanın daha küçük zaman dilimlerinden daha az çubuk içermesidir. Örneğin, EURUSD'nin M1 geçmişi yaklaşık 4 milyon çubuk içerir ve H1 geçmişi yalnızca 70000 çubuktan (4000000/60) oluşur. Bu nedenle Trender'da arabellek sayısını azalttıktan sonra bellek tüketiminde aynı düşüşü beklememelisiniz;
  2. Bellek, yalnızca göstergenin kendisi tarafından değil, gösterge tarafından kullanılan fiyat serileri tarafından da tüketilir. Trender beş zaman dilimi kullanır. Dolayısıyla, arabellek sayısını birkaç kez azaltırsak, toplam bellek tüketimi o kadar azalmayacaktır. Çünkü, bellekteki o beş fiyat serisinin tamamı kullanılacaktır.

Tüketimi ölçerken, bellek tüketimini etkileyen başka faktörlerle karşılaşabilirsiniz. Bu pratik ölçümleri yapmamızın nedeni budur - Göstergenin optimizasyonunun bir sonucu olarak gerçek tasarrufu görmek.

Aşağıda tüm ölçümlerin sonucunu içeren tabloyu bulabilirsiniz. Her şeyden önce, boş terminal tarafından tüketilen bellek boyutunu ölçtüm. Bu değeri bir sonraki ölçümden çıkararak, bir grafik tarafından tüketilen bellek boyutunu hesaplayabiliriz. Terminal tarafından tüketilen belleği ve sonraki ölçümlerden bir grafiği çıkararak, her gösterge tarafından tüketilen bellek boyutunu elde ederiz.

Bellek Tüketicisi
Gösterge Arabellekleri
Zaman Dilimleri
Bellek Tüketimi
İstemci terminali
0
0
Terminal için 38 Mb
Grafik
0
1
Bir boş grafik için 12 Mb
Trender göstergesi
41
5
Bir gösterge için 46 Mb
Trender-Need göstergesi
31
5
Bir gösterge için 42 Mb
Trender-Aggregate göstergesi 21
5
Bir gösterge için 37 Mb
Trender-Include göstergesi 1
5
Bir gösterge için 38 Mb


Ölçümlerin sonuçlarına dayanarak varılan sonuç şu şekildedir:

  • Gösterge arabelleklerinin sayısının azaltılması, gösterge tarafından kullanılan belleğin eşit şekilde azalmasına yol açmaz.
Bu etkinin kaynağı bu bölümde yukarıda açıklanmıştır. Belki gösterge daha az zaman dilimi kullansaydı, arabellek sayısını azaltmanın etkisi daha anlamlı olurdu.
  • Yardımcı göstergelerin kodunu ana göstergeye taşımak her zaman en iyi sonucu vermez.

Peki Include yöntemi neden Aggregate kadar etkili değil? Sebebini belirlemek için, bu göstergelerin kodlarındaki temel farklılıkları hatırlamamız gerekir. Aggregate'de, hesaplamalar için gerekli fiyat serileri, OnCalculate'de giriş dizileri olarak terminal tarafından iletilir. Include'da, CopyHigh, CopyLow ve CopyClose kullanılarak her çubuk için tüm veriler (tüm zaman dilimleri için) aktif olarak istenir. Muhtemelen, bu işlevler kullanıldığında fiyat zaman serilerinin önbelleğe alınmasının özelliklerinden kaynaklanan ek bellek tüketimine yol açan şey budur.


Sonuç

Bu makalede, yardımcı göstergelerde bellek tüketimini azaltmanın 3 çalışma yöntemi ve istemci terminalini ayarlayarak 1 bellek tasarrufu yöntemi açıklanmıştır.

Uygulanması gereken yöntem, sizin durumunuzdaki kabul edilebilirliğe ve uygunluğa bağlıdır. Kaydedilen arabelleklerin ve megabaytların sayısı, birlikte çalıştığınız göstergelere bağlıdır: Bazılarında çok fazla "kesebilirsiniz" ve diğerlerinde hiçbir şey yapamazsınız.

Bellekten tasarruf etmek, terminalde aynı anda kullanılan para birimi çiftlerinin sayısının artırılmasına olanak tanıyacaktır. Bu, alım satım portföyünüzün güvenilirliğini artırır. Bilgisayarınızın teknik kaynaklarına bu kadar basit bir özen gösterilmesi, para yatırma işleminizde para kaynaklarına dönüşebilir.


Ekler

Makalede açıklanan göstergeler ektedir. Her şeyin çalışması için bunları "MQL5\Indicators\TestSlaveIndicators" klasörüne kaydedin; zira Trender göstergesinin tüm sürümleri (Trender-Include hariç) kendi yardımcı göstergelerini onda arar.


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

Ekli dosyalar |
ichimoku.mq5 (4.97 KB)
price_channel.mq5 (4.34 KB)
trender.mq5 (2.94 KB)
trender-need.mq5 (2.94 KB)
MQL5'te Elliott Dalgalarının Otomatik Analizini Uygulama MQL5'te Elliott Dalgalarının Otomatik Analizini Uygulama
Piyasa analizinin en popüler yöntemlerinden biri Elliott Dalga Prensibi'dir. Ancak, bu süreç oldukça karmaşıktır; bu da bizi ek araçların kullanımına götürür. Bu enstrümanlardan biri otomatik işaretleyicidir. Bu makalede, MQL5 dilinde Elliott Dalgalarının otomatik bir analizörünü oluşturma süreci anlatılmaktadır.
MQL5 Sihirbazında Kendi Expert Advisor'ınızı Oluşturun MQL5 Sihirbazında Kendi Expert Advisor'ınızı Oluşturun
Programlama dilleri bilgisi artık alım satım robotları oluşturmak için bir ön koşul değildir. Daha önce programlama becerilerinin eksikliği, kişilerin kendi alım satım stratejilerini uygulaması için aşılmaz bir engeldi, ancak MQL5 Sihirbazı'nın ortaya çıkmasıyla durum kökten değişti. Acemi yatırımcılar, programlama deneyimi eksikliği nedeniyle endişelenmeyi bırakabilirler - Expert Advisor kodu oluşturmanıza olanak tanıyan yeni Sihirbaz ile artık buna gerek kalmadı.
MQL5'te WinInet'i Kullanma.  Bölüm 2:  POST İstekleri ve Dosyalar MQL5'te WinInet'i Kullanma. Bölüm 2: POST İstekleri ve Dosyalar
Bu makalede, HTTP isteklerini kullanarak İnternet ile çalışma ve sunucu ile bilgi alışverişi yapma prensiplerini incelemeye devam edeceğiz. Burada CMqlNet sınıfının yeni işlevleri, formlardan bilgi gönderme yöntemleri ve POST isteklerini kullanarak dosya gönderme yöntemleri ve ayrıca Çerezleri kullanarak oturum açtığınız web sitelerindeki yetkilendirme açıklanmaktadır.
Expert Advisor Nasıl Sipariş Edilir ve İstenen Sonuç Nasıl Elde Edilir? Expert Advisor Nasıl Sipariş Edilir ve İstenen Sonuç Nasıl Elde Edilir?
Gereklilikler Spesifikasyonu doğru şekilde nasıl yazılır? Expert Advisor veya gösterge sipariş ederken bir programcıdan ne beklenmeli ve ne beklenmemelidir? İletişim nasıl sağlanır ve hangi anlara özellikle dikkat edilmesi gerekir? Bu makalede, birçok kişi için genellikle açık görünmeyen diğer birçok sorunun yanı sıra bunların yanıtları da verilmektedir.