English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Italiano
MQL5 Nesne Yönelimli Programlama Yaklaşımını Kullanarak Expert Advisor Yazma

MQL5 Nesne Yönelimli Programlama Yaklaşımını Kullanarak Expert Advisor Yazma

MetaTrader 5Örnekler | 15 Aralık 2021, 10:38
415 0
Samuel Olowoyo
Samuel Olowoyo

Giriş

İlk makalede, MQL5'te bir Expert Advisor oluşturma, hata ayıklama ve test etme temel adımlarına doğru bir yolculuğa çıktık.

Yaptığımız her şey çok basit ve ilginçti, ancak, yeni MQL5 dilinin sunacağı çok daha fazla şey var. Bu makalede, ilk makalede yaptığımız şeyi yapmak için Nesne Yönelimli Yaklaşımı ele alacağız. Birçok kişi bunun zor olduğunu düşünüyor, ancak bu makaleyi okumayı bitirdiğinizde, nesne yönelimi temelinde kendi Expert Advisor'ınızı yazabileceğinizi garanti ediyorum.

İlk makalede öğrendiğimiz konulardan bazılarını tekrarlamayacağız; bu nedenle öncelikli olarak okumadıysanız makaleyi okumanızı öneririm.


1. Nesne Yönelimli Paradigma

Yeni MQL5'i MQL4'ten çok daha güçlü ve sağlam hale getiren şeylerden biri OOP (Nesne Yönelimli Programlama) yaklaşımıdır.

OOP'de, bir nesnenin uygulama ayrıntılarının hiçbirini göstermemesi önerilir. Bu şekilde, nesneyi kullanan kodu değiştirmeden uygulaması değiştirilebilir. Bu, bir sınıfın, bir programcının yazdığı sınıfın nasıl uygulandığını gizlemesine (ve ayrıca değişiklikleri engellemesine) izin verdiği anlamına gelir.

Konuyu daha da netleştirmek için, az önce sözü edilen "sınıf" ve "nesne" terimleri üzerinde biraz duralım.

  • SINIF. Bir sınıf daha çok bir veri yapısının genişletilmiş kavramına benzer, ancak yalnızca verileri içermek yerine hem verileri hem de işlevleri içerir. Sınıf, sınıfın üyeleri olarak adlandırılan birkaç değişken ve işlev içerebilir. Verileri değiştiren veri üyelerinin ve işlevlerin bir kapsüllemesidir. Sınıf, çok daha güçlüdür; zira tüm Expert Advisor işlevlerinizi bir sınıfta toplayabilirsiniz. EA kodunuzda işlevlere yalnızca ihtiyaç duyduğunuzda başvuracaksınız. Bu arada, bu makalenin konusu bu.
  • NESNE. Nesne, bir sınıfın bir örneğidir. Bir sınıf oluşturulduktan sonra, sınıfı kullanmak için bir sınıf örneği bildirmeliyiz. Bu, nesne olarak adlandırılır. Başka bir deyişle, bir nesne oluşturmak için bir sınıfa ihtiyacınız vardır.

1.1. SINIFI BİLDİRME

Sınıf, temel olarak, sınıftan oluşturmak istediğiniz bir nesnenin üyelerinin (özellikler ve işlevler/yöntemler) açıklamasını içerir. Şimdi bir örneğe göz atalım…

Kapıları, koltukları, lastikleri, ağırlığı vb. olan ve ayrıca hareket edebilen, vites değiştirebilen, durabilen ve korna çalabilen bir nesne oluşturmak istiyorsak o zaman bunun için bir sınıf yazmamız gerekir. Kapılar, koltuklar, lastikler, ağırlık, hareket etme, vites değiştirme, durma ve korna çalma sınıfın üyeleri olacaktır.

Kuşkusuz ki, bu üyelerin kategorilere ayrıldığını göreceksiniz; bazıları nesnemizin içereceği şeylerdir (özellikler), diğerleri ise nesnemizin gerçekleştireceği şeylerdir (eylemler – işlevler/yöntemler). Sınıfımızı bildirmek için onun için çok iyi ve açıklayıcı bir ad düşünmemiz gerekir. Bu durumda, sınıfımızı ARABA olarak adlandıracağız. ARABA sınıfımız, üyeleri olarak yukarıda belirtilen özellikleri ve işlevleri içerecektir.

Bir sınıf bildirmek için, sınıf anahtar sözcüğünü, ardından sınıfın adını ve daha sonra sınıfın üyelerini içeren bir çift kaşlı ayraç yazmayla başlarız.

Dolayısıyla, bir sınıfın temel biçimi aşağıda gösterildiği gibidir:

class class_name 
{
  access_keyword_1:
    members1;

  access_keyword_2:
    members2;
  ...
};

Burada, class_name, yazmak istediğimiz sınıf için geçerli bir tanımlayıcıdır, members1 ve members2 sınıfın veri üyeleridir.

access_keyword, sınıfımızın üyelerinin erişim hakkını belirtir.  access_keyword, özel, korumalı veya genel olabilir. Gerçekte uygulama ayrıntılarını göstermeden kendimiz ve başkaları tarafından kullanılabilecek bir sınıf yazmaya çalıştığımızı unutmayın. Bu nedenle erişim hakları gereklidir.

Sınıfımızın dışından erişmek istemediğimiz bazı üyeler olabilir. Bunlar, özel erişim bölümünde özel veya korumalı anahtar sözcüğü kullanılarak bildirilir. Sınıfımızın dışından erişmek istediğimiz diğer üyeler daha sonra genel erişim bölümünde public anahtar sözcüğü kullanılarak bildirilecektir. Yeni ARABA sınıfımız şimdi aşağıdaki gibi görünecek:

class CAR 
{
  private:
    int        doors;
    int        sits;
    int        tyres;
    double     weight;

  public:
    bool       start();
    void       changegear();
    void       stop();
    bool       horn();
  ...
};

ARABA sınıfımız, sınıf anahtar sözcüğü kullanılarak bildirilir. Bu sınıf dört üyesi özel erişime ve dört üyesi genel erişime sahip sekiz üye içerir. Özel bölümdeki dört üye veri üyeleridir. Üçü tamsayı (int) veri türünde ve biri çift veri türündedir. Bu üyelere, bu sınıfın dışında bildirilen başka bir işlev tarafından erişilemez.

Genel bölümdeki dört üye, işlev üyeleridir. İki dönüş bool veri türü ve iki dönüş void türüdür. Bunlar, sınıfımızı kullanan herhangi biri tarafından her oluşturulduğunda, bu sınıfın herhangi bir nesnesine erişilebilen üyelerdir. Sınıfımızın bir nesnesi oluşturulduğunda, bu üyeler kolaylıkla kullanılabilecektir.

Doğru şekilde gözlemleyeceğiniz üzere, erişim anahtar sözcüklerinin (özel, genel, korumalı) ardından her zaman iki nokta üst üste işareti gelir. Sınıf bildirimi de noktalı virgülle sona erer. Üyeler doğru veri türleri kullanılarak bildirilir.

Bir sınıfı bildirdiğinizde, yukarıda yaptığımız gibi açıkça belirtilmediği sürece sınıfın tüm üyelerine özel erişim hakları verildiği unutulmamalıdır. Örneğin, aşağıdaki sınıf bildiriminde olduğu gibi:

class CAR 
{
    int        doors;
    int        sits;
    int        tyres;
    double     weight;

  public:
    bool       start();
    void       changegear();
    void       stop();
    bool       horn();
  ...
};

Herkese açık erişim anahtar sözcüğünün üzerinde bildirilen dört üyenin tamamı otomatik olarak herkese açık erişime sahiptir.

Sınıfımızın kullanılabilmesi için öncelikle sınıfın bir nesnesi oluşturulmalıdır. Şimdi, sınıfımızın bir türü olan bir nesne oluşturalım. Bunu yapmak için sınıf adımızı, ardından nesneye vermek istediğimiz adı kullanacağız.

ARABA Honda;

Veya başka bir nesne oluşturabiliriz

ARABA Toyota;

Honda veya Toyota artık bir ARABA türüdür ve üye işlevlerin herkese açık erişim bölümünde bildirilmesi koşuluyla, artık ARABA sınıfımızın tüm üye işlevlerine erişebilir. Bu konuya daha sonra döneceğiz.

Bu sınıftan istediğimiz kadar nesne oluşturabileceğimizi görebilirsiniz. Bu, Nesne Yönelimli programlamanın faydalarından biridir.

Bu noktada, MQL5'te bir sınıfın biçimini ayrıntılı olarak ele alalım.

class class_name 
{
  private:
    members1;
    members2;
    members3;

  public:
    class_name()  //Constructor;
    ~class_name() //Destructor;
    Members4();
    Members5();

  protected:
    members6;
    members7;
};

Bu, class_name adının sınıfın adı olduğu bir sınıfın bildirimidir. Bu sınıfın dokuz üyesi var, ancak bu dokuz üyeden ikisi özel üye.

Oluşturucu:
Oluşturucu (class_name() olarak temsil edilir), sınıf türünden yeni bir nesne oluşturulduğunda otomatik olarak çağrılan özel bir işlevdir. Bu durumda, bu sınıf türünde bir nesne oluşturduğunuzda

class_name nesne;

oluşturucu, class_name() otomatik olarak çağrılır. Oluşturucunun adı sınıfın adıyla eşleşmelidir; bu nedenle oluşturucuyu class_name() olarak adlandırdık. MQL5'te, bir oluşturucu herhangi bir giriş parametresi almaz ve dönüş türüne sahip değildir. Sınıf üyelerinin bellek ayırma ve başlatma işlemleri normalde oluşturucu çağrıldığında yapılır. Oluşturucular, normal üye işlevleriymiş gibi açıkça çağrılamaz. Yalnızca o sınıfın yeni bir nesnesi oluşturulduğunda yürütülürler. MQL5'teki bir sınıf yalnızca bir oluşturucu içerebilir.

Yıkıcı:
İkinci özel üye ~class_name() olarak temsil edilir. Bu, sınıf adından önce bir gelgit (~) ile yazılmış sınıf yıkıcıdır. Bir sınıf nesnesi yok edildiğinde otomatik olarak çağrılır. Bu aşamada, sınıfın başlatılmasının geri alınması gereken tüm üyeleri sıfırlanır ve yıkıcıyı açıkça bildirip bildirmemeniz gerçekten önemli değildir.

Veri Üyeleri:
Bir sınıfın üyeleri, herhangi bir geçerli veri türü, sınıf türü veya yapı türü olabilir. Başka bir deyişle, bir sınıfın üye değişkenlerini bildirirken, herhangi bir geçerli veri türünü (int, double, string, vb.), başka bir sınıfın nesnesini veya bir yapı türünü (örneğin, MQL5 MqlTradeRequest, vb.) kullanabilirsiniz.

İşlev Üyeleri:
Bunlar, veri üyelerini değiştirmek ve sınıfın ana işlevlerini/ yöntemlerini yürütmek için kullanılan sınıfın üyeleridir. İşlev üyeleri için dönüş türü, herhangi bir geçerli dönüş türünde (bool, void, double, string, vb.) olabilir.

Özel:
Bu bölümde bildirilen üyelere yalnızca sınıfın işlev üyeleri tarafından erişilebilir. Bunlara, sınıf dışında başka bir işlev tarafından erişilemez.

Korumalı:
Bu bölümde bildirilen üyelere, sınıfın işlev üyeleri tarafından erişilebilir ve ayrıca bu sınıftan türetilen diğer sınıfların üye işlevleri tarafından erişilebilir. Bu, bu sınıftan yeni bir sınıf da oluşturabileceğimiz anlamına gelir. Bu durumda, bu sınıftan türetilen yeni sınıf (artık temel sınıf olacak), temel sınıfın korumalı üyelerine erişebilecektir. Bu, OOP'deki devralma kavramıdır. Bunu az sonra ele alacağız, rahatlayın…

Genel:
Bu bölümde bildirilen üyeler, sınıfın bir nesnesi tarafından sınıfın dışında kullanılabilir. Sınıfı diğer programlarda kullanmak için gerekli olacak bazı işlevlerin bildirileceği yer burasıdır.

Bir sınıfın temel biçimini gözden geçirdik, umarım henüz sıkılmamışsınızdır; zira sonunda Expert Advisor'ımız için bir sınıf paketi oluşturmaya geçmeden önce, sınıfların hala göz atmamız gereken başka ilginç yönleri var.

1.2. DEVRALMA

Diyelim ki ilk sınıfımız olan base_class sınıfından başka bir sınıf oluşturmak istiyoruz. Üyeler ilk sınıftan yeni bir sınıf türetme biçimi şu şekildedir:

Temel Sınıf:

class base_class 
{
  private:
    members1;
    members2;
    members3;

  public:
    class_name()  //Constructor;
    ~class_name() //Destructor;
    Members4();
    Members5();

  protected:
    members6;
    members7;
};

Türetilmiş Sınıf:

class new_class : access_keyword base_class 
{
  private:
    members8;

  public:
    new_class()  //Constructor;
    ~new_class() //Destructor;
    Members9();
};

Ayrıntıları açıklamaya devam etmeden önce burada birkaç açıklamada bulunulacaktır. Üyeler gösterildiği gibi iki nokta üst üste işareti ve bir access_keyword kullanılarak new_class sınıfı base_class sınıfından türetilir. Artık, base_class sınıfından türetilen/yapılan new_class, base_class sınıfının hem genel hem de korumalı üyelerine erişebilir (veya devralabilir) fakat base_class sınıfının özel üyelerine erişemez (veya devralamaz). new_class, base_class sınıfından farklı yeni üye yöntemleri/işlevleri de uygulayabilir. Başka bir deyişle, new_class sınıfı base_class sınıfından devraldıkları dışında kendi veri ve işlev üyelerini de içerebilir.

Türetilmiş sınıfın oluşturulmasında genel anahtar sözcüğü kullanılırsa bu, temel sınıfın genel ve korumalı üyelerinin, türetilmiş sınıfın genel ve korumalı üyeleri olarak devralınacağı anlamına gelir. Korumalı anahtar sözcüğü kullanılırsa temel sınıfın genel ve korumalı üyeleri, türetilmiş sınıfın korumalı üyeleri olarak devralınacaktır. Özel anahtar sözcüğü kullanılırsa temel sınıfın genel ve korumalı üyeleri, türetilmiş sınıfın özel üyeleri olarak devralınacaktır.

new_class sınıfının (türetilmiş sınıf) yeni bir nesnesi oluşturulduğunda, base_class sınıfının oluşturucusunun new_class sınıfının oluşturucusundan önce çağrıldığına; nesne yok edildiğinde, new_class sınıfının (türetilmiş sınıf) yıkıcısının base_class sınıfının yıkıcısından önce çağrıldığına dikkat etmek önemlidir.

Bu devralma kavramını daha iyi anlamak için başlangıç sınıfımız olan ARABA'ya geri dönelim.

class CAR 
{
  protected:
    int        doors;
    int        sits;
    double     weight;

  public:
    bool       start();
    void       changegear();
    void       stop();
    bool       horn();

  private:
    int        tyres;
};

Bu sınıftan başka bir SALON sınıfını türetebiliriz. ARABA sınıfının veri üyelerinden üçünü korumalı olarak bildirdiğime dikkat edin. Bu, yeni sınıf SALON'un bu üyeleri devralmasını sağlamak içindir.

Ayrıca, erişim anahtar sözcüklerini yerleştirdiğiniz sıranın önemli olmadığını bilmenizi istiyorum. Önemli olan, bir erişim anahtar sözcüğü altında bildirilen tüm üyelerin o anahtar sözcüğe ait olmasıdır.

class SALOON : public CAR 
{
  private:
    int        maxspeed;

  public:
    void       runathighspeed();
};

Türetilmiş SALON sınıfımız iki üye içerir ve aynı zamanda ARABA temel sınıfından yedi üyeyi (korumalı ve genel üyeler) devralır. Bu, SALON sınıfına ait bir nesne oluşturulduğunda, ARABA'nın genel üye işlevlerine erişebileceği anlamına gelir; bunlar kendi genel üye işlevi olan yüksekhızdaçalış() ile birlikte hareket et(), vites değiştir(), dur() ve korna çal()'dır. Bu, devralma kavramıdır.

Tıpkı babamızın/ebeveynlerimizin (temel sınıf) bazı karakteristik özelliklerinin/davranışlarının (yöntemler) bizde, onların çocuklarında (türetilmiş sınıf) ortaya çıkması gibi; zira bu davranışları (yöntemler/işlevler) onlardan genetik ya da başka bir şekilde devralıyoruz. Üzgünüm, sağlık personeli değilim ama çizmeye çalıştığım tabloyu çok iyi kavradığınıza inanıyorum. Bu arada, MQL5 çoklu devralmayı desteklemediği için bunun hakkında konuşmaya gerek olmadığını düşünüyorum.

Hımm!!! Umarım OOP veya SINIF olarak adlandırılan gizemli şeyin üzerini örten siyah perde yavaş yavaş aralanıyordur… Kendinizi yormayın; bu noktada hala neleri ele aldığımız konusunda net olmadığınızı hissediyorsanız rahatlamanız gerekebilir; bir fincan kahve alın ve sonra geri gelin ve baştan başlayın. Bu, sandığınız kadar gizemli değil…

Bu noktaya geri döndüyseniz, açıklamamı takip ettiğinizi varsayıyorum. Temel sınıfımız olan ARABA'dan daha kaç sınıf türetebileceğinizi söylemenizi istiyorum. Lütfen.. Yanıtınıza ihtiyacım var. Ciddiyim. Türettiğiniz sınıfları adlandırın ve bildirimlerini yazıp bana e-posta ile gönderin. Hepsini adlandırabilirseniz, sizi yemeğe çıkaracağım… (şaka yapmıyorum)

Artık daha fazlası için hazır olduğunuza göre, devam edelim…

Yazı yazma tarzımın babamınkine benzediği doğrudur. Babamın el yazısı da benimki gibi çok düzgün ve şıktır. Sanırım bu, ondan bana miras kalan bir şey ama bir küçük farkla. Babam yazarken sol elini kullanırken ben sağ elimi kullanıyorum ve yazım tarzımız birbirine o kadar çok benziyor ki yazdıklarımızı görseniz güçlükle ayırt edersiniz. Peki buradaki sorun ne? Güzel el yazım babamdan miras kaldı ama babam gibi sol elimle yazamıyorum. Bu, bana miras kalan şey olmasına ve benzer görünmesine rağmen, eyleme dönüştürme tarzımın babamınkinden farklı olduğu anlamına gelir. Bu sizin için bir anlam ifade ediyor mu? Bu, OOP'de çok biçimlilik olarak adlandırılan şeyin bir fikridir.

Türetilmiş bir sınıf (yukarıdaki örnekte olduğu gibi kendim) temel bir sınıftan (babam) bir üye işlevi (writefine() – el yazım için) devralır, ancak söz konusu sınıf (Ben), işlevi (writefine() ) temel sınıftan (Babam) farklı bir şekilde uygular.

ARABA sınıfımıza ve türetilmiş SALON sınıfına geri dönelim;

class CAR 
{
  protected:
    int        doors;
    int        sits;
    double     weight;

  public:
    bool               start();
    virtual void       changegear(){return(0);}
    void               stop();
    bool               horn();

  private:
    int        tyres;
};
class SALOON : public CAR 
{
  private:
    int        maxspeed;

  public:
    void               runathighspeed();
    virtual  void       changegear(){gear1=reverse; gear2=low; gear3=high;}
  };

class WAGON : public CAR 
{
  private:
    bool               hasliftback;

  public:
   virtual  void       changegear(){gear1=low; gear2=high; gear3=reverse;}
};

Burada yaptığımız birkaç değişikliğe göz atalım. İlk olarak, ARABA sınıfından iki üyeli VAGON adında yeni bir türetilmiş sınıf bildirdik. Ayrıca vites değiştirme() üye işlevini de temel sınıfta sanal işlev olacak şekilde değiştirdik. Peki neden vites değiştirme() üye işlevini sanal işlev yaptık? Bunun nedeni, işlevi temel sınıftan devralan herhangi bir sınıfın onu kendine göre uygulayabilmesidir.

Başka bir deyişle, bir sınıfın sanal üye işlevleri, bildirildikleri sınıftan türetilen herhangi bir sınıfta farklı şekilde geçersiz kılınabilen veya uygulanabilen üye işlevleridir. Üye işlevi gövdesi daha sonra türetilmiş sınıfta yeni bir uygulama kümesi ile değiştirilebilir. Sanal kelimesini türetilmiş sınıflarda tekrar kullanmasak da bunu türetilmiş sınıflarda her zaman kullanmak iyi bir programlama uygulamasıdır.

Yukarıdaki örneklerden, SALON ve VAGON sınıfları, vites değiştir() işlevini kendilerine göre uygular.

1.3. SINIF YÖNTEMLERİNİ TANIMLAMA (ÜYE İŞLEVLERİ)

Bir dereceye kadar sınıfları nasıl bildireceğimizi bildiğimiz için bir sınıfın üye işlevlerinin nasıl tanımlanacağını ele alarak daha fazla ilerleme kaydedelim. Sınıfı bildirdikten sonra sıra sınıfımızın üye işlevlerini tanımlamaktır. ARABA sınıfımıza tekrar bakalım

class CAR 
{
  protected:
    int        doors;
    int        sits;
    double     weight;

  public:
    void       CAR() // Constructor
    bool       start();
    void       changegear();
    void       stop();
    bool       horn(){press horn;}

  private:
    int        tyres;
};

 void CAR::CAR()
{
 // initialize member variables here
}

bool CAR::start()
{
 // car start procedure here
}

void CAR::changegear()
{
// car changegear procedure here
}

void CAR::stop()
{
// car stop procedure here
}

Üye işlevlerini tanımlarken, kapsam işleci adında bir çift iki nokta üst üste (::) işleci kullandık. Bu, normal işlevler gibi yazılır; tek farkı sınıf adı ve eklenen kapsam işlecidir. Ayrıca işlevlerden birinin zaten sınıf içinde tanımlanmış olduğunu göreceksiniz. (üye işlevi korna çal()). Burada gördüğünüz gibi bir üye işlevi sınıf bildiriminin içinde veya sınıf bildiriminin dışında tanımlanabilir.

İlerlemeden önce işlevler kavramını biraz gözden geçirmenin önemli olacağını düşünüyorum.

1.4. İŞLEVLER

Bu arada, işlev nedir?

Bazen üç çocuğun olduğu bir evde, evdeki tüm işleri bir çocuğun yapması yerine; birine her gün akşam yemeğinden sonra tabakları yıkama, diğerine döşemeleri süpürme, üçüncüsüne de her sabah yatakları düzeltme görevi verilebilir.

Evde yapılması gereken birtakım işler vardı ve tüm işleri bir çocuğa yüklemek yerine bunları üç çocuk arasında bölüştürmüş olduk. Bu, yalnızca birine yük olmaktan ziyade, her biri için görevi çok kolay ve hafif hale getirecektir. Ayrıca çocuklardan biri görevini yapmazsa hangisini cezalandırmamız gerektiğini kolaylıkla biliriz. İşlevlerin arkasındaki fikir budur.

Çoğu zaman birçok görevi yerine getirecek bir kod yazmak isteriz. İşlevlerin devreye girdiği yer burasıdır. Görevi daha küçük görevlere ayırmaya karar verebilir ve ardından daha küçük görevlerin her birini gerçekleştirmek için bir işlev yazabiliriz. İşlev, bir dizi işlemi gerçekleştiren veya uygulayan bir kod bloğudur. Bu, bir programın herhangi bir noktasından her çağrıldığında yürütülen bir ifade grubudur.

Bir işlev aşağıdaki gibi tanımlanabilir:

Return_type function_name (parameters1,parameters2,…)
{
  Expressions; //(actions to carry out by the function)
}
  • Return_type: İşlev tarafından döndürülen veri türü (geçerli bir veri türü olmalıdır veya hiçbir şey döndürmezse void olmalıdır)
  • Function_name: İşlevi çağırmak için kullanılacak işlevin adı (geçerli bir ad olmalıdır)
  • Parametreler: Parametreler, işlev içinde yerel bir değişken olarak hareket edecek geçerli veri türü değişkenleridir. Bir işlevin birden fazla parametresi varsa bunlar virgülle ayrılır.
  • Açıklamalar: İfadeler bloğunu içeren işlevin gövdesi

Bir işlev örneği:

int doaddition (int x, int y)
{
 return (x+y);
}

İşlev dönüş türü tamsayı (int), doaddition işlevin adıdır ve int x ve int y parametrelerdir. İşlevin yaptığı, kendisine sağlanan herhangi iki giriş parametresini eklemek ve sonucu döndürmektir. Bu nedenle, işleve 2 ve 3 tamsayı değişkeni sağlarsak işlev, ekleme işlemini yapacak ve sonuç olarak 5 döndürecektir.

int doaddition(2,3) // returns 5

İşlevler hakkında daha fazla bilgi için lütfen MQL5 Referans kılavuzuna bakın.

Artık işe koyulmamıza yetecek kadar teori mevcut.

Bu makalenin özü, MQL5'te sunulan Nesne Yönelimli yaklaşımı kullanarak Expert Advisor'ınız için nasıl bir sınıf yazabileceğinizi öğretmektir.

Şimdi harekete geçme zamanı…

2. Expert Advisor Yazma

Bu noktada, ilk makalede oluşturduğumuz Expert Advisor'a atıfta bulunacağız. Makaleyi okumadıysanız lütfen yeterli bilgi sahibi olmak için okuyun; bu noktadan sonra tartışacağımız şeylerin çoğunun size tuhaf gelmesini istemeyiz. Ancak, yine de gerekli olabilecek birkaç şeyi gözden geçirebilirim.

Sınıfınızı yazabilmeniz için önce oturup alım satım stratejinizi geliştirmeniz gerekir. Bunu ilk makalede zaten yaptık. Sıradaki husus, sınıfımıza devretmek istediğimiz işlevleri seçmektir. Bu işlevler sınıfımızın üye değişkenlerini belirleyecektir. İlk makaleden alım satım stratejimizin bir özeti.

EA'mız ne yapacak?

  • Belirli bir göstergeyi izleyecek ve belirli bir koşul karşılandığında (veya belirli koşullar karşılandığında), karşılanan mevcut koşula bağlı olarak bir alım satım işlemi (Kısa Pozisyon/Sat veya Uzun Pozisyon/Al) yapacaktır.

Yukarıdakiler alım satım stratejisi olarak adlandırılır. Bir EA yazmadan önce, EA'da otomatikleştirmek istediğiniz stratejiyi geliştirmelisiniz. Bu durumda, yukarıdaki ifadeyi bir EA'ya dönüştürmek istediğimiz stratejiyi yansıtacak şekilde değiştirelim.

  • Dönemi 8 olan Hareketli Ortalama adlı bir gösterge kullanacağız (Herhangi bir dönemi seçebilirsiniz, ancak stratejimizin amacı için biz 8'i kullanacağız)
  • EA'mızın Hareketli Ortalama-8 (tartışmamızı kolaylaştırması için, ben HO-8 olarak adlandıracağım) yukarı doğru çıktığında ve fiyat onun üzerine yakın olduğunda Uzun (Alış) bir alım satım işlemi yapmasını, HO-8 aşağı doğru düştüğünde ve fiyat bunun altına yakın olduğunda Kısa (Satış) bir alım satım işlemi yapmasını istiyoruz.
  • Ayrıca, piyasanın trend olup olmadığını belirlememize yardımcı olması için 8. döneme sahip Ortalama Yönlü Hareket (ADX) adlı başka bir gösterge kullanacağız. Bunu, alım satım işlemine yalnızca piyasa trendde girdiğinde girmek ve piyasa değişkenken (yani trendde değilken) rahatlamak istediğimiz için yapıyoruz. Bunu başarmak için, alım satım işlemlerimizi (Al veya Sat) yalnızca yukarıdaki koşullar karşılandığında ve ADX değeri 22'den büyük olduğunda gerçekleştireceğiz. ADX 22'den büyükse fakat düşüyorsa veya ADX 22'den küçükse, B koşulu karşılansa dahi alım satım yapmayacağız.
  • Ayrıca 30 piplik bir Zararı Durdur ayarlayarak kendimizi korumak istiyoruz; Kar hedefimiz için 100 piplik bir kar hedefleyeceğiz.
  • Ayrıca EA'mızın yalnızca yeni bir çubuk oluştuğunda Alış/Satış fırsatlarını aramasını istiyoruz ve aynı zamanda Alış koşulları karşılanıyorsa ve halihazırda açılmış bir pozisyonumuz yoksa bir Alış pozisyonu açtığımızdan ve Satış koşulları karşılanıyorsa ve halihazırda açılmış bir pozisyonumuz yoksa bir Satış pozisyonu açtığımızdan emin olacağız.

Bunun yanı sıra, bir alım satım işlemi yaparken kullanılabilecek Serbest Marjımızın yüzdesini kontrol edebildiğimizden ve ayrıca herhangi bir alım satım işlemi yapmadan önce mevcut serbest marjı kontrol ettiğimizden emin olmak istiyoruz. EA'mız, yalnızca alım satım işlemi için mevcut marj yeterliyse alım satım yapacaktır.

Artık ne yapmak istediğimizi anlıyorsunuz. Sınıfımıza devretmek istediğimiz işlevler şunlardır:

  • Alış ve Satış koşullarını kontrol edin
  • Kontrol edilen koşulların sonucuna göre Alım/Satım yapın

Temel olarak, EA'mızın yapmasını istediğimiz tek şey bu. Bu iki işlev ana işlevlerdir, ancak daha fazlası da vardır. Örneğin Alış/Satış pozisyonları kontrol edilirken göstergeler kullanılmalıdır. Bu, göstergelerin değerlerini elde etmenin de sınıfımızda olması gerektiği anlamına gelir. Bu nedenle şunları ekleriz:

  • Tüm gösterge tanıtıcılarını almak (EA OnInit bölümünde)
  • Tüm gösterge arabelleklerini almak (EA OnTick bölümünde)
  • Tüm gösterge tanıtıcılarını serbest bırakmak (EA OnDeinit bölümünde)

Gösterge değerlerini alırken, sınıfımızın MA ve ADX dönemlerini, grafik dönemini ve sembolünü (birlikte çalıştığımız para birimi çifti) bilmesi gerekecek; bu nedenle şunları da eklemeliyiz:

  • ADX ve MA dönemlerini ve grafik dönemi ve sembolü gibi diğer önemli parametreleri almak.

Ayrıca, bir alım satım işlemi yapmadan önce serbest marjın kontrolü için,

  • alım satım işlemi için kullanılacak Serbest Marjı/hesap yüzdesini Kontrol Etmeyi dahil edeceğiz.

Bununla zaten sınıfımızda hangi değişkenlerin ve işlevlerin olması gerektiğine ilişkin bir fikrimiz var.

Peki, sizin için düşündüm; şimdi biraz kod yazma zamanı.

2.1. Sınıf yazma

MetaEditor'u başlatarak başlayalım (bunu zaten bildiğinizi düşünüyorum). MetaEditor açıldıktan sonra Yeni araç çubuğuna veya Ctrl+N'ye tıklayarak yeni bir MQL belgesi başlatalım. Sihirbaz penceresinde "Ekle" öğesini seçin ve İLERİ düğmesine tıklayın.

Şekil 1. Yeni bir MQL5 belgesi başlatma

Şekil 1. Yeni bir MQL5 belgesi başlatma

Aşağıda gösterildiği gibi dosyanın adını yazın ve Bitir'e tıklayın:

Şekil 2. Yeni bir belgeyi adlandırma

Şekil 2. Yeni bir belgeyi adlandırma

Ekleme seçeneğini seçtik; zira sınıfımız, kullanmaya hazır olduğumuzda EA kodumuza dahil edilecek bir içerme dosyası olacak. Bu nedenle giriş parametrelerini girmek için yeriniz yok.

Her zamanki gibi, düzenleyici size yapmak istediğinizi düşündüğü şeyin bir taslağını sağlar.


Başlamak için lütfen "#property link ..." kod satırının altındaki her şeyi silin. Artık buna benzer bir şeye sahip olmalısınız.

//+------------------------------------------------------------------+
//|                                              my_expert_class.mqh |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"

Artık sınıfımızın bildirimini yazalım, sınıfımızı MyExpert olarak adlandıralım.

//+------------------------------------------------------------------+
//| CLASS DECLARATION                                                |
//+------------------------------------------------------------------+
class MyExpert
{

Sınıf bildirimini analiz edelim. Bildirim, sınıfın adıyla başlar. Daha sonra sınıfın özel üyelerini bildirdik.

Bu Üyeler:

//+------------------------------------------------------------------+
//| CLASS DECLARATION                                                |
//+------------------------------------------------------------------+
class MyExpert
{
//--- private members
private:
   int               Magic_No;   // Expert Magic Number
   int               Chk_Margin; // Margin Check before placing trade? (1 or 0)
   double            LOTS;       // Lots or volume to Trade
   double            TradePct;   // Percentage of Account Free Margin to trade
   double            ADX_min;    // ADX Minimum value
   int               ADX_handle; // ADX Handle
   int               MA_handle;  // Moving Average Handle
   double            plus_DI[];  // array to hold ADX +DI values for each bars
   double            minus_DI[]; // array to hold ADX -DI values for each bars
   double            MA_val[];   // array to hold Moving Average values for each bars
   double            ADX_val[];  // array to hold ADX values for each bars
   double            Closeprice; // variable to hold the previous bar closed price 
   MqlTradeRequest   trequest;    // MQL5 trade request structure to be used for sending our trade requests
   MqlTradeResult    tresult;     // MQL5 trade result structure to be used to get our trade results
   string            symbol;     // variable to hold the current symbol name
   ENUM_TIMEFRAMES   period;      // variable to hold the current timeframe value
   string            Errormsg;   // variable to hold our error messages
   int               Errcode;    // variable to hold our error codes

Daha önce açıklandığı gibi, bu özel üye değişkenlerine sınıf dışındaki herhangi bir işlev tarafından erişilemez. Değişkenlerin çoğu bildirimlerinde çok net olduğu için söz konusu değişkenler hakkında konuşarak zaman kaybetmeyeceğim.

Ancak, tartışmamızda üye değişkenlerin herhangi bir geçerli veri türü, yapısı veya sınıfı olabileceğini belirttiğimizi hatırlayacaksınız.

Bunun işleyiş biçimini burada, MqlTradeRequest ve MqlTradeResults türlerinin bildirimi ile görebileceğinize inanıyorum.

Oluşturucu

//--- Public member/functions
public:
   void              MyExpert();                                  //Class Constructor

Oluşturucu herhangi bir giriş parametresi almaz; lütfen kendi sınıfınızı yazarken bunu göz önünde bulundurun.

Üye işlevleri

//--- Public member/functions
public:
   void              MyExpert();                                 //Class Constructor
   void              setSymbol(string syb){symbol = syb;}         //function to set current symbol
   void              setPeriod(ENUM_TIMEFRAMES prd){period = prd;} //function to set current symbol timeframe/period
   void              setCloseprice(double prc){Closeprice=prc;}   //function to set prev bar closed price
   void              setchkMAG(int mag){Chk_Margin=mag;}          //function to set Margin Check value
   void              setLOTS(double lot){LOTS=lot;}               //function to set The Lot size to trade
   void              setTRpct(double trpct){TradePct=trpct/100;}   //function to set Percentage of Free margin to use for trading
   void              setMagic(int magic){Magic_No=magic;}         //function to set Expert Magic number
   void              setadxmin(double adx){ADX_min=adx;}          //function to set ADX Minimum values

Bu üye işlevlerini, sınıfımızın işlevini yerine getirmesi için ihtiyaç duyacağı önemli değişkenleri belirlememize izin verecek şekilde tanımladık. Bu işlevler kullanılmadan bu değişkenler sınıfımız tarafından kullanılamaz. Sizin de fark edeceğiniz gibi, bu işlevler tarafından ayarlandıktan sonra bu değerleri tutacak karşılık gelen bir değişkeni sınıfımızda zaten bildirmiştik.

Dikkat edilmesi gereken başka bir nokta da, bu üye işlevlerini sınıf bildiriminde tanımlamış olmamızdır. Daha önce açıkladığım gibi, buna izin verilir. Bu, çok yakında göreceğiniz üzere, diğer üye işlevlerini tanımlarken bunları yeniden tanımlamamıza gerek olmayacağı anlamına gelir.

Normal işlevler gibi, bunlar, her işlevin döndürülen değerlerine bağlı olarak doğru veri türünde parametrelere sahiptir. Bunun size tuhaf gelmemesi gerektiğine inanıyorum.

void              doInit(int adx_period,int ma_period);         //function to be used at our EA intialization
void              doUninit();                                  //function to be used at EA de-initializatio
bool              checkBuy();                                  //function to check for Buy conditions
bool              checkSell();                                 //function to check for Sell conditions
void              openBuy(ENUM_ORDER_TYPE otype,double askprice,double SL,
                         double TP,int dev,string comment="");   //function to open Buy positions
void              openSell(ENUM_ORDER_TYPE otype,double bidprice,double SL,
                          double TP,int dev,string comment="");  //function to open Sell positions

Yalnızca bu üye işlevleri bildiririz, ancak bunları tanımlamadık. Çünkü bunu daha sonra yapacağız. Bunlar, sınıfımızın üye değişkenlerinde saklanan değerlerin çoğunu değiştirecek işlevlerdir ve aynı zamanda sınıfımızın ana rolü için işlevleri oluştururlar. Bunları daha sonra tartışacağız.

Korumalı üyeler

Bu üyeler, sınıfımızdan türetilen herhangi bir sınıf tarafından devralınır. Bu sınıftan başka bir sınıf türetmeyi düşünmüyorsanız gerçekten gerekli değildir. Onları özel üyeler olarak da yerleştirebilirsiniz. Bunu yalnızca daha önce sınıflar hakkında tartıştığımız çeşitli konuları anlamanızı sağlamak için yapıyorum.

//--- Protected members
protected:
   void              showError(string msg, int ercode);   //function for use to display error messages
   void              getBuffers();                       //function for getting Indicator buffers
   bool              MarginOK();                         //function to check if margin required for lots is OK

Bu üç işlev, sınıfımıza dahil olsa da çok önemlidir. showError hatalarımızı gösterecek ve gösterge arabelleklerini almak için getBuffers kullanılacaktır. MarginOK bir pozisyon açmak için yeterli serbest marj olup olmadığını kontrol eder.

Sınıf bildirimini tamamladıktan sonra noktalı virgülü unutmayın. Bu, çok önemlidir.

};   // end of class declaration

Bu bildirdikten hemen sonra yapılacak şey, bildirim bölümünde tanımlanmamış üye işlevleri tanımlamaktır.

//+------------------------------------------------------------------+
// Definition of our Class/member functions
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//|  This CLASS CONSTRUCTOR
//|  *Does not have any input parameters
//|  *Initilizes all the necessary variables                                          
//+------------------------------------------------------------------+
void MyExpert::MyExpert()
  {
//initialize all necessary variables
   ZeroMemory(trequest);
   ZeroMemory(tresult);
   ZeroMemory(ADX_val);
   ZeroMemory(MA_val);
   ZeroMemory(plus_DI);
   ZeroMemory(minus_DI);
   Errormsg="";
   Errcode=0;
  }

Bu, bizim sınıf oluşturucumuzdur.  Burada sınıf adı ile üye işlev adı arasında çift iki nokta üst üste (::) (kapsam işleci) kullandık. Söylemeye çalıştığımız şey şudur:

Bu üye işlevini sınıf bildiriminin dışında tanımlamamıza rağmen, yine de sınıfın kapsamındadır. Adı iki adet iki nokta üst üste işaretinden (kapsam işleci) önce gelen sınıfın bir üyesidir.

 Herhangi bir giriş parametresi yoktur. Bu noktada gerekli üye değişkenlerin çoğunu başlatırız ve bunu gerçekleştirmek için ZeroMemory işlevini kullanırız.

void  ZeroMemory(
     void & değişkeni      // reset değişkeni
   );

Bu işlev, kendisine iletilen değişkenlerin değerlerini sıfırlar. Bu durumda, bunu, yapı türlerimizin (MqlTradeRequest ve MqlTradeResult) ve dizilerimizin değerlerini sıfırlamak için kullanırız.

showError işlevi:

 

//+------------------------------------------------------------------+
//|  SHOWERROR FUNCTION
//|  *Input Parameters - Error Message, Error Code                                                               
//+------------------------------------------------------------------+
void MyExpert::showError(string msg,int ercode)
  {
   Alert(msg,"-error:",ercode,"!!"); // display error
  }
 

Bu, sınıfımızın herhangi bir nesnesinin işlemleri sırasında karşılaşılan tüm hataları görüntülemek için kullanılan korumalı bir üye işlevidir. İki bağımsız değişken/parametre alır – Hata açıklaması ve hata kodu.

getBuffers işlevi:

//+------------------------------------------------------------------+
//|  GETBUFFERS FUNCTION                                                                
//|  *No input parameters
//|  *Uses the class data members to get indicator's buffers
//+------------------------------------------------------------------+
void MyExpert::getBuffers()
  {
   if(CopyBuffer(ADX_handle,0,0,3,ADX_val)<0 || CopyBuffer(ADX_handle,1,0,3,plus_DI)<0
      || CopyBuffer(ADX_handle,2,0,3,minus_DI)<0 || CopyBuffer(MA_handle,0,0,3,MA_val)<0)
     {
      Errormsg="Error copying indicator Buffers";
      Errcode = GetLastError();
      showError(Errormsg,Errcode);
     }
  }
  

Bu işlev, ilgili gösterge tanıtıcısını kullanarak tüm gösterge arabelleklerimizi üye değişkenlerde belirttiğimiz dizilere kopyalamak için kullanılır.

CopyBuffer işlevi, ilk makalede zaten açıklandı. Sınıfın üye değişkenlerinden gelen değerleri kullandığımız için getBuffers işlevinde herhangi bir giriş parametresi yoktur.

Arabellekleri kopyalama sürecinde oluşabilecek herhangi bir hatayı görüntülemek için burada dahili hata işlevimizi kullandık.

MarginOK işlevi:

//+------------------------------------------------------------------+
//|  MARGINOK FUNCTION
//| *No input parameters
//| *Uses the Class data members to check margin required to place a trade
//|  with the lot size is ok
//| *Returns TRUE on success and FALSE on failure
//+------------------------------------------------------------------+
bool MyExpert::MarginOK()
  {
   double one_lot_price;                                                        //Margin required for one lot
   double act_f_mag     = AccountInfoDouble(ACCOUNT_FREEMARGIN);                //Account free margin
   long   levrage       = AccountInfoInteger(ACCOUNT_LEVERAGE);                 //Leverage for this account
   double contract_size = SymbolInfoDouble(symbol,SYMBOL_TRADE_CONTRACT_SIZE);  //Total units for one lot
   string base_currency = SymbolInfoString(symbol,SYMBOL_CURRENCY_BASE);        //Base currency for currency pair
                                                                                //
   if(base_currency=="USD")
     {
      one_lot_price=contract_size/levrage;
     }
   else
     {
      double bprice= SymbolInfoDouble(symbol,SYMBOL_BID);
      one_lot_price=bprice*contract_size/levrage;
     }
// Check if margin required is okay based on setting
   if(MathFloor(LOTS*one_lot_price)>MathFloor(act_f_mag*TradePct))
     {
      return(false);
     }
   else
     {
      return(true);
     }
  }
  
 

Bu işlev aslında iki görev görür. Alım satım işlemi yapmak için yeterli serbest marjımız olup olmadığını kontrol eder ve ayrıca alım satım işlemi yapmak için mevcut serbest marjın belirli bir yüzdesinden fazlasını kullanıp kullanmadığımızı kontrol eder. Bu şekilde, her alım satım işlemi için ne kadar para kullandığımızı kontrol edebiliriz.

Hesap için Serbest Marj almak için AccountInfoDouble() işlevini ENUM_ACCOUNT_INFO_DOUBLE tanımlayıcısı  ile birlikte kullanırız.  Ayrıca, hesap için Kaldıraç almak için AccountInfoInteger() işlevini ENUM_ACCOUNT_INFO_INTEGER tanımlayıcısı ile birlikte kullanırız.  AccountInfoInteger() ve AccountInfoDouble(), EA kullanarak cari hesabın ayrıntılarını almak için kullanılan hesap işlevleridir.

double  AccountInfoDouble(
   int  property_id      // identifier of the property
   );

Ayrıca, geçerli sembol (para birimi çifti) için sözleşme boyutunu ve temel para birimini almak için SymbolInfoDouble() ve SymbolInfoString() sembol özellikleri işlevlerini kullandık. SymbolInfoDouble() işlevi, parametreler olarak sembol adını ve ENUM_SYMBOL_INFO_DOUBLE tanımlayıcısını alırken SymbolInfoString() işlevi, parametreler olarak sembol adını ve ENUM_SYMBOL_INFO_STRING tanımlayıcısını alır. Bu işlevlerin sonuçları, her veri türü için bildirilen değişkenlerde saklanır.

double  SymbolInfoDouble(
   string  name,        // symbol
   int     prop_id      // identifier of the property
   );

Bu yaptığımız hesaplama çok basittir.

Alım satım yapmak için gerekli marjı elde etmek için iki durumu göz önünde bulundururuz:

  1. Temel para birimi USD'dir (USD/CAD, USD/CHF, USD/JPY vb.)

Gerekli marj = Lot /Kaldıraç başına sözleşme boyutu

     2.  Temel para birimi USD değildir (EUR/USD vb.)

Gerekli marj = Sembolün mevcut fiyatı * lot/Kaldıraç başına sözleşme boyutu.

Artık, belirtilen lot boyutu veya hacmiyle alım satım yapmak için gereken marjın, bir alım satım işlemi için kullanmak istediğiniz serbest marj yüzdesinden büyük olup olmadığını kontrol etmeye karar verme zamanı. Gerekli marj daha azsa işlev TRUE değerini döndürür ve alım satım işlemi yapılır; aksi takdirde FALSE değerini döndürür ve alım satım işlemi yapılmaz.

doInit işlevi:

//+-----------------------------------------------------------------------+
// OUR PUBLIC FUNCTIONS                                                   |
//+-----------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| DOINIT FUNCTION
//| *Takes the ADX indicator's Period and Moving Average indicator's 
//| period as input parameters 
//| *To be used in the OnInit() function of our EA                                                               
//+------------------------------------------------------------------+
void MyExpert::doInit(int adx_period,int ma_period)
  {
//--- Get handle for ADX indicator
   ADX_handle=iADX(symbol,period,adx_period);
//--- Get the handle for Moving Average indicator
   MA_handle=iMA(symbol,period,ma_period,0,MODE_EMA,PRICE_CLOSE);
//--- What if handle returns Invalid Handle
   if(ADX_handle<0 || MA_handle<0)
     {
      Errormsg="Error Creating Handles for indicators";
      Errcode=GetLastError();
      showError(Errormsg,Errcode);
     }
// Set Arrays as series
// the ADX values arrays
   ArraySetAsSeries(ADX_val,true);
// the +DI value arrays
   ArraySetAsSeries(plus_DI,true);
// the -DI value arrays
   ArraySetAsSeries(minus_DI,true);
// the MA values arrays
   ArraySetAsSeries(MA_val,true);
  }

Bu, yakında yazacağımız EA'mızın OnInit() işlevinde kullanmayı planladığımız genel bir işlevdir ve iki işlev görecektir.

İlk olarak, göstergelerimiz için tanıtıcıları belirleyecek ve ayrıca dizi değişkenleri üzerinde array-set-as-series eylemini gerçekleştirecektir. EA kodumuzdan sağlanacak iki giriş parametresi içerir.

doUninit işlevi:

//+------------------------------------------------------------------+
//|  DOUNINIT FUNCTION
//|  *No input parameters
//|  *Used to release ADX and MA indicators handleS                                                                |
//+------------------------------------------------------------------+
void MyExpert::doUninit()
  {
//--- Release our indicator handles
   IndicatorRelease(ADX_handle);
   IndicatorRelease(MA_handle);
  }
  

Bu, işlev aynı zamanda, kullandığımız göstergeler için tüm tanıtıcıları serbest bırakmak için EA'mızın UnDeInit işlevinde kullanılacak bir genel üye işlevidir. Herhangi bir giriş parametresi yoktur.

checkBuy işlevi:

//+------------------------------------------------------------------+
//| CHECKBUY FUNCTION
//| *No input parameters
//| *Uses the class data members to check for Buy setup based on the
//|  the defined trade strategy 
//| *Returns TRUE if Buy conditions are met or FALSE if not met
//+------------------------------------------------------------------+
bool MyExpert::checkBuy()
  {
/*
    Check for a Long/Buy Setup : MA increasing upwards, 
    previous price close above MA, ADX > ADX min, +DI > -DI
*/
   getBuffers();
//--- Declare bool type variables to hold our Buy Conditions
   bool Buy_Condition_1=(MA_val[0]>MA_val[1]) && (MA_val[1]>MA_val[2]); // MA Increasing upwards
   bool Buy_Condition_2=(Closeprice>MA_val[1]);         // previous price closed above MA
   bool Buy_Condition_3=(ADX_val[0]>ADX_min);          // Current ADX value greater than minimum ADX value
   bool Buy_Condition_4=(plus_DI[0]>minus_DI[0]);       // +DI greater than -DI
//--- Putting all together   
   if(Buy_Condition_1 && Buy_Condition_2 && Buy_Condition_3 && Buy_Condition_4)
     {
      return(true);
     }
   else
     {
      return(false);
     }
  }

  

Bu işlev, bir satın alma koşulunun belirlenip belirlenmediğini kontrol etmek için kullanılacaktır. Bu nedenle dönüş türü bool'dur. Bu, TRUE veya FALSE döndüreceği anlamına gelir. Alış işlemi stratejimizi burada tanımladık. Tanımladığımız stratejiye göre bir satın alma koşulu karşılanırsa TRUE değerini döndürecek; ancak Satın alma koşulu karşılanmazsa FALSE değerini döndürecektir. Kodumuzda bu işlevi kullanırken, TRUE değerini döndürürse bir satın alma işlemi yapacağız.

Burada yaptığımız ilk şey, checkBuy işlevinin ihtiyaç duyduğu tüm dizi değerlerini karşılık gelen dizi değişkenlerine kopyalayacak olan getBuffers() dahili üye işlevini çağırmaktır.

Buraya kodlanan koşullar birinci makalede halihazırda açıklandı.

checkSell işlevi:

//+------------------------------------------------------------------+
//| CHECKSELL FUNCTION
//| *No input parameters
//| *Uses the class data members to check for Sell setup based on the
//|  the defined trade strategy 
//| *Returns TRUE if Sell conditions are met or FALSE if not met
//+------------------------------------------------------------------+
bool MyExpert::checkSell()
  {
/*
    Check for a Short/Sell Setup : MA decreasing downwards, 
    previous price close below MA, ADX > ADX min, -DI > +DI
*/
   getBuffers();
//--- Declare bool type variables to hold our Sell Conditions
   bool Sell_Condition_1=(MA_val[0]<MA_val[1]) && (MA_val[1]<MA_val[2]);  // MA decreasing downwards
   bool Sell_Condition_2=(Closeprice <MA_val[1]);                         // Previous price closed below MA
   bool Sell_Condition_3=(ADX_val[0]>ADX_min);                            // Current ADX value greater than minimum ADX
   bool Sell_Condition_4=(plus_DI[0]<minus_DI[0]);                        // -DI greater than +DI

//--- Putting all together
   if(Sell_Condition_1 && Sell_Condition_2 && Sell_Condition_3 && Sell_Condition_4)
     {
      return(true);
     }
   else
     {
      return(false);
     }
  }

Normal checkBuy gibi, bu işlev bir Satış koşulunun belirlenip belirlenmediğini kontrol etmek için kullanılacaktır. Bu nedenle dönüş türü de bool'dur. Bu, bir TRUE veya FALSE değerini döndüreceği anlamına gelir.  Satış işlemi stratejimizi burada tanımladık. Tanımladığımız stratejiye göre bir satış koşulu karşılanırsa TRUE değerini döndürecek; ancak satış koşulu karşılanmazsa FALSE değerini döndürecektir.

Kodumuzda bu işlevi kullanırken, TRUE değerini döndürürse satış yapacağız. Tıpkı checkBuy işlevindeki gibi, ilk olarak getBuffers() dahili işlevini çağırdık. Üyeler kodlanan koşullar aynı zamanda birinci makalede de açıklanmıştır.

openBuy işlevi:

//+------------------------------------------------------------------+
//| OPENBUY FUNCTION
//| *Has Input parameters - order type, Current ASK price, Stop Loss,
//|  Take Profit, deviation, comment
//| *Checks account free margin before pacing trade if trader chooses
//| *Alerts of a success if position is opened or shows error
//+------------------------------------------------------------------+
void MyExpert::openBuy(ENUM_ORDER_TYPE otype,double askprice,double SL,double TP,int dev,string comment="")
  {
//--- do check Margin if enabled
   if(Chk_Margin==1)
     {
      if(MarginOK()==false)
        {
         Errormsg= "You do not have enough money to open this Position!!!";
         Errcode =GetLastError();
         showError(Errormsg,Errcode);
        }
      else
        {
         trequest.action=TRADE_ACTION_DEAL;
         trequest.type=otype;
         trequest.volume=LOTS;
         trequest.price=askprice;
         trequest.sl=SL;
         trequest.tp=TP;
         trequest.deviation=dev;
         trequest.magic=Magic_No;
         trequest.symbol=symbol;
         trequest.type_filling=ORDER_FILLING_FOK;
         // send
         OrderSend(trequest,tresult);
         // check result
         if(tresult.retcode==10009 || tresult.retcode==10008) //Request successfully completed 
           {
            Alert("A Buy order has been successfully placed with Ticket#:",tresult.order,"!!");
           }
         else
           {
            Errormsg= "The Buy order request could not be completed";
            Errcode =GetLastError();
            showError(Errormsg,Errcode);
           }
        }
     }
   else
     {
      trequest.action=TRADE_ACTION_DEAL;
      trequest.type=otype;
      trequest.volume=LOTS;
      trequest.price=askprice;
      trequest.sl=SL;
      trequest.tp=TP;
      trequest.deviation=dev;
      trequest.magic=Magic_No;
      trequest.symbol=symbol;
      trequest.type_filling=ORDER_FILLING_FOK;
      //--- send
      OrderSend(trequest,tresult);
      //--- check result
      if(tresult.retcode==10009 || tresult.retcode==10008) //Request successfully completed 
        {
         Alert("A Buy order has been successfully placed with Ticket#:",tresult.order,"!!");
        }
      else
        {
         Errormsg= "The Buy order request could not be completed";
         Errcode =GetLastError();
         showError(Errormsg,Errcode);
        }
     }
  }

Bu, EA'mızda her çağrıldığında bir alış pozisyonu açan işlevdir. Giriş parametreleri olarak, alım satım yapmak için ihtiyaç duyulacak değişkenlerin çoğunu içerir ve bazı değişkenler EA kodumuz tarafından sağlanacaktır. İlk makalede açıklandığı gibi, burada MqlTraderequest türü değişkenleri kullandığımızı fark edeceksiniz.

Onları EA kodumuzda kullanmamız gerekmeyecek. Bir alım satım işlemi yapılmadan önce, kullanıcının marjı kontrol etmek isteyip istemediğini doğrulamak isteriz; Chk_Margin (EA'dan alınacak olan) değeri 1 ise, MarginOK() işlevi bu işlemi bizim için yapar. Bu işlevin sonucu, atılacak bir sonraki adımı belirler.  Ancak, kullanıcı marjı kontrol etmek istemiyorsa devam edip alım satım yaparız.

openSell işlevi:

//+------------------------------------------------------------------+
//| OPENSELL FUNCTION
//| *Has Input parameters - order type, Current BID price, Stop Loss,
//|  Take Profit, deviation, comment
//| *Checks account free margin before pacing trade if trader chooses
//| *Alerts of a success if position is opened or shows error
//+------------------------------------------------------------------+
void MyExpert::openSell(ENUM_ORDER_TYPE otype,double bidprice,double SL,double TP,int dev,string comment="")
  {
//--- do check Margin if enabled
   if(Chk_Margin==1)
     {
      if(MarginOK()==false)
        {
         Errormsg= "You do not have enough money to open this Position!!!";
         Errcode =GetLastError();
         showError(Errormsg,Errcode);
        }
      else
        {
         trequest.action=TRADE_ACTION_DEAL;
         trequest.type=otype;
         trequest.volume=LOTS;
         trequest.price=bidprice;
         trequest.sl=SL;
         trequest.tp=TP;
         trequest.deviation=dev;
         trequest.magic=Magic_No;
         trequest.symbol=symbol;
         trequest.type_filling=ORDER_FILLING_FOK;
         // send
         OrderSend(trequest,tresult);
         // check result
         if(tresult.retcode==10009 || tresult.retcode==10008) //Request successfully completed 
           {
            Alert("A Sell order has been successfully placed with Ticket#:",tresult.order,"!!");
           }
         else
           {
            Errormsg= "The Sell order request could not be completed";
            Errcode =GetLastError();
            showError(Errormsg,Errcode);
           }
        }
     }
   else
     {
      trequest.action=TRADE_ACTION_DEAL;
      trequest.type=otype;
      trequest.volume=LOTS;
      trequest.price=bidprice;
      trequest.sl=SL;
      trequest.tp=TP;
      trequest.deviation=dev;
      trequest.magic=Magic_No;
      trequest.symbol=symbol;
      trequest.type_filling=ORDER_FILLING_FOK;
      //--- send
      OrderSend(trequest,tresult);
      //--- check result
      if(tresult.retcode==10009 || tresult.retcode==10008) //Request successfully completed 
        {
         Alert("A Sell order has been successfully placed with Ticket#:",tresult.order,"!!");
        }
      else
        {
         Errormsg= "The Sell order request could not be completed";
         Errcode =GetLastError();
         showError(Errormsg,Errcode);
        }
     }
  }

openBuy işlevi gibi, bu işlev de EA'mızda her çağrıldığında bir satış pozisyonu açar. Giriş parametreleri olarak, alım satım yapmak için ihtiyaç duyulacak değişkenlerin çoğunu içerir ve bazı değişkenler EA kodumuz tarafından sağlanacaktır.

Bir Alış pozisyonu açarken yaptığımız gibi, bir alım satım işlemi yapılmadan önce, kullanıcının marjı kontrol etmek isteyip istemediğini, Chk_Margin değerinin (EA'dan alınacak) 1 olup olmadığını doğrulamak istiyoruz, bu durumda da bunu bizim için yapması için MarginOK() işlevini çağırırız.

Bu işlevin sonucu, atılacak bir sonraki adımı belirler. Ancak, kullanıcı marjı kontrol etmek istemiyorsa devam edip alım satım işlemini yaparız.

Artık sınıfımızın ve üye işlevlerin bildirimini ve tanımını bitirdik, ancak EA kodumuzda ele almayı düşündüğümüz diğer bazı görevlerin üzerinde durmadık. Bunlar, mevcut çubukların, yeni çubukların ve mevcut açık pozisyonların kontrol edilmesini içerir. Bunlar, EA kodumuzda ele alınacaktır.

Sınıfımızın tüm işlevlerinin ve yöntemlerinin bir listesini görmek için aşağıda gösterildiği gibi MetaEditor'daki işlevler komutuna/menüsüne tıklayın. İşlev, kodumuzda açıkça bildirmediğimiz Yıkıcı dahil tüm üye işlevlerini görüntüler.

Korumalı üyeler yeşil oklarla, Oluşturucu ve yıkıcı ise mavi oklarla gösterilir.

 

Sınıf üyesi işlevleri

Şekil 3. Sınıf yıkıcıyı gösteren sınıf üyesi işlevlerimiz

Peki sırada ne var?

Hata ayıklama dediğinizi duyar gibiyim. Haklı olabilirsiniz. Kodunuzda hata olup olmadığını test etmek ve görmek her zaman iyidir; aksi takdirde kodu herkese açık olarak serbest bıraktığınızda hayal kırıklığına uğrayacaksınız. Buradaki sorun, bunun yalnızca bir içerme dosyası olması, grafiğe eklenebilecek bir expert adviser kodu veya script dosyası veya gösterge kodu olmamasıdır. Bu noktada iki seçeneğiniz var (deneyimlerime göre);,

  • düzenleyicinizdeki hata ayıklama düğmesine basma olasılığınız vardır; böylece hata ayıklayıcı, bir .mqh dosyası bir .ex5 dosyasına derlenemediği için görüntülenecek olan 'yürütülebilir dosya oluşturulmadı' hatası hariç olmak üzere kodunuzdaki herhangi bir hatayı bildirecektir.  VEYA
  • Devam edin ve sınıfınızı kullanacak EA için kod yazın. EA'da hata ayıklamaya başladığınızda, içerilen dosya onunla birlikte kontrol edilecektir. Aslında, bunu yapmanın en iyi ve en kabul edilebilir yolu budur.

Şekil 4. .mqh dosyaları derlenemez

2.2. EXPERT ADVISOR'I YAZMA

Düzenleyicinizin hala açık olduğunu tahmin ediyorum. Yeniden yeni bir belge başlatın, ancak bu kez Expert Advisor'ı seçin. (Ayrıntılar için lütfen ilk makaleye bakın). Ancak bu kez EA'nıza 'my_oop_ea' adını verin.

Bu, anda olmanız gereken yer burası:


Artık OOP tabanlı EA'mızı yazmaya hazırız.

Bu yapacağımız ilk şey, az önce yazdığımız sınıfı #include ön işlemci komutunu kullanarak eklemektir. Son önişlemci özellik komutundan hemen sonra sınıfı ekleyin

//+------------------------------------------------------------------+
//|                                                    my_oop_ea.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
// Include  our class
#include <my_expert_class.mqh>

Bir dosya eklemenin iki yolu vardır;

// Include  using angle brackets
#include <my_expert_class.mqh>
// Include  using quotations
#include "my_expert_class.mqh"

Açılı ayracı (< ... >) kullandığımızda, bu, eklenecek dosyanın standart içerme dizininden (yani MQL5 dizini içindeki içerme klasörü) alınacağı anlamına gelir.  Geçerli dizin (MQL5 dizini içindeki Experts klasörü, dosyayı aramak için olası bir yer olarak kabul edilmeyecektir). Ancak, dosya tırnak işaretleri (" ... ") içine alınırsa dosyanın geçerli dizinde (Experts klasörü olan) olduğu kabul edilecek ve standart dizin (İçerme klasörü) kontrol edilmeyecektir.

Sınıfınız Include klasöründe (standart dizin) saklanmışsa ve açılı ayraçların yerine tırnak işaretlerini kullanırsanız veya tırnak işaretlerinin yerine açılı ayraçları kullanırsanız kodu derlerken bir hata alırsınız.


Şekil 5. Include dosyası bulunamadığında görüntülenen bir hata mesajı

EA GİRİŞ PARAMETRELERİ

//--- input parameters
input int      StopLoss=30;      // Stop Loss
input int      TakeProfit=100;   // Take Profit
input int      ADX_Period=14;    // ADX Period
input int      MA_Period=10;     // Moving Average Period
input int      EA_Magic=12345;   // EA Magic Number
input double   Adx_Min=22.0;     // Minimum ADX Value
input double   Lot=0.2;          // Lots to Trade
input int      Margin_Chk=0;     // Check Margin before placing trade(0=No, 1=Yes)
input double   Trd_percent=15.0; // Percentage of Free Margin To use for Trading

Buradaki giriş parametrelerinin çoğu yeni değil. O halde yenilerini tartışalım.

Marj kontrolü kullanmak isteyip istemediğimizden bağımsız olarak 0, 1 değerini tutmak için bir tamsayı değişkeni ekledik. Ayrıca, bir pozisyon açarken kullanılacak maksimum Serbest marj yüzdesini tutmak için başka bir değişken bildirdik. Bu değerler oluşturulduğunda daha sonra sınıf nesnemizde kullanılacaktır.

Giriş parametrelerinden hemen sonra, giriş değişkenlerinin değerlerini değiştiremediğimiz için manipüle etmek (5 ve 3 basamaklı fiyatları karşılamak için) istediğimiz diğer iki parametreyi (STP ve TKP) tanımlarız. Ardından, EA kodumuzda kullanılmak üzere sınıfımızın bir nesnesini oluştururuz.

//--- Other parameters
int STP,TKP;   // To be used for Stop Loss & Take Profit values
// Create an object of our class
MyExpert Cexpert;

Daha önce açıklandığı gibi, bir sınıfın nesnesini oluşturmak için sınıf adını ve ardından oluşturmak istediğiniz nesnenin adını kullanırsınız. Burada MyExpert sınıfının bir türü olan Cexpert nesnesini oluşturduk. Artık Cexpert, MyExpert sınıfının tüm genel üye işlevlerine erişmek için kullanılabilir.

EA'YI BAŞLATMA BÖLÜMÜ

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {

//--- Run Initialize function
   Cexpert.doInit(ADX_Period,MA_Period);
//--- Set all other necessary variables for our class object
   Cexpert.setPeriod(_Period);     // sets the chart period/timeframe
   Cexpert.setSymbol(_Symbol);     // sets the chart symbol/currency-pair
   Cexpert.setMagic(EA_Magic);    // sets the Magic Number
   Cexpert.setadxmin(Adx_Min);    // sets the ADX miniumm value
   Cexpert.setLOTS(Lot);          // set the Lots value
   Cexpert.setchkMAG(Margin_Chk); // set the margin check variable
   Cexpert.setTRpct(Trd_percent); // set the percentage of Free Margin for trade
//--- Let us handle brokers that offers 5 digit prices instead of 4
   STP = StopLoss;
   TKP = TakeProfit;
   if(_Digits==5 || _Digits==3)
     {
      STP = STP*10;
      TKP = TKP*10;
     }  
//---
   return(0);
  }

Bu noktada, sınıfımızın doInit işlevini çağırdık ve ona ADX ve MA dönem değişkenlerini ilettik. Daha sonra, yeni oluşturduğumuz nesnenin gerektireceği diğer tüm değişkenleri, sınıfımızı yazarken daha önce tanımladığımız işlevleri kullanarak nesnenin üye değişkenlerinde saklanacak şekilde ayarlıyoruz.

Bir sonraki kod satırı garip olmamalı; yalnızca üç ve beş basamaklı fiyatlar için Zararı Durdur ve Kar Al değerlerimizi ayarlamaya karar veriyoruz.

EA'YI YENİDEN BAŞLATMA BÖLÜMÜ

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Run UnIntilialize function
   Cexpert.doUninit();
  }

Yalnızca başlatma işlevinde oluşturulmuş olması gereken tüm gösterge tanıtıcılarını serbest bırakmak için sınıfın doUninit işlevini çağırdık.

EA'YI ONTICK KESİTİ

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- 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
   MqlRates mrate[];          // To be used to store the prices, volumes and spread of each bar
/*
     Let's make sure our arrays values for the Rates
     is store serially similar to the timeseries array
*/
// the rates arrays
   ArraySetAsSeries(mrate,true);

Burada yaptığımız ilk şey, toplam kullanılabilir çubukları kontrol etmektir. Çubuklar EA'mızın alım satım yapması için yeterliyse alım satım yapacaktır; aksi takdirde yeterli çubuğumuz (yani 60 çubuk) olana kadar alım satım yapmayacaktır. Bunun ardından MQL5 yapısının (MqlTick ve MqlRates) iki değişkenini bildirdik. Ve son olarak, oranlar dizisinde ArraySetAsSeries işlevini kullanıyoruz.

//--- 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;
     }

//--- 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;
     }

//--- EA should only check for new trade if we have a new bar
// lets declare a static datetime variable
   static datetime Prev_time;
// lest get the start time for the current bar (Bar 0)
   datetime Bar_time[1];
// copy time
   Bar_time[0] = mrate[0].time;
// We don't have a new bar when both times are the same
   if(Prev_time==Bar_time[0])
     {
      return;
     }
//copy time to static value, save
   Prev_time = Bar_time[0]; 
   

Burada, en son fiyat teklifini almak için SymbolInfoTick işlevini kullandık ve son üç çubuk (mevcut çubuk dahil) için son oranları almak için CopyRates kullandık. Sonraki kod satırları, yeni bir çubuğumuz olup olmadığını kontrol eder. Biri statik değişken (Prev_Time) ve diğeri Bar_Time olmak üzere iki tarih saat değişkeni bildirdik.

Yeni bir çubuğumuz varsa çubuk zamanı, bir sonraki tick'te değerini Bar_Time değeriyle karşılaştırabilmemiz için Prev_Time statik değişkeninde saklanır. Bir sonraki tick'te, Prev_Time, Bar_Time değerine eşitse bu durumda, bu, hala zamanı saklanan aynı çubuktur. Yani EA'mız rahatlayacak.

Ancak Bar_Time, Prev_Time değerine eşit değilse o zaman yeni bir çubuğumuz olur. Yeni çubuk başlangıç zamanını statik tarih saat değişkeninde, Prev_Time saklamaya karar verdik; EA'mız artık yeni ALIŞ veya SATIŞ fırsatlarını kontrol etmeye devam edebilir.

//--- we have no errors, so continue
//--- Do we have positions opened already?
    bool Buy_opened = false, Sell_opened=false; // variables to hold the result of the 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
         }
    }

Zaten açık bir pozisyonumuz olup olmadığını kontrol etmeye karar verdik. Açılmış bir Alış olmadığında bir alış işlemi ve açılmış bir satış olmadığında bir satış işlemi açtığımızdan emin olmak istiyoruz.

// Copy the bar close price for the previous bar prior to the current bar, that is Bar 1
   Cexpert.setCloseprice(mrate[1].close);  // bar 1 close price
//--- Check for Buy position
   if(Cexpert.checkBuy()==true)
     {
      // Do we already have an opened buy position
      if(Buy_opened)
        {
         Alert("We already have a Buy Position!!!");
         return;    // Don't open a new Buy Position
        }
      double aprice = NormalizeDouble(latest_price.ask,_Digits);              // current Ask price
      double stl    = NormalizeDouble(latest_price.ask - STP*_Point,_Digits); // Stop Loss
      double tkp    = NormalizeDouble(latest_price.ask + TKP*_Point,_Digits); // Take profit
      int    mdev   = 100;                                                    // Maximum deviation
      // place order
      Cexpert.openBuy(ORDER_TYPE_BUY,aprice,stl,tkp,mdev);
     }

Şimdi oluşturduğumuz nesneye geri döndük; peki neden? Çünkü nesnemizin işini yapması için gerekli tüm kontrolleri yapabildik.

Yaptığımız ilk şey, nesneler üye işlevimizi setCloseprice kullanarak önceki çubuğun kapanış fiyatını elde etmektir.

Ardından, satın alma için bir koşulun belirlenip belirlenmediğini öğrenmek için checkBuy işlevini çağırırız; TRUE değerini döndürürse halihazırda açık bir satın alma pozisyonumuz olmadığından emin olmak isteriz. Halihazırda açık bir satın alma pozisyonumuz yoksa talimatımız için kullanılacak gerekli değişkenleri, (talimat türü, güncel SATIŞ fiyatı, zararı durdur, kar al ve maksimum sapma) hazırlarız ve openBuy işlevini çağırırız. Yazdığımız sınıfı kullanmanın ne kadar kolay olduğuna bakın.

//--- Check for any Sell position
   if(Cexpert.checkSell()==true)
     {
      // Do we already have an opened Sell position
      if(Sell_opened)
        {
         Alert("We already have a Sell position!!!");
         return;    // Don't open a new Sell Position
        }
      double bprice=NormalizeDouble(latest_price.bid,_Digits);                 // Current Bid price
      double bstl    = NormalizeDouble(latest_price.bid + STP*_Point,_Digits); // Stop Loss
      double btkp    = NormalizeDouble(latest_price.bid - TKP*_Point,_Digits); // Take Profit
      int    bdev=100;                                                         // Maximum deviation
      // place order
      Cexpert.openSell(ORDER_TYPE_SELL,bprice,bstl,btkp,bdev);
     }

Bu, yukarıda yaptığımız şeyle aynı. Satışı kontrol ettiğimiz için checkSell işlevini çağırdık; bu, TRUE değerini döndürürse ve halihazırda açılmış bir satış pozisyonumuz yoksa, talimatımızı vermek için gerekli değişkenleri hazırlarız ( talimat türü, geçerli SATIŞ fiyatı, zararı durdur, kar al ve maksimum sapma) ve ardından openSell işlevini çağırırız.

Oldukça kolay, değil mi? Kod yazma işlemini tamamladık. Şimdi kodumuzda hata ayıklama zamanı. Hata ayıklayıcıyı nasıl kullanacağınızı bilmiyorsanız daha iyi anlamak için lütfen ilk makaleyi okuyun.

F5 tuşuna veya hata ayıklama düğmesine bastığınızda, içerilen dosya (sınıfımız) eklenecek ve kontrol edilecek ve herhangi bir hata varsa bunu bildirecektir. Hatayı gördüğünüzde koda geri dönmeniz ve hatayı düzeltmeniz gerekir.

Şekil 6. İçerme dosyamız, ana EA kodunda hata ayıklarken eklenir

Her şey yolundaysa, adımları doğru şekilde uyguladınız demektir. Şimdi strateji test cihazını kullanarak EA'mızı test etme zamanı. Strateji Test Cihazı ile test etmeden önce EA'mızı derlememiz gerekiyor. Bunu yapmak için, Derle düğmesine tıklayın veya bilgisayar klavyenizde F7 tuşuna basın.

Şekil 7. Kodumuzu derlemek için Derle menü düğmesine tıklayın

Alım satım terminali menü çubuğundan, Görünüm Strateji Test Cihazı seçeneğine gidin veya strateji test cihazını başlatmak için CONTROL+R tuşlarına basın. (Test cihazının nasıl kullanılacağına ilişkin ayrıntılar için lütfen ilk makaleyi okuyun).

EA'yı strateji test cihazı ile test edebilmeniz için öncelikle onu derlemeniz gerekir. Derleme yapmazsanız strateji test cihazının ayarlar çubuğunda Expert Advisor seçtiğinizde bir hata alırsınız. (Bunu yalnızca terminalin yeni sürümünde keşfettim.)

Şekil 8. EA kodu, Strateji Test Cihazında kullanılmadan önce derlenmelidir

OOP tabanlı Expert Advisor'ımız için Strateji test cihazının sonuçlarını aşağıda bulabilirsiniz.

 Şekil 9. Nesne Yönelimli Expert Advisor'ımız için alım satım işlemi sonuçları

Grafik:

Şekil 10. Nesne Yönelimli Expert Advisor'ımızın grafik sonuçları

Alım satım etkinliği raporu/günlüğü:


Şekil 11. Nesne Yönelimli Expert Advisor'ımız için alım satım etkinliği sonuçları


Test grafiği:

Şekil 12. Nesne Yönelimli Expert Advisor'ımız için alım satım grafiği sonuçları

Sonuç

Bu makalede, bir sınıfın temellerini ve bu sınıfın basit bir Expert Advisor yazarken nasıl kullanılacağını bir dereceye kadar ele aldık. Sınıfların ileri düzey alanlarına çok fazla girmedik ancak bu makalede tartıştıklarımız, kendinizi kendi nesne yönelimli Expert Advisor kodunuzu yazabilecek seviyeye getirmenize yardımcı olmak için yeterli olacaktır. 

Ayrıca, açmak istediğimiz pozisyon için mevcut serbest marj yeterli olmadığında EA'mızın alım satım işlemi yapmaması için serbest marjı nasıl kontrol edebileceğimizi de tartıştık.

Artık yeni MQL5 dilinin sunabileceği çok şey olduğu ve bu yeni dilin avantajlarından yararlanmak için bir programlama gurusu olmanız gerekmediği konusunda benimle hemfikirsinizdir. Adım adım kılavuzları yazmanın ana nedeni budur.


MetaQuotes Ltd tarafından İngilizceden çevrilmiştir.
Orijinal makale: https://www.mql5.com/en/articles/116

Ekli dosyalar |
my_expert_class.mqh (17.52 KB)
my_oop_ea.mq5 (6.77 KB)
Belirtilen Sihirli Sayıya Göre Toplam Pozisyon Hacmini Hesaplamak İçin Optimum Yöntem Belirtilen Sihirli Sayıya Göre Toplam Pozisyon Hacmini Hesaplamak İçin Optimum Yöntem
Bu makalede, belirtilen sembol ve sihirli sayının toplam pozisyon hacminin hesaplanması sorunu ele alınmaktadır. Önerilen yöntem, yatırım geçmişinin yalnızca gerekli olan minimum bölümünü ister, toplam pozisyonun sıfıra eşit olduğu en yakın zamanı bulur ve son yatırımlarla hesaplamaları gerçekleştirir. Ayrıca istemci terminalinin genel değişkenleriyle çalışmak da dikkate alınır.
Adlandırılmış Kanalları kullanarak MetaTrader 5 terminalleri arasında iletişim kurmak için DLL içermeyen bir çözüm Adlandırılmış Kanalları kullanarak MetaTrader 5 terminalleri arasında iletişim kurmak için DLL içermeyen bir çözüm
Makalede, adlandırılmış kanallar kullanılarak MetaTrader 5 istemci terminalleri arasında İşlemler Arası İletişimin nasıl uygulanacağı açıklanmaktadır. Adlandırılmış kanalların kullanımı için CNamedPipes sınıfı geliştirilmiştir. Kullanımını test etmek ve bağlantı verimini ölçmek için tick göstergesi, sunucu ve istemci script dosyaları sunulur. Adlandırılmış kanalların kullanılması, gerçek zamanlı fiyat teklifleri için yeterlidir.
Karı Geri Çekme İşlemlerini Modellemek için TesterWithdrawal() İşlevini Kullanma Karı Geri Çekme İşlemlerini Modellemek için TesterWithdrawal() İşlevini Kullanma
Bu makalede, işlem sırasında varlıkların belirli bir bölümünün geri çekilmesini gerektiren alım satım sistemlerindeki riskleri tahmin etmek için TesterWithDrawal() işlevinin kullanımı açıklanmaktadır. Ayrıca, bu işlevin strateji test cihazında hisse senedi düşüşü hesaplama algoritması üzerindeki etkisi de açıklanmaktadır. Bu işlev, Expert Advisor'larınızın parametresini optimize ederken kullanışlıdır.
Google Chart API ile Grafik Oluşturmak için Kitaplık Google Chart API ile Grafik Oluşturmak için Kitaplık
Çeşitli diyagram türlerinin oluşturulması, piyasa durumuna ilişkin analizlerin ve bir alım satım sisteminin test edilmesinin önemli bir parçasıdır. Sıklıkla, güzel görünümlü bir diyagram oluşturmak için, veri çıktısını bir dosyada düzenlemek ve ardından MS Excel gibi uygulamalarda kullanmak gerekir. Bu, çok elverişli değildir ve bizi verileri dinamik olarak güncelleme özelliğinden mahrum eder. Google Charts API, sunucuya özel bir istek göndererek çevrimiçi modlarda grafikler oluşturmak için araçlar sağladı. Bu makalede, böyle bir istek oluşturma ve Google sunucusundan grafik alma sürecini otomatikleştirmeye çalışıyoruz.