Göstergeler Arası Veri Değişimi: Bu Kolaydır
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:
- 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
- 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ı
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
- Ücretsiz alım-satım uygulamaları
- İşlem kopyalama için 8.000'den fazla sinyal
- Finansal piyasaları keşfetmek için ekonomik haberler
Gizlilik ve Veri Koruma Politikasını ve MQL5.com Kullanım Şartlarını kabul edersiniz