
Yeni Başlayanlar için MQL5’te Uzman Danışman Yazmak İçin Adım Adım Açıklamalı Kılavuz
Giriş
Bu makale, yeni MQL5 dilinde basit Uzman Danışmanların nasıl yazılacağını öğrenmek isteyen yeni başlayanlara yöneliktir. Önce EA’mızın (Uzman Danışman) ne yapmasını istediğimizi tanımlayarak başlayacağız, sonra EA’nın bunu nasıl yapmasını istediğimize geçeceğiz.
1. Alım-Satım Stratejisi
EA’mız şunları yapacak:
- Belirli bir koşul (veya koşullar) karşılandığında belirli bir göstergeyi izleyecek, karşılanan mevcut koşula göre bir alım satım işlemi (Kısa/Satış veya Uzun/Alış) yapacaktır.
Buna alım satım stratejisi denir. Bir EA yazmadan önce, ilk olarak EA’da otomatikleştirmek istediğiniz stratejiyi geliştirmelisiniz. Bu yüzden EA’da geliştirmek istediğimiz stratejiyi yansıtması için yukarıdaki ifadeyi değiştirelim.
8 dönemli ve Hareketli Ortalama dediğimiz bir göstergeyi kullanacağız (İstediğiniz dönemi seçebilirsiniz ama bizim stratejimiz için 8’i kullanacağı)
- Hareketli Ortalama-8 (tartışmamızın kolaylığı için buna MA-8 diyeceğim) yükseldiğinde ve fiyat bunun üstünde kapandığında EA’mızın Uzun (Alış) işlem yapmasını, MA-8 düştüğünde ve fiyat bunun altında kapandığında Kısa (Satış) işlem yapmasını istiyoruz.
- Ayrıca piyasanın rağbet görüp görmediğini belirlememize yardımcı olması için dönem 8 ile Average Directional Movement (ADX) adlı başka bir göstergeyi de kullanacağız. Bunu yapıyoruz çünkü alım satıma yalnızca piyasa yükseldiğinde girmek ve piyasa dalgalandığında da (yükselmediğinde) sakin kalmak istiyoruz. Bunu başarmak için işlemimizi (Alış veya Satış) yalnızca yukarıdaki koşullar karşılandığında ve ADX değeri 22’den yüksek olduğunda yapacağız. ADX 22’den yüksekse ama düşüyorsa veya ADX 22’den düşükse B koşulu karşılansa bile alım satım yapmayacağız.
- Ayrıca 30 pip’lik Zararı durdur (Stop loss) belirleyerek kendimizi koruyacağız ve Kâr hedefimiz için 100 pip kâra odaklanacağız.
- Aynı zamanda EA’mızın yalnızca yeni bir çubuk oluşturulduğunda Alış/Satış fırsatlarına bakmasını istiyoruz ve Alış koşulları karşılandığında ve zaten açılmamışsa bir Alış pozisyonu açtığımızdan; Satış koşulları karşılandığında da ve zaten açılmamışsa bir Satış pozisyonu açtığımızdan emin olmak istiyoruz.
Artık stratejimizi geliştirdiğimize göre şimdi kodumuzu yazmaya başlayabiliriz.
2. Bir Uzman Danışmanın Yazılması
2.1 MQL5 Sihirbazı
MetaQuotes Language Editor 5’i açarak başlayalım. Ardından Ctrl+N tuşlarına basın veya Menü çubuğunda Yeni düğmesine tıklayın
Şekil 1. Yeni bir MQL5 belgesine başlamak
MQL5 Sihirbazı penceresinde Uzman Danışmanı seçin ve Şekil 2’de gösterildiği gibi “Sonraki” üzerine tıklayın:
Şekil 2. Program türünü seçmek
Sonraki pencerede EA’nıza vermek istediğiniz Adı, Ad kutusuna yazın. Bu örnekte ben My_First_EA yazdım. Ardından kendi adınızı Yazar kutusuna ve web sitenizin adresini ya da e-posta adresinizi Bağlantı kutusuna (varsa) yazabilirsiniz.
Şekil 3. Uzman Danışmanın genel özellikleri
Değerlerin hangilerinin bize en iyi sonucu verebileceğini görmek için EA’mızın bazı parametrelerini değiştirebiliyor olmak istediğimizden bunları “Ekle” düğmesine tıklayarak eklemeliyiz.
Şekil 4. EA giriş parametrelerini ayarlamak
EA’mızda Zararı Durdur, Kâr Al, ADX Dönemi ve Hareketli Ortalama Dönemi ayarlarıyla deney yapabiliyor olmak istiyoruz bu yüzden bunları bu noktada tanımlayacağız.
Ad kısmının altına Çift Tıklayın ve parametrenin adını yazın, ardından parametrenin veri türünü seçmek için Tür altına çift tıklayın ve Başlangıç değeri altına çift tıklayarak parametrenin başlangıç değerini yazın.
Bitirdikten sonra şu şekilde görünmelidir:
Şekil 5. EA giriş parametrelerinin veri türleri
Yukarıda gördüğünüz gibi tüm parametreler için tam sayı (int) veri türünü seçtim. Biraz veri türleri hakkında konuşalım.
- char: char türü 1 baytlık bellek işgal eder (8 bit) ve ikili notasyon 2^8=256 değerlerinin ifade edilmesini sağlar. char türü negatif ve pozitif değerlerin her ikisini de barındırabilir. Değer aralığı -128'den 127'ye kadardır.
- uchar : Uchar tam sayı türü, char türü gibi 1 baytlık bellek işgal eder ama onun tersine uchar yalnızca pozitif değerler içindir. En küçük değeri sıfırdır, en büyük değeri ise 255'tir. Uchar türünün adındaki ilk harf olan u unsigned (işaretsiz) kelimesinin kısaltmasıdır.
- short: Short türünün boyutu 2 bayttır (16 bit) ve bu yüzden 2 ila 16 kuvvete eşit değerler aralığını ifade etmeyi mümkün kılar: 2^16 = 65 536. Short türü bir işaret türü olduğundan hem pozitif hem de negatif değerleri içerir, değerler aralığı -32 768 ve 32 767’dir.
- ushort: İşaretsiz short türü, ushort türüdür ve yine 2 bayt boyutundadır. En küçük 0, en büyük değer ise 65 535'tir.
- int : Int türünün boyutu 4 bayttır (32 bit). En küçük değer -2 147 483 648, en büyük değer ise 2 147 483 647’dir.
- uint : İşaretsiz tam sayı tipi uint'tir. 4 baytlık bellek işgal eder ve 0 ile 4 294 967 295 arası tam sayıların ifade edilmesini sağlar.
- long : Long türünün boyutu 8 bayttır (64 bit). En küçük değer -9 223 372 036 854 775 808, en büyük değer ise 9 223 372 036 854 775 807'dir.
- ulong : Ulong türü de 8 bayt kaplar ve 0 ile 18 446 744 073 709 551 615 arası değerleri saklayabilir.
Çeşitli veri türlerinin yukarıdaki açıklamasına göre, işaretsiz tam sayı türleri negatif değerleri saklamak için tasarlanmamıştır, negatif bir değer belirlemeyi denemek beklenmedik sonuçlara yol açabilir. Örneğin negatif değerleri saklamak istiyorsanız bunları işaretsiz türlerin içinde (yani uchar, uint, ushort, ulong) içinde saklayamazsınız.
EA’mıza geri dönelim. Veri türlerine bakarsak, char veya uchar veri türlerini kullanmamız gerektiği konusunda benimle aynı fikirdesinizdir, çünkü bu parametrelerde saklamak istediğimiz veri, sırasıyla 127 veya 255’ten düşüktür. İyi bir bellek yönetimi için yapılması gereken en doğru şey budur. Ancak tartışmamızın yararı açısından int türünü kullanacağız.
Gereken tüm parametreleri ayarladıktan sonra Bitti düğmesine tıklayın ve MetaQuotes Editor sonraki şekilde gösterildiği gibi sizin için kodun iskeletini oluşturacaktır.
Daha iyi anlamak için şimdi kodu çeşitli parçalara ayıralım.
Kodun üst kısmı (başlık), EA’nın özelliğinin tanımlandığı bölümdür. Şekil 3’te MQL5 Sihirbazında girdiğiniz değerlerin olduğunu görebilirsiniz.
Kodun bu kısmında açıklama (EA’nın kısa metin açıklaması) gibi ek parametreleri tanımlayabilir, sabitleri bildirebilir, ek dosyaları veya içe aktarma fonksiyonlarını dahil edebilirsiniz.
Bir ifade # sembolüyle başlıyorsa önişlemci direktifi olarak adlandırılır ve noktalı virgül “;” ile bitmez, diğer önişlemci örnekleri arasında şunlar yer alır:
#define :
#define direktifi sabitlerin deklarasyonu için kullanılır. Şu formda yazılır:
#define tanımlayıcı token_string
Bu, kodunuzdaki tanımlayıcıyı her seferinde token_string değeriyle değiştirir.
Örnek:
#define ABC 100
#define COMPANY_NAME "MetaQuotes Software Corp."
COMPANY_NAME geçtiğinde bunu "MetaQuotes Software Corp." dizgisi ile değiştirecektir veya ABC her geçtiğinde kodunuzda bunu char (veya tam sayı) 100 ile değiştirecektir.
Önişlemci direktifleri hakkında daha fazla bilgiyi MQL5 Kılavuzunda bulabilirsiniz. Şimdi tartışmamıza devam edelim.
Kodumuzun başlığının ikinci kısmı giriş parametreleri kısmıdır:
Bu bölümde EA’mızda kullanılacak tüm parametreleri belirtiyoruz. Bunlara, EA’mızda yazacağımız tüm fonksiyonlar tarafından kullanılacak tüm değişkenler dahildir.
Bu seviyede bildirilen değişkenlere Global Değişkenler denir çünkü EA’mızda bunlara ihtiyaç duyan her fonksiyon, bunlara erişebilir. Giriş parametreleri yalnızca EA’mızın dışında değiştirilebilecek parametrelerdir. Ayrıca bu bölümde EA’mızın gidişatı sırasında değiştirebileceğimiz ama EA’mızın dışında kullanılamayacak diğer değişkenleri de ifade edebiliriz.
Şimdi sıra EA’yı başlatma fonksiyonuna geldi. EA başlatıldığına ilk çağrılan veya bir grafiğe eklendiğinde ve sadece bir kez çağrılan fonksiyondur.
EA’mızın çok iyi çalıştığından emin olmak amacıyla bazı önemli kontrolleri yapmak için burası en uygun kısımdır.
Grafikte EA’mızın çalışmasına yetecek çubuk olup olmadığını bilmeye karar verebiliriz vs.
Ayrıca göstergelerimiz (ADX ve Hareketli Ortalama göstergeleri) için kullanacağımız tanıtıcı değerleri edinmek için en uygun yerdir.
EA grafikten kaldırıldığından dolayı OnDeinit fonksiyonu çağrılır.
EA’mız için bu bölümde başlatma sırasında Göstergelerimiz için oluşturulan tanıtıcı değerleri serbest bırakacağız.
Bu fonksiyon, bir sembol için yeni bir teklif alındığında oluşturulan Yeni Tik olayını işler.
Unutmayın ki istemci terminalinde Uzman Danışmanlarının kullanımına izin verilmediyse (“Otomatik Alım Satım” düğmesi) Uzman Danışman alım satım işlemleri yapamaz.
Şekil 6. Otomatik alım satım etkindir
Daha önceden geliştirilmiş alım satım stratejimizi uygulayacak kodlarımızın çoğu bu bölümde yazılacaktır.
EA’mızın kodunun çeşitli kısımlarına baktığımıza göre iskelete biraz kas eklemeye başlayalım.
2.2 GİRİŞ PARAMETRELERİ BÖLÜMÜ
//--- input parameters input int StopLoss=30; // Stop Loss input int TakeProfit=100; // Take Profit input int ADX_Period=8; // ADX Period input int MA_Period=8; // Moving Average Period input int EA_Magic=12345; // EA Magic Number input double Adx_Min=22.0; // Minimum ADX Value input double Lot=0.1; // Lots to Trade //--- Other parameters int adxHandle; // handle for our ADX indicator int maHandle; // handle for our Moving Average indicator double plsDI[],minDI[],adxVal[]; // Dynamic arrays to hold the values of +DI, -DI and ADX values for each bars double maVal[]; // Dynamic array to hold the values of Moving Average for each bars double p_close; // Variable to store the close value of a bar int STP, TKP; // To be used for Stop Loss & Take Profit values
Gördüğünüz gibi daha fazla parametre ekledik. Yeni parametreleri tartışmaya devam etmeden önce şimdi görebileceğiniz bir şeyi irdeleyelim. İki ileri bölü işareti “//” kodlarımıza yorumlar eklememizi sağlar. Yorumlar sayesinde değişkenlerimizin neyi temsil ettiğini veya kodumuzda o anda ne yaptığımızı bilebiliriz. Ayrıca kodumuzu daha iyi anlamamızı sağlar. Yorumları yazmanın iki temel yolu vardır:
// Diğer Parametreler …
Bu tek satırlık yorumdur
/*
Bu çok satırlı yorumdur
*/
Bu çok satırlı yorumdur. Çok satırlı yorumlar /* sembol çifti ile başlar ve */ sembol çifti ile biter.
Kodunuz derlenirken derleyici, tüm yorumları yok sayar.
Giriş parametreleri için tek satırlık yorumları kullanmak, EA kullanıcılarımızın bu parametrelerin neyi temsil ettiğini anlamasını sağlamanın güzel bir yoludur. EA Giriş özelliklerinde kullanıcılarımız parametrenin kendisini görmeyecek, bunun yerine aşağıda gösterildiği gibi yorumları görecektir:
Şekil 7. Uzman Danışman için giriş parametreleri
Şimdi kodumuza geri dönelim...
EA’mız için ilave parametreler eklemeye karar verdik. EA_Magic EA’mızın gerçekleştirdiği tüm emirler için sihirli sayıdır. En küçük ADX değeri (Adx_Min) double veri türü olarak bildirilir. Kayan nokta sabitlerini saklamak için double veri kullanılır ve bunun içinde tam sayı kısmı, ondalık noktası ve kesirli kısım bulunur.
Örnek:
double mysum = 123.5678;
double b7 = 0.09876;
İşlem lotu (Lot) alım satım yapmak istediğimiz finansal enstrümanın hacmini temsil eder. Ardından kullanacağımız diğer parametreleri ifade ettik:
adxHandle ADX göstergesi tanıtıcı değerini saklamak için kullanılır, maHandle ise Hareketli Ortalama göstergesini saklamak içindir. plsDI[], minDI[], adxVal[], grafikteki her çubuk için +DI, -DI ve ana ADX (ADX Göstergesinin) değerlerini tutacak olan dinamik dizilerdir. maVal[] , grafikteki her çubuk için Hareketli Ortalama göstergesinin değerlerini tutacak olan dinamik bir dizidir.
Bu arada, dinamik diziler nedir? Dinamik dizi, boyut olmadan bildirilen bir dizidir. Diğer bir deyişle, köşeli parantez çiftinde belirtilen bir değer yoktur. Diğer yandan statik dizi ise ifade etme noktasında tanımlanan kendi boyutlarına sahiptir.
Örnek:
double allbars[20]; // bu, 20 eleman gerektirecektir
p_close , Alış/Satış işlemlerinin kontrolü amacıyla izleyeceğimiz çubuk için Kapanış fiyatı öğesini saklamak amacıyla kullanacağımız bir değişkendir.
STP ve TKP EA’mızdaki Zararı Durdur (Stop Loss) ve Kâr Al (Take Profit) değerlerini saklamak için kullanılacaktır.
2.3. EA’YI BAŞLATMA BÖLÜMÜ
int OnInit() { //--- Get handle for ADX indicator adxHandle=iADX(NULL,0,ADX_Period); //--- Get the handle for Moving Average indicator maHandle=iMA(_Symbol,_Period,MA_Period,0,MODE_EMA,PRICE_CLOSE); //--- What if handle returns Invalid Handle if(adxHandle<0 || maHandle<0) { Alert("Error Creating Handles for indicators - error: ",GetLastError(),"!!"); }
Burada ilgili gösterge fonksiyonlarını kullanarak göstergemizin tanıtıcı değerlerini ediniriz.
ADX gösterge tanıtıcı değeri, iADX fonksiyonunu kullanarak elde edilir. İndisi parametreler veya argümanlar olarak hesaplamak için grafik sembolü (NULL aynı zamanda geçerli grafikteki geçerli sembol anlamına gelir), grafik dönemi/zaman aralığı (0 aynı zamanda geçerli grafikteki geçerli zaman aralığı anlamına gelir), ADX ortalama dönemi (daha önce giriş parametreleri kısmında açıklamıştık) gereklidir.
int iADX(
string symbol, // sembol adı
ENUM_TIMEFRAMES period, // dönem
int adx_period // ortalama dönemi
);
Hareketli Ortalama gösterge tanıtıcı değeri iMA fonksiyonu kullanılarak elde edilir. Aşağıdaki argümanlara sahiptir:
- Grafik sembolü (geçerli grafikte geçerli sembol için _symbol, symbol() veya NULL kullanılarak elde edilebilir),
- Grafik dönemi/zaman aralığı (geçerli grafikte geçerli zaman aralığı için _period, period() veya 0 kullanılarak elde edilebilir),
- Hareketli Ortalama ortalama dönemi (daha önce giriş parametreleri kısmında açıklamıştık),
- Göstergenin kaydırması fiyat grafiğine göredir (burada kaydırma 0’dır),
- Hareketli ortalama düzleştirme türü (aşağıdaki ortalama yöntemlerinden herhangi biri olabilir: Basit Ortalama-MODE_SMA, Üssel Ortalama-MODE_EMA, Düzleştirilmiş Ortalama-MODE_SMMA veya Doğrusal Ağırlıklı Ortalama-MODE_LWMA) ve
- Ortalama için kullanılan fiyat (burada kapanış fiyatını kullanıyoruz).
int iMA( |
Bu gösterge fonksiyonları hakkında daha fazla bilgi almak için lütfen MQL5 kılavuzunu okuyun. Bu her göstergenin nasıl kullanılacağını daha iyi anlamanızı sağlar.
Fonksiyonun işleyiciyi vermeme ihtimaline karşı yine hata olup olmadığını kontrol etmeye çalışacağız, aksi takdirde INVALID_HANDLE hatası oluşacaktır. Hatayı görüntülemek için GetlastError fonksiyonu aracılığıyla uyarı fonksiyonunu kullanıyoruz.
//--- Let us handle currency pairs with 5 or 3 digit prices instead of 4 STP = StopLoss; TKP = TakeProfit; if(_Digits==5 || _Digits==3) { STP = STP*10; TKP = TKP*10; }
Zararı Durdur (Stop Loss) ve Kâr Al (Take Profit) değişkenlerini daha önce bildirmiş olduğumuz STP ve TKP değişkenlerinde saklamaya karar verdik. Bunu neden yapıyoruz?
Çünkü GİRİŞ parametrelerinde saklanan değerler salt okunur ve değiştirilemez. Bu yüzden EA’mızın tüm aracılarla iyi çalıştığından emin olmak istiyoruz. Digits veya Digits() bize mevcut grafik sembolünün fiyat doğruluğunu belirleyen ondalık basamak sayısını verir. 5 basamaklı veya 3 basamaklı fiyat grafiğinde hem Zararı Durdur (Stop Loss) hem de Kâr Al (Take Profit) öğesini 10 ile çarpıyoruz.
2.4. EA’NIN SONLANDIRILMASI BÖLÜMÜ
EA devre dışı kaldığında ve bir grafikten kaldırıldığında bu fonksiyon çağrıldığı için başlatma sürecinde oluşturduğumuz tüm gösterge tanıtıcı değerlerini burada geri çağıracağız. Biri ADX göstergesi ve diğeri Hareketli Ortalama göstergesi olmak üzere iki tanıtıcı değer oluşturduk.
Bunu yapmak için IndicatorRelease() fonksiyonunu kullanacağız. Bir argüman gereklidir (gösterge tanıtıcı değeri)
bool IndicatorRelease(
int indicator_handle, // gösterge tanıtıcı değeri
);
Fonksiyon bir gösterge tanıtıcı değerini kaldırır ve kullanılmıyorsa hesaplama bloğunu serbest bırakır.
2.5 EA ONTICK BÖLÜMÜ
Burada ilk yapmamız gereken, mevcut grafikte yeterince çubuğumuz olup olmadığını kontrol etmektir. Herhangi bir grafiğin geçmişindeki toplam çubuk sayısını Çubuklar fonksiyonunu kullanarak öğrenebiliriz. İki parametre gereklidir; bunlar symbol (bunu _Symbol veya Symbol() kullanarak elde edebiliriz. Bu ikisi EA’mızın eklenmiş olduğu geçerli grafikteki geçerli sembolü verir) ve geçerli grafiğin dönem veya zaman dilimi öğeleridir (Period veya Period() kullanarak elde edilebilir. Bunlar EA’nın eklenmiş olduğu geçerli grafiğin zaman dilimini verecektir).
Mevcut çubukların toplam sayısı 60’tan az ise EA’mızın, grafikte yeterli sayıda çubuğa sahip olana kadar rahatlamasını isteriz. Uyarı fonksiyonu, ayrı bir pencerede bir mesaj gösterir. Parametreler/argümanlar olarak virgülle ayrılmış herhangi bir değer yeterlidir. Bu durumda yalnızca tek dizgi değerimiz olur. Döndürme, EA’mızın başlatmasından çıkar.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Do we have enough bars to work with if(Bars(_Symbol,_Period)<60) // if total bars is less than 60 bars { Alert("We have less than 60 bars, EA will now exit!!"); return; } // We will use the static Old_Time variable to serve the bar time. // At each OnTick execution we will check the current bar time with the saved one. // If the bar time isn't equal to the saved time, it indicates that we have a new tick. static datetime Old_Time; datetime New_Time[1]; bool IsNewBar=false; // copying the last bar time to the element New_Time[0] int copied=CopyTime(_Symbol,_Period,0,1,New_Time); if(copied>0) // ok, the data has been copied successfully { if(Old_Time!=New_Time[0]) // if old time isn't equal to new bar time { IsNewBar=true; // if it isn't a first call, the new bar has appeared if(MQL5InfoInteger(MQL5_DEBUGGING)) Print("We have new bar here ",New_Time[0]," old time was ",Old_Time); Old_Time=New_Time[0]; // saving bar time } } else { Alert("Error in copying historical times data, error =",GetLastError()); ResetLastError(); return; } //--- EA should only check for new trade if we have a new bar if(IsNewBar==false) { return; } //--- Do we have enough bars to work with int Mybars=Bars(_Symbol,_Period); if(Mybars<60) // if total bars is less than 60 bars { Alert("We have less than 60 bars, EA will now exit!!"); return; } //--- Define some MQL5 Structures we will use for our trade MqlTick latest_price; // To be used for getting recent/latest price quotes MqlTradeRequest mrequest; // To be used for sending our trade requests MqlTradeResult mresult; // To be used to get our trade results MqlRates mrate[]; // To be used to store the prices, volumes and spread of each bar ZeroMemory(mrequest); // Initialization of mrequest structure
Uzman Danışman alım satım işlemlerini yeni bir çubuğun başlangıcında yapacaktır, bu yüzden sorunu yeni çubuk tanımlamasıyla çözmek gereklidir. Diğer bir deyişle EA’mızın her tikte Uzun/Kısa ayarlarını kontrol etmediğinden ve EA’mızın yalnızca yeni bir çubuk olduğunda Uzun/Kısa pozisyonları kontrol ettiğinden emin olmak istiyoruz.
Çubuk zamanını saklayacak olan statik bir tarih saat değişkeni olan Old_Time öğesini bildirerek başlayacağız. Bunu statik olarak bildiriyoruz çünkü değerin, OnTick fonksiyonunun bir sonraki çağrılmasında bellekte tutulmasını istiyoruz. Ardından bunun değerini yeni (geçerli) çubuk zamanını tutacak elemanın bir dizisi olan New_Time değişkeni (ayrıca tarih/saat veri türünün değişkeni) ile karşılaştırabileceğiz. Ayrıca IsNewBar bool veri türü değişkenini bildirdik ve değerini false olarak ayarladık. Bunun sebebi, bu değerin yalnızca yeni bir çubuğumuz olduğunda TRUE olmasını istememizdir.
Geçerli çubuğun zamanını elde etmek için CopyTime fonksiyonunu kullanırız. Çubuk zamanını New_Time dizisine tek elemanla kopyalarız ve başarılı olursa yeni çubuğun zamanını önceki çubuk zamanı ile karşılaştırırız. Zamanlar eşit değilse bu, yeni bir çubuğumuz olduğu anlamına gelir ve IsNewBar TRUE olarak ayarlanır ve geçerli çubuk zamanının değerini Old_Time değişkenine kaydederiz.
IsNewBar değişkeni, yeni bir çubuğumuz olduğunu belirtir. FALSE ise OnTick fonksiyonunun yürütülmesini bitiriyoruz.
Koda bakın
if(MQL5InfoInteger(MQL5_DEBUGGING)) Print("We have new bar here ",New_Time[0]," old time was ",Old_Time);
Hata ayıklama modunun yürütülmesini kontrol eder, hata ayıklama modunda çubuk zamanları hakkındaki mesajı yazdırır, buna daha çok eğileceğiz.
Burada yapmak istediğimiz bir sonraki şey çalışabilmemiz için yeterli çubuğumuz olup olmadığını kontrol etmektir. Bunu neden tekrarlıyoruz? Sadece EA’mızın doğru çalıştığından emin olmak istiyoruz. OnInit fonksiyonu yalnızca EA bir grafiğe eklendiğinde bir defa çağrılırken OnTick fonksiyonunun her yeni tikte (fiyat teklifi) çağrıldığı unutulmamalıdır.
Yine burada bunu daha farklı şekilde yaptığımızı görüyorsunuz. Toplam çubukları, OnTick fonksiyonuyla birlikte bildirilen
int Mybars=Bars(_Symbol,_Period);
yeni bir değişken olan Mybars değişkenindeki ifadeden elde ettiğimiz geçmişte saklamaya karar verdik. Bu yeni değişken türü, kodumuzun GİRİŞ PARAMETRELERİ bölümünde bildirdiğimiz değişkenin aksine yerel bir değişkendir. Kodumuzun Giriş Parametreleri bölümünde bildirilen değişkenlere, kodumuzdaki bunlara ihtiyaç duyabilecek tüm fonksiyonlar erişebilecektir, tek fonksiyonda bildirilen değişkenler kısıtlıdır ve sadece o fonksiyon tarafından kullanılabilir. Bu fonksiyonun dışında kullanılamaz.
Sonra, EA’mızın bu bölümünde kullanılacak olan MQL5 yapı türlerinin birkaç değişkenini bildirdik. MQL5’te, EA geliştiricileri için bir çok şeyi kolaylaştıran çok sayıda yerleşik Yapı mevcuttur. Yapıları birbiri ardına ele alalım.
Bu, sembollerin en son fiyatlarını saklamak için kullanılan bir yapıdır.
struct MqlTick
{
datetime time; // Son fiyat güncellemesinin zamanı
double bid; // Mevcut Alış fiyatı
double ask; // Mevcut Satış fiyatı
double last; // Son sözleşme fiyatı (Last)
ulong volume; // Mevcut Son fiyatın hacmi
};
MqlTick türü olarak bildirilen her değişken, Satış, Alış, Son ve Hacim öğelerinin geçerli değerlerini elde etmek için (SymbolInfoTick() fonksiyonunu çağırdığınızda) kolayca kullanılabilir.
Bu yüzden latest_price öğesini MqlTick türü olarak bildirdik, böylece Satış ve Alış fiyatlarını almak için bunu kullanabiliriz
Bu yapı, bir alım satım işleminin tüm alım satım taleplerini gerçekleştirmek için kullanılır. Bu yapıda bir alım satım sözleşmesi yapmak için gereken tüm alanları içerir.
struct MqlTradeRequest
{
ENUM_TRADE_REQUEST_ACTIONS action; // Alım-satım işlemi türü
ulong magic; // Uzman Danışman Kimliği (sihirli sayı)
ulong order; // Emir fişi
string symbol; // Alım-satım sembolü
double volume; // Lot bazında bir sözleşme için istenen hacim
double price; // Fiyat
double stoplimit; // Emrin StopLimit seviyesi
double sl; // Emrin Zararı Durdur (Stop Loss) seviyesi
double tp; // Emrin Kâr Al (Take Profit) seviyesi
ulong deviation; // İstenen fiyattan maksimum olası sapma
ENUM_ORDER_TYPE type; // Emir türü
ENUM_ORDER_TYPE_FILLING type_filling; // Emir gerçekleştirme türü
ENUM_ORDER_TYPE_TIME type_time; // Emir gerçekleştirme zamanı
datetime expiration; // Emir zaman aşımı zamanı (ORDER_TIME_SPECIFIED türü emirler için)
string comment; // Emir yorumu
};
MqlTradeRequest türü olarak bildirilen her değişken kolayca alım satım işlemlerimiz için emirler gönderebilir. Burada mrequest öğesini MqlTradeRequest türü olarak bildirdik.
Herhangi bir alım satım işleminin sonucu MqlTradeResult türünün özel bir önceden tanımlı yapısı olarak geri döner. MqlTradeResult türü olarak bildirilen her değişken, alım satım talebi sonuçlarına erişebilir.
struct MqlTradeResult
{
uint retcode; // İşlem dönüş kodu
ulong deal; // Sözleşme fişi, gerçekleşmişse
ulong order; // Emir fişi, işleme konulmuşsa
double volume; // Sözleşme hacmi, aracı tarafından onaylanmış
double price; // Sözleşme fiyatı, aracı tarafından onaylanmış
double bid; // Geçerli Alış fiyatı
double ask; // Geçerli Satış fiyatı
string comment; // Aracı işlem yorumu (varsayılan olarak bu, işlem açıklaması ile doldurulur)
};
Burada mresult öğesini MqlTradeResult türü olarak bildirdik.
Her çubuğun Fiyatı (Açılış, Kapanış, Yüksek, Düşük), Zamanı ve Hacimleri ve bir sembolün spread’i (alım-satım farkı) bu yapıda saklanır. MqlRates türü olarak bildirilen her dizi, bir sembolün fiyat, hacim ve spread (alım-satım farkı) geçmişini saklamak için kullanılabilir.
struct MqlRates
{
datetime time; // Dönem başlangıç zamanı
double open; // Açılış fiyatı
double high; // Dönemin en yüksek fiyatı
double low; // Dönemin en düşük fiyatı
double close; // Kapanış fiyatı
long tick_volume; // Tik hacmi
int spread; // Spread (alım-satım farkı)
long real_volume; // Alım-satım hacmi
};
Burada bu bilgileri saklamak için kullanılacak olan mrate[] dizisini bildirdik.
/* Let's make sure our arrays values for the Rates, ADX Values and MA values is store serially similar to the timeseries array */ // the rates arrays ArraySetAsSeries(mrate,true); // the ADX DI+values array ArraySetAsSeries(plsDI,true); // the ADX DI-values array ArraySetAsSeries(minDI,true); // the ADX values arrays ArraySetAsSeries(adxVal,true); // the MA-8 values arrays ArraySetAsSeries(maVal,true);
Ardından Çubuk ayrıntılarını seriler olarak saklamak için kullanacağımız tüm dizileri ayarlayacağız. Bu, dizilere kopyalanacak olan değerlerin, zaman serileri olarak yani 0, 1, 2, 3 gibi indisleneceğinden (çubuklar indisine karşılık düşmesi için) emin olmak içindir. Bu yüzden ArraySetAsSeries() fonksiyonunu kullanırız.
bool ArraySetAsSeries(
void array[], // referans dizi
bool set // true değeri indisleme sırasının tersine çevrildiğini ifade eder
);
Bunun ayrıca kodumuzun başlatma bölümünde de yapılabileceğini unutmayın. Ancak bu noktada açıklamamıza faydası dokunması için bunu göstermeye karar verdim.
//--- Get the last price quote using the MQL5 MqlTick Structure if(!SymbolInfoTick(_Symbol,latest_price)) { Alert("Error getting the latest price quote - error:",GetLastError(),"!!"); return; }
Şimdi en son fiyat teklifini elde etmek için SymbolInfoTick fonksiyonunu kullanıyoruz. Bu fonksiyonda iki argüman vardır - grafik symbol ve MqlTick yapısı değişkeni (latest_price). Yine, bir hata varsa bunu bildirdik.
//--- Get the details of the latest 3 bars if(CopyRates(_Symbol,_Period,0,3,mrate)<0) { Alert("Error copying rates/history data - error:",GetLastError(),"!!"); return; }
Ardından son üç çubuk hakkındaki bilgileri Mqlrates türü dizimize CopyRates fonksiyonunu kullanarak kopyaladık. CopyRates fonksiyonu, belirtilen sembol ve dönemin MqlRates yapısının geçmiş verisini MqlRates türü diziye belirtilen miktarda kopyalar.
int CopyRates(
string symbol_name, // sembol adı
ENUM_TIMEFRAMES timeframe, // dönem
int start_pos, // başlangıç pozisyonu
int count, // kopyalanacak veri sayısı
MqlRates rates_array[] // kopyalanacak hedef dizi
);
Sembol adı “_symbol” kullanılarak, geçerli dönem/zaman dilimi de “_period” kullanılarak elde edilir. Başlangıç pozisyonu için mevcut çubuk olan Çubuk 0’dan başlayacağız ve yalnızca üç Çubuğu sayacağız, Çubuk 0, 1 ve 2. Sonuç, mrate[] dizimizde saklanacaktır.
mrate[] dizisi şimdi çubuk 0, 1 ve 2 için tüm fiyat, zaman, hacim ve spread (alım-satım farkı) bilgilerini içerir. Böylece her çubuğun ayrıntılarını öğrenmek için aşağıdakileri kullanacağız:
mrate[bar_number].bar_property
örneğin, her çubuk için aşağıdaki bilgilere sahip olabiliriz:
mrate[1].time // Çubuk 1 Başlangıç zamanı
mrate[1].open // Çubuk 1 Açılış fiyatı
mrate[0].high // Çubuk 0 (geçerli çubuk) yüksek fiyat vs.
Ardından bildirdiğimiz tüm gösterge değerlerini, CopyBuffer fonksiyonunu kullanarak dinamik dizilere kopyaladık.
int CopyBuffer(
int indicator_handle, // gösterge tanıtıcı değeri
int buffer_num, // gösterge tamponunun numarası
int start_pos, // başlangıç pozisyonu
int count, // kopyalanacak miktar
double buffer[] // kopyalanacak hedef dizi
);
Gösterge tanıtıcı değeri, OnInit bölümünde oluşturduğumuz tanıtıcı değerdir. Tampon numaraları konusunda ADX göstergesinin üç (3) tamponu vardır:
- 0 - MAIN_LINE,
- 1 - PLUSDI_LINE,
- 2 - MINUSDI_LINE.
Hareketli Ortalama göstergesinde sadece bir (1) tampon bulunur:
- 0 – MAIN_LINE.
Mevcut çubuktan (0) önceki iki çubuğa kopyalıyoruz. Böylece kopyalanacak kayıt miktarı 3 olur (çubuk 0, 1 ve 2). buffer[], daha önce bildirdiğimiz hedef dinamik dizilerdir – adxVal, plsDI, minDI ve maVal.
Burada yine gördüğünüz gibi kopyalama sürecinde olabilecek hataları yakalamaya çalışıyoruz. Hata varsa ilerlemeye gerek yoktur.
CopyBuffer() ve CopyRates() fonksiyonunun, başarıyla kopyalanan kayıtları verdiğini ve hata olduğunda -1 değerini verdiğini unutmamak önemlidir. Bu yüzden burada hata kontrolü fonksiyonlarında 0’dan (sıfır) küçük bir değeri kontrol ediyoruz.
//--- Copy the new values of our indicators to buffers (arrays) using the handle if(CopyBuffer(adxHandle,0,0,3,adxVal)<0 || CopyBuffer(adxHandle,1,0,3,plsDI)<0 || CopyBuffer(adxHandle,2,0,3,minDI)<0) { Alert("Error copying ADX indicator Buffers - error:",GetLastError(),"!!"); return; } if(CopyBuffer(maHandle,0,0,3,maVal)<0) { Alert("Error copying Moving Average indicator buffer - error:",GetLastError()); return; }
Bu noktada zaten açık bir Alış veya Satış pozisyonumuzun olup olmadığını kontrol etmek, diğer bir deyişle tek seferde sadece BİR Satış veya Alış işlemi olduğundan emin olmak istiyoruz. Zaten varsa yeni bir Alış pozisyonu açmak istemiyoruz ve zaten varsa yeni bir Satış pozisyonu açmak istemiyoruz.
Bunun için önce açılmış bir Alış veya Satış pozisyonu varsa TRUE değerine sahip olan iki bool veri türü değişkenini bildireceğiz (Buy_opened ve Sell_opened).
//--- we have no errors, so continue //--- Do we have positions opened already? bool Buy_opened=false; // variable to hold the result of Buy opened position bool Sell_opened=false; // variable to hold the result of Sell opened position if (PositionSelect(_Symbol) ==true) // we have an opened position { if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { Buy_opened = true; //It is a Buy } else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { Sell_opened = true; // It is a Sell } }
Açık bir pozisyonumuz olup olmadığını görmek için PositionSelect alım satım fonksiyonunu kullanırız. Zaten açılmış bir pozisyon varsa bu fonksiyon TRUE değerini ve yoksa FALSE değerini verir.
bool PositionSelect( string symbol // Symbol name );
Ana argüman/parametre olarak kontrol etmek istediğimiz sembol (para birimi çifti) gereklidir. Burada _symbol öğesini kullanırız çünkü geçerli sembolü (para birimi çifti) kontrol ediyoruz.
Bu ifade TRUE değerini verirse açılan pozisyonun Alış mı Satış mı olduğunu kontrol ederiz. Bunun için PositionGetInteger fonksiyonunu kullanırız. Bunu POSITION_TYPE değiştiricisi ile kullandığımızda bize açılan pozisyon türünü verir. POSITION_TYPE_BUY veya POSITION_TYPE_SELL olabilecek Pozisyon türü tanımlayıcısını verir.
long PositionGetInteger( ENUM_POSITION_PROPERTY property_id // Property identifier );
Bizim örneğimizde bunu hangi pozisyonun zaten açılmış olduğunu belirlemek için kullandık. Bu bir Satışsa Sell_opened içinde TRUE değerini saklarız ve bu bir Alışsa Buy_opened içinde TRUE değerini saklarız. Daha sonra kodumuzda Satış veya Alış koşullarımızı kontrol ederken bu iki değişkeni kullanabileceğiz.
Şimdi Alış/Satış ayarımız için kullanacağımız çubuğun kapanış fiyatını saklama işlemine sıra geldi. Bunun için daha önce bir değişken bildirdiğimizi unutmayın
// Copy the bar close price for the previous bar prior to the current bar, that is Bar 1 p_close=mrate[1].close; // bar 1 close price
Bunu yaptığımıza göre sonraki adıma ilerliyoruz.
/* 1. Check for a long/Buy Setup : MA-8 increasing upwards, previous price close above it, ADX > 22, +DI > -DI */ //--- Declare bool type variables to hold our Buy Conditions bool Buy_Condition_1 = (maVal[0]>maVal[1]) && (maVal[1]>maVal[2]); // MA-8 Increasing upwards bool Buy_Condition_2 = (p_close > maVal[1]); // previuos price closed above MA-8 bool Buy_Condition_3 = (adxVal[0]>Adx_Min); // Current ADX value greater than minimum value (22) bool Buy_Condition_4 = (plsDI[0]>minDI[0]); // +DI greater than -DI //--- Putting all together if(Buy_Condition_1 && Buy_Condition_2) { if(Buy_Condition_3 && Buy_Condition_4) { // any opened Buy position? if (Buy_opened) { Alert("We already have a Buy Position!!!"); return; // Don't open a new Buy Position } mrequest.action = TRADE_ACTION_DEAL; // immediate order execution mrequest.price = NormalizeDouble(latest_price.ask,_Digits); // latest ask price mrequest.sl = NormalizeDouble(latest_price.ask - STP*_Point,_Digits); // Stop Loss mrequest.tp = NormalizeDouble(latest_price.ask + TKP*_Point,_Digits); // Take Profit mrequest.symbol = _Symbol; // currency pair mrequest.volume = Lot; // number of lots to trade mrequest.magic = EA_Magic; // Order Magic Number mrequest.type = ORDER_TYPE_BUY; // Buy Order mrequest.type_filling = ORDER_FILLING_FOK; // Order execution type mrequest.deviation=100; // Deviation from current price //--- send order OrderSend(mrequest,mresult);
Şimdi bir Alış imkanını kontrol etmeye başlamanın sırası geldi.
Daha önce tasarladığımız stratejiyi temsil ettiği için yukarıdaki ifadeyi analiz edelim. Bir emir verilmeden önce karşılanması gereken her koşulumuz için bir bool türü değişken bildiriyoruz. Bool türü bir değişken yalnızca TRUE veya FALSE içerebilir. Bu yüzden Alış stratejimiz dört koşula ayrılır. Koşullardan herhangi biri karşılanır veya gerçekleşirse bool türü değişkenimizde TRUE değeri saklanır, aksi takdirde FALSE değeri saklanacaktır. Bunlara tek tek bakalım.
bool Buy_Condition_1 = (maVal[0]>maVal[1]) && (maVal[1]>maVal[2]);
Burada MA-8 değerlerine (Çubuk 0, 1 ve 2 üzerindeki) bakıyoruz. Geçerli çubuktaki MA-8 değeri önceki Çubuk 1 üzerindeki değerinden büyükse ve ayrıca Çubuk 1 üzerindeki MA-8 değeri Çubuk 2 üzerindeki değerinden büyükse MA-8 yükselerek artıyor demektir. Bu, Alış ayarımızın koşullarından birini karşılar.
bool Buy_Condition_2 = (p_close > maVal[1]);
Bu ifade, Çubuk 1 Kapanış fiyatının aynı dönemdeki (Çubuk 1 dönemi) MA-8 değerinden yüksek olup olmadığını kontrol eder. Fiyat daha yüksekse ikinci koşulumuz da karşılanmıştır, ardından diğer koşulları kontrol edebiliriz. Ancak belirttiğimiz iki koşul karşılanmazsa diğer koşulları kontrol etmeye ihtiyaç olmayacaktır. Bu yüzden bu iki başlangıç koşulundaki sonraki ifadeleri dahil etmeye karar verdik (ifadeler).
bool Buy_Condition_3 = (adxVal[0]>Adx_Min);
Şimdi geçerli ADX değerinin (Çubuk 0’daki ADX değeri) giriş parametrelerinde bildirilen En Düşük ADX değerinden büyük olup olmadığını kontrol etmek istiyoruz. İfade doğru ise yani mevcut ADX değeri gereken En Düşük değerden büyükse ayrıca plusDI değerinin de minusDI değerinden büyük olduğundan emin olmak isteriz. Sonraki ifadede elde ettiğimiz budur
bool Buy_Condition_4 = (plsDI[0]>minDI[0]);
Tüm bu koşullar karşılandığında, yani true sonucunu verdiğinde zaten bir Alış pozisyonu varsa yeni bir Alış pozisyonu açmadığımızdan emin olmak isteriz. Şimdi kodumuzda daha önce bildirdiğimiz Buy_opened değişkeninin değerini kontrol etme zamanı geldi.
// any opened Buy position? if (Buy_opened) { Alert("We already have a Buy Position!!!"); return; // Don't open a new Buy Position }
Buy_opened, true ise başka bir Alış pozisyonu açmak istemeyiz, bu yüzden bizi bilgilendirecek ve ardından geri dönecek bir uyarı görüntüleriz ve böylece EA’mız şimdi sonraki Tiki bekler. Ancak Buy_opened FALSE ise o zaman kayıtlarımızı, emrimizi göndermek için daha önce bildirdiğimiz MqlTradeRequest türü değişkenini (mrequest) kullanarak hazırlarız.
- Buradaki alım satım işlemi türündeki eylem TRADE_ACTION_DEAL’dir çünkü hemen gerçekleştirilmek üzere bir alım satım emri veriyoruz. Bir emri değiştiriyorsak TRADE_ACTION_MODIFY öğesini kullanırız. Bir emri silmek için TRADE_ACTION_REMOVE öğesini kullanırız. MqlTick türü latest_price öğemizi kullanarak en son Satış fiyatını aldık. Zararı durdur (Stop loss) emrinin fiyatı, puan bazında StopLoss değerini Satış fiyatından çıkararak, kâr al (take profit) emrinin fiyatı da puan bazında TakeProfit değerini Satış fiyatına ekleyerek elde edilir. Ayrıca Satış fiyatı, StopLoss ve TakeProfit değerleri için NormalizeDouble fonksiyonunu kullandığımızı fark edeceksiniz, bu fiyatları alım satım sunucusuna göndermeden önce para birimi çiftini basamak sayısına göre normalleştirmek iyi bir uygulamadır.
- Sembol, geçerli semboldür (_Symbol veya Symbol()). Emir türü verdiğimiz emir türüdür, burada ORDER_TYPE_BUY satış emri verdik. Satış emri için bu, ORDER_TYPE_SELL olacaktır.
- type_filling emri, emir gerçekleştirme türüdür; ORDER_FILLING_FOK, sözleşmenin emirde belirtilen fiyata eşit veya bundan yüksek bir fiyattan ve belirtilen hacimle özel olarak gerçekleştirilebileceği anlamına gelir. Emir sembolünde yeterli emir hacmi yoksa emir gerçekleştirilmez.
OrderSend() fonksiyonu için iki argüman gerekir, MqlTradeRequest türü değişken ve MqlTradeResult türü değişken.
bool OrderSend( MqlTradeRequest& request // query structure MqlTradeResult& result // structure of the answer );
Burada yine gördüğünüz gibi MqlTradeRequest türü değişkenimizi ve MqlTradeResult türü değişkenimizi kullanarak OrderSend öğesinden faydalandık.
// get the result code if(mresult.retcode==10009 || mresult.retcode==10008) //Request is completed or order placed { Alert("A Buy order has been successfully placed with Ticket#:",mresult.order,"!!"); } else { Alert("The Buy order request could not be completed -error:",GetLastError()); ResetLastError(); return; }
Emrimizi gönderdikten sonra şimdi emrimizin sonucunu kontrol etmek için MqlTradeResult türü değişkeni kullanacağız. Emrimizin başarıyla gerçekleştirilip gerçekleştirilmediğini bilmek isteriz. Emir verilirse MqlTradeResult türü değişken “mresult” ile İşlem dönüş koduna ve ayrıca emir fişi numarasına erişebilirsiniz.
10009 dönüş kodu OrderSend talebinin başarıyla tamamlandığını gösterirken 10008 ise emrimizin verildiğini gösterir. Bu yüzden bu iki dönüş kodunu kontrol ettik. Bunlardan herhangi biri varsa emrimizin tamamlandığından veya verildiğinden emin oluruz.
Satış Fırsatını kontrol etmek için ADX’imizin belirtilen En düşük değerden büyük olması şartı haricinde Alış Fırsatı için yaptıklarımızın tam tersini kontrol ederiz.
/* 2. Check for a Short/Sell Setup : MA-8 decreasing downwards, previous price close below it, ADX > 22, -DI > +DI */ //--- Declare bool type variables to hold our Sell Conditions bool Sell_Condition_1 = (maVal[0]<maVal[1]) && (maVal[1]<maVal[2]); // MA-8 decreasing downwards bool Sell_Condition_2 = (p_close <maVal[1]); // Previous price closed below MA-8 bool Sell_Condition_3 = (adxVal[0]>Adx_Min); // Current ADX value greater than minimum (22) bool Sell_Condition_4 = (plsDI[0]<minDI[0]); // -DI greater than +DI //--- Putting all together if(Sell_Condition_1 && Sell_Condition_2) { if(Sell_Condition_3 && Sell_Condition_4) { // any opened Sell position? if (Sell_opened) { Alert("We already have a Sell position!!!"); return; // Don't open a new Sell Position } mrequest.action = TRADE_ACTION_DEAL; // immediate order execution mrequest.price = NormalizeDouble(latest_price.bid,_Digits); // latest Bid price mrequest.sl = NormalizeDouble(latest_price.bid + STP*_Point,_Digits); // Stop Loss mrequest.tp = NormalizeDouble(latest_price.bid - TKP*_Point,_Digits); // Take Profit mrequest.symbol = _Symbol; // currency pair mrequest.volume = Lot; // number of lots to trade mrequest.magic = EA_Magic; // Order Magic Number mrequest.type= ORDER_TYPE_SELL; // Sell Order mrequest.type_filling = ORDER_FILLING_FOK; // Order execution type mrequest.deviation=100; // Deviation from current price //--- send order OrderSend(mrequest,mresult);
Tıpkı alış bölümünde yaptığımız gibi bir emir verilmeden önce karşılanması gereken her koşulumuz için bir bool türü değişken bildiriyoruz. Bool türü bir değişken yalnızca TRUE veya FALSE içerebilir. Bu yüzden Satış stratejimiz dört koşula ayrılır. Koşullardan herhangi biri karşılanır veya gerçekleşirse bool türü değişkenimizde TRUE değeri saklanır, aksi takdirde FALSE değeri saklanacaktır. Alış kısmında yaptığımız gibi bunlara tek tek bakalım
bool Sell_Condition_1 = (maVal[0]<maVal[1]) && (maVal[1]<maVal[2]);
Burada MA-8 değerlerine (Çubuk 0, 1 ve 2 üzerindeki) bakıyoruz. Geçerli çubuktaki MA-8 değeri önceki Çubuk 1 üzerindeki değerinden küçükse ve ayrıca Çubuk 1 üzerindeki MA-8 değeri Çubuk 2 üzerindeki değerinden küçükse bu, MA-8 azalarak düşüyor anlamına gelir. Bu, Satış ayarımızın koşullarından birini karşılar.
bool Sell_Condition_2 = (p_close <maVal[1]);
Bu ifade, Çubuk 1 Kapanış fiyatının, aynı dönemde (Çubuk 1 dönemi) MA-8 değerinden düşük olup olmadığını kontrol eder. Fiyat düşükse ikinci koşulumuz da karşılanmış demektir ardından diğer koşulları kontrol edebiliriz. Ancak belirttiğimiz iki koşul karşılanmadıysa diğer koşulları kontrol etmemiz gerekmez. Bu yüzden bu iki başlangıç koşulundaki sonraki ifadeleri dahil etmeye karar verdik (ifadeler).
bool Sell_Condition_3 = (adxVal[0]>Adx_Min);
Şimdi geçerli ADX değerinin (Çubuk 0’daki ADX değeri) giriş parametrelerinde bildirilen En Düşük ADX değerinden büyük olup olmadığını kontrol etmek istiyoruz. İfade doğru ise yani mevcut ADX değeri gereken En Düşük değerden büyükse ayrıca MinusDI değerinin de plusDI değerinden büyük olduğundan emin olmak isteriz. Sonraki ifadede elde ettiğimiz budur
bool Sell_Condition_4 = (plsDI[0]<minDI[0]);
Bu koşullar karşılandığında, yani true sonucunu verdiğinde zaten bir Alış pozisyonu varsa yeni bir Alış pozisyonu açmadığımızdan emin olmak isteriz. Şimdi kodumuzda daha önce bildirdiğimiz Buy_opened değişkeninin değerini kontrol etme zamanı geldi.
// any opened Sell position? if (Sell_opened) { Alert("We already have a Sell position!!!"); return; // Don't open a new Sell Position }
Sell_opened, true ise başka bir Satış pozisyonu açmak istemeyiz, bu yüzden bizi bilgilendirecek ve ardından geri dönecek bir uyarı görüntüleriz ve böylece EA’mız şimdi sonraki Tiki bekler. Ancak Sell_opened FALSE olursa Satış işlemi talebimizi Alış emrinde olduğumuz gibi ayarlarız.
Buradaki büyük fark, zararı durdurma fiyatımızı ve kâr alma fiyatımızı hesaplama şeklimizdir. Ayrıca satış yaptığımız için Alış fiyatından satıyoruz, bu yüzden MqlTick türü latest_price değişkenimizi kullanarak en son alış fiyatını elde ediyoruz. Daha önce açıkladığımız gibi buradaki diğer tür ORDER_TYPE_SELL’dir.
Ayrıca burada Alış fiyatı, StopLoss ve TakeProfit değerleri için NormalizeDoublefonksiyonunu kullandığımızı fark edeceksiniz, bu fiyatları alım satım sunucusuna göndermeden önce para birimi çiftinin basamak sayısına göre normalleştirmek iyi bir uygulamadır.
Tıpkı Alış emrimizde yaptığımız gibi, ayrıca Satış emrimizin de başarılı olup olmadığını kontrol etmeliyiz. Bu yüzden Alış emrimizdeki aynı ifadeyi kullandık.
if(mresult.retcode==10009 || mresult.retcode==10008) //Request is completed or order placed { Alert("A Sell order has been successfully placed with Ticket#:",mresult.order,"!!"); } else { Alert("The Sell order request could not be completed -error:",GetLastError()); ResetLastError(); return; } }
3. Hata Ayıklama ve Uzman Danışmanımızı Test Etme
Bu noktada, stratejimizin işe yarayıp yaramadığını test etmemiz gerekir. Ayrıca EA kodumuzda bir veya iki hata olabilir. Bu, sonraki adımda keşfedilecektir.
3.1 HATA AYIKLAMA
Kodumuzda hata ayıklamak, kodumuzun satır satır nasıl çalıştığını (kesme noktaları belirlediysek) görmemizi ve hemen o anda kodumuzdaki hatayı veya açığı fark etmemizi, kodumuzu gerçek alım satım işlemlerinde kullanmadan önce düzeltmeleri hızla yapabilmemizi sağlar.
Burada önce kırılma noktaları belirleyerek ve ikinci olarak kesme noktaları olmadan Uzman Danışmanımızda adım adım hata ayıklama sürecine başlayacağız. Bunu yapmak için Editor’ı kapattığınızdan emin olun. Öncelikle EA’mızı test etmek istediğimiz grafiği seçelim. Editor Menü çubuğunda Araçlar üzerine tıklayın ve aşağıda gösterildiği gibi Seçenekleröğesine tıklayın:
Şekil 8. Hata ayıklama seçeneklerini ayarlama
Seçenekler penceresi göründüğünde para birimi çiftini ve kullanılacak dönem/zaman aralığını seçin ve Tamam düğmesine tıklayın:
Hata ayıklayıcıyı başlatmadan önce kesme noktalarını ayarlayalım. Kesme noktaları seçilen konumlarda veya satırlarda kodumuzun davranışını/performansını izlememizi sağlar. Tüm kodu tek seferde incelemek yerine hata ayıklayıcı bir kesme noktası gördüğünde duracak ve sonraki eyleminizi bekleyecektir. Bu sayede kodumuzu analiz edebilecek ve her kırılma noktasında davranışını izleyebileceğiz. Ayrıca bazı değişkenlerimizin değerlerini inceleyebilecek ve her şeyin tasarladığımız gibi olup olmadığını görebileceğiz.
Bir kesme noktası eklemek için kodunuzda kesme noktasını belirlemek istediğiniz satıra gidin. Sol tarafta kod satırının sınırının yanındaki gri alana çift tıklayın ve içinde beyaz bir kare olan küçük yuvarlak bir mavi düğme göreceksiniz. Alternatif olarak fare imlecini kod satırında kırılma noktasının görünmesini istediğiniz yere getirin ve F9 tuşuna basın. Kesme noktalarını kaldırmak için tekrar F9 tuşuna basın veya üstüne çift tıklayın.
Şekil 10. Bir kesme noktasını ayarlama
Kodumuz için beş farklı satırda kesme noktası belirleyeceğiz.
Açıklamanın kolaylığı açısından bunları 1 - 5 olarak etiketleyeceğiz.
Devam etmek için aşağıdaki şekilde gösterildiği gibi yedi kod satırında kesme noktası belirleyin. Kesme noktası 1 yukarıda oluşturduğumuz kesme noktasıdır.
Şekil 11. Ek kesme noktaları ayarlama
Kesme noktalarımızı ayarlamayı bitirdikten sonra kodumuzda hata ayıklamaya başlayabiliriz.
Hata ayıklayıcıyı başlatmak için F5’e basın veya MetaEditor Araç Çubuğunda yeşil düğmeye tıklayın:
Şekil 12. Hata ayıklayıcıyı başlatmak
Editor’ın yaptığı ilk şey kodu derlemektir o sırada bir hata varsa bunu görüntüleyecek ve hata yoksa kodun başarıyla derlendiğini size bildirecektir.
Şekil 13. Derleme Raporu
Lütfen kodun başarıyla derlenmesinin, kodunuzda hata olmadığı anlamına gelmediğini unutmayın. Kodunuzun nasıl yazıldığına bağlı olarak çalışma zamanı hataları olabilir. Örneğin ifadelerimizden herhangi biri ufak bir gözden kaçırmadan dolayı doğru değerlendirme yapmazsa kodumuz doğru derleme yapacaktır ama düzgün çalışmayabilir. Çok konuştuk, şimdi uygulamaya geçelim...
Hata ayıklayıcı kodu derlemeyi bitirdikten sonra sizi alım satım terminaline götürür ve EA’yı MetaEditor Seçenekleri ayarlarında belirttiğiniz grafiğe ekleyecektir. Aynı zamanda EA’nın Giriş parametreleri bölümünü gösterir. Henüz hiçbir şeyi ayarlamadığımız için sadece Tamam düğmesine tıklayın.
Şekil 14. Hata Ayıklama İçin Uzman Danışman Giriş Parametreleri
Şimdi grafiğin sağ üst köşesinde EA’yı net bir şekilde göreceksiniz.
OnTick() öğesini başlattıktan sonra kesme noktası 1’e gelir gelmez duracaktır.
Şekil 15. Hata ayıklayıcı ilk kesme noktasında durur
O kod satırında yeşil bir ok göreceksiniz. Bu size önceki kod satırının yürütüldüğünü ve şimdi mevcut satırı yürütmeye hazır olduğumuzu anlatır.
Devam etmeden önce burada bazı açıklamalar yapayım. Editor’ın Araç Çubuğunda daha önce gri olan kıvrımlı oklara sahip üç düğmenin şimdi etkinleştirildiğini göreceksiniz. Bunun nedeni, o anda hata ayıklayıcıyı çalıştırıyor olmamızdır. Bu düğmeler/komutlar kodumuzda adımlar oluşturmak için kullanılır (Gir, Atla veya Dışarı Adımla)
Şekil 16. Gir komutu
Gir komutu bir program yürütme adımından sonraki adıma geçmek ve o kod satırında çağrılan fonksiyonlara girmek için kullanılır. Komutu başlatmak için düğmeye tıklayın veya F11 tuşuna basın. (Kodumuzun adım adım hata ayıklamasında bu komutu kullanacağız.)
Şekil 17. Atla komutu
Atla komutu ise o kod satırında çağrılan herhangi bir fonksiyona girmez. Komutu başlatmak için düğmeye tıklayın veya F10 düğmesine basın
Şekil 18. Dışarı adımla komutu
Bir üst seviye program adımını yürütmek için bu düğmeye tıklayın veya Shift+F11 tuşuna basın.
Ayrıca Editor’ın alt kısmında araç kutusu penceresini göreceksiniz. Bu penceredeki Hata Ayıklama sekmesinde aşağıdaki başlıklar bulunur:
- Dosya: Burada, çağrılan dosyanın adı gösterilir
- Fonksiyon: Bu, çağrılan dosyadaki mevcut fonksiyonu görüntüler
- Satır: Bu, fonksiyonun çağrıldığı dosyadaki kod satırının numarasını görüntüler.
- İfade: Burada kodumuzda izlemek istediğiniz herhangi bir ifadeyi/değişkenin adını yazabilirsiniz.
- Değer: Bu, İfade alanında yazdığımız ifadenin/değişkenin değerini görüntüler.
- Tür: Bu, izlenen ifadenin/değişkenin veri türünü görüntüler.
Şimdi hata ayıklama işlemimize geri dönelim…
Burada yapmak istediğimiz bir sonraki şey kodumuzda izlemek istediğimiz değişkenleri/ifadeleri yazmaktır. Yalnızca kodunuzdaki gerçekten önemli değişkenleri/ifadeleri izlediğinizden emin olun. Bizim örneğimizde aşağıdakileri izleyeceğiz:
- Old_Time (eski çubuk zamanı)
- New_Time[0] (geçerli çubuk zamanı)
- IsNewBar (yeni çubuğu belirten bayrak)
- Mybars (Geçmişteki toplam çubuklar) - EA’mız buna bağlıdır
Örneğin ADX değerleri, MA-8 değerleri gibi başka öğeleri ekleyebilirsiniz.
İfade/değişken eklemek için İfadeler alanının altına çift tıklayın veya İfadeler alanının altına sağ tıklayın ve yukarıdaki şekilde gösterildiği gibi Ekle öğesini seçin.
İzlenecek veya gözlemlenecek ifadeyi/değişkeni yazın.
Şekil 19. İfadeleri izleme penceresi
Gerekli tüm değişkenleri/ifadeleri yazın...
Şekil 20. İzlenecek ifadeleri veya değişkenleri ekleme
Değişken henüz bildirilmemişse türü “Bilinmeyen tanımlayıcı” olur (statik değişkenler hariç).
Şekil 21. Gir komutu uygulanırken
Gir düğmesine tıklayın veya F11 tuşuna basarak ne olacağını gözlemleyin. Kesme noktası 2’yi elde edene kadar bu düğmeye veya F11’e basmaya devam edin, aşağıda gösterildiği gibi kesme noktası 4’e ulaşana kadar devam edin ve ifade izleme penceresini gözlemleyin.
Şekil 22. İfadeleri veya değişkenleri izleme
Şekil 23. İfadeleri veya değişkenleri izleme
Şekil 24. İfadeleri veya değişkenleri izleme
Yeni bir tik olduğunda OnTick() fonksiyonunun ilk kod satırına geri dönecektir. Değişkenlerimizin/ifademizin tüm değerleri şimdi sıfırlanacaktır çünkü bunlardan herhangi biri statik bir değişken olarak bildirilmediği takdirde bu yeni bir tiktir. Bizim örneğimizde bir statik Old_Time değişkenimiz var.
Şekil 25. NewTick olayındaki değişkenlerin değerleri
Süreci tekrar gözden geçirmek için F11 tuşuna basmaya devam edin ve ifadeleri izleme penceresindeki değişkenleri izlemeyi sürdürün. Hata ayıklayıcıyı durdurabilir ve ardından tüm kesme noktalarını kaldırabilirsiniz.
Gördüğümüz gibi Hata Ayıklama modunda “Burada yeni bir çubuğumuz var...” mesajını yazdırır.
Şekil 26. Uzman Danışman, Hata Ayıklayıcı modunda mesajı yazdırır
Hata ayıklama sürecini bu sefer kesme noktaları olmadan başlatalım. Her tiki izlemeye devam edin, Alış/Satış koşullarımızdan herhangi biri karşılanırsa bir alım satım işlemi gerçekleştirecektir ve kodumuzu, bir emrin başarıyla verilip verilmediğini bildirecek şekilde yazdığımız için bir uyarı görürüz.
Şekil 27. Uzman Danışman, hata ayıklama sırasında alım satımı işleme koyar
EA’yı birkaç dakika boyunca çalışmaya bırakıp bir fincan kahve alabilirsiniz. Geri döndüğünüzde ve biraz para kazandıktan sonra (şaka yapıyorum) hata ayıklamayı durdurmak için MetaEditor’da DURDUR (Kırmızı) düğmesine tıklayın.
Şekil 28. Hata ayıklayıcıyı durdurmak
Aslında burada yaptığımız şey EA’mızın yalnızca yeni bir Çubuk açıldığında bir işlemi kontrol ettiğini ve EA’mızın gerçekten çalıştığını görmektir. EA kodumuzda hala ayarlamalar yapabiliriz.
Açıklamaya çalışayım, bu noktada Alım Satım terminali internete bağlı olmalıdır, aksi takdirde hata ayıklama çalışmayacaktır çünkü terminal, işlem yapamayacaktır.
3.2 EA STRATEJİMİZİ TEST ETME
Bu noktada Alım Satım Terminaline yerleşik Strateji Test Cihazını kullanarak EA’mızı test etmek istiyoruz. Strateji Test Cihazını başlatmak için CONTROL+R üzerine basın veya aşağıda gösterildiği gibi Terminal Menü Çubuğunda Görüntüle menüsüne tıklayın ve Strateji Test Cihazına tıklayın
Şekil 26. Strateji Testini Başlatmak
Test Cihazı (Strateji Test Cihazını) terminalin alt kısmında gösterilir. Test Cihazının tüm ayarlarını görmeniz için bunu genişletmeniz/yeniden boyutlandırmanız gerekir. Bunu yapmak için fare imlecinizi kırmızı ok ile gösterilen noktaya getirin (aşağıda gösterildiği gibi)
Şekil 27. Strateji Test Cihazı penceresi
Fare imleci, çift uçlu oka dönüşür, fareyi basılı tutun ve çizgiyi yukarıya sürükleyin. Ayarlar sekmesinde her şeyi görebildiğinizi fark edince durun.
Şekil 28. Strateji Test Cihazı Ayarları Sekmesi
- Sınamak istediğiniz EA’yı seçin
- Sınama için kullanılacak Para birimi çiftini seçin
- Sınama için kullanılacak Dönem/Zaman Aralığını seçin
- Özel Dönemi seçin ve 5’te tarihleri ayarlayın
- Sınama için kullanılacak özel dönemin tarihlerini ayarlayın
- Yürütme Normaldir
- Sınama için kullanılarak yatırma tutarını USD cinsinden seçin
- Optimizasyonu Devre Dışı olarak ayarlayın (Şu anda optimizasyon yapmıyoruz, sadece sınama yapıyoruz)
- Teste başlamak için bu düğmeye tıklayın.
Başlat düğmesine tıklamadan önce Test Cihazındaki diğer sekmelere bakalım
Aracılar Sekmesi
Test için Test Cihazı tarafından kullanılacak işlemci. Bilgisayarınızın işlemci türüne bağlıdır. Benimki yalnızca bir (1) çekirdekli işlemci.
Şekil 29. Strateji Test Cihazı Aracıları sekmesi
Aracıya baktığınızda aşağıdaki şekle benzer bir şey göreceksiniz
Şekil 30. Test sırasında Strateji Test Cihazı Aracıları sekmesi
Günlük Sekmesi
Burası test boyunca devam eden tüm olayların görüntülendiği yerdir
Şekil 31. Alım satım işlemlerini gösteren Strateji Test Cihazı Günlüğü sekmesi
Girdiler Sekmesi
Burası EA için giriş parametrelerini belirteceğiniz yerdir.
Şekil 32. Strateji Test Cihazı Girdiler sekmesi
EA’mızı optimize ediyorsak daire içindeki alandaki değerleri ayarlamamız gereklidir.
- Başlat, Test Cihazının başlamasını istediğiniz değerlerdir.
- Adım, seçtiğiniz değerin artış oranıdır ve
- Durdur ise Test Cihazının o parametre için değeri artırmayı durduracağı değerdir.
Ancak bizim örneğimizde biz EA’mızı optimize etmiyoruz, bu yüzden buna şimdilik dokunmayacağız.
Her şey ayarlandıktan sonra Ayarlar sekmesine geri dönüyoruz ve Başlat düğmesine tıklıyoruz. Test cihazı çalışmaya başlar. Şimdi tek yapacağınız isterseniz bir fincan kahve daha almaktır veya benim gibiyseniz her bir olayı izlemek isteyebilir, ardından Günlük sekmesine dönebilirsiniz.
Grafik sekmesi
Günlük sekmesinde gönderilmiş emirlerle ilgili mesajlar görmeye başladığınızda Grafik adlı YENİ oluşturulmuş bir sekmeye geçmek isteyebilirsiniz. Grafik sekmesine geçtiğinizde, işlemlerinizin sonucuna bağlı olarak grafiğin yükseldiğini veya düştüğünü göreceksiniz.
Şekil 33. Uzman Danışman Testinin grafik sonucu
Sonuçlar sekmesi
Sınama tamamlandığında Sonuçlar adlı başka bir sekme göreceksiniz. Sonuçlar sekmesine geçin ve az önce yaptığımız testin bir özetini göreceksiniz.
Şekil 34. Test sonuçlarının özetini gösteren Strateji Test Cihazı Sonuçlar sekmesi
Toplam Brüt Kar, Net Kar, toplam alım satımlar, toplam zararlı alım satımlar ve daha pek çok öğeyi görebilirsiniz. Testimiz için seçtiğimiz dönemde yaklaşık 1.450,0 USD olduğunu görmek gerçekten ilginç. En azından biraz kârımız var.
Burada size bir şeyi net olarak açıklayayım. Strateji test cihazında gördüğünüz EA parametrelerinin ayarlarının, EA’nın Giriş parametrelerindeki başlangıç ayarlarından farklı olduğunu göreceksiniz. EA’nızdan en iyi şekilde faydalanmanız için bu giriş parametrelerinin herhangi birini değiştirebileceğinizi gösterdim. Hareketli Ortalama ve ADX için her biri 8 dönemlik olanı kullanmak yerinde Hareketli Ortalama için 10 ve ADX için 14 olarak bunu değiştirdim. Ayrıca Zararı Durdur (Stop Loss) öğesini 30’dan 35’e değiştirdim. Son ama önemli bir nokta da 2 Saatlik zaman dilimini kullanmamdır. Unutmayın, bu Strateji Test Cihazıdır.
Testin tam raporunu görmek isterseniz Sonuçlar sekmesinde herhangi bir yere sağ tıklayın, bir menü göreceksiniz. Bu menüden “Rapor Olarak Kaydet” öğesini seçin.
Şekil 35. Test sonucunu kaydetme
Kaydetme iletişim kutusu görünür, raporunuz için bir ad girin (isterseniz varsayılan adı da bırakabilirsiniz) ve kaydet düğmesine tıklayın. Raporun tamamı sizin için HTML formatında kaydedilecektir.
Yapılan testin grafiğini görmek için Grafiği Aç üzerine tıklayın, grafik görüntülenecektir
Şekil 36. Testi gösteren grafik
İşte bu kadar, EA’mızı başarıyla yazdık, sınadık ve üzerinde çalışacağımız bir sonucumuz var. Şimdi strateji test cihazı Ayarlar sekmesine geri dönebilir ve diğer Zaman Aralıkları/Dönem için test yapabilirsiniz.
Atama
Farklı para birimi çiftlerini, farklı zaman dilimlerini, farklı Zararı Durdur (Stop Loss), farklı Kâr al (Take profit) öğelerini kullanarak test yapmanızı ve EA’nın nasıl çalıştığını görmenizi istiyorum. Yeni Hareketli Ortalama ve ADX değerlerini bile deneyebilirsiniz. Daha önce söylediğim gibi Strateji test aracının temeli budur. Ayrıca sonuçlarınızı benimle paylaşmanızı isterim.
Sonuç
Bu adım adım kılavuzda geliştirilmiş bir alım satım stratejisine dayanarak basit bir Uzman Danışman yazmak için gereken temel adımlara bakmış olduk. Hata ayıklayıcıyı kullanarak EA’mızda hata olup olmadığını nasıl kontrol edeceğimizi inceledik. Ayrıca Strateji Test Cihazını kullanarak EA’mızın performansını nasıl sınayacağımızı tartıştık. Bu sayede yeni MQL5 dilinin gücünü ve sağlamlığını gördük. Gerçek alım satım işlemlerinde kullanılması için hala yapılacak pek çok ayarlama olduğundan EA’mız henüz mükemmel veya tamamlanmış değil.
Hala öğrenilecek başka şeyler var ve MQL5 kılavuzuyla birlikte makaleyi tekrar okumanızı ve bu makalede öğrendiğiniz her şeyi denemenizi istiyorum, yakın gelecekte harika bir EA geliştirici olacağınızdan emin olabilirsiniz.
Keyifli kod yazmalar diliyorum.
MetaQuotes Ltd tarafından İngilizceden çevrilmiştir.
Orijinal makale: https://www.mql5.com/en/articles/100





- Ü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