English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Italiano
Göstergeler Arası Veri Değişimi: Bu Kolaydır

Göstergeler Arası Veri Değişimi: Bu Kolaydır

MetaTrader 5Örnekler | 10 Aralık 2021, 17:08
83 0
Alexey Subbotin
Alexey Subbotin

Giriş

Yeni başlayanları takdir etmemizin nedeni, inatla aramayı kullanmak istememeleri ve "SSS", "Yeni başlayanlar için" veya "Bu listeden soru soran kişiler cehennemde yanacak" gibi birçok konu olmasıdır. Bunların gerçek görevleri, "Nasıl yapılır?", "Mümkün mü?" gibi sorular sormak ve genellikle "İmkanı yok", "Bu imkansız" gibi yanıtlar almaktır.

Ebedi "bir daha asla, asla deme" sözü, bilim insanlarını, mühendisleri ve programcıları düşünmeye ve unutulmuş eski bir şeyleri yaratmaya teşvik ediyor.

1. Sorunun Tanımı

Örneğin, MQL4 Topluluk forumundaki konulardan birinden alıntı yapalım (Rusçadan çevrilmiştir):

 ...iki gösterge vardır (bunlara A ve B diyelim). A göstergesi, her zamanki gibi doğrudan fiyat grafiğindeki verileri kullanır ve B, A göstergesinin verilerini kullanır. Soru şu şekilde: İCustom("A göstergesi", ...) kullanmak yerine A göstergesinin (halihazırda ekte olan) verilerini kullanarak B'nin hesaplamasını dinamik olarak yapmak için ne yapmalıyım? Yani, A Göstergesinin bazı ayarlarını değiştirirsem, bunun, B göstergesindeki parametre değişikliklerine yansıtılması gerekir.

veya başka bir ifadeyle:

...Bir grafiğe Hareketli Ortalama eklediğimizi varsayalım. Veri arabelleğine doğrudan erişim nasıl sağlanır?

ve bu:

... iCustom kullanarak bazı göstergeleri çağırırsam, çağırmadan önce yüklenmiş olsa dahi yeniden yüklenir. Bunu önlemenin bir yolu var mı?

Bu tür soruların listesi uzayabilir; bu sorular yalnızca yeni başlayanlar tarafından değil, birçok kişi tarafından tekrar tekrar sorulur. Genel olarak sorun, MetaTrader'ın iCustom (MQL4) veya iCustom + CopyBuffer bağlaması (MQL5) kullanılmadan özel gösterge verilerine erişmek için hiçbir tekniğe sahip olmamasıdır. Ancak, bir sonraki başyapıt geliştirme için MQL kodunda veri alacak veya belirli bir grafikteki verileri kullanarak bir şeyler hesaplayacak bazı işlevleri yazmak çok cazip olacaktır.

Yukarıda bahsedilen standart işlevlerle çalışmak uygun değildir: Örneğin, MQL4'te, iCustom çağrıldığında her çağıran için göstergenin kopyaları oluşturulur; MQL5'te sorun, tanıtıcılar kullanılarak kısmen çözüldü, şimdi bir ana kopya için hesaplamalar yalnızca bir kez gerçekleştiriliyor. Bu, yine de her derde deva değil: Bahsettiğimiz gösterge çok fazla hesaplama kaynağı tüketiyorsa her yeniden başlatmada terminal gereksinimi neredeyse bir düzine saniye ile sanal olarak garanti edilir.

Aşağıdakiler de dahil olmak üzere, kökene erişmenin birkaç yolunu hatırlıyorum:

  • Fiziksel disk veya bellekteki dosyaları eşleme,
  • Veri alışverişi için DLL yoluyla paylaşılan bellek kullanımı,
  • Veri alışverişi ve bunların saklanması için istemci terminalinin genel değişkenlerinin kullanımı.

Ve yukarıda bahsedilenlerin varyasyonları olan diğer bazı yöntemler ve ayrıca yuvalar, ileti yuvaları vb. gibi bazı ilginç yollar vardır (Expert Advisor'larda sıklıkla kullanılan radikal bir yöntem vardır - Gösterge hesaplamalarının doğrudan Expert Advisor koduna aktarılması); ancak bu, makalemizin kapsamı dışındadır.

Yazarın bakış açısına göre, tüm bu yöntemlerde birtakım şeyler var, ancak hepsi ortak bir dezavantaja sahip: Veriler önce bir konuma kopyalanır ve daha sonra diğerlerine dağıtılır. İlk olarak, bu, bazı CPU kaynakları gerektirir ve ikincisi, iletilen verilerin uygunluğu ile ilgili yeni bir sorun yaratır (burada bunu dikkate almayacağız).

Öyleyse, sorunu tanımlamaya çalışalım:

Bir grafiğe eklenmiş göstergelerin verilerine erişim sağlayacak ve şu özelliklere sahip olacak şöyle bir ortam oluşturmak istiyoruz: Veri kopyalama olmaması

  • (ve alaka düzeyiyle ilgili sorunun), kullanmamız gerekirse mevcut
  • yöntemlerin kodunun minimum düzeyde değiştirilmesi, MQL kodunun tercih edilebilir
  • olması (elbette DLL kullanmak zorundayız, ancak yalnızca bir düzine C++ kodu dizesi kullanacağız).

Yazar, DLL'ler oluşturmak için C++ Builder'ı ve MetaTrader 4 ve MetaTrader 5 istemci terminallerini kullandı. Aşağıda sunulan kaynak kodları MQL5 ile yazılmış olup MQL4 kodları makaleye eklenmiştir; bunların aralarındaki temel farklar tartışılacaktır.

2. Diziler

İlk olarak, biraz teoriye ihtiyacımız var; zira gösterge arabellekleriyle çalışacağız ve verilerinin bellekte nasıl konumlandığını bilmemiz gerekiyor. Bu bilgiler düzgün bir şekilde belgelenmemiştir.

MQL'deki dinamik diziler değişken boyutlu yapılardır; bu nedenle dizi boyutu artmışsa ve diziden hemen sonra boş bellek yoksa MQL'nin veri yeniden tahsis sorununu nasıl çözdüğü ilginçtir. Bunu çözmenin iki yolu vardır:

  1. Kullanılabilir belleğin ek bölümünde yeni verileri yeniden tahsis etmek (ve bu dizi için tüm bölümlerin adreslerini, örneğin başvurulan bir liste kullanarak kaydetmek) veya
  2. tüm diziyi bir bütün olarak, onu tahsis etmek için yeterli olan belleğin yeni bölümüne taşımak.

İlk yöntem bazı ek sorunlara yol açar; zira bu durumda MQL derleyicisi tarafından oluşturulan veri yapılarını incelemek zorundayız. Aşağıdaki değerlendirme hala ikinci varyantı (hala daha yavaş olan) doğrulamaktadır: Dinamik bir dizi harici bir işleve (DLL'ye) iletildiğinde, sonuncusu işaretçiyi verinin ilk öğesine götürür, diğer dizi öğeleri sıralanır ve ilk dizi öğesinden hemen sonra konumlandırılır.

Referans ile iletirken, dizi verileri değiştirilebilir; bu nedenle ilk yöntemde tüm diziyi harici bir işleve aktarmak için ayrı bir bellek alanına kopyalamak ve ardından sonucu kaynak dizisine eklemek için bir sorun olduğu anlamına gelir; yani ikinci yöntemin gerektirdiği aynı işlemleri gerçekleştirin.

Bu sonuç mantıksal olarak %100 mutlak olmasa da, yine de oldukça güvenilir olarak kabul edilebilir (bu fikre dayalı olarak ürünümüzün doğru çalışmasıyla da kanıtlanmıştır).

Bu nedenle, aşağıdaki ifadelerin doğru olduğunu varsayacağız:

  • Zamanın her anında, dinamik dizilerin verileri bellekte art arda sıralı olarak bulunur ve dizi, bilgisayar belleğinin diğer bölümlerine yeniden konumlandırılabilir; ancak bu, yalnızca bir bütün olarak yapılabilir.
  • İlk dizi öğesinin adresi, bir dinamik dizi referans yoluyla bir parametre olarak harici bir işleve iletildiğinde DLL kitaplığına iletilir.

Yine de başka varsayımlar alıyoruz: Zamanın her anında ilgili göstergenin OnCalculate() (MQL4 için - start ()) işlevinin çağrıldığı zaman anlarını kastediyoruz; ayrıca, basitlik için, veri arabelleklerimizin aynı boyut ve türde çift [] olduğunu varsayıyoruz.

3. Temel Koşul

MQL, MetaQuotes Yazılımı temsilcileri tarafından defalarca belirtildiği ve onaylandığı gibi, işaretçileri desteklemez (yaygın anlamıyla işaretçi olmayan nesne işaretçileri olarak adlandırılanlar hariç). Öyleyse bunun doğru olup olmadığını kontrol edelim.

İşaretçi nedir? Bu yalnızca yıldız işaretli bir tanımlayıcı değil, aynı zamanda bilgisayar belleğindeki bir hücrenin adresidir. Peki hücre adresi nedir? Belirli bir başlangıçtan başlayan sıra numarasıdır. Ve son olarak, sayı nedir? Bilgisayar belleğinin bir hücresine karşılık gelen bir tamsayıdır. Ve bir işaretçiyle neden tam sayı gibi çalışamıyoruz? Evet, bunu yapabiliriz; zira MQL programları tamsayılarla mükemmel bir şekilde çalışır!

Fakat bir işaretçiyi bir tamsayıya nasıl dönüştürebilirim? Dinamik Bağlantı Kitaplığı bize yardımcı olacak, C++ tür dönüşümü olanaklarını kullanacağız. C++ işaretçilerinin dört baytlık veri türü olması nedeniyle, bizim durumumuzda tamsayıyı dört baytlık veri türü olarak kullanmak uygundur.

İşaret biti önemli değildir ve bunu dikkate almayacağız (1'e eşitse, bu yalnızca tamsayının negatif olduğu anlamına gelir); temel nokta, tüm işaretçi bitlerini değiştirmeden tutabilmemizdir. Elbette, işaretsiz tamsayılar kullanabiliriz, ancak MQL5 ve MQL4'ün kodlarının benzer olması daha iyi olacaktır; zira MQL4 işaretsiz tamsayı sağlamaz.

Evet,

extern "C" __declspec(dllexport) int __stdcall GetPtr(double *a)
{
        return((int)a);
}

Vay canına, şimdi dizinin başlangıç adresinin değerine sahip long türünde bir değişkenimiz var! Şimdi i-th dizi öğesinin değerini nasıl okuyacağımızı öğrenmemiz gerekiyor:

extern "C" __declspec(dllexport) double __stdcall GetValue(int pointer,int i)
{
        return(((double*) pointer)[i]);
}

... ve değeri yazın (bizim durumumuzda bu, gerçekten gerekli değil, yine de ...)

extern "C" __declspec(dllexport) void __stdcall SetValue(int pointer,int i,double value)
{
        ((double*) pointer)[i]=value;
}

Hepsi bu kadar. Artık MQL'de işaretçilerle çalışabiliriz.

4. Paket

Sistemin çekirdeğini oluşturduk; şimdi onu MQL programlarında uygun şekilde kullanılmak üzere hazırlamamız gerekiyor. Yine de, estetiğe önem veren kişiler üzülmesin; söyleyeceklerim henüz bitmedi.

Bunu yapmanın milyarlarca yolu var. Biz, aşağıdaki yolu seçeceğiz. İstemci terminalinin ayrı MQL programları arasında veri alışverişi için özel bir özelliği olduğunu hatırlayalım - Genel Değişkenler. En doğal yol, bunları, erişeceğimiz gösterge arabelleklerine işaretçileri saklamak için kullanmaktır. Bu tür değişkenleri bir tanımlayıcı tablosu olarak ele alacağız. Her tanımlayıcı, aşağıdaki gibi bir dizeyle temsil edilen bir ada sahip olacaktır:

string_identifier#buffer_number#symbol#period#buffer_length#indexing_direction#random_number,

ve değeri, bilgisayar belleğindeki karşılık gelen arabellek işaretçisinin tamsayı temsiline eşit olacaktır.

Tanımlayıcı alanları hakkında bazı ayrıntılar vardır.

  • string_identifier – Herhangi bir dize (örneğin, göstergenin adı - short_name değişkeni kullanılabilir vb.) gerekli işaretçiyi aramak için kullanılacaktır; bununla, bazı göstergelerin tanımlayıcıları aynı tanımlayıcıyla kaydedeceğini ve alanı kullanarak aralarında ayrım yapacağını kastediyoruz:
  • buffer_number – arabellekleri ayırt etmek için kullanılacaktır,
  • buffer_length – sınırları kontrol etmek için buna ihtiyacımız var; aksi takdirde istemci terminalinin ve Windows'un çökmesine sebebiyet verebiliriz Mavi Ekran Hatası :),
  • sembol, dönem – grafik penceresini belirtmek için sembol ve dönem,
  • ordering_direction – dizi öğelerinin sıralanma yönünü belirtir: 0 – olağan, 1 – dönüş (AS_SERIES bayrağı true),
  • random_number – bir istemci terminaline bağlı farklı pencerelere veya farklı parametre kümelerine sahip olsa da bir göstergenin birkaç kopyası varsa kullanılacaktır (birinci ve ikinci alanların aynı değerlerini ayarlayabilirler; bu nedenle onları bir şekilde ayırt etmemiz gerekir) - Bu, belki en iyi çözüm değil ama işe yarıyor.

İlk olarak, tanımlayıcıyı kaydetmek ve silmek için bazı işlevlere ihtiyacımız var. İşlevin ilk dizesine bir göz atın - Genel değişkenler listesindeki eski tanımlayıcıları silmek için UnregisterBuffer() işlevinin çağrısı gereklidir.

Her yeni çubukta arabellek boyutu 1 değer artacaktır; bu nedenle RegisterBuffer() işlevini çağırmamız gerekir. Ve arabellek boyutu değiştiyse, tabloda yeni tanımlayıcı oluşturulur (boyutunun bilgisi adında bulunur), eski tanımlayıcı tabloda kalacaktır. Bu, bu nedenle kullanılır.

void RegisterBuffer(double &Buffer[], string name, int mode) export
{
   UnregisterBuffer(Buffer);                    //first delete the variable just in case
   
   int direction=0;
   if(ArrayGetAsSeries(Buffer)) direction=1;    //set the right ordering_direction

   name=name+"#"+mode+"#"+Symbol()+"#"+Period()+"#"+ArraySize(Buffer)+"#"+direction;
   int ptr=GetPtr(Buffer);                      // get the buffer pointer

   if(ptr==0) return;
   
   MathSrand(ptr);                              //it's convenient to use the pointer value instead
                                                //of the current time for the random numbers base 
   while(true)
   {
      int rnd=MathRand();
      if(!GlobalVariableCheck(name+"#"+rnd))    //check for unique name - we assume that 
      {                                         //nobody will use more RAND_MAX buffers :)
         name=name+"#"+rnd;                     
         GlobalVariableSet(name,ptr);           //and write it to the global variable
         break;
      }
   }   
}
void UnregisterBuffer(double &Buffer[]) export
{
   int ptr=GetPtr(Buffer);                      //we will register by the real address of the buffer
   if(ptr==0) return;
   
   int gt=GlobalVariablesTotal();               
   int i;
   for(i=gt-1;i>=0;i--)                         //just look through all global variables
   {                                            //and delete our buffer from all places
      string name=GlobalVariableName(i);        
      if(GlobalVariableGet(name)==ptr)
         GlobalVariableDel(name);
   }      
}

Burada ayrıntılı yorumlarda bulunmak gereksiz; biz yalnızca birinci işlevin genel değişken listesinde yukarıdaki biçimde yeni bir tanımlayıcı oluşturduğu gerçeğini özetledik; ikincisi tüm genel değişkenleri arar, değişkenleri siler ve işaretçiye eşit değere sahip tanımlayıcıları siler.

Şimdi ikinci görevi ele alalım; göstergeden veri elde etmek. Verilere doğrudan erişimi uygulamadan önce, karşılık gelen bir tanımlayıcı bulmamız gerekir. Bu, aşağıdaki işlev kullanılarak çözülebilir. Algoritması şu şekilde olacaktır: Tüm genel değişkenleri inceleriz ve tanımlayıcıda belirtilen alan değerlerinin olup olmadığını kontrol ederiz.

Bulduysak, adını son parametre olarak iletilen diziye ekleriz. Sonuç olarak işlev, arama koşullarıyla eşleşen tüm bellek adreslerini döndürür. Döndürülen değer, bulunan tanımlayıcıların sayısıdır.

int FindBuffers(string name, int mode, string symbol, int period, string &buffers[]) export
{
   int count=0;
   int i;
   bool found;
   string name_i;
   string descriptor[];
   int gt=GlobalVariablesTotal();

   StringTrimLeft(name);                                    //trim string from unnecessary spaces
   StringTrimRight(name);
   
   ArrayResize(buffers,count);                              //reset size to 0

   for(i=gt-1;i>=0;i--)
   {
      found=true;
      name_i=GlobalVariableName(i);
      
      StringExplode(name_i,"#",descriptor);                 //split string to fields
      
      if(StringFind(descriptor[0],name)<0&&name!=NULL) found=false; //check each field for the match
                                                                    //condition
      if(descriptor[1]!=mode&&mode>=0) found=false;
      if(descriptor[2]!=symbol&&symbol!=NULL) found=false;
      if(descriptor[3]!=period&&period>0) found=false;
      
      if(found)
      {
         count++;                                           //conditions met, add it to the list
         ArrayResize(buffers,count);
         buffers[count-1]=name_i;
      }
   }
   
   return(count);
}

İşlev kodundan gördüğümüz gibi, bazı arama koşulları atlanabilir. Örneğin, ad olarak NULL iletirsek, string_identifier kontrolü yapılmayacaktır. Aynısı diğer alanlar için de geçerlidir: mod<0, sembol:=BOŞ veya dönem<=0. Bu, tanımlayıcılar tablosundaki arama seçeneklerinin genişletilmesine olanak tanır.

Örneğin, tüm Hareketli Ortalama göstergelerini tüm grafik pencerelerinde veya yalnızca M15 vb. dönemi içeren EURUSD grafiklerinde bulabilirsiniz. Ek açıklama: string_identifier kontrolü, mutlak eşitlik kontrolü yerine StringFind() işlevi tarafından gerçekleştirilir. Bu, tanımlayıcının bir kısmı ile arama yapma imkanı sağlamak için yapılır (örneğin, birkaç gösterge “MA(xxx)” türünde bir dizi ayarladığında, arama “MA” alt dizisi ile gerçekleştirilebilir - Sonuç olarak, kaydedilen tüm Hareketli Ortalamaları bulacağız).

Ayrıca StringExplode(s dizesi, dize ayırıcı, dize ve sonuç[]) işlevini de kullandık. Ayırıcıyı kullanarak belirtilen s dizesini alt dizelere böler ve sonuçları sonuç dizisine yazar.

void StringExplode(string s, string separator, string &result[])
{
   int i,pos;
   ArrayResize(result,1);
   
   pos=StringFind(s,separator); 
   if(pos<0) {result[0]=s;return;}
   
   for(i=0;;i++)
   {
      pos=StringFind(s,separator); 
      if(pos>=0)
      {
         result[i]=StringSubstr(s,0,pos);
         s=StringSubstr(s,pos+StringLen(separator));
      }
      else break;
      ArrayResize(result,ArraySize(result)+1);
   }
}

Gerekli tüm tanımlayıcıların listesini aldığımızda, göstergeden veri elde edebiliriz:

double GetIndicatorValue(string descriptor, int shift) export
{
   int ptr;
   string fields[];
   int size,direction;
   if(GlobalVariableCheck(descriptor)>0)               //check that the descriptor is valid
   {                
      ptr = GlobalVariableGet(descriptor);             //get the pointer value
      if(ptr!=0)
      {
         StringExplode(descriptor,"#",fields);         //split its name to fields
         size = fields[4];                             //we need the current array size
         direction=fields[5];                                 
         if(direction==1) shift=size-1-shift;          //if the ordering_direction is reverse
         if(shift>=0&&shift<size)                      //check for its validity - to prevent crashes
            return(GetValue(MathAbs(ptr),shift));      //ok, return the value
      }   
   } 
   return(EMPTY_VALUE);                                //overwise return empty value 
}

Gördüğünüz gibi, bu, DLL'den GetValue() işlevinin bir paketidir. Dizi sınırları için tanımlayıcı geçerliliğini kontrol etmek ve gösterge arabellek sıralamasını göz önünde bulundurmak gerekir. Başarısız olursa, işlev EMPTY_VALUE değerini döndürür.

Gösterge arabelleğinin değiştirilmesi benzerdir:

bool SetIndicatorValue(string descriptor, int shift, double value) export
{
   int ptr;
   string fields[];
   int size,direction;
   if(GlobalVariableCheck(descriptor)>0)               //check for its validity
   {                
      ptr = GlobalVariableGet(descriptor);             //get descriptor value
      if(ptr!=0)
      {
         StringExplode(descriptor,"#",fields);         //split it to fields
         size = fields[4];                             //we need its size
         direction=fields[5];                                 
         if(direction==1) shift=size-1-shift;          //the case of the inverse ordering
         if(shift>=0&&shift<size)                      //check index to prevent the crash of the client terminal
         {
            SetValue(MathAbs(ptr),shift,value);
            return(true);
         }   
      }   
   }
   return(false);
}

Tüm değerler doğruysa, DLL'den SetValue() işlevini çağırır. Döndürülen değer, değişikliğin sonucuna karşılık gelir: Başarılıysa true ve bir hata durumunda false.

5. Nasıl Çalıştığını Kontrol Etme

Şimdi kontrol etmeye çalışalım. Hedef olarak, standart paketteki Ortalama Gerçek Aralık (ATR) değerini kullanacağız ve değerlerini başka bir gösterge penceresine kopyalamak için kodunda neleri değiştirmemiz gerektiğini göstereceğiz. Ayrıca arabellek verilerinin veri değişikliğini test edeceğiz.

Yapmamız gereken ilk şey, OnCalculate() işlevine bazı kod satırları eklemek:

//+------------------------------------------------------------------+

//| Average True Range                                               |
//+------------------------------------------------------------------+
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 &TickVolume[],
                const long &Volume[],
                const int &Spread[])
  {
   if(prev_calculated!=rates_total)
      RegisterBuffer(ExtATRBuffer,"ATR",0);
…

Gördüğümüz gibi, yeni arabellek kaydı, her yeni çubuklara sahip olduğumuzda yapılmalıdır; bu durum zaman geçtikçe (normalde) veya ek geçmiş verilerinin indirilmesi durumunda ortaya çıkar.

Bunun yanında, göstergenin çalışması bittikten sonra tanımlayıcıları tablodan silmemiz gerekir. Bu nedenle deinit olay işleyicisine bir kod eklemek gereklidir (ancak bunun void türünü döndürmesi ve bir const int reason giriş parametresine sahip olması gerektiğini unutmayın):

void OnDeinit(const int reason)
{
   UnregisterBuffer(ExtATRBuffer);
}

İstemci terminali grafik penceresindeki (Şek. 1) değiştirilmiş göstergemiz ve genel değişkenler listesindeki tanımlayıcısı (Şek. 2) şu şekildedir:

 

Şek. 1. Ortalama Gerçek Aralık

Şek. 2. İstemci terminalinin genel değişkenleri listesinde oluşturulan tanımlayıcı

Şek. 2. İstemci terminalinin genel değişkenleri listesinde oluşturulan tanımlayıcı

Bir sonraki adım, ATR verilerine erişim sağlamaktır. Aşağıdaki kodu kullanarak yeni bir gösterge oluşturalım (bunu test olarak adlandıralım):

//+------------------------------------------------------------------+
//|                                                         test.mq5 |
//|                                             Copyright 2009, alsu |
//|                                                 alsufx@gmail.com |
//+------------------------------------------------------------------+
#property copyright "2009, alsu"
#property link      "alsufx@gmail.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_plots   1

#include <exchng.mqh>

//---- plot ATRCopy
#property indicator_label1  "ATRCopy"
#property indicator_type1   DRAW_LINE
#property indicator_color1  Red
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- indicator buffers
double ATRCopyBuffer[];

string atr_buffer;
string buffers[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+

int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,ATRCopyBuffer,INDICATOR_DATA);
//---
   return(0);
  }

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[])
  {
//---
   int found=FindBuffers("ATR",0,NULL,0,buffers);   //search for descriptors in global variables
   if(found>0) atr_buffer=buffers[0];               //if we found it, save it to the atr_buffer
   else atr_buffer=NULL;
   int i;
   for(i=prev_calculated;i<rates_total;i++)
   {
      if(atr_buffer==NULL) break;
      ATRCopyBuffer[i]=GetIndicatorValue(atr_buffer,i);  //now it easy to get data
      SetIndicatorValue(atr_buffer,i,i);                 //and easy to record them
   }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

Gördüğümüz gibi, bunu yapmak zor değil. Tanımlayıcılar listesini bir alt diziye göre alın ve işlevlerimizi kullanarak gösterge arabelleğinin değerlerini okuyun. Ve son olarak, oraya bir şeyler yazalım (örneğimizde, dizi öğesi indeksine karşılık gelen değerleri yazıyoruz).

Test göstergesi çalışıyor (hedef ATR'miz grafiğe eklenmiş olmalıdır). Şek. 3'te gördüğümüz gibi, ATR değerleri aşağıdaki alt pencerededir (test göstergemizde), onun yerine düz çizgiyi görüyoruz; aslında, bu, dizi indekslerine karşılık gelen değerlerle dolu.

 

Şek. 3. Test.mq5 işleminin sonucu

İşte ustalık - Hepsi bu kadar:)

6. Geriye Dönük Uyumluluk

Yazar, MQL5'te yazılmış MQ4 standartlarına maksimum düzeyde yakın olan bir kitaplık oluşturmaya çalıştı. Dolayısıyla, MQL4'te kullanılabilmesi için yalnızca birkaç değişiklik yapılması gerekiyor.

Özellikle, yalnızca MQL5'te görünen dışa aktar anahtar sözcüğünü kaldırmalı ve dolaylı tür dönüşümünü kullandığımız kodu değiştirmelisiniz (MQL4 o kadar esnek değildir). Aslında, göstergelerdeki işlevlerin kullanımı tamamen aynıdır, farklılıklar yalnızca yeni çubuğun yönteminde ve geçmiş ekleme kontrolündedir.

Sonuç

Avantajları hakkında birkaç şey söyleyeceğim.

Görünüşe göre, bu makalede anlatılan yöntemin kullanımı yalnızca asıl amacı ile sınırlı kalmayabilir. Yüksek hızlı kademeli göstergelerin oluşturulmasına ek olarak, kitaplık, geçmiş test durumları da dahil olmak üzere Expert Advisor'lara başarıyla uygulanabilir. Yazar için diğer uygulamaları hayal etmek zor ama sanırım, sevgili kullanıcılar bu doğrultuda çalışmaktan dolayı mutluluk duyacaklar.

Aşağıdaki teknik anların gerekli olabileceğine inanıyorum:

  • Tanımlayıcılar tablosunu iyileştirme (özellikle pencereleri belirtme),
  • Tanımlayıcı oluşturma sıralaması için ek tablo geliştirme; bu, alım satım sistemlerinin istikrarı için önemli olabilir.

Ayrıca kitaplığı geliştirmek için başka öneriler almaktan da memnuniyet duyacağım.

Gerekli tüm dosyalar makaleye eklenmiştir. MQL5, MQL4 için kodlar ve exchng.dll kitaplığının kaynak kodu sunulmuştur. Dosyalar, istemci terminal klasörlerinde olması gerektiği gibi aynı konumlara sahiptir.

Feragatname

Bu makalede sunulan materyaller, dikkatsiz kullanılmaları halinde bilgisayarınızda çalışan yazılıma zarar verebilecek programlama tekniklerini açıklamaktadır. Ekli dosyalar yalnızca riski size ait olmak koşuluyla kullanılmalıdır ve şimdiye kadar açıklanmasa da olası yan etkilere yol açabilir.

Teşekkür

Yazar, MQL4.Topluluk https://forum.mql4.com forumunda ileri sürülen sorunları ve kullanıcılarının fikirlerini ele almıştır: igor.senych, satop, bank, StSpirit, TheXpert, jartmailru, ForexTools, marketeer, IlyaA – Liste eksik ise üzgünüm.

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

Ekli dosyalar |
cpp.zip (0.36 KB)
mql4.zip (39.29 KB)
mql521x.zip (46.87 KB)

Bu yazarın diğer makaleleri

MQL5'te Gösterge Emisyonlarının Çizimi MQL5'te Gösterge Emisyonlarının Çizimi
Bu makalede, piyasa araştırmasına yeni bir yaklaşım olan göstergelerin emisyonunu ele alacağız. Emisyon hesaplaması, farklı göstergelerin kesişimine dayanmaktadır: Her tick'ten sonra farklı renk ve şekillerde daha fazla nokta belirir. Bulutsular, bulutlar, parçalar, çizgiler, kavisler vb. gibi çok sayıda küme oluştururlar. Bu şekiller, piyasa fiyatlarının hareketini etkileyen görünmez kavisleri ve güçleri tespit etmeye yardımcı olur.
Veri Değişimi Nasıl Yapılır? 10 Dakikada MQL5 için bir DLL Veri Değişimi Nasıl Yapılır? 10 Dakikada MQL5 için bir DLL
Artık pek çok geliştirici basit bir DLL'nin nasıl yazılacağını ve farklı sistem bağlayıcılarının özel özelliklerinin neler olduğunu hatırlamıyor. Birkaç örnek kullanarak, basit DLL oluşturma işleminin tamamını 10 dakikada göstermeye çalışacağım ve ayrıca bağlayıcı uygulamamızın bazı teknik ayrıntılarını ele alacağım. Visual Studio'da DLL oluşturma işlemini adım adım farklı değişken türlerini (sayılar, diziler, dizeler vb.) değiştirme örnekleriyle göstereceğim. Ayrıca, istemci terminalinizi özel DLL'lerdeki çökmelerden nasıl koruyacağınızı açıklayacağım.
WCF Hizmetlerini Kullanarak МetaTrader 5'ten .NET Uygulamalarına Fiyat Tekliflerini Dışa Aktarma WCF Hizmetlerini Kullanarak МetaTrader 5'ten .NET Uygulamalarına Fiyat Tekliflerini Dışa Aktarma
MetaTrader 5'ten kendi uygulamanıza fiyat tekliflerinin dışa aktarılmasını düzenlemek ister misiniz? MQL5-DLL birleşimi, bu tür çözümler oluşturmanıza olanak tanır! Bu makalede size fiyat tekliflerini MetaTrader 5'ten .NET'te yazılmış uygulamalara dışa aktarmanın yollarından biri gösterilecektir. Benim için bu platformu kullanarak fiyat tekliflerinin dışa aktarımını uygulamak daha ilginç, mantıklı ve kolaydı. Ne yazık ki, sürüm 5 hala .NET'i desteklemiyor; bu nedenle eski günlerde olduğu gibi .NET destekli win32 dll'yi ara katman olarak kullanacağız.
Fiyat Histogramı (Piyasa Profili) ve MQL5'te uygulanması Fiyat Histogramı (Piyasa Profili) ve MQL5'te uygulanması
Piyasa Profili, gerçekten parlak bir düşünür olan Peter Steidlmayer tarafından geliştirildi. Tamamen farklı model kümelerine yol açan "yatay" ve "dikey" piyasa hareketleri hakkındaki bilgilerin alternatif temsilini kullanmayı önerdi. Piyasanın altında yatan bir nabzın ya da denge ve dengesizlik döngüsü adı verilen temel bir modelin olduğunu varsaydı. Bu makalede, Piyasa Profilinin basitleştirilmiş bir modeli olan Fiyat Histogramını ele alacak ve MQL5'te uygulanmasını anlatacağım.