English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Italiano
MQL5'e Giriş: Basit Uzman Danışman (EA) ve Özel Gösterge nasıl yazılır

MQL5'e Giriş: Basit Uzman Danışman (EA) ve Özel Gösterge nasıl yazılır

MetaTrader 5Örnekler | 9 Aralık 2021, 10:23
470 0
Denis Zyatkevich
Denis Zyatkevich

Giriş

MetaTrader 5 İstemci Terminaline dahil edilen MetaQuotes Programlama Dili 5 (MQL5), MQL4'e kıyasla birçok yeni imkana ve daha yüksek performansa sahiptir. Bu makale, bu yeni programlama dili hakkında bilgi edinmenize yardımcı olacaktır. Uzman Danışman ve Özel Göstergenin nasıl yazılacağına dair basit örnekler bu makalede sunulmuştur. Bu örnekleri anlamak için gerekli olan MQL5 dilinin bazı ayrıntılarını da ele alacağız.

MQL5'in makale ayrıntıları ve tam açıklaması MetaTrader 5’e dahil edilen MQL5 Referansı içinde bulunabilir. MQL5 yerleşik yardımda yer alan bilgiler dili incelemek için yeterlidir. Bu makale, MQL4'e aşina olanlar ve ayrıca alım satım sistemlerini ve göstergelerini programlamaya yeni başlayanlar için yararlı olabilir.


MQL5'i kullanmaya başlarken

MetaTrader 5 alım satım platformu, finansal araçların teknik analizini yapmanıza ve hem manuel hem de otomatik modda alım satım yapmanıza olanak tanır. MetaTrader 5 selefinden farklıdır - MetaTrader 4. Özellikle, sözleşme, pozisyon ve emir kavramları düzeltilmiştir.

  • Pozisyon - bir piyasa taahhüdüdür, satın alınan veya satılan finansal enstrümanın sözleşmelerinin sayısıdır.
  • Emir - belirli koşullar altında bir miktar finansal enstrümanın satın alma veya satış emridir.
  • Sözleşme - aracı tarafından bazı emirlerin yürütülmesi gerçeğidir, bu da pozisyonun açılmasına, değiştirilmesine veya kapatılmasına yol açar.

İstemci Terminali, farklı amaçlarla çeşitli program türleri yazmanıza olanak tanıyan yerleşik programlama dili MQL5'e sahiptir:

  • Uzman Danışman (EA) - belirtilen bazı algoritmalara göre alım satım yapan bir programdır. Uzman Danışman (EA), otomatik alım satım için alım satım sistemini uygulamanızı sağlar (alım satım işlemleri bir yatırımcı olmadan gerçekleştirilebilir). Uzman Danışman (EA) alım satım işlemleri gerçekleştirebilir, pozisyonları açıp kapatabilir ve bekleyen emirleri yönetebilir.
  • Gösterge - verileri grafik formunda sunmayı sağlayan, analiz için uygun olan bir programdır.
  • Komut dosyası - aynı anda bazı işlemler dizisi gerçekleştirmeyi sağlayan bir programdır.

Uzman Danışmanlar (EA), Göstergeler ve Komut Dosyaları, işletim sistemi kitaplıkları da dahil olmak üzere MQL5 standart kitaplığı fonksiyonlarını ve DLL fonksiyonlarını çağırabilir. Diğer dosyalarda bulunan kod parçaları, MQL5 ile yazılmış program metnine dahil edilebilir.

Bir program (Uzman Danışman (EA), Gösterge veya Komut Dosyası) yazmak için MetaTrader 5 İstemci Terminalini başlatabilir ve Araçlar menüsünden MetaQuotes Dil Düzenleyicisini seçebilir veya F4 tuşuna basabilirsiniz.

Şekil 1. MetaEditor’ın Başlatılması.

MetaEditor 5 penceresinde Dosya menüsünden Yeni seçeneğini seçin veya Ctrl+N tuşlarına basın.

Şekil 2. Yeni Program Oluşturma.

MQL5 Sihirbazı penceresinde, oluşturmak istediğiniz programın türünü seçin: 

Şekil 3. MQL5 Sihirbazı.

Bir sonraki adımda, programı başlattıktan sonra kullanıcıdan istenecek program adını, yazar hakkındaki bilgileri ve parametreleri belirtebilirsiniz.

Şekil 4. Uzman Danışmanın (EA) genel özellikleri.

Bundan sonra, kodunuzu düzenleyip doldurabileceğiniz program şablonu (Uzman Danışman (EA), Gösterge veya Komut Dosyası) oluşturulur:

Şekil 5. Yeni bir programın şablonu.

Program hazır olduğunda, bunu derlemek gerekir. Programı derlemek için Dosya menüsünden Derle seçeneğini seçin veya F7 tuşuna basın:

Şekil 6. Program derleme.

Program kodunda hata yoksa, .ex5 uzantılı dosya oluşturulur. Bundan sonra, bu yeni Uzman Danışmanı (EA), Göstergeyi veya Komut Dosyasını yürütme için MetaTrader 5 İstemci Terminali grafiğine ekleyebilirsiniz.

MQL5 programı bir operatör dizisidir. Her operatör noktalı virgül sembolü ";" ile sona erer. Kolaylık için kodunuza "/*" ve */" sembollerinin içine veya"//” sonrasında satır sonuna yerleştirilen yorumlar ekleyebilirsiniz. MQL5 "etkinlik odaklı" programlama dilidir. Bu, belirli etkinlikler (program başlatma veya sonlandırma, yeni teklif gelmesi vb.) gerçekleştiğinde, İstemci Terminalinin kullanıcı tarafından yazılan ve belirtilen işlemleri gerçekleştiren ilgili fonksiyonu (alt program) başlattığı anlamına gelir. İstemci Terminali aşağıdaki önceden tanımlanmış etkinliklere sahiptir:

  • Başlat etkinliği, Komut Dosyası çalışırken gerçekleşir (yalnızca Komut Dosyalarında kullanılır). OnStart fonksiyonunun yürütülmesine yol açar. MQL4 eşdeğeri - Komut Dosyalarında başlat fonksiyonu.
  • Init etkinliği, Uzman Danışman (EA) veya Gösterge başlatıldığında gerçekleşir. OnInit fonksiyonunun yürütülmesine yol açar. MQL4 eşdeğeri - init fonksiyonu.
  • Deinit etkinliği, Uzman Danışman (EA) veya Gösterge sonlandırıldığında gerçekleşir (örneğin, grafikten çıktıktan, İstemci Terminalini kapattıktan vb. sonra). OnDeinit fonksiyonunun yürütülmesine yol açar. MQL4 eşdeğeri - deinit fonksiyonu.
  • NewTick etkinliği, geçerli finansal enstrüman için yeni teklif geldiğinde gerçekleşir (yalnızca Uzman Danışmanlarda (EA) kullanılır). OnTick fonksiyonunun yürütülmesine yol açar. MQL4 eşdeğeri - Uzman Danışmanlarda (EA) başlat fonksiyonu.
  • Calculate etkinliği Gösterge başlatıldığında (OnInit fonksiyonunun yürütülmesinden sonra) ve geçerli finansal enstrüman için yeni teklif geldiğinde (yalnızca Göstergelerde kullanılır) gerçekleşir. OnCalculate fonksiyonunun yürütülmesine yol açar. MQL4 eşdeğeri - Göstergelerde başlat fonksiyonu.
  • Trade etkinliği, emir yürütüldüğünde, değiştirildiğinde veya silindiğinde, pozisyon açıldığında, değiştirildiğinde veya kapatıldığında gerçekleşir (yalnızca Uzman Danışmanlarda (EA) kullanılır). OnTrade fonksiyonunun yürütülmesine yol açar. MQL4’te bu etkinlik ve fonksiyonunun eşdeğeri yoktur.
  • BookEvent etkinliği, Piyasa Derinliği değiştiğinde gerçekleşir (yalnızca Uzman Danışmanlarda (EA) kullanılır). OnBookEvent fonksiyonunun yürütülmesine yol açar. MQL4’te bu etkinlik ve fonksiyonun eşdeğeri ve ayrıca Piyasa Derinliği yoktur.
  • ChartEvent etkinliği, kullanıcı grafikle çalıştığında gerçekleşir: grafik penceresi odaktayken fareye tıklar ve tuşlara basar. Ayrıca grafik nesnelerin oluşturulması, hareketi veya silinmesi sırasında vb. gerçekleşir (Uzman Danışmanlarda ve göstergelerde kullanılır). OnChartEvent fonksiyonunun yürütülmesine yol açar. Bu etkinliğin ve fonksiyonun MQL4’te eşdeğeri yoktur.
  • Timer etkinliği, zamanlayıcı tetiklendiğinde periyodik olarak, EventSetTimer fonksiyonu kullanılarak etkinleştirilirse gerçekleşir. OnTimer fonksiyonunun yürütülmesine yol açar. MQL4’te bu etkinlik ve fonksiyonun eşdeğeri ve ayrıca bir zamanlayıcı yoktur.

Değişkenleri kullanmadan önce, her birinin veri türünü belirtmek gerekir. MQL5, MQL4'ten daha fazla veri türünü destekler:

  • bool mantıksal değerleri (doğru veya yanlış) depolamak için tasarlanmıştır. 1 bayt bellek gerektirir.
  • char aşağıdaki aralıktaki tamsayı değerlerini depolamak için tasarlanmıştır -128 ila 127. 1 bayt bellek gerektirir.
  • uchar 0 ila 255 arasındaki işaretsiz tamsayı değerlerini depolamak için tasarlanmıştır. 1 bayt bellek gerektirir.
  • short -32 768 ila 32 767 arasındaki tamsayı değerlerini depolamak için tasarlanmıştır. 2 bayt bellek gerektirir.
  • ushort, 0 ila 65 535 arasındaki işaretsiz tamsayı değerlerini depolamak için tasarlanmıştır. 2 bayt bellek gerektirir.
  • int, -2 147 483 648 ila 2 147 483 647 arasındaki tamsayı değerlerini depolamak için tasarlanmıştır. 4 bayt bellek gerektirir.
  • uint 0 ila 4 294 967 295 arasındaki işaretsiz tamsayı değerlerini depolamak için tasarlanmıştır. 4 bayt bellek gerektirir.
  • long -9 223 372 036 854 775 808 ila 9 223 372 036 854 775 807 arasındaki tamsayı değerlerini depolamak için tasarlanmıştır. 8 bayt bellek gerektirir.
  • ulong, 0 ila 18 446 744 073 709 551 615 arasındaki işaretsiz tamsayı değerlerini depolamak için tasarlanmıştır. 8 bayt bellek gerektirir.
  • float kayan nokta değerlerini depolamak için tasarlanmıştır. 4 bayt bellek gerektirir.
  • double kayan nokta değerlerini depolamak için tasarlanmıştır. Genellikle fiyat verilerini depolamak için kullanılır. 8 bayt bellek gerektirir.
  • datetime tarih ve saat değerlerini depolamak için tasarlanmıştır, 01.01.1970 00:00:00 tarihinden itibaren geçen saniye sayısıdır. 8 bayt bellek gerektirir.
  • color renk hakkındaki bilgileri depolamak için tasarlanmıştır, üç renk bileşeninin özelliklerini içerir - kırmızı, yeşil ve mavi. 4 bayt bellek gerektirir.
  • enum numaralandırma anlamına gelir. Belirli sınırlı veri setinin bir türünü belirtmeyi sağlar. 4 bayt bellek gerektirir.
  • string metin dizelerini depolamak için tasarlanmıştır. Dahili gösterimi, dize içeren tampon boyutunu ve bu tampona işaretçiyi içeren 8 baytlık bir yapıdır.

En iyi performans ve rasyonel bellek kullanımı için uygun veri türünü seçmek gerekir. MQL5'te yapı denilen yeni bir kavram vardır. Yapı mantıksal olarak ilişkili verileri birleştirir.

Alım satım sistemi

Bu makalede örnek olarak kullanılan alım satım sistemi, Avrupa finans kurumlarının sabah açılması ve daha sonra ekonomik olayların ABD'de yayınlanmasının EURUSD'nin trendine yol açtığı varsayımına dayanmaktadır. Grafik dönemi önemli değildir, ancak dakika çubuklarını kullanmanızı öneririm, çünkü tüm gün (veya bir kısmı) aynı anda görülebilir, bu nedenle gözlem için çok uygundur.

Şekil 7. Alım satım sistemi.

Sabah 7'de (sunucu saati) Alış Durdur ve Satış Durdur bekleyen emirleri, geçerli günün fiyat aralığının bir puan ötesinde bir mesafede verilir. Alış Durdur bekleyen emirleri için spread dikkate alınır. StopLoss seviyeleri aralığın karşıt taraflarına yerleştirilir. Yürütmeden sonra, StopLoss sırası basit hareketli ortalamaya taşınır, ancak yalnızca karlıysa taşınır.

Klasik Takip Eden Durdurma (Trailing Stop) ile karşılaştırıldığında bu tür bir takibin yararı aşağıdaki gibidir: düzeltmelerle fiyat artışları durumunda pozisyonun erken kapanmasını önlemeyi sağlar. Öte yandan, trend sona erdiğinde ve düz hareket başladığında pozisyonun kapanmasına yol açar. Basit hareketli ortalama, dakika grafiği verileri kullanılarak hesaplanır ve şuna eşit ortalama süreye sahiptir 240.

Kar seviyesi mevcut piyasa volatilitesine bağlıdır. Piyasa volatilitesini belirlemek için, Ortalama Gerçek Aralık (ATR) göstergesi (günlük grafiğe uygulanan 5'e eşit dönem ile) kullanılır. Yani, geçen haftanın ortalama günlük aralığını gösterir. Uzun pozisyon için Kar Al seviye değerini belirlemek için ATR göstergesinin değerini geçerli günün minimum fiyatına ekleyeceğiz. Kısa pozisyonlar için de aynı şey geçerlidir: ATR göstergesinin değerini geçerli günün maksimum fiyatından çıkaracağız. Emir fiyat değeri StopLoss ve TakeProfit seviyelerinin ötesindeyse emir alınmaz. Akşam 7’den (sunucu saati) sonra bekleyen tüm emirler silinir ve bu gün verilmez (açık pozisyonlar kapanışa kadar izlenir).


Bir gösterge yazma

Yukarıda açıklanan alım satım sisteminin kar seviyelerini gösteren bir gösterge yazalım.

Bir satırdaki birinci simge "#" ise, bu dizenin bir önişlemci yönergesi olduğu anlamına gelir. Yönergeler ek program özelliklerini belirtmek, sabitleri bildirmek, üst bilgi dosyalarını ve içe aktarılan fonksiyonları dahil etmek için kullanılır. Önişlemci yönergelerinden sonra noktalı virgül (;) sembollerinin olmadığını unutmayın.

#property copyright   "2010, MetaQuotes Software Corp."
#property link        "http://www.mql5.com"
#property description "This indicator calculates TakeProfit levels"
#property description "using the average market volatility. It uses the values"
#property description "of Average True Range (ATR) indicator, calculated"
#property description "on daily price data. Indicator values are calculated"
#property description "using maximal and minimal price values per day."
#property version     "1.00"

Yazar ve web sayfası hakkındaki bilgiler telif hakkı ve bağlantı özelliklerinde belirtilebilir, açıklama özelliği kısa açıklamayı eklemenizi sağlar, sürüm özelliği program sürümünü belirtmenizi sağlar. Gösterge çalışırken bu bilgiler aşağıdaki gibi görünür:

Şekil 8. Gösterge bilgileri.

Göstergelerin konumunu belirtmek gerekir: bir grafikte veya ayrı bir pencerede. Şu özelliklerden birini belirterek yapılabilir: indicator_chart_window veya indicator_separate_window:

#property indicator_chart_window

Ayrıca, kullanılacak gösterge tamponlarının sayısını ve grafik serisinin sayısını belirtmeniz gerekir. Bizim durumumuzda, her birinin kendi tamponu olan iki satır vardır - çizilecek verileri içeren bir dizi.

#property indicator_buffers 2
#property indicator_plots   2

Her gösterge satırı için aşağıdaki özellikleri belirtelim: tür (indicator_type özelliği), renk (indicator_color özelliği), çizim stili (indicator_style özelliği) ve metin etiketi (indicator_label özelliği):

#property indicator_type1   DRAW_LINE
#property indicator_color1  C'127,191,127'
#property indicator_style1  STYLE_SOLID
#property indicator_label1  "Buy TP"
#property indicator_type2   DRAW_LINE
#property indicator_color2  C'191,127,127'
#property indicator_style2  STYLE_SOLID
#property indicator_label2  "Sell TP"

Temel satır türleri şunlardır: DRAW_LINE - çizgiler için, DRAW_SECTION - bölümler için, DRAW_HISTORAM - histogramlar için. Başka birçok çizim stili vardır. Rengi, üç RGB bileşeninin parlaklığını belirterek veya önceden tanımlanmış renkleri, örneğin, Kırmızı, Yeşil, Mavi, Beyaz, vb. kullanarak tanımlayabilirsiniz. Çizgi stilleri şunlardır: STYLE_SOLID - düz çizgi, STYLE_DASH - kesik çizgi, STYLE_DOT - noktalı çizgi, STYLE_DASHDOT - çizgi nokta çizgisi, STYLE_DASHDOTDOT - çizgi-iki nokta.

Şekil 9. Gösterge satırlarının açıklaması.

Giriş değiştiricisini kullanarak, harici değişkenleri (göstergeyi başlattıktan sonra değerlerini belirtebilirsiniz), türlerini ve varsayılan değerlerini belirtelim:

input int             ATRper       = 5;         //ATR Period
input ENUM_TIMEFRAMES ATRtimeframe = PERIOD_D1; //Indicator timeframe

Parametrelerin adları yorumlarda belirtilebilir - bunlar değişkenlerin adları yerine görünürler:

Şekil 10. Göstergenin giriş parametreleri.

Bir global seviyede (tüm fonksiyonlar tarafından görülebilir), göstergemizin farklı fonksiyonları tarafından kullanılacak değişkenleri (ve türlerini) belirteceğiz.

double bu[],bd[];
int hATR;

bu[] ve bd[] dizileri göstergenin üst ve alt satırları için kullanılacaktır. Dinamik diziler (yani belirtilen öğe sayısı olmayan diziler) kullanacağız, çünkü kullanılacak öğelerin sayısını tam olarak bilmiyoruz (boyutları otomatik olarak tahsis edilecektir). Yerleşik teknik göstergenin tanıtıcısı hATR değişkeninde depolanacaktır. Gösterge tanıtıcı, göstergeyi kullanmak için gereklidir.

OnInit fonksiyonu, gösterge çalıştırıldıktan sonra (grafiğe ekledikten sonra) çağrılır.

void OnInit()
  {
   SetIndexBuffer(0,bu,INDICATOR_DATA);
   SetIndexBuffer(1,bd,INDICATOR_DATA);
   hATR=iATR(NULL,ATRtimeframe,ATRper);
  }

SetIndexBuffer fonksiyonu, bu[] ve bd[] dizilerinin göstergenin tamponları olduğu gerçeğini belirtmek için gereklidir;bu gösterge çizgileri olarak çizilen gösterge değerlerini depolamak için kullanılacaktır. Birinci parametre göstergenin tampon dizinini tanımlar, sıralama 0'dan başlar. İkinci parametre, göstergenin tamponuna atanan bir dizi belirtir. Üçüncü parametre, göstergenin tamponu içinde depolanan veri türünü belirtir: INDICATOR_DATA - çizim için veriler, INDICATOR_COLOR_INDEX - çizim rengi, INDICATOR_CALCULATIONS - ara hesaplamalar için yardımcı tamponlar.

IATR fonksiyonu tarafından döndürülen göstergenin tanıtıcısı hATR değişkeninde depolanır. iATR fonksiyonunun birinci parametresi alım satım sembolüdür, NULL - geçerli grafiğin sembolüdür. İkinci parametre, gösterge hesaplaması için kullanılan grafik dönemini belirtir. Üçüncü parametre, ATR göstergesinin ortalama süresidir.

OnCalculate fonksiyonu, OnInit fonksiyonunun yürütülmesi bittikten hemen sonra ve geçerli simge için yeni teklif gelişinden sonra her seferinde çağrılır. Bu fonksiyonu çağırmanın iki yolu vardır. Göstergemizde kullanılan bunlardan biri aşağıdaki gibi görünür:

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[])

OnCalculate fonksiyonunu çağırdıktan sonra, istemci terminali aşağıdaki parametreleri geçirir:  rates_total - geçerli grafikteki çubukların sayısı, prev_calculated  - gösterge tarafından zaten hesaplanan çubuk sayısı, time[], open[], high[], low[], close[], tick_volume[], volume[], spread[] - sırasıyla her çubuk için zaman, açık, yüksek, düşük, kapanış, tick_volume, birim ve spread değerlerini içeren diziler. Hesaplama süresini azaltmak için, önceden hesaplanmış ve değişmemiş gösterge değerlerini yeniden hesaplamak gerekmez. OnCalculate fonksiyonunu çağırdıktan sonra, önceden hesaplanmış çubuk sayısını verir.

OnCalculate fonksiyonunun kodu parantez içine alınmıştır. Fonksiyonda kullanılan yerel değişkenler - türleri ve adları - başlar.

  {
   int i,day_n,day_t;
   double atr[],h_day,l_day;

i değişkeni bir döngü sayacı olarak kullanılır, day_n ve day_t değişkenleri gün sayısını depolamak ve gün içinde maksimum ve minimum fiyat değerlerini hesaplarken gün sayısını geçici olarak depolamak için kullanılır. atr[] dizisi ATR göstergesinin değerlerini depolamak için kullanılır ve h_day ve l_day değişkenleri gün içinde maksimum ve minimum fiyat değerlerini depolamak için kullanılır.

İlk olarak, CopyBuffer fonksiyonunu kullanarak ATR göstergesinin değerlerini atr[] dizisine kopyalamamız gerekir. Bu fonksiyonun birinci parametresi olarak ATR göstergesinin tanıtıcısını kullanacağız. İkinci parametre, göstergenin tamponlarının sayısıdır (numaralandırma 0'dan başlar), ATR göstergesinin yalnızca bir tamponu vardır. Üçüncü parametre, başlanacak birinci öğenin numarasını belirtir, dizin oluşturma geçmişten bugüne gerçekleştirilir, sıfırıncı öğe geçerli (tamamlanmamış) çubuğa karşılık gelir. Dördüncü parametre, kopyalanacak öğe sayısını belirtir.

Yalnızca son (tamamlanmış) çubuğa karşılık gelen sondan bir önceki öğeyle ilgilendiğimiz için iki öğe kopyalayalım. Son parametre, verilerin kopyalanacağı hedef dizidir.

   CopyBuffer(hATR,0,0,2,atr);

Dizi dizin oluşturma yönü AS_SERIES bayrağına bağlıdır. Bu, ayarlanırsa (yani doğru ile eşitse), dizi zaman serisi olarak kabul edilir, öğeler dizinleme en son verilerden en eskisine kadar gerçekleştirilir. Bu, ayarlamazsa (yani yanlış ile eşitse), eski öğeler daha düşük dizine sahiptir, en yeni öğeler daha büyüğüne sahiptir.

   ArraySetAsSeries(atr,true);

atr[] dizisi için ArraySetAsSeries fonksiyonunu kullanarak AS_SERIES bayrağını doğru olarak ayarladık (bu fonksiyonun birinci parametresi bayrağın değiştirilmesi gereken dizidir, ikinci parametre yeni bayrak değeridir). Şimdi, geçerli (tamamlanmamış) çubuğun dizini 0'a, sondan bir önceki (tamamlanmış) çubuğun dizini 1'e eşittir.

For operatörü bir döngü oluşturmayı sağlar.

   for(i=prev_calculated;i<rates_total;i++)
     {
      day_t=time[i]/PeriodSeconds(ATRtimeframe);
      if(day_n<day_t)
        {
         day_n=day_t;
         h_day=high[i];
         l_day=low[i];
        }
        else
        {
         if(high[i]>h_day) h_day=high[i];
         if(low[i]<l_day) l_day=low[i];
        }
      bu[i]=l_day+atr[1];
      bd[i]=h_day-atr[1];
     }

For operatöründen sonra köşeli parantezdeki birinci operatör bir ifadedir: i=prev_calculated. Sonra, bir ifade var, bizim durumumuzda şudur: i<rates_total. Bu bir döngü koşuludur - döngü, bu doğruyken yürütülür. Üçüncüsü, döngünün her yürütülmesinden sonra yürütülen ifadedir. Bizim durumumuzda, bu i++’dur (i=i+1'e eşittir ve i değişkenini 1 artırma anlamına gelir).

Döngümüzde i değişkeni, prev_calculated değerinden adım 1 ile rates_total-1’e eşit olan değere değişir. Geçmiş veri dizileri (time[], high[] ve low[]) varsayılan olarak zaman serisi değildir, sıfırıncı dizin geçmişteki en eski çubuğa karşılık gelir, sonuncusu geçerli tamamlanmamış çubuğa karşılık gelir. Döngüde, birinci hesaplanmamış (prev_calculated) ila son çubuk (rates_total-1) arasındaki tüm çubuklar işlenir. Bu çubukların her biri için göstergemizin değerlerini hesaplıyoruz.

Time[] dizisindeki süre değerleri, 01.01.1970 00:00:00'dan itibaren geçen saniye sayısı olarak depolanır. Gün içinde (veya başka bir dönemde) bunu saniye sayısına bölersek, sonucun tamsayı kısmı 01.01.1970'ten (veya başka bir dönemde) başlayan gün sayısı olacaktır. PeriodSeconds fonksiyonu, parametre olarak tanımlanan zaman dilimindeki saniye sayısını verir. day_t değişkeni, i dizini olan çubuğa karşılık gelen gün sayısıdır. day_n değişkeni, en yüksek ve en düşük fiyat değerlerinin hesaplandığı günün numarasıdır.

Eğer operatörünü düşünelim. Köşeli parantezdeki ifade doğru ise, operatör eğer anahtar sözcüğünden sonra operatör yürütülür. Yanlış ise, operatör diğer anahtar sözcüğünden sonra yürütülür. Her operatör bileşik olabilir, yani birkaç operatörden oluşabilir, bizim durumumuzda bunlar parantez içine alınmıştır.

İşlenen günün en yüksek ve en düşük fiyat değerleri sırasıyla h_day ve l_day değişkenlerinde depolanır. Bizim durumumuzda, aşağıdaki durumu kontrol ediyoruz: analiz edilen çubuk yeni güne karşılık geliyorsa, maksimum ve minimum fiyat değerlerini tekrar hesaplamaya başlarız, aksi takdirde devam ederiz. Her gösterge satırı için değerleri hesaplıyoruz: üst çizgi için - minimum günlük fiyatı kullanıyoruz, alt satır için - fiyatın maksimum değerlerini kullanıyoruz.

OnCalculate fonksiyonunun sonunda dönüş operatörü hesaplanan çubukların sayısını verir.

   return(rates_total);
  }

DeInit fonksiyonu içinde (gösterge grafikten kaldırıldığında veya İstemci Terminali kapatıldığında çalışır) ATR göstergesi tarafından ayrılan bellek IndicatorRelease fonksiyonu kullanılarak serbest bırakılır. Bu fonksiyonun yalnızca bir parametresi vardır - göstergenin tanıtıcısı.

void OnDeinit(const int reason)
  {
   IndicatorRelease(hATR);
  }

Şimdi göstergemiz tamamlandı. Derlemek için MetaEditor'da Dosya menüsünden Derle seçeneğini seçin veya F7 tuşuna basın. Kodda hata yoksa, derleme başarılı olur. Derleme sonuçları Araç Kutusu penceresinin Hatalar sekmesine yazdırılır. Sizin durumunuzda, derleyici aşağıdaki dize için "Dönüşüm olası veri kaybı" uyarısını yazdırabilir:

      day_t=time[i]/PeriodSeconds(ATRtimeframe);

Bu satırda kesirli kısmı kasıtlı olarak atıyoruz, dolayısıyla bu veri kaybı bir hata değildir.

Bir gösterge tamamlandığında ve derlendiğinde, MetaTrader 5 İstemci Terminalindeki grafiklere eklenebilir veya diğer Göstergelerde, Uzman Danışmanlarda (EA) veya Komut Dosyalarında kullanılabilir. Bu göstergenin kaynak kodu bu makalede ek olarak mevcuttur.


Bir Uzman Danışmanın (EA) Yazılması

Şimdi yukarıda açıklanan alım satım sistemini uygulayan bir Uzman Danışman (EA) yazma zamanı. Sadece bir finansal enstrümanı alıp satacağını varsayacağız. Birkaç Uzman Danışmanın (EA) bir enstrüman üzerinde işlem yapabilmesi için, her birinin genel pozisyona katkısını dikkatlice analiz etmek gerekir ve bu, makalenin kapsamı dışındadır.

#property copyright   "2010, MetaQuotes Software Corp."
#property version     "1.00"
#property description "This Expert Advisor places the pending orders during the"
#property description "time from StartHour till EndHour on the price levels, that"
#property description "are 1 point below/lower the current trade range."
#property description "The StopLoss levels are placed at the opposite side"
#property description "of the price range. After order execution, the TakeProfit value"
#property description "is set at the 'indicator_TP' level. The StopLoss level is moved"
#property description "to the SMA values only for the profitable orders."

Bu önişlemci yönergelerinin amacı Gösterge Yazma bölümünde zaten dikkate alınmıştır. Bunlar, Uzman Danışman (EA) için de aynı şekilde çalışır.

Giriş parametrelerinin değerlerini (Uzman Danışmanı (EA) başlattıktan sonra kullanıcı tarafından tanımlanabilen), türlerini ve varsayılan değerlerini belirtelim.

input int    StartHour = 7;
input int    EndHour   = 19;
input int    MAper     = 240;
input double Lots      = 0.1;

StartHour ve EndHour parametreleri, bekleyen emirler için zaman dilimini (başlangıç ve bitiş saatleri) tanımlar. MAper parametresi, takibi sırasında açılan pozisyonun StopLoss seviyesi için kullanılan basit hareketli ortalamanın ortalama süresini tanımlar. Lots parametresi, alım satımda kullanılan finansal enstrümanın hacmini tanımlar.

Farklı alım satım fonksiyonlarında kullanılacak global değişkenleri belirtelim:

int hMA,hCI;

hMA değişkeni MA göstergesinin tanıtıcısını depolamak için kullanılacak ve hCI değişkeni özel göstergenin tanıtıcısını depolamak için kullanılacaktır (bu, yukarıda yazılmış bir göstergedir).

Uzman Danışman (EA) başlatıldığında OnInit fonksiyonu yürütülür.

void OnInit()
  {
   hMA=iMA(NULL,0,MAper,0,MODE_SMA,PRICE_CLOSE);
   hCI=iCustom(NULL,0,"indicator_TP");
  }

Bu fonksiyonda MA göstergesinin ve özel göstergemizin tanıtıcılarını alırız. iMA fonksiyonu ve parametreleri, yukarıda açıklanan iATR fonksiyonuyla aynı şekilde kullanılır.

iCustom fonksiyonunun birinci parametresi, enstrümanın sembolik adıdır, NULL - geçerli grafik için enstrüman anlamına gelir. İkinci parametre - grafik zaman dilimidir, veriler göstergeyi hesaplamak için kullanılır, 0 - geçerli grafik için süre anlamına gelir. Üçüncü parametre göstergenin dosya adıdır (uzantı olmadan). Dosya yolu MQL5\Indicators\ klasörüne göredir.

Her yeni teklif geldikten sonra yürütülen OnTick fonksiyonunu oluşturalım:

void OnTick()
  {

Fonksiyonun kodu parantez içine alınmıştır.

Önceden tanımlanmış veri yapılarını, Uzman Danışmanda (EA) neler kullanılacağını belirtelim:

   MqlTradeRequest request;
   MqlTradeResult result;
   MqlDateTime dt;
Önceden tanımlanmış MqlTradeRequest yapısı, alım satım işlemlerinde OrderSend fonksiyonuna geçirilen emirlere ve pozisyon parametrelerine sahiptir. MqlTradeResult yapısının amacı, OrderSend fonksiyonu tarafından verilen alım satım işlemi sonuçları hakkındaki bilgileri depolamaktır. Önceden tanımlanmış MqlDateTime yapısının amacı tarih ve saat bilgilerini depolamaktır.

OnTick fonksiyonunda kullanılacak yerel değişkenleri (ve türlerini) belirtelim:

   bool bord=false, sord=false;
   int i;
   ulong ticket;
   datetime t[];
   double h[], l[], ma[], atr_h[], atr_l[],
          lev_h, lev_l, StopLoss,
          StopLevel=_Point*SymbolInfoInteger(Symbol(),SYMBOL_TRADE_STOPS_LEVEL),
          Spread   =NormalizeDouble(SymbolInfoDouble(Symbol(),SYMBOL_ASK) - SymbolInfoDouble(Symbol(),SYMBOL_BID),_Digits);
bord ve sord boolean değişkenleri, Alış Durdur ve Satış Durdur bekleyen emirlerinin varlığını gösteren bayraklar olarak kullanılır. Varsa, karşılık gelen değişkenin değeri doğru ile eşittir, aksi takdirde bu yanlıştır. i değişkeni döngü operatörlerinde bir sayaç olarak ve bir depolama ara verisi için kullanılır. Bekleyen emrin bileti bilet değişkeninde depolanır.

t[], h[] ve l[] dizileri, geçmiş verilerindeki her çubuk için süreyi, maksimum ve minimum fiyat değerlerini depolamak için kullanılır. Ma[] dizisi MA göstergesinin değerlerini depolamak için kullanılır, atr_h[] ve atr_l[] dizileri, oluşturduğumuz indicator_TP özel göstergesinin üst ve alt satırlarının değerlerini depolamak için kullanılır.

lev_h ve lev_l değişkenleri, geçerli günün maksimum ve minimum fiyat değerlerini ve bekleyen emirlerin açılış fiyatlarını depolamak için kullanılır. StopLoss değişkeni, açılan pozisyonun Zararı Durdur fiyatını geçici olarak depolamak için kullanılır.

StopLevel değişkeni, geçerli fiyat ile bekleyen emir fiyatı (fiyat birimleri cinsinden) arasındaki minimum mesafe olan STOP_LEVEL değerini depolamak için kullanılır. Bu değeri, nokta cinsinde tanımlanan STOP_LEVEL değişkeninin değerine göre nokta fiyatının (önceden tanımlanmış _Point değişkeni) bir ürünü olarak hesaplıyoruz. STOP_LEVEL değeri SymbolInfoInterger fonksiyonu tarafından verilir. Bu fonksiyonun birinci parametresi sembol adıdır, ikincisi - istenen özelliğin tanımlayıcısıdır. Sembol (finansal enstrümanın adı) Symbol fonksiyonu kullanılarak elde edilebilir (parametresi yoktur).

Spread değeri, spread değerini (fiyat birimlerinde) depolamak için kullanılır. Değeri, NormalizeDouble fonksiyonu kullanılarak normalleştirilen geçerli Satış ve Teklif değerleri arasındaki fark olarak hesaplanır. Bu fonksiyonun birinci parametresi normalleştirmek için çift tür değeridir, ikincisi önceden tanımlanmış _Digits değişkeninden elde ettiğimiz noktadan sonraki basamak sayısıdır. Geçerli Satış ve Teklif değerleri SymbolInfoDouble fonksiyonu kullanılarak elde edilebilir. Bu fonksiyonun birinci parametresi sembol adıdır, ikincisi - özellik tanımlayıcısıdır.

Yapı isteğini, OrderSend fonksiyon çağrılarının çoğunda yaygın olacak değerlerle dolduralım:

   request.symbol      =Symbol();
   request.volume      =Lots;
   request.tp          =0;
   request.deviation   =0;
   request.type_filling=ORDER_FILLING_FOK;
request.symbol öğesi, alım satım yapan enstrümanın sembolik adıdır, request.volume öğesi - finansal enstrümanın hacmi (sözleşme boyutu), request.tp - TakeProfit fonksiyonunun nümerik değeri (bazı durumlarda bunu kullanmayacağız ve 0 ile dolduracağız), request.deviation - alım satım işlemini yürütme sırasında fiyatın izin verilen sapması, request.type_filling - emir türü, aşağıdakilerden biri olabilir:
  • ORDER_FILLING_FOK - sözleşme sadece hacim aşağıda belirtilen sıraya eşitse veya daha iyiyse yürütülebilir. Yeterli hacim yoksa, emir yürütülmez.
  • ORDER_FILLING_IOC - yeterli hacim yok, emir maksimum kullanılabilir piyasa hacminde yürütülecek. 
  • ORDER_FILLING_RETURN - ORDER_FILING_IOC ile aynı ancak bu durumda eksik hacim için ek bir emir verilecektir.

TimeCurrent fonksiyonunu kullanarak geçerli sunucu saatini (son teklifin zamanı) alalım. Bu fonksiyonun tek parametresi, sonuca sahip yapıya işaretçidir.

   TimeCurrent(dt);

Tüm hesaplamalarımız için yalnızca geçerli gün için geçmiş fiyat verilerine ihtiyacımız vardır. Gerekli olan çubuk sayısı (ve bazı rezervlerle) şu formül kullanılarak hesaplanabilir: i = (dt.hour + 1)*60, burada dt.hour - geçerli saati içeren yapı öğesidir. Zaman, maksimal ve minimum fiyat değerleri sırasıyla CopyTime,CopyHigh, CopyLow fonksiyonları kullanılarak t[], h[] ve l[] dizilerine kopyalanır:

   i=(dt.hour+1)*60;
   if(CopyTime(Symbol(),0,0,i,t)<i || CopyHigh(Symbol(),0,0,i,h)<i || CopyLow(Symbol(),0,0,i,l)<i)
     {
      Print("Can't copy timeseries!");
      return;
     }

CopyTime, CopyHigh ve CopyLow fonksiyonlarındaki birinci parametre - sembol adıdır, ikincisi - grafik zaman dilimidir, üçüncüsü - kopyalanması gereken başlangıç öğesidir, dördüncüsü - kopyalanması gereken öğe sayısıdır, beşincisi - veriler için hedef dizidir. Bu fonksiyonların her biri kopyalanan öğelerin sayısını veya hata durumunda -1'e eşit negatif değeri verir.

if operatörü, üç dizi için de kopyalanan öğelerin sayısını denetlemek için kullanılır. Kopyalanan öğelerin sayısı hesaplama için gerekenden daha küçükse (dizilerden biri için bile) veya hata durumunda, "Zaman serisi kopyalanamıyor!" mesajını Uzmanlar günlüğüne yazdırır ve dönüş operatörünü kullanarak OnTick fonksiyonunun yürütülmesini sonlandırır. Mesaj Print fonksiyonu ile yazdırılır. Virgülle ayrılmış her türlü veriyi yazdırabilir.

Fiyat verileri t[], h[] ve l[] dizilerine kopyalandığında, yukarıda dikkate alınan ArraySetAsSeries fonksiyonunu kullanarak AS_SERIES bayrağını doğru olarak ayarladık. Dizi dizin oluşturmayı zaman serisi olarak ayarlamak gerekir (geçerli fiyatlardan eski fiyatlara doğru):

   ArraySetAsSeries(t,true);
   ArraySetAsSeries(h,true);
   ArraySetAsSeries(l,true);

Geçerli günün maksimum ve minimum fiyat değerleri lev_h ve lev_l değişkenlerine yerleştirilir:

   lev_h=h[0];
   lev_l=l[0];
   for(i=1;i<ArraySize(t) && MathFloor(t[i]/86400)==MathFloor(t[0]/86400);i++)
     {
      if(h[i]>lev_h) lev_h=h[i];
      if(l[i]<lev_l) lev_l=l[i];
     }

Geçerli güne ait çubuklarla aramayı kısıtlamak için döngü yalnızca MathFloor(t[i]/86400) == MathFloor(t[0]/86400) koşulu doğruysa yürütülür. Bu ifadenin sol tarafında, sağda bir dizi geçerli çubuk günü vardır - geçerli günün sayısı (86400, gündeki saniye sayısıdır). MathFloor fonksiyonu nümerik değeri yuvarlar, yani pozitif değerler içinyalnızca tamsayı bir parça kullanır. Bu fonksiyonun tek parametresi yuvarlanacak ifadedir. MQL5'te ve MQL4'te eşitlik "==" sembolleri ile tanımlanır (bkz. İlişkilerin işlemleri).

Emir fiyatları şu şekilde hesaplanır: Alış Durdur türünün bekleyen emri için bir noktayı (önceden tanımlanmış _Point değişkeni fiyat birimlerindeki nokta boyutuna eşittir) ve Spread’i lev_h değişkenine ekliyoruz (lev_h+=Spread+_Point veya lev_h=lev_h+Spread+_Point). Satış Durdur türünün bekleyen emirleri için lev_l değişken değerinden bir puan çıkartıyoruz (lev_l-=_Point veya lev_l=lev_l-_Point).

   lev_h+=Spread+_Point;
   lev_l-=_Point;

Daha sonra, CopyBuffer fonksiyonunu kullanarak değerleri göstergenin tamponlarından dizilere kopyalıyoruz. MA değerleri ma[] dizisine kopyalanır, özel göstergemizin üst satır değerleri atr_h[] dizisine kopyalanır, göstergenin alt satır değerleri atr_l[] dizisine kopyalanır. CopyBuffer fonksiyonu, göstergenin ayrıntılarını ele alırken yukarıda açıklanmıştır.

   if(CopyBuffer(hMA,0,0,2,ma)<2 || CopyBuffer(hCI,0,0,1,atr_h)<1 || CopyBuffer(hCI,1,0,1,atr_l)<1)
     {
      Print("Can't copy indicator buffer!");
      return;
     }

Sondan bir önceki (son tamamlanan) çubuğa karşılık gelen MA gösterge değerine ve son çubuğa karşılık gelen göstergemizin değerine ihtiyacımız var, bu nedenle bu iki öğeyi ma[] dizisine ve bir öğeyi atr_h[] ve atr_l[] dizilerine kopyalıyoruz. Kopyalama yapılırken hata olması durumunda veya kopyalanan değerlerin sayısı gerekenden azsa (bu dizilerden herhangi biri için), mesaj Uzmanlar günlüğüne yazdırılır ve OnTick fonksiyonu dönüş operatörü kullanılarak sonlandırılır.

ma[] dizisi için, dizinin zaman serisi dizin oluşturmasını gösteren AS_SERIES bayrağını ayarlıyoruz.

   ArraySetAsSeries(ma,true);

atr_[] ve atr_l[] dizilerinin yalnızca bir öğesi vardır, bu nedenle zaman serisi dizini oluşturma önemli değildir. atr_l[0] değeri TakeProfit seviyesini belirlemek için daha fazla kullanılacağından, kısa pozisyonlar Satış fiyatından kapatılmıştır, ancak Teklif fiyatları fiyat grafiklerinde kullanıldığı için spread'i atr_l[0] değerine ekliyoruz.

   atr_l[0]+=Spread;

PositionsTotal fonksiyonu açılan konum sayısını verir (parametresi yoktur). Pozisyonların dizinleri 0'dan başlar. Açılan tüm pozisyonları arayacak bir döngü oluşturalım:

// in this loop we're checking all opened positions
   for(i=0;i<PositionsTotal();i++)
     {
      // processing orders with "our" symbols only
      if(Symbol()==PositionGetSymbol(i))
        {
         // we will change the values of StopLoss and TakeProfit
         request.action=TRADE_ACTION_SLTP;
         // long positions processing
         if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
           {
            // let's determine StopLoss
            if(ma[1]>PositionGetDouble(POSITION_PRICE_OPEN)) StopLoss=ma[1]; else StopLoss=lev_l;
            // if StopLoss is not defined or lower than needed            
            if((PositionGetDouble(POSITION_SL)==0 || NormalizeDouble(StopLoss-PositionGetDouble(POSITION_SL),_Digits)>0
               // if TakeProfit is not defined or higer than needed
               || PositionGetDouble(POSITION_TP)==0 || NormalizeDouble(PositionGetDouble(POSITION_TP)-atr_h[0],_Digits)>0)
               // is new StopLoss close to the current price?
               && NormalizeDouble(SymbolInfoDouble(Symbol(),SYMBOL_BID)-StopLoss-StopLevel,_Digits)>0
               // is new TakeProfit close to the current price?
               && NormalizeDouble(atr_h[0]-SymbolInfoDouble(Symbol(),SYMBOL_BID)-StopLevel,_Digits)>0)
              {
               // putting new value of StopLoss to the structure
               request.sl=NormalizeDouble(StopLoss,_Digits);
               // putting new value of TakeProfit to the structure
               request.tp=NormalizeDouble(atr_h[0],_Digits);
               // sending request to trade server
               OrderSend(request,result);
              }
           }
         // short positions processing
         if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
           {
            // let's determine the value of StopLoss
            if(ma[1]+Spread<PositionGetDouble(POSITION_PRICE_OPEN)) StopLoss=ma[1]+Spread; else StopLoss=lev_h;
            // if StopLoss is not defined or higher than needed
            if((PositionGetDouble(POSITION_SL)==0 || NormalizeDouble(PositionGetDouble(POSITION_SL)-StopLoss,_Digits)>0
               // if TakeProfit is not defined or lower than needed
               || PositionGetDouble(POSITION_TP)==0 || NormalizeDouble(atr_l[0]-PositionGetDouble(POSITION_TP),_Digits)>0)
               // is new StopLoss close to the current price?
               && NormalizeDouble(StopLoss-SymbolInfoDouble(Symbol(),SYMBOL_ASK)-StopLevel,_Digits)>0
               // is new TakeProfit close to the current price?
               && NormalizeDouble(SymbolInfoDouble(Symbol(),SYMBOL_ASK)-atr_l[0]-StopLevel,_Digits)>0)
              {
               // putting new value of StopLoss to the structure
               request.sl=NormalizeDouble(StopLoss,_Digits);
               // putting new value of TakeProfit to the structure
               request.tp=NormalizeDouble(atr_l[0],_Digits);
               // sending request to trade server
               OrderSend(request,result);
              }
           }
         // if there is an opened position, return from here...
         return;
        }
     }
Döngünün içindeki eğer operatörünü kullanarak, geçerli grafiğin sembolü için açılmış pozisyonları seçiyoruz. PositionGetSymbol fonksiyonu, enstrümanın sembolünü verir, bunun yanı sıra, çalışmak için pozisyonu otomatik olarak seçer. Fonksiyonun yalnızca bir parametresi vardır - açılan pozisyonlar listesindeki konum dizini. Açılan pozisyonların StopLoss ve TakeProfit değerlerini değiştirmek gerekmesi oldukça olasıdır, bu nedenle TRADE_ACTION_SLTP değerini request.action öğesine koyalım. Daha sonra, yönüne bağlı olarak, pozisyonlar uzun ve kısa olanlara ayrılır.

Uzun pozisyonlar için StopLoss seviyesi aşağıdaki gibi belirlenir: MA gösterge değeri pozisyonun açılış fiyatından yüksekse, StopLoss değerinin MA gösterge değerine eşit olduğu varsayılır, aksi takdirde StopLoss değerinin lev_l değişken değerine eşit olduğu varsayılır. Açılan pozisyon için StopLoss geçerli değeri, yalnızca bir parametresi - pozisyon özelliği tanımlayıcısı - olan PositionGetDouble fonksiyonu kullanılarak belirlenir. StopLoss değeri açılan pozisyon için tanımlanmamışsa (0'a eşitse) veya olması gerekenden daha yüksekse - bu pozisyon için StopLoss ve TakeProfit değerlerini değiştireceğiz. TakeProfit değeri tanımlanmamışsa (0'a eşitse) veya olması gerekenden daha yüksekse (göstergemizin üst satırının değerinden daha yüksekse) - bu pozisyon için StopLoss ve TakeProfit değerlerini değiştireceğiz.

StopLoss ve TakeProfit değerlerinin değişme olasılığını kontrol etmemiz gerekir. StopLoss'un yeni değeri geçerli Teklif fiyatından daha düşük (en azından STOP_LEVEL değerine göre) olmalıdır, TakeProfit'in yeni değeri en azından STOP_LEVEL değerine göre geçerli Teklif fiyatından büyük olmalıdır. Karşılaştırma için normalleştirilmiş farkı kullandık, çünkü karşılaştırılan değerler, çift türdeki kayan nokta ikili sayılarının kayan nokta ondalık sayılarına dönüştürülmesinden kaynaklanan yanlışlık nedeniyle son basamaklarda farklılık gösterebilir.

Açılan pozisyon için StopLoss veya TakeProfit seviyelerini değiştirmek gerekiyorsa ve yeni değerler alım satım kurallarına göre geçerliyse, StopLoss ve TakeProfit'in yeni değerlerini yapının karşılık gelen öğelerine koyar ve alım satım sunucusuna veri göndermek için OrderSend fonksiyonunu çağırırız.

Kısa pozisyonlar için StopLoss ve TakeProfit değerlerinin değiştirilmesi aynıdır. Kısa pozisyonlar, uzunlarla karşılaştırıldığında, Satış fiyatları ile kapatılmıştır, bu nedenle Satış değerleri karşılaştırma için kullanılacaktır. Geçerli grafik için açılmış bir pozisyon varsa - dönüş operatörünü kullanarak OnTick fonksiyonunun yürütülmesini sonlandırırız.

OrdersTotal fonksiyonu (parametresi yoktur) bekleyen emirlerin sayısını verir. Dizinler 0'dan başlar. Bekleyen tüm emirleri işlemek için kullanılacak bir döngü oluşturalım:

// in this loop we're checking all pending orders
   for(i=0;i<OrdersTotal();i++)
     {
      // choosing each order and getting its ticket
      ticket=OrderGetTicket(i);
      // processing orders with "our" symbols only
      if(OrderGetString(ORDER_SYMBOL)==Symbol())
        {
         // processing Buy Stop orders
         if(OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_STOP)
           {
            // check if there is trading time and price movement is possible
            if(dt.hour>=StartHour && dt.hour<EndHour && lev_h<atr_h[0])
              {
               // if the opening price is lower than needed
               if((NormalizeDouble(lev_h-OrderGetDouble(ORDER_PRICE_OPEN),_Digits)>0
                  // if StopLoss is not defined or higher than needed
                  || OrderGetDouble(ORDER_SL)==0 || NormalizeDouble(OrderGetDouble(ORDER_SL)-lev_l,_Digits)!=0)
                  // is opening price close to the current price?
                  && NormalizeDouble(lev_h-SymbolInfoDouble(Symbol(),SYMBOL_ASK)-StopLevel,_Digits)>0)
                 {
                  // pending order parameters will be changed
                  request.action=TRADE_ACTION_MODIFY;
                  // putting the ticket number to the structure
                  request.order=ticket;
                  // putting the new value of opening price to the structure
                  request.price=NormalizeDouble(lev_h,_Digits);
                  // putting new value of StopLoss to the structure
                  request.sl=NormalizeDouble(lev_l,_Digits);
                  // sending request to trade server
                  OrderSend(request,result);
                  // exiting from the OnTick() function
                  return;
                 }
              }
            // if there is no trading time or the average trade range has been passed
            else
              {
               // we will delete this pending order
               request.action=TRADE_ACTION_REMOVE;
               // putting the ticket number to the structure
               request.order=ticket;
               // sending request to trade server
               OrderSend(request,result);
               // exiting from the OnTick() function
               return;
              }
            // setting the flag, that indicates the presence of Buy Stop order
            bord=true;
           }
         // processing Sell Stop orders
         if(OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_STOP)
           {
            // check if there is trading time and price movement is possible
            if(dt.hour>=StartHour && dt.hour<EndHour && lev_l>atr_l[0])
              {
               // if the opening price is higher than needed
               if((NormalizeDouble(OrderGetDouble(ORDER_PRICE_OPEN)-lev_l,_Digits)>0
                  // if StopLoss is not defined or lower than need
                  || OrderGetDouble(ORDER_SL)==0 || NormalizeDouble(lev_h-OrderGetDouble(ORDER_SL),_Digits)>0)
                  // is opening price close to the current price?
                  && NormalizeDouble(SymbolInfoDouble(Symbol(),SYMBOL_BID)-lev_l-StopLevel,_Digits)>0)
                 {
                  // pending order parameters will be changed
                  request.action=TRADE_ACTION_MODIFY;
                  // putting ticket of modified order to the structure
                  request.order=ticket;
                  // putting new value of the opening price to the structure
                  request.price=NormalizeDouble(lev_l,_Digits);
                  // putting new value of StopLoss to the structure
                  request.sl=NormalizeDouble(lev_h,_Digits);
                  // sending request to trade server
                  OrderSend(request,result);
                  // exiting from the OnTick() function
                  return;
                 }
              }
            // if there is no trading time or the average trade range has been passedе
            else
              {
               // we will delete this pending order
               request.action=TRADE_ACTION_REMOVE;
               // putting the ticket number to the structure
               request.order=ticket;
               // sending request to trade server
               OrderSend(request,result);
               // exiting from the OnTick() function
               return;
              }
            // setting the flag, that indicates the presence of Sell Stop order
            sord=true;
           }
        }
     }
OrderGetTicket fonksiyonunu kullanarak, bununla daha fazla çalışmak için emri seçiyoruz ve emir biletini bilet değişkenine kaydediyoruz. Bu fonksiyonun yalnızca bir parametresi vardır - açılan emirler listesindeki emir dizini. OrderGetString fonksiyonu sembolün adını almak için kullanılır. Yalnızca bir parametresi vardır - emir özelliği tanımlayıcısı. Sembol adını mevcut grafiğin adıyla karşılaştırıyoruz, emirleri yalnızca Uzman Danışmanın (EA) çalıştığı enstrümana göre seçmeye izin verir. Emir türü, karşılık gelen emir türü tanımlayıcısına sahip OrderGetInteger fonksiyonu ile belirlenir. Alış Durdur ve Satış Durdur emirlerini ayrı olarak işleyeceğiz.

Geçerli saat StartHour ile EndHour aralığındaysa ve Alış Durdur emrinin açılış fiyatı göstergenin üst satırını geçmiyorsa, açılış fiyatını ve StopLoss seviyesinin değerini (gerekirse) değiştiririz, aksi takdirde emri sileriz.

Daha sonra bekleyen emir için açılış fiyatını veya StopLoss seviyesini değiştirmemiz gerekip gerekmediğini belirleriz. Alış Durdur emrinin açılış fiyatı olması gerekenden düşükse veya StopLoss tanımlanmamışsa veya daha yüksekse, TRADE_ACTION_MODIFY değerini request.action öğesine koyarız - bu bekleyen emir parametrelerinin değiştirilmesi gerektiği anlamına gelir. Ayrıca emir biletini request.ticket öğesine koyarız ve OrderSend fonksiyonunu kullanarak alım satım sunucusuna alım satım isteği göndeririz. Buna karşılık, açılış fiyatının olması gerekenden daha düşük olduğu durumu belirleriz, çünkü spread değeri değişebilir, ancak her spread değişikliğinden sonra emri değiştirmeyiz, bu, maksimum spread değerine karşılık gelen en yüksek seviyede ayarlanır.

Ayrıca, yalnızca StopLoss'un değerinin olması gerekenden daha yüksek olduğu durumlarda belirleyeceğiz, çünkü fiyat aralığı gün içinde genişliyor olabilir ve fiyatın yeni düşüklerinden sonra StopLoss emrini değerini aşağı taşımak gerekir. Alım satım sunucusuna istek gönderdikten sonra, OnTick fonksiyonunun yürütülmesi dönüş operatörü kullanılarak sonlandırılır. Alış Durdur emirleri varsa bord değişkeni değeri doğruolarak ayarlanır.

Satış Durdur emirleri, Alış Durdur emirleri ile aynı şekilde işlenir.

Şimdi bunların olmadığı durumda, Alış Durdur ve Satış Durdur bekleyen emirlerini verelim. TRADE_ACTION_PENDING değerini request.action öğesine koyarız (bu, bekleyen emrin verildiği anlamına gelir).

   request.action=TRADE_ACTION_PENDING;

Geçerli saatin değeri emir verme süresi içindeyse, emirleri veririz:

   if(dt.hour>=StartHour && dt.hour<EndHour)
     {
      if(bord==false && lev_h<atr_h[0])
        {
         request.price=NormalizeDouble(lev_h,_Digits);
         request.sl=NormalizeDouble(lev_l,_Digits);
         request.type=ORDER_TYPE_BUY_STOP;
         OrderSend(request,result);
        }
      if(sord==false && lev_l>atr_l[0])
        {
         request.price=NormalizeDouble(lev_l,_Digits);
         request.sl=NormalizeDouble(lev_h,_Digits);
         request.type=ORDER_TYPE_SELL_STOP;
         OrderSend(request,result);
        }
     }
  }
Alış Durdur ve Satış Durdur emirlerini verirken bord ve sord değişkenlerinin değerlerini analiz ederek aynı emirlerin varlığını kontrol ederiz. Ayrıca aşağıdaki koşulu kontrol ederiz: Emir fiyatı göstergemizin değerleri içinde olmalıdır. Emrin normalleştirilmiş fiyatı request.price öğesine yerleştirilir, normalleştirilmiş StopLoss değeri request.sl değişkenine yerleştirilir, emir türü (ORDER_BUY_STOP veya ORDER_SELL_STOP) request.type değişkenine yerleştirilir. Bundan sonra alım satım sunucusuna istek göndeririz. OnTick fonksiyonunun kodu noktalı virgülle sona erer.

Göstergeler tarafından ayrılan kaynaklar, yukarıda ele alınan IndicatorRelease fonksiyonu kullanılarak OnDeinit fonksiyonu içinde serbest bırakılır.

void OnDeinit(const int reason)
  {
   IndicatorRelease(hCI);
   IndicatorRelease(hMA);
  }

Uzman Danışman (EA) tamamlandı, herhangi bir hata yoksa derleme başarılı olmalıdır. Şimdi bunu grafiğe ekleyerek çalıştırabiliriz. Kaynak kodu bu makalenin eklerinde indirilebilir.


Başlatma ve Hata Ayıklama

Uzman Danışman (EA) ve Gösterge hazır olduğunda, bunların nasıl başlatılacağını ve yerleşik MetaEditor hata ayıklayıcısını kullanarak nasıl hata ayıklanacağını düşünelim.

Uzman Danışmanı (EA) başlatmak için, bunu Gezgin penceresinin Uzman Danışmanlar (EA) grubunda bulmak gerekir. Bundan sonra farenin sağ düğmesine tıklayınca görünecek olan bağlam menüsünden Grafiğe ekle seçeneğini seçin:

Şekil 11. Uzman Danışmanın (EA) Başlatılması.

Uzman Danışmanın (EA) giriş parametrelerine sahip pencere görünecektir, gerekirse bu parametreleri değiştirebilirsiniz. Tamam düğmesine basıldıktan sonra, simge  grafiğin sağ üst köşesinde görünecektir, bu simge Uzman Danışmanın (EA) çalıştığını gösterir. Gezgin penceresini göstermek veya kapatmak için Görünüm menüsünden Gezgin seçeneğini seçebilir veya Ctrl+Ntuşlarına basabilirsiniz. Uzman Danışmanı (EA) başlatmanın ikinci yolu, Ekle menüsünün Uzmanlar alt menüsünde bunu seçmektir. 

Uzman Danışmanın (EA) alım satım yapabilmesi için, İstemci Terminali seçeneklerinde AutoTrading'e izin verilmelidir: Araçlar menüsü -> Seçenekler penceresi -> Uzman Danışmanlar sekmesi -> AutoTrading’e İzin Ver seçeneği etkinleştirilmelidir. Uzman Danışmanın (EA) DLL'lerden fonksiyonları çağırabilmesi için DLL içe aktarmalarına izin ver seçeneği de etkinleştirilmelidir.

Şekil 12. Terminal seçenekleri - AutoTrading’e izin verme.

Ayrıca, ilgili seçenekleri denetleyerek her Uzman Danışman (EA) için harici DLL kitaplıklarının alım satımının ve içe aktarımının yetkilendirilmesini veya yasaklanmasını ayarlayabilirsiniz.

Uzman Danışmanımızın (EA) göstergeleri kullanmasına rağmen, göstergelerin çizgileri grafikte çizilmiyor. Gerekirse, göstergeleri manuel olarak ekleyebilirsiniz.

Göstergeleri başlatmak Uzman Danışmanlarla (EA) aynıdır: yerleşik göstergeleri başlatmak istiyorsanız, Gezgin penceresinde Göstergeler ağacını genişletin (özel göstergeler için Özel Göstergeler ağacını genişletmek gerekir), açılır menünün görünmesi için sağa tıklayın, ardından bağlam menüsünden Grafiğe Ekle seçeneğini seçin. İkinci yol, Ekle menüsünden Göstergeler seçeneğini seçmek, grubu (veya özel göstergeler için Özel seçeneğini seçmek) ve göstergenin kendisini seçmektir.

Komut dosyaları, Uzman Danışmanlar (EA) ve Göstergeler ile aynı şekilde başlatılır.

İstemci Terminali etkinlikleri (alım satım sunucusuna bağlantı/bağlantının kesilmesi, otomatik güncelleme, pozisyonlar ve emir değişiklikleri, çalışan Uzman Danışmanlar (EA) ve Komut Dosyaları, hata mesajları) hakkındaki bilgileri Araç Kutusu penceresinin Günlük sekmesinde bulabilirsiniz. Uzman Danışmanlar (EA), Göstergeler ve Komut Dosyaları tarafından yazdırılan mesajlar Uzmanlar sekmesinde bulunur.

MetaEditor'da yerleşik hata ayıklayıcı vardır. Bu, programlarda hata ayıklamanızı sağlar - Uzman Danışmanların (EA), Göstergelerin ve Komut Dosyalarının adım adım yürütülmesi. Hata ayıklama, program kodundaki hataları bulmaya ve Uzman Danışman (EA), Gösterge veya Komut Dosyası yürütme sırasında işlemleri gözlemlemeye yardımcı olur. Bir programı hata ayıklama modunda çalıştırmak için Hata Ayıklama menüsünden Başlat seçeneğini seçmek veya F5 tuşuna basmak gerekir. Program derlenecek ve ayrı grafikte hata ayıklama modunda çalışacaktır, süresi ve sembolü MetaEditor'ın Seçenekler penceresinin Hata Ayıklama sekmesinde belirtilebilir.

Şekil 13. Düzenleyici seçenekleri - Hata ayıklama.

Kesme noktalarını F9 tuşuna basarak veya satırın sol tarafına çift tıklayarak veya Hata Ayıklama penceresinden Kesme Noktasını Değiştir seçeneğini seçerek ayarlayabilirsiniz. Hata ayıklama modunda, programın yürütülmesi kesme noktası olan operatör önünde duracaktır. Durduktan sonra, Hata Ayıklama sekmesi Araç Kutusu penceresinde görünecektir (Şekil 14.). Sol tarafta bir çağrı yığını paneli vardır - dosya, fonksiyon ve bir satırın numarası burada görüntülenir. Sağ tarafta izleme paneli vardır - izlenen değişkenlerin değerleri burada görüntülenir. Değişkeni izleme listesine eklemek için, panelde sağa tıklayın ve Ekle seçeneğini seçin veya Ekle tuşuna basın.

Şekil 14. Program Hata Ayıklama.

Adım adım program yürütme F11, F10 veya Shift+F11 tuşlarına basılarak gerçekleştirilebilir. F11 tuşuna bastıktan veya Hata Ayıklama menüsünden İçeri Adım seçeneğini seçtikten sonra, çağrılan tüm fonksiyonları girerek program yürütmesinin bir adımına geçecektir. F10 tuşuna bastıktan veya Hata Ayıklama menüsünden Adımla seçeneğini seçtikten sonra, çağrılan fonksiyonları girmeden program yürütmenin bir adımına geçecektir. Shift+F11 tuşlarına bastıktan veya Hata Ayıklama menüsünden Dışarı Adım seçeneğini seçtikten sonra, bir program adımının bir seviye daha yüksek yürütülmesini çalıştırır. Kodun sol tarafındaki yeşil ok, yürütülecek kodun satırını işaretler.


Sonuç

Makalede basit bir Uzman Danışman (EA) ve Gösterge yazma örneği sunulmuş ve MQL5 programlama dilinin temelleri açıklanmıştır. Burada sağlanan alım satım sistemi örnek olarak seçilmiştir, bu nedenle yazar bunun gerçek bir alım satımda kullanımından sorumlu değildir.


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

Ekli dosyalar |
indicator_tp.mq5 (2.17 KB)
expert.mq5 (11.3 KB)
Nesne işaretleyicilerini MQL5'te Kullanma Nesne işaretleyicilerini MQL5'te Kullanma
Varsayılan olarak, MQL5'teki tüm nesneler referansla iletilir, ancak nesne işaretçilerini kullanma olasılığı vardır. Ancak, nesne başlatılmamış olabileceğinden, işaretçi denetiminin gerçekleştirilmesi gereklidir. Bu durumda MQL5 programı kritik bir hata ile sonlandırılır ve kaldırılır. Otomatik olarak oluşturulan nesneler böyle bir hataya neden olmaz, dolayısıyla bu anlamda oldukça güvenlidirler. Bu makalede, nesne referansı ile nesne işaretçisi arasındaki farkı anlamaya çalışacağız ve işaretçileri kullanan güvenli kodun nasıl yazılacağını ele alacağız.
Yeni Başlayanlar için MQL5: Uzman Danışmanlarda Teknik Göstergeleri Kullanma Rehberi Yeni Başlayanlar için MQL5: Uzman Danışmanlarda Teknik Göstergeleri Kullanma Rehberi
Bir Uzman Danışmanda yerleşik veya özel bir göstergenin değerlerini elde etmek için, öncelikle ilgili işlev kullanılarak tanıtıcı değeri oluşturulmalıdır. Makaledeki örnekler, kendi programlarınızı oluştururken teknik göstergelerin nasıl kullanılacağını gösterir. Bu makale, MQL5 dilinde oluşturulan göstergeleri açıklar. Alım satım stratejileri geliştirme konusunda fazla deneyimi olmayanlar için tasarlanmıştır ve sunulan fonksiyon kitaplığını kullanarak göstergelerle çalışmanın basit ve net yollarını sunar.
Yeni Başlayanlar için MQL5'te Özel Göstergeler Yeni Başlayanlar için MQL5'te Özel Göstergeler
Herhangi bir yeni konu, bir acemi için karmaşık ve öğrenmesi zor görünür. Bildiğimiz konular ise bize çok basit ve anlaşılır gelir. Ancak, herkesin bir şeyi sıfırdan ve hatta ana dilimizden öğrenmek zorunda olduğunu hatırlamıyoruz. Aynısı, kişinin kendi alım satım stratejilerini geliştirmesi için geniş olanaklar sunan MQL5 programlama dili için de geçerlidir - bunu temel kavramlardan ve en basit örneklerden öğrenmeye başlayabilirsiniz. Teknik bir göstergenin MetaTrader 5 istemci terminali ile etkileşimi, bu makalede basit özel gösterge SMA örneğinde ele alınmaktadır.
MQL5'te Nesne Yaratma ve Yok Etme MQL5'te Nesne Yaratma ve Yok Etme
İster özel bir nesne, ister dinamik bir dizi veya bir nesne dizisi olsun, her nesne MQL5 programında kendine özgü şekilde oluşturulur ve silinir. Çoğu zaman, bazı nesneler diğer nesnelerin bir parçasıdır ve sonlandırma sırasında nesne silme sırası özellikle önemli hale gelir. Bu makale, nesnelerle çalışma mekanizmalarını kapsayan bazı örnekler sunmaktadır.