English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Italiano
Alım Satım Modellerine Dayalı Çoklu Uzman Danışmanlar Oluşturma

Alım Satım Modellerine Dayalı Çoklu Uzman Danışmanlar Oluşturma

MetaTrader 5Sınayıcı | 16 Aralık 2021, 14:34
134 0
Vasiliy Sokolov
Vasiliy Sokolov


Giriş

MetaTrader 5 terminalinin teknik yetenekleri ve strateji test edicisi çoklu para birimli alım satım sistemlerinin çalışmasını ve test edilmesini belirler. Öncelikle, MetaTrader 4 için bu tür sistemleri geliştirmenin karmaşıklığı, birkaç alım satım aracının aynı anda dakika gün hafta ve aylık bazda grafikler ile test edilememesinden kaynaklanmaktadır. Buna ek olarak MQL4 dilinin sahip olduğu sınırlı dil kaynakları karmaşık veri yapılarının düzenlenmesine ve verilerin verimli bir şekilde yönetilmesine izin vermemekteydi.

MQL5'in piyasaya sürülmesiyle bu durum değişti. Artık MQL5 nesne yönelimli yaklaşımı desteklemekle beraber gelişmiş bir yardımcı fonksiyon mekanizmasına dayanmaktadır ve hatta veri organizasyonundan standart sistem fonksiyonları için çalışma arayüzlerine kadar kullanıcıların günlük görevlerini kolaylaştırmaya yönelik bir dizi Standart Kütüphane temel sınıfına sahiptir.

Ve strateji test edicinin ve terminalin teknik özellikleri çok para birimli EA'ların kullanımına izin verse de, tek bir EA'nın çalışmasının eşzamanlı olarak birkaç araç veya zaman diliminde paralelleştirilmesine yönelik yerleşik yöntemlere sahip değildir. Daha önce olduğu gibi, en basit durumda bir EA'nın çalışması için onu alım satım aracının adını ve zaman dilimini belirleyen sembolün penceresinde çalıştırmanız gerekir. Sonuç olarak, MetaTrader 4 zamanından kabul edilen çalışma metodolojisi strateji test ediciden ve MetaTrader 5 terminalinden tam olarak yararlanmaya izin vermemektedir.

Her bir araç için yalnızca o araçtaki toplam işlem miktarına eşit olan bir kümülatif pozisyona izin verilmesiyle durum karmaşık bir hal almaktadır; elbette net bir pozisyona geçiş doğru ve zamanındadır. Net pozisyon, alım satım yapan kişinin belirli bir piyasaya olan ilgisinin mükemmele en yakın temsilidir.

Ancak böyle bir organizasyon alım satım sürecini basitleştirmez ve kolayca görselleştirilmez. Daha öncesinde bir EA'nın açılış emrini seçmesi (örneğin, emir sihirli sayı kullanılarak tanımlanabilir) ve gerekli eylemi gerçekleştirmesi yeterliydi. Fakat şimdi bir araçta net bir pozisyonun olmaması bile şu anda bu konuda bir EA'nın belirli bir örneğinin piyasada olmadığı anlamına gelmez!

Üçüncü taraf geliştiriciler sanal emirlerin özel yöneticilerini yazmaktan (bkz: pozisyon merkezli MT5 ortamındaki emirleri izlemek için Sanal Emir Yöneticisi makalesi) sihirli sayıyı kullanarak girdileri toplu bir pozisyonda entegre etmeye kadar (bkz: Belirtilen Sihirli Sayıya Göre Toplam Pozisyon Hacmini Hesaplamak İçin Optimal Yöntem veya ORDER_MAGIC'in Tek Bir Araç Üzerinde Farklı Uzman Danışmanlarla İşlem Yapmak İçin Kullanımı) net pozisyonla ilgili sorunu çözmek için çeşitli yollar sunar.

Ancak toplu pozisyonla ilgili sorunlara ek olarak, aynı EA'nın birden fazla araç üzerinde işlem yapması gerektiğinde çoklu para birimi olarak adlandırılan bir sorun meydana gelmektedir. Bu sorunun çözümü Farklı Araçlarla İşlem Yapan Uzman Danışman Oluşturma makalesinde bulunabilir.

Önerilen yöntemlerin tümü çalışır durumdadır ve kendi avantajlarına sahiptir. Ancak bu yöntemlerin en büyük kusurları her birinin konuya kendi perspektifinden yaklaşmaya çalışmasıdır; örneğin, tek bir araç üzerinde birkaç EA tarafından eşzamanlı alım satım için çok uygun olan, ancak çoklu para birimi çözümleri için uygun olmayan çözümler sunarlar.

Bu makale tüm sorunları tek bir çözümle gidermeyi amaçlamaktadır. Bu çözümden yararlanmak çoklu para birimi sorununu ve hatta tek bir araçta farklı EA'lar arasındaki etkileşimin çoklu sistem şeklinde test edilmesi sorununu bile çözebilir. Bunu başarmak zor ve hatta imkânsız görünür ama aslında çok kolaydır.

Tek bir EA ile eşzamanlı olarak birkaçdüzine alım satım stratejisinde, mevcut tüm araçlarda ve olası tüm zaman dilimlerinde işlem yapabildiğini hayal edin!  Buna ek olarak, EA test edicide kolayca test edilir ve bileşimine dâhil edilen tüm stratejiler için bir veya birkaç para yönetimi çalışma sistemine sahiptir.

Öyleyse tamamlamamız gereken temel görevler şunlardır:

  1. EA'nın aynı anda birkaç alım satım sistemi temelinde alım satım yapması gerekiyor. Ek olarak, tek bir alım satım sisteminde olduğu kadar birden fazla alım satım sisteminde de aynı kolaylıkta alım satım yapmalıdır;
  2. EA'da uygulanan alım satım sisteminin tamamı birbiriyle çelişmemelidir. Her alım satım sistemi toplam net pozisyona yalnızca kendi katkısını ve emirlerini işlemelidir;
  3. Her bir sistemde aracın tek bir zaman diliminde ve aynı anda tüm zaman dilimlerinde aynı kolaylıkta alım satım yapılmalıdır.
  4. Her bir sistemde tek bir alım satım aracında ve aynı anda mevcut tüm araçlarda aynı kolaylıkta alım satım yapılmalıdır.

Tamamlamamız gereken görevlerin listesini dikkatlice incelersek üç boyutlu bir diziye ulaşırız. Bu dizinin ilk boyutu alım satım sistemlerinin sayısı, ikinci boyutu belirli TS'nin üzerinde çalışması gereken zaman dilimlerinin sayısı ve üçüncüsü de TS için alım satım araçlarının sayısıdır. Basit bir hesaplama, MACD Sample gibi basit bir EA'nın bile 8 ana para birimi çifti üzerinde eşzamanlı olarak çalışırken 152 bağımsız çözüme sahip olacağını gösteriyor: 1 EA * 8 çift * 19 zaman dilimi (haftalık ve aylık zaman dilimleri dâhil değildir).

Alım satım sisteminin çok daha büyük ve EA'ların alım satım portföyünün de önemli ölçüde daha kapsamlı olması halinde çözümlerin sayısı kolayca 500'ün üzerinde ve bazı durumlarda 1000'in üzerinde olabilir! Her bir kombinasyonu ayrı ayrı manuel olarak yapılandırmanın ve ardından yüklemenin imkansız olduğu açıktır. Bu nedenle her kombinasyonun otomatik olarak ayarlanacağı, EA'nın belleğine yükleyeceği ve EA’nın daha sonra bu kombinasyonun belirli bir örneğinin kurallarına dayanarak alım satım yapacağı bir sistem inşa etmek gerekir.


Terimler ve kavramlar

Burada ve daha sonrasında “alım satım stratejisi” kavramı daha spesifik bir terim olan alım satım modeli veya kısaca model olarak kullanılacaktır. Bir alım satım modeli alım satım stratejisini tam olarak tanımlayan belirli kurallara göre oluşturulmuş özel bir sınıftır: alım satımda kullanılan göstergeler, giriş ve çıkışın alım satım koşulları, para yönetimi yöntemleri vb. Her alım satım modeli soyuttur ve işleyişi için belirli parametreleri tanımlamaz.

İki hareket eden ortalamanın çaprazlanmasına dayanan alım satım taktiği buna basit bir örnektir. Hızlı hareket eden ortalama yavaş olanla yukarı doğru kesişirse satın alım için bir işlem başlatır; eğer tam tersi olursa satış için bir işlem başlatır. Bu formülasyon kendi temelinde alım satım yapan bir alım satım modeli yazmak için yeterlidir.

Ancak böyle bir model tanımlandıktan sonra hareketli ortalamaların yöntemlerinin ortalama dönemleri ile veri penceresinin dönemininve bu modelin alım satım yapacağı aracın belirlenmesi gerekmektedir. Genel olarak bu soyut model belirli bir model örneği oluşturmanız gerektiğinde girilmesi gereken parametreleri içerecektir. Bu yaklaşım ile soyut bir modelin parametrelerinde farklılık gösteren birçok model örneğine dayanak olabileceği açıktır.


Net pozisyonun muhasebeleştirilmesinin tamamen reddedilmesi 

Birçok geliştirici toplu pozisyonu takip etmeye çalışır. Bununla birlikte, yukarıdan da görebileceğimiz üzere ne toplu pozisyonu boyutu ne de dinamikleri modelin belirli bir örneğiyle ilgili değildir. Model kısa olabilir ve toplu pozisyon hiç mevcut olmayabilir (nötr toplu pozisyon). Bunun aksine toplu pozisyon kısa olabilirken model uzun pozisyona sahip olacaktır.

Hadi bu durumları daha ayrıntılı bir şekilde ele alalım. Bir aracın her biri kendi bağımsız para yönetimi sistemine sahip olan üç farklı alım satım taktiği ile alım satım yaptığını varsayalım. Ayrıca üç sistemden ilkinin teminatsız üç sözleşme satmaya karar verdiğini veya üç sözleşme hacmiyle kısa pozisyon açmaya karar verdiğini varsayalım. İşlemlerin tamamlanmasından sonra, net pozisyon yalnızca ilk alım satım sisteminin işlemlerinden oluşacak; hacmiyse üç sözleşme eksik veya sadece teminatsız üç kısa sözleşme olacaktır. Bir süre sonra ikinci alım satım sistemi aynı varlığın 4 sözleşmesini satın alma kararı verir.

Sonuç olarak net pozisyon değişecek ve 1 adet uzun sözleşmeden oluşacaktır. Bu sefer iki alım satım sisteminin katkılarını içerecektir. Ayrıca üçüncü alım satım sistemi devreye girer ve aynı varlıkla tek bir standart sözleşme hacmiyle kısa bir pozisyon oluşturur. Net pozisyon nötr olacaktır çünkü -3 kısa+ 4 uzun - 1 kısa = 0.

Net pozisyonun olmaması, üç alım satım sisteminin hepsinin piyasada olmadığı anlamına mı gelir? Hayır, gelmez. Bunlardan ikisi teminatsız dört sözleşmeye sahiptir, bu da teminatın zamanla yapılacağı anlamına gelir. Öte yandan, üçüncü sistem henüz geri satılmamış 4 uzun sözleşmeye sahiptir. Yalnızca dört kısa sözleşmenin tam geri ödemesi tamamlandığında ve dört uzun sözleşmenin teminatlı satışı yapıldığında nötr pozisyon her üç sistemde de gerçek bir pozisyon eksikliği anlamına gelecektir.

Elbette her seferinde modellerin her biri için tüm eylem sırasını yeniden oluşturabilir ve böylece mevcut pozisyonun boyutu açısından özel katkısını belirleyebiliriz ancak çok daha basit bir yöntem mevcuttur. Bu yöntem basittir: herhangi bir boyutta olabilen ve hem dış (örneğin manuel alım satım) hem de iç faktörlere (EA'nın diğer modellerinin bir araçta çalışması) bağlı olabilen toplu pozisyonun muhasebesinden tamamen vazgeçmek gerekir. Mevcut toplu pozisyona bağlı olunamayacağına göre belirli bir model örneğinin eylemlerini nasıl hesaplarız?

En basit ve en etkili yol, bir modelin her örneğini hem bekleyen hem de bir işlem ile başlatılan veya silinen tüm emirleri kapsayacak olan kendi emir tablosuyla donatmak olacaktır. Emirlerle ilgili kapsamlı bilgiler alım satım sunucusunda saklanır. Emirin fişini bildiğimizde bir emirle ilgili açılış anından hacmine kadar hemen hemen her bilgiyi alabiliriz.

Yapmamız gereken tek şey emir fişini modelin belirli bir örneğiyle ilişkilendirmektir. Her model örneği özel bir sınıfın kendi örneğini, yani model örneği tarafından belirlenen mevcut emirlerin bir listesini içeren emirler tablosunu içermelidir.


Soyut bir alım satım modeli tasarlama

Şimdi modelin belirli alım satım taktiklerinin dayandırılacağı ortak soyut sınıfını açıklamaya çalışalım. EA birden fazla model (veya sınırsız) kullanması gerektiğinden dolayı bu sınıfın harici bir güç uzmanının sinyalleri vereceği tek tip bir arayüze sahip olması gerektiği açıktır.

Örneğin, bu arayüz Processing() fonksiyonu olabilir. Basitçe söylemek gerekirse her CModel sınıfının Processing() fonksiyonu olacaktır. Bu fonksiyon her tikte veya her dakikada veya yeni bir Alım Satım türü olayı meydana geldiğinde devreye girecektir.

Bu görevi tamamlamanın basit bir örneği şudur:

class CModel
{
protected:
   string            m_name;
public:
   void             CModel(){m_name="Model base";}
   bool virtual      Processing(void){return(true);}
};

class cmodel_macd : public CModel
{
public:
   void              cmodel_macd(){m_name="MACD Model";}
   bool              Processing(){Print("Model name is ", m_name);return(true);}
};

class cmodel_moving : public CModel
{
public:
   void              cmodel_moving(){m_name="Moving Average";}
   bool              Processing(){Print("Model name is ", m_name);return(true);}
};

cmodel_macd     *macd;
cmodel_moving   *moving;

Şimdi kodun nasıl çalıştığını bulalım. CModel temel sınıfı m_name adı verilen korumalı bir string (dizgi) türü değişkeni barındırır. “Korumalı” anahtar sözcüğü bu değişkenin sınıf alt ögeleri tarafından kullanılmasına izin verir; dolayısıyla onun soyundan gelenler bu değişkeni zaten içerecektir. Ayrıca temel sınıf Processing() sanal fonksiyonunu tanımlar. Bu durumda “sanal sözcüğü bunun Uzman Danışman ile modelin belirli örneği arasındaki bir sarmalayıcı veya arayüz olduğunu belirtir.

CModel'den gelen herhangi bir sınıfın etkileşim için Processing() arayüzüne sahip olması garanti altına alınacaktır. Bu fonksiyonun kodunun uygulanması onun soyundan gelenlere devredilmiştir. Bu devretme işlemi açıktır çünkü modellerin iç işleyişi birbirinden önemli ölçüde farklılık gösterebilir ve bu nedenle genel bir CModel düzeyinde bulunabilecek ortak bir genelleme mevcut değildir.

Sırada cmodel_macd ve cmodel_moving adında iki sınıfın tanımı var. Her ikisi de CModel sınıfından üretilir, bu nedenle her ikisi de Processing() fonksiyonunda ve m_name değişkeninde kendi örneklerine sahiptir. Her iki modelin Processing() fonksiyonunun dâhili uygulamasının farklı olduğuna dikkat edin. İlk modelde bu Print'ten oluşur (“Bu, cmodel_macd'dir. Model adı “, m_name’dir), ve bu Print'in ikincisinde yer alır (“Bu, cmodel_moving’tir. Model adı “, m_namedir). Daha sonrasında iki işaretçi oluşturulur; biri cmodel_macd türünün sınıfını, diğeriyse cmodel_moving türünün sınıfını gösterir.

OnInit fonksiyonunda bu işaretçiler dinamik olarak oluşturulan sınıf modellerini devralır; ardından OnEvent() fonksiyonu içinde her sınıfta bulunan bir Processing() fonksiyonu çağrılır. Her iki işaretçi de genel düzeyde bildirilir, bu nedenle OnInit() fonksiyonundan çıktıktan sonra bile içinde oluşturulan sınıflar silinmez ancak genel düzeyde var olmaya devam eder. Şimdi her beş saniyede bir OnTimer() fonksiyonu sırayla her iki modeli de örnekleyecek ve bunlara uygun Processing() fonksiyonunu çağıracaktır.

Yeni oluşturduğumuz modelleri örnekleyen bu ilkel sistem esneklik ve ölçeklenebilirlikten yoksundur. Bu tür birkaç düzine modelle çalışmak istediğimizde ne yaparız? Her biri ile ayrı ayrı çalışmak sakıncalıdır. Tüm modelleri örneğin bir dizi gibi tek bir toplulukta toplamak ve ardından bu tür her ögenin Processing() fonksiyonunu çağırarak bu dizinin tüm ögelerini yinelemek çok daha kolay olurdu.

Ancak sorun şu ki, dizilerin organizasyonu yönünden içlerinde depolanan verilerin aynı türde olmasını gerekmektedir. Bizim durumumuzda cmodel_macd ve cmodel_moving modelleri birbirine çok benzer olsa da bunlar özdeş değildir; bu da otomatik olarak bunların dizilerde kullanılmasını imkânsız hale getirir.

Neyse ki verileri özetlemenin tek yolu diziler değildir, daha esnek ve ölçeklenebilir başka genellemeler de mevcuttur. Bunlardan biri bağlı listeler tekniğidir. Çalışma şeması basittir. Genel listeye dâhil edilen her öge iki adet işaretçi içermelidir. Bir işaretçi önceki liste ögesine, ikincisi de bir sonrakine işaret eder. 

Ayrıca ögenin endeks numarasını bildiğinizde her zaman bu ögeye başvurabilirsiniz. Bir öge eklemek veya silmek istediğinizde işaretçilerini ve komşu ögelerin işaretçilerini yeniden oluşturarak sürekli olarak birbirlerine başvurmalarını sağlayabilirsiniz. Bu tür toplulukların iç organizasyonunu bilmek gerekli değildir, ortak yöntemlerini anlamak yeterlidir.

MetaTrader 5'in standart kurulumu bağlı listelerle çalışma imkânı sağlayan özel bir yardımcı CList sınıfını içerir. Ancak bu listenin ögesi yalnızca CObject türünde bir nesne olabilir çünkü yalnızca bağlı listelerle çalışmak için özel işaretçilere sahiptirler. Kendi başına CObject sınıfı oldukça ilkeldir, yalnızca CList sınıfıyla etkileşime yönelik bir arayüzdür.

Bunu uygulamasına bir göz atarak görebilirsiniz:

//+------------------------------------------------------------------+
//|                                                       Object.mqh |
//|                      Copyright © 2010, MetaQuotes Software Corp. |
//|                                       https://www.metaquotes.net/ |
//|                                              Revision 2010.02.22 |
//+------------------------------------------------------------------+
#include "StdLibErr.mqh"
//+------------------------------------------------------------------+
//| Class CObject.                                                   |
//| Purpose: Base class element storage.                             |
//+------------------------------------------------------------------+
class CObject
  {
protected:
   CObject          *m_prev;               // previous list item
   CObject          *m_next;               // next list item

public:
                     CObject();
   //--- methods of access to protected data
   CObject          *Prev()                { return(m_prev); }
   void              Prev(CObject *node)   { m_prev=node;    }
   CObject          *Next()                { return(m_next); }
   void              Next(CObject *node)   { m_next=node;    }
   //--- methods for working with files
   virtual bool      Save(int file_handle) { return(true);   }
   virtual bool      Load(int file_handle) { return(true);   }
   //--- method of identifying the object
   virtual int       Type() const          { return(0);      }

protected:
   virtual int       Compare(const CObject *node,int mode=0) const { return(0); }
  };
//+------------------------------------------------------------------+
//| Constructor CObject.                                             |
//| INPUT:  no.                                                      |
//| OUTPUT: no.                                                      |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
void CObject::CObject()
  {
//--- initialize protected data
   m_prev=NULL;
   m_next=NULL;
  }
//+------------------------------------------------------------------+

Görülebileceği gibi bu sınıfın temelinde tipik özelliklerin uygulandığı iki işaretçi bulunur.

Şimdi en önemli kısıma geldik. Kalıtım mekanizması sayesinde bu sınıfı alım satım modeline dâhil etmek mümkündür, bu da alım satım modelinin sınıfının CList türü bir listeye dâhil edilebileceği anlamına gelir! Hadi bunu yapmaya çalışalım.

Ve böylece soyut CModel sınıfımızı CObject sınıfının bir alt ögesi yapacağız:

class CModel : public CObject

cmodel_moving ve cmodel_average sınıflarımız CModel sınıfından geldiği için CObject sınıfının veri ve yöntemlerini içerir; bu nedenle CListtürü listesine dâhil edilebilirler. İki koşullu alım satım modelini oluşturan, bunları listeye yerleştiren ve sırayla her bir tiki örnekleyen kaynak kodu aşağıda sunulmuştur:

//+------------------------------------------------------------------+
//|                                            ch01_simple_model.mq5 |
//|                            Copyright 2010, Vasily Sokolov (C-4). |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, Vasily Sokolov (C-4)."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include <Arrays\List.mqh>

// Base model
class CModel:CObject
{
protected:
   string            m_name;
public:
        void              CModel(){m_name="Model base";}
        bool virtual      Processing(void){return(true);}
};

class cmodel_macd : public CModel
{
public:
   void              cmodel_macd(){m_name="MACD Model";}
   bool              Processing(){Print("Processing ", m_name, "...");return(true);}
};

class cmodel_moving : public CModel
{
public:
   void              cmodel_moving(){m_name="Moving Average";}
   bool              Processing(){Print("Processing ", m_name, "...");return(true);}
};

//Create list of models
CList *list_models;

void OnInit()
{
   int rezult;
   // Great two pointer
   cmodel_macd          *m_macd;
   cmodel_moving        *m_moving;
   list_models =        new CList();
   m_macd   =           new cmodel_macd();
   m_moving =           new cmodel_moving();
   //Check valid pointer
   if(CheckPointer(m_macd)==POINTER_DYNAMIC){
      rezult=list_models.Add(m_macd);
      if(rezult!=-1)Print("Model MACD successfully created");
      else          Print("Creation of Model MACD has failed");
   }
   //Check valid pointer
   if(CheckPointer(m_moving)==POINTER_DYNAMIC){
      rezult=list_models.Add(m_moving);
      if(rezult!=-1)Print("Model MOVING AVERAGE successfully created");
      else          Print("Creation of Model MOVING AVERAGE has failed");
   }
}

void OnTick()
{
   CModel               *current_model;
   for(int i=0;i<list_models.Total();i++){
      current_model=list_models.GetNodeAtIndex(i);
      current_model.Processing();
   }
}

void OnDeinit(const int reason)
{
   delete list_models;
}

Bu program derlenip çalıştırıldığında EA'nın normal çalışmasını gösteren benzer satırlar “Uzmanlar” günlüğünde görünmelidir.

2010.10.10 14:18:31     ch01_simple_model (EURUSD,D1)   Prosessing Moving Average...
2010.10.10 14:18:31     ch01_simple_model (EURUSD,D1)   Processing MACD Model...
2010.10.10 14:18:21     ch01_simple_model (EURUSD,D1)   Model MOVING AVERAGE was created successfully
2010.10.10 14:18:21     ch01_simple_model (EURUSD,D1)   Model MACD was created successfully  

Şimdi bu kodun nasıl çalıştığını ayrıntılı olarak analiz edelim. Yukarıda bahsedildiği gibi, temel alım satım modelimiz olan CModel bize temel modelin alt ögelerini CList türü listesine dâhil etme hakkını veren CObject sınıfından türetilmiştir:

rezult=list_models.Add(m_macd);
rezult=list_models.Add(m_moving);

Verilerin organizasyonu işaretçilerle çalışmayı gerektirir. Belirli modellerin işaretçileri OnInit() fonksiyonunun yerel düzeyinde oluşturulduğunda ve genel liste list_ models’e girildiğinde onlara olan ihtiyaç ortadan kalkar ve bu fonksiyonun diğer değişkenleriyle birlikte güvenli bir şekilde yok edilebilir.

Genel olarak, önerilen modelin ayırt edici bir özelliği de tek global değişkenin (model sınıflarının kendilerine ek olarak) bu modellerin dinamik olarak bağlı bir listeye sahip olmasıdır. Bu nedenle, başlangıçtan itibaren projede yüksek derecede kapsülleme desteği mevcuttur.

Modelin oluşturulması herhangi bir nedenle başarısız olursa (örneğin gerekli parametrelerin değerleri yanlış listelenmişse) bu model listeye eklenmeyecektir. Bu yalnızca listeye başarıyla eklenen modelleri ele alacağından dolayı EA'nın genel çalışmasını etkilemeyecektir.

Oluşturulan modellerin örneklenmesi OnTick() fonksiyonunda yapılır. Bu bir döngüden oluşur. Bu döngüde ögelerin sayısı belirlenir, ardından döngünün ilk ögesinden (i = 0) son ögesine(i <list_models.Total();i++) bir seri geçiş vardır:

CModel               *current_model;
for(int i=0;i<list_models.Total();i++){
   current_model=list_models.GetNodeAtIndex(i);
   current_model.Processing();
}

CModel temel sınıfının işaretçisi evrensel bir adaptör olarak kullanılır. Bu şekilde bu gösterge tarafından desteklenen herhangi bir fonksiyonun türev modellerde kullanılabilir olması sağlanır. Bu durumda yalnızca Processing() fonksiyonuna ihtiyacımız vardır. Her model dâhili uygulaması diğer modellerin benzer fonksiyonlarından farklı olabilen kendi Processing() sürümüne sahiptir. Bu fonksiyonun aşırı yüklenmesi gerekli değildir ve yalnızca bir biçimde var olabilir: herhangi bir girdi parametresine sahip olmamak ve bool türünün değerini döndürmek.

Bu fonksiyona düşen görevler kapsamlıdır:

  1. Fonksiyon kendi alım satım modellerine dayanarak mevcut piyasa durumunu bağımsız olarak belirlemelidir.
  2. Piyasaya girmeye karar verildikten sonra fonksiyon, işlemde yer alan gerekli teminat miktarını (marj), işlem hacmini, olası maksimum zararın değerini veya kâr seviyesini bağımsız bir şekilde hesaplamalıdır.
  3. Modelin davranışı önceki eylemleriyle ilişki halinde olmalıdır. Örneğin, model tarafından başlatılan bir kısa pozisyon bulunması halinde bunu gelecekte daha da geliştirmek imkânsız olabilir. Tüm bu doğrulamalar Processing() fonksiyonu içinde gerçekleştirilmelidir.
  4. Bu fonksiyonların her birinin hesap durumu gibi ortak parametrelere erişimi olmalıdır. Bu verilere dayanarak bu fonksiyon, modelinde gömülü olan parametreleri kullanarak kendi para yönetimini yapmalıdır. Örneğin, modellerden birinde para yönetimi optimum f formülü aracılığıyla yapılıyorsa her bir model için değeri farklı olmalıdır.

Açıktır ki Processing()fonksiyonu kendisine verilen görevlerin büyüklüğü nedeniyle yardımcı sınıfların geliştirilmiş aparatına, MetaTrader 5'e dâhil edilenlere ve özellikle bu çözüm için tasarlanmış olanlara dayanarak hareket edecektir.

Görülebileceği gibi işin çoğu modelin belirli örneklerine devredilmiştir. EA'nın dış seviyesi her bir modele sırayla kontrolü verir ve çalışmaları bunun üzerinde tamamlanır. Spesifik model tarafından yapılacaklar onun kendi mantığına bağlı olacaktır.

Genel olarak, kurduğumuz etkileşim sistemi aşağıdaki şema ile tanımlanabilir:

Modellerin sıralanması yukarıdaki kodda gösterildiği gibi OnTick() fonksiyonu içinde gerçekleşse de tamamen böyle olması gerekmediğini unutmayın. Sıralama döngüsü OnTrade() veya OnTimer() gibi istenen herhangi bir başka fonksiyona kolayca yerleştirilebilir. 

 

Sanal emirler tablosu; modelin temeli

Tüm alım satım modellerini tek bir listede birleştirdiğimize göre alım satım sürecini açıklamanın zamanı geldi. Şimdi CModel sınıfına geri dönelim ve onu alım satım sürecinde temel alınabilecek ek verilerle ve fonksiyonlarla tamamlamaya çalışalım.

Yukarıda bahsedildiği gibi yeni net pozisyon paradigması, emirler ve işlemlerle çalışmak için farklı kuralları tanımlar. Meta Trader 4'te her işleme, başlatıldığı andan itibaren ve başlatılan emrin sona ermesine veya işlemin sonlandırılmasına kadar geçen sürede “Alım Satım” sekmesinde bulunan ilgili emir eşlik eder.

MetaTrader 5'te bekleyen emirler sadece işlemin fiilen tamamlandığı ana kadar mevcuttur. Anlaşmadan veya bunun üzerine piyasaya giriş yapıldıktan sonra, bu emirler işlem sunucusunda depolanan emir geçmişine aktarılır. Bu durum belirsizlik yaratmaktadır. EA'nın yerine getirilen bir emir verdiğini varsayalım. Toplu pozisyon değişmiş olsun. Bir süre sonra EA'nın pozisyonunu kapatması gerekiyor.

Belirli bir emri kapatmak, MetaTrader 4'te yapılabildiği gibi gerçekleştirilemez; emirlerin kapanması kavramı bulunmadığından dolayı pozisyonu veya bunun bir kısmını kapatabiliriz. Soru, pozisyonun hangi bölümünün kapatılması gerektiğidir. Alternatif olarak, tüm geçmiş emirlere bakabilir, EA tarafından verilmiş olanları seçebilir ve ardından bu emirleri mevcut piyasa durumuyla ilişkilendirebilir ve gerektiğinde karşı emirlerini bloke edebiliriz. Bu yöntem birçok zorluğu içinde barındırmaktadır.

Örneğin, siparişlerin geçmişte bloke edilmediğini nasıl belirleyebiliriz? Mevcut pozisyonun yalnızca mevcut EA'ya ait olduğunu varsayarak başka bir yoldan ilerleyebiliriz. Bu seçenek yalnızca tek bir strateji üzerinde işlem yaparak tek bir EA ile alım satım yapmayı düşünüyorsanız kullanılabilir. Bu yöntemler karşılaştığımız zorlukları hassas bir şekilde çözemez.

En açık ve en basit çözüm, mevcut modelin emirleri (yani, karşıt işlemler tarafından bloke edilmeyen emirler) hakkında gerekli tüm bilgileri modelin kendisinde depolamak olacaktır.

Örneğin, modelin bir emir vermesi halinde emir fişini bu modelin belleğinin özel bir alanına kaydedilir; örneğin, hâlihazırda aşina olduğumuz bir bağlı listeler sistemi yardımıyla düzenlenebilir.

Emir fişini bilerek onunla ilgili hemen hemen her bilgiyi bulabilirsiniz, bu yüzden tek ihtiyacımız olan emir fişini onu veren modelle ilişkilendirmektir. Emir fişinin CTableOrder özel sınıfında saklanmasını sağlayın. Emir fişine ek olarak emirlerin hacmi, kurulum zamanı, sihirli sayı vb. gibi en önemli bilgileri de barındırabilir.

Bu sınıfın nasıl yapılandırıldığına bir bakalım:

#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"

#include <Trade\_OrderInfo.mqh>
#include <Trade\_HistoryOrderInfo.mqh>
#include <Arrays\List.mqh>
class CTableOrders : CObject
{
private:
   ulong             m_magic;       // Magic number of the EA that put out the order
   ulong             m_ticket;      // Ticket of the basic order
   ulong             m_ticket_sl;    // Ticket of the simulated-Stop-Loss order, assigned with the basic order
   ulong             m_ticket_tp;    // Ticket of the simulated-Take-Profit, assigned with the basic order
   ENUM_ORDER_TYPE   m_type;         // Order type
   datetime          m_time_setup;  // Order setup time
   double            m_price;       // Order price
   double            m_sl;          // Stop Loss price
   double            m_tp;          // Take Profit price
   double            m_volume_initial;  // Order Volume
public:
                     CTableOrders();
   bool              Add(COrderInfo &order_info, double stop_loss, double take_profit);
   bool              Add(CHistoryOrderInfo &history_order_info, double stop_loss, double take_profit);
   double            StopLoss(void){return(m_sl);}
   double            TakeProfit(void){return(m_tp);}
   ulong             Magic(){return(m_magic);}
   ulong             Ticket(){return(m_ticket);}
   int               Type() const;
   datetime          TimeSetup(){return(m_time_setup);}
   double            Price(){return(m_price);}
   double            VolumeInitial(){return(m_volume_initial);}
};

CTableOrders::CTableOrders(void)
{
   m_magic=0;
   m_ticket=0;
   m_type=0;
   m_time_setup=0;
   m_price=0.0;
   m_volume_initial=0.0;
}

bool CTableOrders::Add(CHistoryOrderInfo &history_order_info, double stop_loss, double take_profit)
{
   if(HistoryOrderSelect(history_order_info.Ticket())){
      m_magic=history_order_info.Magic();
      m_ticket=history_order_info.Ticket();
      m_type=history_order_info.Type();
      m_time_setup=history_order_info.TimeSetup();
      m_volume_initial=history_order_info.VolumeInitial();
      m_price=history_order_info.PriceOpen();
      m_sl=stop_loss;
      m_tp=take_profit;
      return(true);
   }
   else return(false);
}

bool CTableOrders::Add(COrderInfo &order_info, double stop_loss, double take_profit)
{
   if(OrderSelect(order_info.Ticket())){
      m_magic=order_info.Magic();
      m_ticket=order_info.Ticket();
      m_type=order_info.Type();
      m_time_setup=order_info.TimeSetup();
      m_volume_initial=order_info.VolumeInitial();
      m_price=order_info.PriceOpen();
      m_sl=stop_loss;
      m_tp=take_profit;
      return(true);
   }
   else return(false);
}

int   CTableOrders::Type() const
{
   return((ENUM_ORDER_TYPE)m_type);
}

CModel sınıfına benzer şekilde CTableOrders sınıfı da CObject ögesinden gelir. Modellerin sınıflarında olduğu gibi, CTableOrders örneklerini CList türünün ListTableOrders listesine yerleştireceğiz.

İlgili sınıf, kendi emir fişine (m_tiket) ek olarak onu üreten EA'nın sihirli numarası (ORDER_MAGIC), türü, açılış fiyatı, hacmi ve tahmini emir çakışma düzeyi hakkında bilgi içerir: zarardurdur (m_sl) ve kâret (m_tp) Son iki değer üzerinde ayrı ayrı konuşmamız gerekiyor. Herhangi bir işlemin er ya da geç karşı bir işlem tarafından kapatılması gerektiği açıktır. Karşı işlem mevcut piyasa durumu veya pozisyonun sonuçlandığı anda önceden belirlenmiş bir fiyat üzerinden kısmi kapanış temelinde başlatılabilir.

MetaTrader4’te yapılan bu tür “pozisyondan koşulsuz çıkışlar” özel çıkış türleridir: ZararDurdur ve KârEt. MetaTrader 4’ün ayırt edici özelliği, bu seviyelerin belirli bir emir için geçerli olmasıdır. Örneğin, aktif emirlerden birinde stop olması bu araçtaki diğer açık emirleri etkilemeyecektir.

MetaTrader 5’te bu durum biraz farklıdır. Belirlenen emirlerin her biri için diğer şeylerin yanı sıra ZararDurdur ve KârEt fiyatını belirtebilseniz de bu seviyeler bu fiyatların belirlendiği belirli bir emire karşı değil, bu araçtaki tüm pozisyona göre hareket edecektir.

ZararDurdur ve KârEt seviyeleri olmadan 1 standart lotluk EURUSD için açık bir ALIŞpozisyonu olduğunu varsayalım. Bir süre sonra EURUSD'ye her biri mevcut fiyattan 100 puan fazla olacak şekildeZararDurdurve KârEt seviyeleriyle 0.1 lot satın alması için başka bir emir verilir. Bir süre sonra fiyat ZararDurdurveyaKârEt seviyesine ulaşır. Bu gerçekleştiğinde EURUSD'de 1.1 lot büyüklüğündeki tüm pozisyon kapatılacaktır.

Başka bir deyişle, ZararDurdurveKârEt belirli bir emire karşı değil, yalnızca toplu pozisyona göre ayarlanabilir. Bu temelde, bu emirleri çok sistemli EA'larda kullanmak imkânsız hale gelir. Bu durum barizdir çünkü bir sistem kendi ZararDurdur veKârEt’ini verecekse o zaman faizleri aracın toplu pozisyonuna dâhil olan diğer tüm sistemler için geçerli olacaktır.

Sonuç olarak alım satım EA'sının alt sistemlerinin her biri her emir için yalnızca kendi dâhili ZararDurdurveKârEt’ini kullanmalıdır. Ayrıca bu kavram aynı alım satım sistemi içinde bile farklı emirlerin farklıZararDurdurveKârEt seviyelerine sahip olabileceği gerçeğinden çıkarılabilir ve yukarıda da belirtildiği üzereMetaTrader 5’te bu çıktılar farklı emirlere atanamaz.

Sanal emirlere sentetik ZararDurdur veKârEt seviyeleri uygularsak EA fiyat bu seviyelere ulaştığında veya bu seviyeleri aştığında mevcut emirleri bağımsız olarak bloke edecektir. Bu emirleri bloke ettikten sonra, aktif emirler listesinden güvenle kaldırılabilirler. Bunun nasıl yapıldığı aşağıda açıklanmıştır.

CTableOrders sınıfı kendi verilerinin yanı sıra oldukça önemli bir Add() fonksiyonunu içerir. Bu fonksiyon tabloya kaydedilmesi gereken emir fişini alır. Emir fişine ek olarak, bu fonksiyon sanalZararDurdur veKârEt seviyelerini alır. Öncelikle Add() fonksiyonu emri sunucuda depolanan geçmiş emirler arasından ayırmaya çalışır. Eğer bunu yapabiliyorsa fiş üzerindeki bilgileri class history_order_info örneğine girer ve ardından bilgileri yeni TableOrders ögesine girmeye başlar. Daha sonra bu öge emir listesine eklenir. Emir seçimi tamamlanamadıysa bekleyen bir emirle karşı karşıya olabilir; bu yüzden bu emri, OrderSelect() fonksiyonu aracılığıyla mevcut emirlerden ayırmaya çalışmalıyız. Bu emrin başarılı bir şekilde seçilmesi durumunda geçmiş emir ile aynı işlemler yapılır.

Şu anda Alım Satım olayını anlatan yapı tanıtılmadan çoklu sistem EA'ları için bekleyen emirlerle çalışmak zordur. Elbette bu yapının devreye girmesinden sonra bekleyen emirlere dayalı EA'lar tasarlamak mümkün hale gelecektir. Ayrıca bir emir tablosu mevcutsa bekleyen emirleri olan hemen hemen her alım satım stratejisi piyasaya taşınabilir. Bu nedenlerle, makalede sunulan tüm alım satım modellerinde piyasa uygulaması olacaktır (ORDER_TYPE_BUY veyaORDER_TYPE_SELL).


CModel - alım satım modelinin temel sınıfı

Ve böylece emir tablosu tam olarak tasarlandığında temel model CModel'in tam sürümünü açıklamanın zamanı gelmiş demektir:

class CModel : public CObject
{
protected:
   long              m_magic;
   string            m_symbol;
   ENUM_TIMEFRAMES   m_timeframe;
   string            m_model_name;
   double            m_delta;
   CTableOrders      *table;
   CList             *ListTableOrders;
   CAccountInfo      m_account_info;
   CTrade            m_trade;
   CSymbolInfo       m_symbol_info;
   COrderInfo        m_order_info;
   CHistoryOrderInfo m_history_order_info;
   CPositionInfo     m_position_info;
   CDealInfo         m_deal_info;
   t_period          m_timing;
public:
                     CModel()  { Init();   }
                     ~CModel() { Deinit(); }
   string            Name(){return(m_model_name);}
   void              Name(string name){m_model_name=name;}
   ENUM_TIMEFRAMES    Timeframe(void){return(m_timeframe);}
   string            Symbol(void){return(m_symbol);}
   void              Symbol(string set_symbol){m_symbol=set_symbol;}
   bool virtual      Init();
   void virtual      Deinit(){delete ListTableOrders;}
   bool virtual      Processing(){return (true);}
   double            GetMyPosition();
   bool              Delete(ENUM_TYPE_DELETED_ORDER);
   bool              Delete(ulong Ticket);
   void              CloseAllPosition();
   //bool virtual      Trade();
protected:
   bool              Add(COrderInfo &order_info, double stop_loss, double take_profit);
   bool              Add(CHistoryOrderInfo &history_order_info, double stop_loss, double take_profit);

   void              GetNumberOrders(n_orders &orders);
   bool              SendOrder(string symbol, ENUM_ORDER_TYPE op_type, ENUM_ORDER_MODE op_mode, ulong ticket, double lot,
                              double price, double stop_loss, double take_profit, string comment);
};

Bu sınıftaki veriler herhangi bir alım satım modelinin temel sabitlerini içerir.

Bunlar sihirli sayı (m_magic), modelin başlatılacağı sembol, (m_symbol) zaman dilimi (m_timeframe) ve en çok alım satım yapılan modelin adıdır (m_name).

Ayrıca model hâlihazırda aşina olduğumuz emirler tablosunun sınıfını (CTableOrders * table) ve her emir için bir kopya bulunan bu tablonun örneklerinin tutulacağı listeyi içerir (CList*ListTableOrders). Tüm veriler dinamik olarak oluşturulacağı için ihtiyaç oldukça bu verilerle çalışma işlemi işaretçiler üzerinden yapılacaktır.

Bunu m_delta değişkeni takip eder. Bu değişken, para yönetimi formüllerinde mevcut lotu hesaplamak için özel bir katsayıya sahip olmalıdır. Örneğin, sabit-kesirli sermayelendirme formülü için bu değişken mesela %2'lik riski için riske atılabilecek hesabın bir payını barındırabilir; bu değişken 0,02'ye eşit olmalıdır. Örneğin optimal yöntem gibi daha agresif yöntemler için bu değişken daha büyük olabilir.

Bu değişkende önemli olan şey bunun tek bir EA'nın parçası olan her bir model için farklı risk seçimine izin vermesidir. Sermayelendirme formülü kullanılmadığında giriş yapmaya gerek yoktur.Varsayılan olarak 0.0'a eşittir.

Ardından hesap bilgilerinden pozisyon bilgilerine kadar tüm gerekli bilgilerin alınmasını ve işlenmesini kolaylaştırmak için tasarlanmış olan tüm yardımcı alım satım sınıflarının dâhil edilmesi gelir. Belirli alım satım modellerinin türevlerinin OrderSelect veya OrderSend türünün normal özelliklerini değil, bu yardımcı sınıfları aktif olarak kullanması gerektiği anlaşılmaktadır.

m_timing değişkeninin ayrıca tanımlanması gerekir. EA'nın çalışması sürecinde belirli zaman aralıklarında belirli olayları çağırmak gerekir. Farklı modeller farklı zaman aralıklarında bulunabileceği için OnTimer() fonksiyonu bunun için uygun değildir.

Örneğin, her yeni çubukta bazı olayların çağrılması gerekir. Model için saatlik bir grafikte alım satım yaparken bu tür olaylar her saat; günlük grafikte alım satım yaparken de her yeni gün çubuğunda çağrılmalıdır. Bu modellerin farklı zaman ayarlarına sahip olduğu ve her birinin sırasıyla kendi modelinde saklanması gerektiği açıktır. CModel sınıfında bulunan t _period yapısı, bu ayarları her biri kendi modelinde olacak şekilde ayrı ayrı saklamanıza izin verir.

Yapı şu şekildedir:

struct t_period
{
   datetime m1;
   datetime m2;
   datetime m3;
   datetime m4;
   datetime m5;
   datetime m6;
   datetime m10;
   datetime m12;
   datetime m15;
   datetime m20;
   datetime m30;
   datetime h1;
   datetime h2;
   datetime h3;
   datetime h4;
   datetime h6;
   datetime h8;
   datetime h12;
   datetime d1;
   datetime w1;
   datetime mn1;  
   datetime current; 
};

Görüldüğü gibi, yapı olağan zaman dilimleri listesini içerir. Yeni bir çubuğun oluşup oluşmadığını görmek için son çubuğun zamanını, t_periyodu yapısında kaydedilen zamanla karşılaştırmanız gerekir. Zamanlar uyuşmuyorsa yeni bir çubuk oluşmuştur ve yapıdaki zamanın mevcut çubuğun zamanına güncellenmesi ve pozitif bir sonuç (true) döndürmesi gerekir. Son çubuğun zamanı ve yapı aynıysa bu durum yeni çubuğun henüz oluşmadığı ve olumsuz bir sonucun döndürülmesi gerektiği anlamına gelir (false).

İşte açıklanan algoritmaya göre çalışan bir fonksiyon:

bool timing(string symbol, ENUM_TIMEFRAMES tf, t_period &timeframes)
{
   int rez;
   MqlRates raters[1];
   rez=CopyRates(symbol, tf, 0, 1, raters);
   if(rez==0)
   {
      Print("Error timing");
      return(false);
   }
   switch(tf){
      case PERIOD_M1:
         if(raters[0].time==timeframes.m1)return(false);
         else{timeframes.m1=raters[0].time; return(true);}
      case PERIOD_M2:
         if(raters[0].time==timeframes.m2)return(false);
         else{timeframes.m2=raters[0].time; return(true);}
      case PERIOD_M3:
         if(raters[0].time==timeframes.m3)return(false);
         else{timeframes.m3=raters[0].time; return(true);}
      case PERIOD_M4:
         if(raters[0].time==timeframes.m4)return(false);
         else{timeframes.m4=raters[0].time; return(true);}
     case PERIOD_M5:
         if(raters[0].time==timeframes.m5)return(false);
         else{timeframes.m5=raters[0].time; return(true);}
     case PERIOD_M6:
         if(raters[0].time==timeframes.m6)return(false);
         else{timeframes.m6=raters[0].time; return(true);}
     case PERIOD_M10:
         if(raters[0].time==timeframes.m10)return(false);
         else{timeframes.m10=raters[0].time; return(true);}
     case PERIOD_M12:
         if(raters[0].time==timeframes.m12)return(false);
         else{timeframes.m12=raters[0].time; return(true);}
     case PERIOD_M15:
         if(raters[0].time==timeframes.m15)return(false);
         else{timeframes.m15=raters[0].time; return(true);}
     case PERIOD_M20:
         if(raters[0].time==timeframes.m20)return(false);
         else{timeframes.m20=raters[0].time; return(true);}
     case PERIOD_M30:
         if(raters[0].time==timeframes.m30)return(false);
         else{timeframes.m30=raters[0].time; return(true);}
     case PERIOD_H1:
         if(raters[0].time==timeframes.h1)return(false);
         else{timeframes.h1=raters[0].time; return(true);}
     case PERIOD_H2:
         if(raters[0].time==timeframes.h2)return(false);
         else{timeframes.h2=raters[0].time; return(true);}
     case PERIOD_H3:
         if(raters[0].time==timeframes.h3)return(false);
         else{timeframes.h3=raters[0].time; return(true);}
     case PERIOD_H4:
         if(raters[0].time==timeframes.h4)return(false);
         else{timeframes.h4=raters[0].time; return(true);}
     case PERIOD_H6:
         if(raters[0].time==timeframes.h6)return(false);
         else{timeframes.h6=raters[0].time; return(true);}
     case PERIOD_H8:
         if(raters[0].time==timeframes.h8)return(false);
         else{timeframes.h8=raters[0].time; return(true);}
     case PERIOD_H12:
         if(raters[0].time==timeframes.h12)return(false);
         else{timeframes.h12=raters[0].time; return(true);}
     case PERIOD_D1:
         if(raters[0].time==timeframes.d1)return(false);
         else{timeframes.d1=raters[0].time; return(true);}
     case PERIOD_W1:
         if(raters[0].time==timeframes.w1)return(false);
         else{timeframes.w1=raters[0].time; return(true);}
     case PERIOD_MN1:
         if(raters[0].time==timeframes.mn1)return(false);
         else{timeframes.mn1=raters[0].time; return(true);}
     case PERIOD_CURRENT:
         if(raters[0].time==timeframes.current)return(false);
         else{timeframes.current=raters[0].time; return(true);}
     default:
         return(false);
   }
}

Şu anda yapıların sıralı bir şekilde sınıflandırılması mümkün değildir. Bu tür bir sıralama, aynı alım satım modelinin bir döngüsünde farklı zaman dilimlerinde alım satım yaparak birden fazla örnek oluşturmanız gerektiğinde gerekli olabilir. Bu yüzden t_period yapısıyla ilgili özel bir fonksiyon-sıralayıcı yazmak zorunda kaldım.

İşte bu fonksiyonun kaynak kodu:

int GetPeriodEnumerator(uchar n_period)
{
   switch(n_period)
   {
      case 0: return(PERIOD_CURRENT);
      case 1: return(PERIOD_M1);
      case 2: return(PERIOD_M2);
      case 3: return(PERIOD_M3);
      case 4: return(PERIOD_M4);
      case 5: return(PERIOD_M5);
      case 6: return(PERIOD_M6);
      case 7: return(PERIOD_M10);
      case 8: return(PERIOD_M12);
      case 9: return(PERIOD_M15);
      case 10: return(PERIOD_M20);
      case 11: return(PERIOD_M30);
      case 12: return(PERIOD_H1);
      case 13: return(PERIOD_H2);
      case 14: return(PERIOD_H3);
      case 15: return(PERIOD_H4);
      case 16: return(PERIOD_H6);
      case 17: return(PERIOD_H8);
      case 18: return(PERIOD_H12);
      case 19: return(PERIOD_D1);
      case 20: return(PERIOD_W1);
      case 21: return(PERIOD_MN1);
      default:
         Print("Enumerator period must be smallest 22");
         return(-1);
   }
}

Tüm bu fonksiyonlar \\Include klasöründeki tek bir dosyada uygun bir şekilde birleştirilir. Adını Time.mqh koyalım.

Temel sınıfımız CModel'e dâhil edilecek olan budur:

#incude <Time.mqh>

Name(), Timeframe(), ve() Symbol(), türündeki get/set basit fonksiyonlarına ek olarakCModel sınıfı type  Init(), GetMyPosition(), Delete(), CloseAllPosition() и Processing() türündeki karmaşık fonksiyonlar barındırır. Son fonksiyonun tanımı size hâlihazırda tanıdık gelmelidir, daha sonra iç yapısını daha ayrıntılı olarak tartışacağız ancak şimdilik CModel temel sınıfının ana fonksiyonlarının bir açıklamasıyla başlayalım.

CModel::Add() fonksiyonudinamik olarak CTableOrderssınıfının bir örneğini oluşturur ve ardından bunu uygunCTabeOrders::Add() fonksiyonu kullanarak girer.. Çalışma prensibi yukarıda açıklanmıştır. Girdikten sonra, bu öge mevcut modelin tüm emirlerinin genel listesine dâhil edilir (ListTableOrders.Add (t)).

CModel::Delete() fonksiyonuysa aktif emirler listesinden CTableOrders türündekiögeyi silmiştir. Bunu yapmak için silinmesi gereken emirlerin fişini belirtmelisiniz. Çalışma prensibi basittir. Fonksiyon doğru emir fişine sahip emri aramak için tüm emir tablosunu sırayla ayıklar. Böyle bir emir bulursa da siler.

CModel::GetNumberOrders() fonksiyonuetkin emirlerin sayısını hesaplar. n_orders:özel yapısını girer.

struct n_orders
{
   int all_orders;
   int long_orders;
   int short_orders;
   int buy_sell_orders;
   int delayed_orders;
   int buy_orders;
   int sell_orders;
   int buy_stop_orders;
   int sell_stop_orders;
   int buy_limit_orders;
   int sell_limit_orders;
   int buy_stop_limit_orders;
   int sell_stop_limit_orders;
};

Görüldüğü gibi çağrıldıktan sonra kaç tane özel emir türünün ayarlandığını öğrenebiliriz. Örneğin, tüm kısa emirlerin sayısını elde etmek için n_ordersörneğinin short_orders’a ait tüm değerlerini okumalısınız.

CModel::SendOrder() fonksiyonu emirlerin alım satım sunucusuna fiili olarak gönderilmesi için temel ve tek fonksiyondur. Sunucuya emir göndermek için her modelin kendi algoritmasına sahip olması yerine, SendOrder() fonksiyonu bu gönderimlere ilişkin genel prosedürü tanımlar. Modelden bağımsız olarak emir verme süreci, merkezi bir konumda verimli bir şekilde uygulanan aynı kontrollerle ilişkilendirilir.

Hadi bu fonksiyonun kaynak kodunu biraz tanıyalım:

bool CModel::SendOrder(string symbol, ENUM_ORDER_TYPE op_type, ENUM_ORDER_MODE op_mode, ulong ticket, 
                          double lot, double price, double stop_loss, double take_profit, string comment)
{
   ulong code_return=0;
   CSymbolInfo symbol_info;
   CTrade      trade;
   symbol_info.Name(symbol);
   symbol_info.RefreshRates();
   mm send_order_mm;
   
   double lot_current;
   double lot_send=lot;
   double lot_max=m_symbol_info.LotsMax();
   //double lot_max=5.0;
   bool rez=false;
   int floor_lot=(int)MathFloor(lot/lot_max);
   if(MathMod(lot,lot_max)==0)floor_lot=floor_lot-1;
   int itteration=(int)MathCeil(lot/lot_max);
   if(itteration>1)
      Print("The order volume exceeds the maximum allowed volume. It will be divided into ", itteration, " deals");
   for(int i=1;i<=itteration;i++)
   {
      if(i==itteration)lot_send=lot-(floor_lot*lot_max);
      else lot_send=lot_max;
      for(int i=0;i<3;i++)
      {
         //Print("Send Order: TRADE_RETCODE_DONE");
         symbol_info.RefreshRates();
         if(op_type==ORDER_TYPE_BUY)price=symbol_info.Ask();
         if(op_type==ORDER_TYPE_SELL)price=symbol_info.Bid();
         m_trade.SetDeviationInPoints(ulong(0.0003/(double)symbol_info.Point()));
         m_trade.SetExpertMagicNumber(m_magic);
         rez=m_trade.PositionOpen(m_symbol, op_type, lot_send, price, 0.0, 0.0, comment); 
         // Sleeping is not to be deleted or moved! Otherwise the order will not have time to get recorded in m_history_order_info!!!
         Sleep(3000);
         if(m_trade.ResultRetcode()==TRADE_RETCODE_PLACED||
            m_trade.ResultRetcode()==TRADE_RETCODE_DONE_PARTIAL||
            m_trade.ResultRetcode()==TRADE_RETCODE_DONE)
         {
               //Print(m_trade.ResultComment());
               //rez=m_history_order_info.Ticket(m_trade.ResultOrder());
               if(op_mode==ORDER_ADD){
                  rez=Add(m_trade.ResultOrder(), stop_loss, take_profit);
               }
               if(op_mode==ORDER_DELETE){
                  rez=Delete(ticket);
               }
               code_return=m_trade.ResultRetcode();
               break;
         }
         else
         {
            Print(m_trade.ResultComment());
         }
         if(m_trade.ResultRetcode()==TRADE_RETCODE_TRADE_DISABLED||
            m_trade.ResultRetcode()==TRADE_RETCODE_MARKET_CLOSED||
            m_trade.ResultRetcode()==TRADE_RETCODE_NO_MONEY||
            m_trade.ResultRetcode()==TRADE_RETCODE_TOO_MANY_REQUESTS||
            m_trade.ResultRetcode()==TRADE_RETCODE_SERVER_DISABLES_AT||
            m_trade.ResultRetcode()==TRADE_RETCODE_CLIENT_DISABLES_AT||
            m_trade.ResultRetcode()==TRADE_RETCODE_LIMIT_ORDERS||
            m_trade.ResultRetcode()==TRADE_RETCODE_LIMIT_VOLUME)
         {
            break;
         }
      }
   }
   return(rez);
}

Bu fonksiyonun yaptığı ilk şey alım satım sunucusunun belirtilen hacmini uygulama olasılığını doğrulamasıdır. Bunu da CheckLot() fonksiyonunu kullanarak yapar. Pozisyonun boyutunda bazı alım satım kısıtlamaları olabilir. Bunların dikkate alınması gerekir.

Aşağıdaki durumu göz önünde bulundurun: Her iki yönde de 15 standart lotluk bir alım satım pozisyonunun boyutunda bir sınır mevcuttur. Mevcut pozisyon uzundur ve 3 lota eşittir. Para yönetimi sistemine dayanan alım satım modeli 18,6 lotluk bir hacimle uzun bir pozisyon açmak istiyor.CheckLot() fonksiyonu işlemin düzeltilmiş hacmini döndürecektir. Bu durumda bu 12 lota eşit olacaktır (çünkü 15 lottan 3'ü zaten başka işlemler tarafından alınmıştır). Mevcut açık pozisyon uzun değil de kısa olsaydı fonksiyon 18,6 yerine 15 lot döndürürdü. Bu mümkün olan maksimum pozisyon hacmidir.

15 lot alış yaptıktan sonra bu durumda net pozisyon 12 lot olacaktır (3: satış, 15:alış). Başka bir model 3 lotluk ilk kısa pozisyonunu geçersiz kıldığında toplu pozisyon satın alma işlemi mümkün olan maksimum lot, yani 15 lot olacaktır. Diğer satın alma sinyalleri model 15 lot satın alımın bir kısmını veya tamamını geçersiz kılana kadar işlenmez. İstenen işlem için olası bir hacim aşıldığında fonksiyon sabit bir EMPTY_VALUE döndürür, bu tür bir sinyal iletilmelidir.

Ayarlanan hacmin olasılığının kontrolü başarılı olursa gerekli marjın değeri üzerinden hesaplamalar yapılır. Hesapta belirtilen hacim için yeterli fon olmayabilir. Bu amaçlar için bir CheckMargin() fonksiyonu mevcuttur. Eğer marj yeterli değilse mevcut serbest marjın açılmaya izin vermesi için sipariş hacmini düzeltmeye çalışacaktır. Eğer marj minimum tutarı açmaya bile yetmiyorsa Marj-Çağrısı durumuna geçmiş oluruz.

Hâlihazırda bir pozisyon yoksa ve marj kullanılmıyorsa bunun tek bir anlamı vardır: teknik marj-çağrısı (bir işlem açmanın imkânsız olduğu bir durumu ifade eder). Hesaba para eklemeden devam edemeyiz. Bir miktar marj hâlâ kullanılıyorsa bu marjı kullanan işlemin kapanmasını beklemekten başka bir çaremiz yoktur. Her durumda marj eksikliği sabit bir EMPTY_VALUE döndürür.

Bu fonksiyonun ayırt edici bir özelliği de mevcut emrin birkaç bağımsız işlemlere bölünebilmesidir. Alım satım modelleri hesabın sermayelendirilmesi sistemini kullanıyorsa gerekli miktar olası tüm limitleri kolayca aşabilir (örneğin, sermayelendirme sistemi birkaç yüz ve bazen de bin standart lotluk bir hacmi olan bir işlemin açılmasını gerektirebilir). Tek bir işlem için böyle bir miktarı garanti etmenin mümkün olmadığı açıktır. Genellikle alım satım koşulları maksimum yüz lotluk işlemin boyutunu belirler ancak bazı alım satım sunucuları başka kısıtlamalara sahiptir, örneğin MetaQuotes Championship 2010 sunucusunda bu sınırlama 5 lottu. Bu tür kısıtlamaların dikkate alınmalıdır açıktır ve buna dayanarak işlemingerçek hacmi doğru bir şekilde hesaplanmalıdır.

İlk olarak, belirlenen hacmi uygulamak için gereken emirlerin sayısı hesaplanır. Belirlenen tutar işlemin maksimum tutarını geçmiyorsa bu emri vermek için yalnızca bir geçiş gerekir. İstenen işlem hacmi mümkün olan maksimum hacmi aşarsa bu hacim birkaç bölüme ayrılır. Örneğin, 11,3 lot EURUSD satın almak istiyorsunuz. Bu araçtaki maksimum işlem büyüklüğü 5,0 lottur. Daha sonra OrderSend fonksiyonu bu hacmi üç emire böler: ilk emir 5,0 lot, ikinci emir 5,0 lot, üçüncü emir 1,3 lot.

Böylece bir emir yerine üç tane emir olacaktır. Her biri emir tablosunda listelenecek ve Zarar Durdur ve Kâr Et sanal değerleri, sihirli sayı ve diğer parametreler gibi kendi bağımsız ayarlarına sahip olacaktır. Bu tür emirlerin işlenmesinde herhangi bir zorluk yaşanmamalıdır çünkü alım satım modelleri, listelerindeki her emri işleyebilecek şekilde tasarlanmaktadır.

Aslında tüm emirler aynı Kâr Et ve Zarar Durdur değerlerine sahip olacaktır. Her biri LongCloseveShortClosefonksiyonlarına göre sıralı olarak sıralanacaktır. Kapatılmaları için doğru koşullar oluştuğunda veya SLve TPeşiklerine ulaştıklarında hepsi kapatılacaktır.

Her emir CTrade sınıfının OrderSend fonksiyonu kullanılarak sunucuya gönderilir. Çalışmanın en ilginç detayı aşağıda gizlidir.

Gerçek şu ki, bir emirin atanması iki yönlü olabilir. Alım veya satım emri sinyalin oluşması üzerine gönderilebilir veya daha önce var olanın bloke edilmesine yönelik bir emir olabilir. OrderSend fonksiyonu gönderilen emrin türünü bilmelidir çünkü bu fonksiyon aslında emir tablosuna tüm emirleri yerleştiren veya belirli olayların oluşması durumunda onları tablodan kaldıran fonksiyondur.

Eklemek istediğiniz emir türü ADD_ORDER ise. Yani, emir tablosuna yerleştirilmesi gereken bağımsız bir emir ise fonksiyon bu emirle ilgili bilgileri sipariş tablosuna ekler. Emir önceden verilen emri geçersiz kılmak için verilirse (örneğin, sanal bir zarar-durdur meydana geldiğinde) o zaman DELETE_ORDER türünde olmalıdır. Yerleştirildikten sonra OrderSend fonksiyonu emir listesinden bağlantılı olduğu emirle ilgili bilgileri manuel olarak kaldırır. Bunun için fonksiyon emir türüne ek olarak bağlantılı olduğu bir emir fişini devralır. Bu ADD_ORDER ise fişe sıfır değeri girilebilir.


Hareketli ortalamaların geçişine dayanan ilk alım satım modeli

CModel temel sınıfının tüm ana unsurlarını tartıştık. Belirli bir alım satım sınıfı üzerinde durmanın zamanı geldi.

Bu amaç doğrultusunda önce basit bir MACD göstergesine dayanan basit bir alım satım modeli oluşturacağız.

Bu model her zaman uzun veya kısa pozisyona sahip olacaktır. Hızlı çizgi yavaş çizgiyi aşağı doğru keser kesmez kısa pozisyon açılacak; eğer varsa da uzun pozisyon kapatılacaktır. Yukarıya doğru kesmesi durumunda uzun pozisyon açılacak; varsa kısa pozisyon kapatılacaktır. Bu modelde koruyucu stoplar ve kâr seviyeleri kullanmıyoruz.

#include <Models\Model.mqh>
#include <mm.mqh>
//+----------------------------------------------------------------------+
//| This model uses MACD indicator.                                      |
//| Buy when it crosses the zero line downward                           |
//| Sell when it crosses the zero line upward                            |
//+----------------------------------------------------------------------+  
struct cmodel_macd_param
{
   string            symbol;
   ENUM_TIMEFRAMES   timeframe;
   int               fast_ema;
   int               slow_ema;
   int               signal_ema;
};
   
class cmodel_macd : public CModel
{
private:
   int               m_slow_ema;
   int               m_fast_ema;
   int               m_signal_ema;
   int               m_handle_macd;
   double            m_macd_buff_main[];
   double            m_macd_current;
   double            m_macd_previous;
public:
                     cmodel_macd();
   bool              Init();
   bool              Init(cmodel_macd_param &m_param);
   bool              Init(string symbol, ENUM_TIMEFRAMES timeframes, int slow_ma, int fast_ma, int smothed_ma);
   bool              Processing();
protected:
   bool              InitIndicators();
   bool              CheckParam(cmodel_macd_param &m_param);
   bool              LongOpened();
   bool              ShortOpened();
   bool              LongClosed();
   bool              ShortClosed();
};

cmodel_macd::cmodel_macd()
{
   m_handle_macd=INVALID_HANDLE;
   ArraySetAsSeries(m_macd_buff_main,true);
   m_macd_current=0.0;
   m_macd_previous=0.0;
}
//this default loader
bool cmodel_macd::Init()
{
   m_magic      = 148394;
   m_model_name =  "MACD MODEL";
   m_symbol     = _Symbol;
   m_timeframe  = _Period;
   m_slow_ema   = 26;
   m_fast_ema   = 12;
   m_signal_ema = 9;
   m_delta      = 50;
   if(!InitIndicators())return(false);
   return(true);
}

bool cmodel_macd::Init(cmodel_macd_param &m_param)
{
   m_magic      = 148394;
   m_model_name = "MACD MODEL";
   m_symbol     = m_param.symbol;
   m_timeframe  = (ENUM_TIMEFRAMES)m_param.timeframe;
   m_fast_ema   = m_param.fast_ema;
   m_slow_ema   = m_param.slow_ema;
   m_signal_ema = m_param.signal_ema;
   if(!CheckParam(m_param))return(false);
   if(!InitIndicators())return(false);
   return(true);
}


bool cmodel_macd::CheckParam(cmodel_macd_param &m_param)
{
   if(!SymbolInfoInteger(m_symbol, SYMBOL_SELECT))
   {
      Print("Symbol ", m_symbol, " selection has failed. Check symbol name");
      return(false);
   }
   if(m_fast_ema == 0)
   {
      Print("Fast EMA must be greater than 0");
      return(false);
   }
   if(m_slow_ema == 0)
   {
      Print("Slow EMA must be greater than 0");
      return(false);
   }
   if(m_signal_ema == 0)
   {
      Print("Signal EMA must be greater than 0");
      return(false);
   }
   return(true);
}

bool cmodel_macd::InitIndicators()
{
   if(m_handle_macd==INVALID_HANDLE)
   {
      Print("Load indicators...");
      if((m_handle_macd=iMACD(m_symbol,m_timeframe,m_fast_ema,m_slow_ema,m_signal_ema,PRICE_CLOSE))==INVALID_HANDLE)
      {
         printf("Error creating MACD indicator");
         return(false);
      }
   }
   return(true);
}

bool cmodel_macd::Processing()
{
   //if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_DISABLED)return(false);
   //if(m_account_info.TradeAllowed()==false)return(false);
   //if(m_account_info.TradeExpert()==false)return(false);
   
   m_symbol_info.Name(m_symbol);
   m_symbol_info.RefreshRates();
   CopyBuffer(this.m_handle_macd,0,1,2,m_macd_buff_main);
   m_macd_current=m_macd_buff_main[0];
   m_macd_previous=m_macd_buff_main[1];
   GetNumberOrders(m_orders);
   if(m_orders.buy_orders>0)   LongClosed();
   else                        LongOpened();
   if(m_orders.sell_orders!=0) ShortClosed();
   else                        ShortOpened();
   return(true);
}

bool cmodel_macd::LongOpened(void)
{
   if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_DISABLED)return(false);
   if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_SHORTONLY)return(false);
   if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_CLOSEONLY)return(false);
   
   bool rezult, ticket_bool;
   double lot=0.1;
   mm open_mm;
   m_symbol_info.Name(m_symbol);
   m_symbol_info.RefreshRates();
   CopyBuffer(this.m_handle_macd,0,1,2,m_macd_buff_main);
   
   m_macd_current=m_macd_buff_main[0];
   m_macd_previous=m_macd_buff_main[1];
   GetNumberOrders(m_orders);
   
   //Print("LongOpened");
   if(m_macd_current>0&&m_macd_previous<=0&&m_orders.buy_orders==0)
   {
      //lot=open_mm.optimal_f(m_symbol, ORDER_TYPE_BUY, m_symbol_info.Ask(), 0.0, m_delta);
      lot=open_mm.jons_fp(m_symbol, ORDER_TYPE_BUY, m_symbol_info.Ask(), 0.1, 10000, m_delta);
      rezult=SendOrder(m_symbol, ORDER_TYPE_BUY, ORDER_ADD, 0, lot, m_symbol_info.Ask(), 0, 0, "MACD Buy");
      return(rezult);
   }
   return(false);
}

bool cmodel_macd::ShortOpened(void)
{
   if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_DISABLED)return(false);
   if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_LONGONLY)return(false);
   if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_CLOSEONLY)return(false);
   
   bool rezult, ticket_bool;
   double lot=0.1;
   mm open_mm;
   
   m_symbol_info.Name(m_symbol);
   m_symbol_info.RefreshRates();
   CopyBuffer(this.m_handle_macd,0,1,2,m_macd_buff_main);
   
   m_macd_current=m_macd_buff_main[0];
   m_macd_previous=m_macd_buff_main[1];
   GetNumberOrders(m_orders);
   
   if(m_macd_current<=0&&m_macd_previous>=0&&m_orders.sell_orders==0)
   {
      //lot=open_mm.optimal_f(m_symbol, ORDER_TYPE_SELL, m_symbol_info.Bid(), 0.0, m_delta);
      lot=open_mm.jons_fp(m_symbol, ORDER_TYPE_SELL, m_symbol_info.Bid(), 0.1, 10000, m_delta);
      rezult=SendOrder(m_symbol, ORDER_TYPE_SELL, ORDER_ADD, 0, lot, m_symbol_info.Bid(), 0, 0, "MACD Sell");
      return(rezult);
   }
   return(false);
}

bool cmodel_macd::LongClosed(void)
{
   if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_DISABLED)return(false);
   CTableOrders *t;
   int total_elements;
   int rez=false;
   total_elements=ListTableOrders.Total();
   if(total_elements==0)return(false);
   for(int i=total_elements-1;i>=0;i--)
   {
      if(CheckPointer(ListTableOrders)==POINTER_INVALID)continue;
      t=ListTableOrders.GetNodeAtIndex(i);
      if(CheckPointer(t)==POINTER_INVALID)continue;
      if(t.Type()!=ORDER_TYPE_BUY)continue;
      m_symbol_info.Refresh();
      m_symbol_info.RefreshRates();
      CopyBuffer(this.m_handle_macd,0,1,2,m_macd_buff_main);
      if(m_symbol_info.Bid()<=t.StopLoss()&&t.StopLoss()!=0.0)
      {
         
         rez=SendOrder(m_symbol, ORDER_TYPE_SELL, ORDER_DELETE, t.Ticket(), t.VolumeInitial(), 
                       m_symbol_info.Bid(), 0.0, 0.0, "MACD: buy close buy stop-loss");
      }
      if(m_macd_current<0&&m_macd_previous>=0)
      {
         //Print("Long position closed by Order Send");
         rez=SendOrder(m_symbol, ORDER_TYPE_SELL, ORDER_DELETE, t.Ticket(), t.VolumeInitial(), 
                       m_symbol_info.Bid(), 0.0, 0.0, "MACD: buy close by signal");
      }
   }
   return(rez);
}

bool cmodel_macd::ShortClosed(void)
{
   if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_DISABLED)return(false);
   CTableOrders *t;
   int total_elements;
   int rez=false;
   total_elements=ListTableOrders.Total();
   if(total_elements==0)return(false);
   for(int i=total_elements-1;i>=0;i--)
   {
      if(CheckPointer(ListTableOrders)==POINTER_INVALID)continue;
      t=ListTableOrders.GetNodeAtIndex(i);
      if(CheckPointer(t)==POINTER_INVALID)continue;
      if(t.Type()!=ORDER_TYPE_SELL)continue;
      m_symbol_info.Refresh();
      m_symbol_info.RefreshRates();
      CopyBuffer(this.m_handle_macd,0,1,2,m_macd_buff_main);
      if(m_symbol_info.Ask()>=t.StopLoss()&&t.StopLoss()!=0.0)
      {
         rez=SendOrder(m_symbol, ORDER_TYPE_BUY, ORDER_DELETE, t.Ticket(), t.VolumeInitial(),
                                 m_symbol_info.Ask(), 0.0, 0.0, "MACD: sell close buy stop-loss");
      }
      if(m_macd_current>0&&m_macd_previous<=0)
      {
         rez=SendOrder(m_symbol, ORDER_TYPE_BUY, ORDER_DELETE, t.Ticket(), t.VolumeInitial(),
                                 m_symbol_info.Ask(), 0.0, 0.0, "MACD: sell close by signal");
      }
   }
   return(rez);
}

CModel temel sınıfı, al ögelerinin dâhili içeriğine herhangi bir kısıtlama getirmez. Zorunlu kıldığı tek şey Processing() arayüz fonksiyonunun kullanılmasıdır. Bu fonksiyonun dâhili organizasyonunun tüm sorunları belirli bir model sınıfına devredilir. Processing() fonksiyonunun içine yerleştirilebilecek herhangi bir evrensel algoritma yoktur. Bu nedenle belirli bir modelin nasıl düzenlenmesi gerektiğine ilişkin yöntemini alt ögelerine uygulamak için hiçbir neden yoktur. Ancak hemen hemen her modelin iç yapısı standart hale getirilebilir. Böyle bir standardizasyon, dış yapıyı ve hatta kodunuzun anlaşılmasını büyük ölçüde kolaylaştıracak ve modeli daha “formüle” bir hale getirecektir.

Her modelin kendi başlatıcısı olmalıdır. Modelin başlatıcısı, çalışması için gerekli olan doğru parametreleri yüklemekten sorumludur; örneğin modelimizin çalışması için MACD gösterge değerlerini seçmemiz, ilgili aralığın bir işleyicisini elde etmemiz ve buna ek olarak model için alım satım aracını ve zaman dilimini belirlememiz gerekmektedir. Bütün bunlar başlatıcı tarafından yapılmalıdır.

Modellerin başlatıcıları sınıflarının basit ve aşırı yüklenmiş yöntemleridir. Bu yöntemlerin ortak adı Init’tir. Gerçek şu ki, MQL5 aşırı yüklenmiş oluşturucuları desteklemez; bu nedenle oluşturucusunda modelin başlatıcısını yaratmanın bir yolu yoktur çünkü giriş parametreleri için aşırı yüklemeye ihtiyaç duyulacaktır . Her modelin oluşturucusundatemel parametrelerini belirtme konusunda bizi kısıtlayan bir şey olmasa da

Her modelin üç başlatıcısı olmalıdır. Birincisi varsayılan başlatıcıdır. Parametreleri talep etmeden modeli varsayılan olarak yapılandırmalı ve yüklemelidir. Bu işlem “olduğu gibi” kullanılan modda test etmek için çok uygun olabilir. Örneğin, modelimiz için bir araç aygıtı olarak varsayılan başlatıcı ve modelin zaman dilimi mevcut grafiği ve mevcut zaman dilimini seçecektir.

MACD göstergesininayarları da standart olacaktır: hızlı EMA = 12, yavaş EMA = 26, sinyal MA = 9; Modelin belirli bir şekilde yapılandırılması gerekiyorsa bu başlatıcı artık uygun olmayacaktır. Parametreli başlatıcılara ihtiyacımız olacaktır. İki tür oluşturmak iyi bir seçenektir (ancak zorunlu değildir). İlki, parametrelerini klasik bir fonksiyon olarak alacaktır: Init (param1 türü, param2 türü, ..., paramN türü). İkincisi, bu parametreleri kaydeden özel bir yapı kullanarak modelin parametrelerini bulacaktır. Bu seçenek bazen daha fazla tercih edilir çünkü bazen parametre sayısı fazla olabilir, bu durumda bunları yapıların içinden geçirmek uygun olacaktır.

Her model kendi parametrelerinin yapısına sahiptir. Bu yapı herhangi bir ada sahip olabilir ancak onu modelname_param modeliyle çağırmak tercih edilir. Modelin yapılandırılması çoklu zaman dilimi/çoklu sistem/çoklu para birimi alım satım fırsatlarını kullanmada çok önemli bir adımdır. Bu aşamada bu modelin nasıl ve hangi araçta işlem göreceği belirlenir.

Alım satım modelimizin yalnızca dört alım satım fonksiyonu vardır. Uzun bir pozisyon açma fonksiyonu: LongOpen, kısa birpozisyonu açma fonksiyonu: ShortOpen, uzun bir pozisyonu kapatma fonksiyonu: LongClosed, kısa bir pozisyonu kapatma fonksiyonu: ShortClosed. LongOpen ve ShortOpen fonksiyonlarının çalışması önemsizdir. Her ikisi de bundan önceki iki çubuğun değeriyle karşılaştırılan MACD önceki çubuğunun gösterge değerini alır. “Yeniden çizimi” önlemek için mevcut (sıfır) çubuk kullanılmayacaktır.

Aşağı doğru bir kesişme varsa ShortOpenmmmqhbaşlık dosyasında bulunan fonksiyonları kullanarak fonksiyonları hesaplarmm., ardından gerekli lot komutunuOrderSend fonksiyonuna gönderir. The LongCloseşu anda tüm bunların aksine modeldeki tüm uzun pozisyonları kapatır. Bunun nedeni, fonksiyonun modelin emirler tablosundaki tüm geçerli açık emirleri sıralı olarak sıralamasıdır. Mevcut uzun bir emir varsa fonksiyon onu bir karşı emirle kapatır. Aynı işlem ters yönde ShortClose() fonksiyonu tarafından yapılır. Bu fonksiyonların çalışması yukarıda verilen listede bulunabilir.

Alım satım modelinde mevcut lotun nasıl hesaplandığını daha ayrıntılı olarak analiz edelim.

Yukarıda belirtildiği gibi, bu amaçlar için hesabın sermayelendirilmesine ilişkin özel fonksiyonlar kullanıyoruz. Sermayelendirme formüllerine ek olarak bu fonksiyonlar kullanılan marjın düzeyine ve alım satım pozisyonlarının boyutunun sınırlandırılmasına dayalı olarak lot hesaplamasının doğrulanmasını içerir.Pozisyonun boyutunda bazı alım satım kısıtlamaları olabilir. Bunların dikkate alınması gerekir.

Aşağıdaki durumu göz önünde bulundurun: Her iki yönde de 15 standart lotluk bir alım satım pozisyonunun boyutunda bir sınır mevcuttur. Mevcut pozisyon uzundur ve 3 lota eşittir. Sermaye yönetimi sistemine dayanan alım satım modeli 18,6 lotluk bir uzun pozisyon açmak istiyor. CheckLot() fonksiyonu emir hacminin düzeltilmiş miktarını döndürür. Bu durumda bu 12 lota eşit olacaktır (çünkü 15 lottan 3'ü zaten başka işlemler tarafından alınmıştır). Mevcut açık pozisyon uzun değil de kısa olsaydı fonksiyon 18,6 yerine 15 lot döndürürdü. Bu mümkün olan maksimum pozisyon hacmidir.

15 lot alış yaptıktan sonra, bu durumda net pozisyon 12 lot olacaktır (3 - sat, 15 - al). Başka bir model 3 lotluk ilk kısa pozisyonunu geçersiz kıldığında toplu pozisyon satın alma işlemi mümkün olan maksimum lot, yani 15 lot olacaktır. Diğer satın alma sinyalleri model 15 lot satın alımın bir kısmını veya tamamını geçersiz kılana kadar işlenmez. İstenen işlem için kullanılabilir hacim tükenirse fonksiyon bir EMPTY_VALUE sabitidöndürür. Bu sinyal iletilmelidir.

Ayarlanan hacmin olasılığının kontrolü başarılı olursa gerekli marjın değeri üzerinden hesaplamalar yapılır. Hesap fonları belirlenen hacim için yetersiz olabilir. Bu amaçlar için bir CheckMargin() fonksiyonu mevcuttur. Marj yeterli değilse mevcut serbest marjın açılmaya izin vermesi için işlemin belirtilen tutarını düzeltmeye çalışacaktır. Eğer marj minimum hacmi açmaya bile yetmiyorsa, Marj-Çağrısı durumuna geçmiş oluruz.

Hâlihazırda bir pozisyon yoksa ve marj kullanılmıyorsa bunun tek bir anlamı vardır: teknik marj-çağrısı (bir işlem açmanın imkânsız olduğu bir durumu ifade eder). Hesaba para eklemeden devam edemeyiz. Eğer hâlâ bir miktar marj kullanılıyorsa bu marjı kullanan pozisyonun kapanmasını beklemekten başka seçeneğimiz yoktur. Her durumda marj eksikliği sabit bir EMPTY_VALUE döndürür.

Lot boyutu ve marjları kontrol etme fonksiyonları genellikle doğrudan kullanılmaz. Sermayeyi yönetmeye ilişkin özel fonksiyonlar tarafından çağrılırlar. Bu fonksiyonlar, hesapların sermayelendirilmesi için formülleri uygular. mm.mqh dosyası, sermaye yönetiminin yalnızca iki temel fonksiyonunu içerir; biri hesabın sabit bir bölümü temelinde hesaplanır, diğeri ise Ryan Jones tarafından önerilen ve sabit oranlar yöntemi olarak bilinen yönteme dayanır.

İlk yöntemin amacı, hesabın riske atılabilecek sabit bir bölümünü tanımlamaktır. Örneğin, izin verilen risk hesabın %2'si ise ve hesap 10,000 dolara eşitse maksimum risk tutarı 200 dolardır. 200 dolarlık bir stopta hangi lotun kullanılması gerektiğini hesaplamak için fiyatın açılan pozisyona karşı ulaşabileceği maksimum mesafeyi tam olarak bilmeniz gerekir. Dolayısıyla bu formül üzerinden lotu hesaplamak için Zarar Durdur'u ve işlemin yapılacağı fiyat seviyesini doğru bir şekilde belirlememiz gerekir.

Ryan Jones tarafından önerilen yöntem öncekinden farklıdır. Temelinde yatan şey, sermayelendirmenin ikinci dereceden bir denklemin belirli bir durumu tarafından tanımlanan fonksiyon tarafından yapılmasıdır.

Çözümü şöyledir:

x=((1.0+MathSqrt(1+4.0*d))/2)*Step;

yani, x: bir sonraki seviyeye geçişin alt sınırı d = (Kâr / delta) * 2.0 Adım- 0,1 gibi deltanın bir adımı.

Deltanın boyutu ne kadar küçükse fonksiyon pozisyon sayısını o kadar agresif bir şekilde arttırmaya çalışır.Bu fonksiyonun nasıl oluşturulduğu hakkında daha fazla ayrıntı için Ryan Jones'un kitabına göz atabilirsiniz: The Trading Game: Playing by the Numbers to Make Millions.

Sermaye yönetimi fonksiyonlarının kullanılması planlanmıyorsa lot ve marj kontrolü fonksiyonlarının doğrudan çağrılması gerekir.

Temel EA'mızın tüm unsurlarını gözden geçirdik. Şimdi çalışmalarımızın meyvelerini toplamanın zamanı geldi.

Başlamak için dört model oluşturalım. Bir model EURUSDvarsayılan parametreleriyle alım satım yapsın; ikincisi de EURUSD ile alım satım yapsın ancak bu 15 dakikalık bir zaman diliminde gerçekleşsin. Üçüncü model varsayılan parametrelerle GBPUSDgrafiğinde başlatılacaktır. Dördüncü model de aşağıdaki parametrelerle iki saatlik bir grafikte USDCHF'de başlatılacaktır: SlowEMA= 6 FastEMA = 12 SignalEMA = 9. Test süresi - H1, test modu - tüm kutucuklar, 01.01.2010 ile 01.09.2010 arası.

Ancak bu modeli dört farklı modda çalıştırmadan önce bunu her bir araç ve zaman dilimi için ayrı ayrı test etmeye çalışacağız.

Testin ana göstergelerinin verildiği tablo:

Sistem
İşlemlerin sayısı
Kâr, $
MACD(9,12,26)H1 EURUSD
123
1092
MACD (9,12,26) EURUSD M15
598
-696
MACD(9,6,12) USDCHF H2
153
-1150
MACD(9,12,26) GBPUSD H1
139
-282
Tüm sistemler
1013
-1032

Tablo tüm modeller için toplam işlem sayısının 1013 ve toplam kârın da 1032 $ olması gerektiğini göstermektedir.

Sonuç olarak, bunlar bu sistemleri aynı anda test etmek istediğimizde elde etmemiz gereken değerlerle aynıdır. Bazı küçük sapmalar olmasına rağmen sonuçlar farklılık göstermemelidir.

Son test şöyledir:


Görülebileceği gibi, işlem sayısı sadece 1 azalmıştır ve kâr farkı 10 $ olmuştur; bu da 0,1'lik bir lot ile sadece 10 puan farka tekabül eder. Para yönetimi sisteminin kullanılması durumunda toplu test sonuçlarının her modelin kendi test sonuçlarının miktarından tamamen farklı olacağına dikkat edilmelidir. Bunun nedeni, bakiye dinamiklerinin sistemlerin her birini etkilemesidir, o yüzden lotların hesaplanan değerleri değişecektir. 

Her ne kadar sonuçlar tek başına ilgi çekici olmasa da EA'nın karmaşık ama çok esnek ve yönetilebilir bir yapısını oluşturduk. Yapısını kısaca tekrar ele alalım.

Bunun için aşağıda gösterilen şemaya döneceğiz:

Sınıf referansı

Şema, modelin örneğinin temel yapısını gösterir.

Alım satım modelinin bir sınıf örneği oluşturulduğunda aşırı yüklenmiş Init() fonksiyonunu çağırırız. Gerekli parametreleri başlatır, verileri hazırlar ve kullanılıyorsa Göstergelerin işleyicilerini yükler. Tüm bunlar EA'nın başlatılması sırasında yani OnInit() fonksiyonu içinde gerçekleşir. Verilerin alım satımı kolaylaştırmak için tasarlanmış temel sınıfların örneklerini içerdiğini unutmayın. Alım satım modellerinin MQL5'in standart fonksiyonları yerine bu sınıfları aktif olarak kullanması gerektiği varsayılmaktadır. Model sınıfı başarıyla oluşturulduktan ve başlatıldıktan sonra, CList modelleri listesine girer. Bununla daha sonraki iletişimler, evrensel bir adaptör olan CObject aracılığıyla gerçekleştirilir.

OnTrade() veya OnTick() olaylarının oluşmasından sonra, listedeki tüm model örneklerinin sıralı bir sıralaması yapılır. Onlarla iletişim Processing() fonksiyonu çağrılarak gerçekleştirilir. Ayrıca kendi modelinin alım satım fonksiyonlarını çağırır (mavi fonksiyonlar grubu). Listeleri ve adları kesin olarak tanımlanmamıştır ancak LongOpened(), ShortClosed() vb. gibi standart adları kullanmak uygundur. Bu fonksiyonlar kendilerine özgü çalışma mantığı doğrultusunda işlemi tamamlama zamanını seçer ve ardından SendOrder() fonksiyonunun işleminin açılması veya kapanması için özel olarak oluşturulmuş bir istek gönderir.

İkincisi gerekli kontrolleri yapar ve ardından emirleri piyasaya gönderir. Alım satım fonksiyonları, temel yardımcı sınıfları (mor grup) aktif olarak kullanan modelin yardımcı fonksiyonlarına (yeşil grup) dayanır. Tüm yardımcı sınıflar veri bölümünde (pembe grup) sınıf örnekleri olarak temsil edilir. Gruplar arasındaki etkileşim koyu mavi oklarla gösterilir.


Bollinger Bantları göstergesine dayalı alım satım modeli

Artık veri ve yöntemlerin genel yapısı netleştiğine göre Bollinger Bantlarının trend göstergelerine dayalı başka bir alım satım modeli oluşturabiliriz. Bu alım satım modelinin temeli olarak, Andrei Kornishkin'in Bollinger Bantlarına dayanan basit bir EA (Uzman Danışman) kullandık. Bollinger Bantları basit hareketli ortalamadaki belirli bir standart sapma boyutuna eşit seviyelerdir. Bu göstergenin nasıl oluşturulduğuna ilişkin daha fazla ayrıntı MetaTrader 5 terminaline eklenmiş teknik analiz için Yardım bölümünde bulunabilir.

Alım satım fikrinin özü basittir: fiyat döndürme özelliğine sahiptir, yani fiyat belirli bir seviyeye ulaşırsa büyük olasılıkla ters yöne dönecektir. Bu tez herhangi bir gerçek alım satım aracının normal dağılımı üzerinde yapılan bir testle kanıtlanmıştır: normal dağılım eğrisi hafifçe uzamıştır. Bollinger bantları fiyat seviyelerinin en olası doruk noktalarını belirler. Bunlara ulaşıldığında (üst veya alt Bollinger bantları), fiyatın ters yöne dönmesi muhtemeldir.

Alım satım taktiğini biraz basitleştiriyoruz ve yardımcı göstergeyi; çift üstel hareketli ortalamayı (Çift Üstel Hareketli Ortalama veya DEMA) kullanmayacağız. Ancak güçlü ve koruyucu stoplar (sanal Zarar Durdur) kullanacağız. Alım satım sürecini daha istikrarlı hale getirecekler ve aynı zamanda her alım satım modelinin kendi bağımsız koruyucu stop seviyesini kullandığı bir örneği anlamamıza yardımcı olacaklar.

Koruyucu stop seviyesi için mevcut fiyat artı veya eksi gösterge değeri volatilitesi ATR'yi kullanırız. Örneğin, ATR'nin mevcut değeri 68 puana eşitse ve 1,25720 fiyatından satış sinyali mevcutsa bu işlem için sanal Zarar Durdur değeri 1,25720 + 0,0068 = 1,26400'e eşit olacaktır. Aynı işlem benzer şekilde ancak ters yönde satın alım için yapılır: 1,25720 - 0,0068 = 1,25040.

Bu modelin kaynak kodu aşağıda verilmiştir:

#include <Models\Model.mqh>
#include <mm.mqh>
//+----------------------------------------------------------------------+
//| This model use Bollinger bands.
//| Buy when price is lower than lower band
//| Sell when price is higher than upper band
//+----------------------------------------------------------------------+  
struct cmodel_bollinger_param
{
   string            symbol;
   ENUM_TIMEFRAMES   timeframe;
   int               period_bollinger;
   double            deviation;
   int               shift_bands;
   int               period_ATR;
   double            k_ATR;
   double            delta;
};
   
class cmodel_bollinger : public CModel
{
private:
   int               m_bollinger_period;
   double            m_deviation;
   int               m_bands_shift;
   int               m_ATR_period;
   double            m_k_ATR;
   //------------Indicators Data:-------------
   int               m_bollinger_handle;
   int               m_ATR_handle;
   double            m_bollinger_buff_main[];
   double            m_ATR_buff_main[];
   //-----------------------------------------
   MqlRates          m_raters[];
   double            m_current_price;
public:
                     cmodel_bollinger();
   bool              Init();
   bool              Init(cmodel_bollinger_param &m_param);
   bool              Init(ulong magic, string name, string symbol, ENUM_TIMEFRAMES TimeFrame, double delta,
                          uint bollinger_period, double deviation, int bands_shift, uint ATR_period, double k_ATR);
   bool              Processing();
protected:
   bool              InitIndicators();
   bool              CheckParam(cmodel_bollinger_param &m_param);
   bool              LongOpened();
   bool              ShortOpened();
   bool              LongClosed();
   bool              ShortClosed();
   bool              CloseByStopSignal();
};

cmodel_bollinger::cmodel_bollinger()
{
   m_bollinger_handle   = INVALID_HANDLE;
   m_ATR_handle         = INVALID_HANDLE;
   ArraySetAsSeries(m_bollinger_buff_main,true);
   ArraySetAsSeries(m_ATR_buff_main,true);
   ArraySetAsSeries(m_raters, true);
   m_current_price=0.0;
}
//this default loader
bool cmodel_bollinger::Init()
{
   m_magic              = 322311;
   m_model_name         =  "Bollinger Bands Model";
   m_symbol             = _Symbol;
   m_timeframe          = _Period;
   m_bollinger_period   = 20;
   m_deviation          = 2.0;
   m_bands_shift        = 0;
   m_ATR_period         = 20;
   m_k_ATR              = 2.0;
   m_delta              = 0;
   if(!InitIndicators())return(false);
   return(true);
}

bool cmodel_bollinger::Init(cmodel_bollinger_param &m_param)
{
   m_magic              = 322311;
   m_model_name         = "Bollinger Model";
   m_symbol             = m_param.symbol;
   m_timeframe          = (ENUM_TIMEFRAMES)m_param.timeframe;
   m_bollinger_period   = m_param.period_bollinger;
   m_deviation          = m_param.deviation;
   m_bands_shift        = m_param.shift_bands;
   m_ATR_period        = m_param.period_ATR;
   m_k_ATR              = m_param.k_ATR;
   m_delta              = m_param.delta;
   //if(!CheckParam(m_param))return(false);
   if(!InitIndicators())return(false);
   return(true);
}

bool cmodel_bollinger::Init(ulong magic, string name, string symbol, ENUM_TIMEFRAMES timeframe, double delta,
                           uint bollinger_period, double deviation, int bands_shift, uint ATR_period, double k_ATR)
{
   m_magic           = magic;
   m_model_name      = name;
   m_symbol          = symbol;
   m_timeframe       = timeframe;
   m_delta           = delta;
   m_bollinger_period= bollinger_period;
   m_deviation       = deviation;
   m_bands_shift     = bands_shift;
   m_ATR_period      = ATR_period;
   m_k_ATR           = k_ATR;
   if(!InitIndicators())return(false);
   return(true);
}


/*bool cmodel_bollinger::CheckParam(cmodel_bollinger_param &m_param)
{
   if(!SymbolInfoInteger(m_symbol, SYMBOL_SELECT)){
      Print("Symbol ", m_symbol, " select failed. Check valid name symbol");
      return(false);
   }
   if(m_ma == 0){
      Print("Fast EMA must be bigest 0. Set MA = 12 (default)");
      m_ma=12;
   }
   return(true);
}*/

bool cmodel_bollinger::InitIndicators()
{
   m_bollinger_handle=iBands(m_symbol,m_timeframe,m_bollinger_period,m_bands_shift,m_deviation,PRICE_CLOSE);
   if(m_bollinger_handle==INVALID_HANDLE){
      Print("Error in creation of Bollinger indicator. Restart the Expert Advisor.");
      return(false);
   }
   m_ATR_handle=iATR(m_symbol,m_timeframe,m_ATR_period);
   if(m_ATR_handle==INVALID_HANDLE){
      Print("Error in creation of ATR indicator. Restart the Expert Advisor.");
      return(false);
   }
   return(true);
}

bool cmodel_bollinger::Processing()
{
   //if(timing(m_symbol,m_timeframe, m_timing)==false)return(false);
   
   //if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_DISABLED)return(false);
   //if(m_account_info.TradeAllowed()==false)return(false);
   //if(m_account_info.TradeExpert()==false)return(false);
   
   //m_symbol_info.Name(m_symbol);
   //m_symbol_info.RefreshRates();
   //Copy last data of moving average
 
   GetNumberOrders(m_orders);
   
   if(m_orders.buy_orders>0)   LongClosed();
   else                        LongOpened();
   if(m_orders.sell_orders!=0) ShortClosed();
   else                        ShortOpened();
   if(m_orders.all_orders!=0)CloseByStopSignal();
   return(true);
}

bool cmodel_bollinger::LongOpened(void)
{
   //if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_DISABLED)return(false);
   //if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_SHORTONLY)return(false);
   //if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_CLOSEONLY)return(false);
   //Print("Model Bollinger: ", m_orders.buy_orders);
   bool rezult, time_buy=true;
   double lot=0.1;
   double sl=0.0;
   double tp=0.0;
   mm open_mm;
   m_symbol_info.Name(m_symbol);
   m_symbol_info.RefreshRates();
   //lot=open_mm.optimal_f(m_symbol,OP_BUY,m_symbol_info.Ask(),sl,delta);
   CopyBuffer(m_bollinger_handle,2,0,3,m_bollinger_buff_main);
   CopyBuffer(m_ATR_handle,0,0,3,m_ATR_buff_main);
   CopyRates(m_symbol,m_timeframe,0,3,m_raters);
   if(m_raters[1].close>m_bollinger_buff_main[1]&&m_raters[1].open<m_bollinger_buff_main[1])
   {
      sl=NormalizeDouble(m_symbol_info.Ask()-m_ATR_buff_main[0]*m_k_ATR,_Digits);
      SendOrder(m_symbol,ORDER_TYPE_BUY,ORDER_ADD,0,lot,m_symbol_info.Ask(),sl,tp,"Add buy");
   }
   return(false);
}

bool cmodel_bollinger::ShortOpened(void)
{
   //if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_DISABLED)return(false);
   //if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_LONGONLY)return(false);
   //if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_CLOSEONLY)return(false);
   
   bool rezult, time_sell=true;
   double lot=0.1;
   double sl=0.0;
   double tp;
   mm open_mm;
   
   m_symbol_info.Name(m_symbol);
   m_symbol_info.RefreshRates();
   CopyBuffer(m_bollinger_handle,1,0,3,m_bollinger_buff_main);
   CopyBuffer(m_ATR_handle,0,0,3,m_ATR_buff_main);
   CopyRates(m_symbol,m_timeframe,0,3,m_raters);
   if(m_raters[1].close<m_bollinger_buff_main[1]&&m_raters[1].open>m_bollinger_buff_main[1])
   {   
      sl=NormalizeDouble(m_symbol_info.Bid()+m_ATR_buff_main[0]*m_k_ATR,_Digits);
      SendOrder(m_symbol,ORDER_TYPE_SELL,ORDER_ADD,0,lot,m_symbol_info.Ask(),sl,tp,"Add buy");
   }
   return(false);
}

bool cmodel_bollinger::LongClosed(void)
{
   if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_DISABLED)return(false);
   CTableOrders *t;
   int total_elements;
   int rez=false;
   total_elements=ListTableOrders.Total();
   if(total_elements==0)return(false);
   m_symbol_info.Name(m_symbol);
   m_symbol_info.RefreshRates();
   CopyBuffer(m_bollinger_handle,1,0,3,m_bollinger_buff_main);
   CopyBuffer(m_ATR_handle,0,0,3,m_ATR_buff_main);
   CopyRates(m_symbol,m_timeframe,0,3,m_raters);
   if(m_raters[1].close<m_bollinger_buff_main[1]&&m_raters[1].open>m_bollinger_buff_main[1])
   {
      for(int i=total_elements-1;i>=0;i--)
      {
         if(CheckPointer(ListTableOrders)==POINTER_INVALID)continue;
         t=ListTableOrders.GetNodeAtIndex(i);
         if(CheckPointer(t)==POINTER_INVALID)continue;
         if(t.Type()!=ORDER_TYPE_BUY)continue;
         m_symbol_info.Refresh();
         m_symbol_info.RefreshRates();
         rez=SendOrder(m_symbol, ORDER_TYPE_SELL, ORDER_DELETE, t.Ticket(), t.VolumeInitial(), 
                       m_symbol_info.Bid(), 0.0, 0.0, "BUY: close by signal");
      }
   }
   return(rez);
}

bool cmodel_bollinger::ShortClosed(void)
{
   if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_DISABLED)return(false);
   CTableOrders *t;
   int total_elements;
   int rez=false;
   total_elements=ListTableOrders.Total();
   if(total_elements==0)return(false);
   CopyBuffer(m_bollinger_handle,2,0,3,m_bollinger_buff_main);
   CopyBuffer(m_ATR_handle,0,0,3,m_ATR_buff_main);
   CopyRates(m_symbol,m_timeframe,0,3,m_raters);
   if(m_raters[1].close>m_bollinger_buff_main[1]&&m_raters[1].open<m_bollinger_buff_main[1])
   {
      for(int i=total_elements-1;i>=0;i--)
      {
         if(CheckPointer(ListTableOrders)==POINTER_INVALID)continue;
         t=ListTableOrders.GetNodeAtIndex(i);
         if(CheckPointer(t)==POINTER_INVALID)continue;
         if(t.Type()!=ORDER_TYPE_SELL)continue;
         m_symbol_info.Refresh();
         m_symbol_info.RefreshRates();
         rez=SendOrder(m_symbol, ORDER_TYPE_BUY, ORDER_DELETE, t.Ticket(), t.VolumeInitial(),
                       m_symbol_info.Ask(), 0.0, 0.0, "SELL: close by signal");
      }
   }
   return(rez);
}

bool cmodel_bollinger::CloseByStopSignal(void)
{
   if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_DISABLED)return(false);
   CTableOrders *t;
   int total_elements;
   bool rez=false;
   total_elements=ListTableOrders.Total();
   if(total_elements==0)return(false);
   for(int i=total_elements-1;i>=0;i--)
   {
      if(CheckPointer(ListTableOrders)==POINTER_INVALID)continue;
      t=ListTableOrders.GetNodeAtIndex(i);
      if(CheckPointer(t)==POINTER_INVALID)continue;
      if(t.Type()!=ORDER_TYPE_SELL&&t.Type()!=ORDER_TYPE_BUY)continue;
      m_symbol_info.Refresh();
      m_symbol_info.RefreshRates();
      CopyRates(m_symbol,m_timeframe,0,3,m_raters);
      if(m_symbol_info.Bid()<=t.StopLoss()&&t.Type()==ORDER_TYPE_BUY)
      {
         rez=SendOrder(m_symbol, ORDER_TYPE_SELL, ORDER_DELETE, t.Ticket(), t.VolumeInitial(),
                       m_symbol_info.Bid(), 0.0, 0.0, "BUY: close by stop");
         continue;
      }
      if(m_symbol_info.Ask()>=t.StopLoss()&&t.Type()==ORDER_TYPE_SELL)
      {
         rez=SendOrder(m_symbol, ORDER_TYPE_BUY, ORDER_DELETE, t.Ticket(), t.VolumeInitial(),
                       m_symbol_info.Ask(), 0.0, 0.0, "SELL: close by stop");
         continue;
      }
   }
   return(rez);
}

Görüldüğü gibi, alım satım modelinin kodu önceki alım satım taktiklerinin kaynak koduna çok benzerdir. Buradaki ana değişiklik sanal stop emirlerinin oluşması ve bu koruyucu stoplara hizmet eden cmodel_bollinger:: CloseByStopSignal() fonksiyonudur.

Aslında koruyucu stopların kullanılması durumunda, değerleri basitçe SendOrder() fonksiyonuna iletilmelidir. Ve bu fonksiyon bu seviyeleri emir tablosuna girecektir. Fiyat bu seviyeleri geçtiğinde veya bu seviyeleri aştığında CloseByStopSignal() fonksiyonu bir karşı emirle işlemi kapatacak ve bunu aktif emirler listesinden kaldıracaktır.


Alım satım modellerinin araçların ve zaman dilimlerinin tek bir varlıkta birleşimi

Artık iki alım satım modelimiz olduğuna göre, bunları aynı anda test etme zamanı gelmiş demektir. Modellerin bir yerleşimini yapmadan önce, en etkili parametrelerini belirlemek faydalı olacaktır. Bunun için her modelin optimizasyonunu ayrı ayrı yapmamız gerekmektedir.

Optimizasyon, modelin en çok hangi piyasada ve zaman diliminde etkili olduğunu gösterecektir. Her model için en iyi zaman dilimlerinden sadece ikisi ve mevcut en iyi üç araç seçilecektir. Sonuç olarak, beraber test edilecek on iki bağımsız (2 model * 3 araç * 2 zaman dilimi) çözüm elde ediyoruz. Elbette ki seçilen optimizasyon yönteminde sonuçların sözde “ayarlanması” sıkıntı yaratır ancak bizim amacımız için bu önemli değildir.

Aşağıdaki grafikler örneğin en iyi sonuçlarını göstermektedir:

1.1 MACD EURUSD M30

MACD EURUSD M30

1.2 . MACD EURUSD H3


MACD EURUSD H3

1.3 MACD AUDUSD H4

MACD AUDUSD H4

1.4 . MACD AUDUSD H1


MACD AUDUSD H1

1.5 MACD GBPUSD H12


MACD GBPUSD H12

1.6 MACD GBPUSD H6


MACD GBPUSD H6

2.1 Bollinger GBPUSD M15


Bollinger GBPUSD M15

2.2 Bollinger GBPUSD H1


Bollinger GBPUSD H1

2.3 Bollinger EURUSD M30

Bollinger EURUSD M30

 

2.4  Bollinger EURUSD H4


Bollinger EURUSD H4

 

2.5 Bollinger USDCAD M15


Bollinger USDCAD M15

 

2.6 Bollinger USDCAD H2

Bollinger USDCAD H2

Artık en uygun sonuçlar bilindiğine göre geriye sadece yapılacak birkaç şey daha kaldı; o da sonuçları tek bir varlıkta toplamak.

Aşağıda, yukarıda gösterilen on iki alım satım modelini oluşturan fonksiyon yükleyicinin kaynak kodu yer almaktadır; EA sürekli olarak bunlar üzerinde alım satım yapmaya başlar:

bool macd_default=true;
bool macd_best=false;
bool bollinger_default=false;
bool bollinger_best=false;

void InitModels()
{
   list_model = new CList;             // Initialized pointer of the model list
   cmodel_macd *model_macd;            // Create the pointer to a model MACD
   cmodel_bollinger *model_bollinger;  // Create the pointer to a model Bollinger
   
//----------------------------------------MACD DEFAULT----------------------------------------
   if(macd_default==true&&macd_best==false)
    {
      model_macd = new cmodel_macd; // Initialize the pointer by the model MACD
      // Loading of the parameters was completed successfully
      if(model_macd.Init(129475, "Model macd M15", _Symbol, _Period, 0.0, Fast_MA,Slow_MA,Signal_MA))
      { 
      
         Print("Print(Model ", model_macd.Name(), " with period = ", model_macd.Period(), 
              " on symbol ", model_macd.Symbol(), " successfully created");
         list_model.Add(model_macd);// Загружаем модель в список моделей
      }
      else
      {
                                 // The loading of parameters was completed successfully
         Print("Print(Model ", model_macd.Name(), " with period = ", model_macd.Period(), 
         " on symbol ", model_macd.Symbol(), " creation has failed");
      }
   }
//-------------------------------------------------------------------------------------------
//----------------------------------------MACD BEST------------------------------------------
   if(macd_best==true&&macd_default==false)
   {
      // 1.1 EURUSD H30; FMA=20; SMA=24; 
      model_macd = new cmodel_macd; // Initialize the pointer to the model MACD
      if(model_macd.Init(129475, "Model macd H30", "EURUSD", PERIOD_M30, 0.0, 20,24,9))
      { 
         Print("Print(Model ", model_macd.Name(), " with period = ", model_macd.Period(), 
               " on symbol ", model_macd.Symbol(), " created successfully");
         list_model.Add(model_macd);// load the model into the list of models
      }
      else
      {// Loading parameters was completed unsuccessfully
         Print("Print(Model ", model_macd.Name(), " with period = ", model_macd.Period(), 
         " on symbol ", model_macd.Symbol(), " creation has failed");
      }
      // 1.2 EURUSD H3; FMA=8; SMA=12; 
      model_macd = new cmodel_macd; // Initialize the pointer by the model MACD
      if(model_macd.Init(129475, "Model macd H3", "EURUSD", PERIOD_H3, 0.0, 8,12,9))
      { 
         Print("Print(Model ", model_macd.Name(), " with period = ", model_macd.Period(), 
              " on symbol ", model_macd.Symbol(), " successfully created");
         list_model.Add(model_macd);// Load the model into the list of models
      }
      else
       {// Loading of parameters was unsuccessful
         Print("Print(Model ", model_macd.Name(), " with period = ", model_macd.Period(), 
         " on symbol ", model_macd.Symbol(), " creation has failed");
      }
      // 1.3 AUDUSD H1; FMA=10; SMA=18; 
      model_macd = new cmodel_macd; // Initialize the pointer by the model MACD
      if(model_macd.Init(129475, "Model macd M15", "AUDUSD", PERIOD_H1, 0.0, 10,18,9))
      { 
         Print("Print(Model ", model_macd.Name(), " with period = ", model_macd.Period(), 
              " on symbol ", model_macd.Symbol(), " successfully created");
         list_model.Add(model_macd);// Load the model into the list of models
      }
      else
      {// The loading of parameters was unsuccessful                       
         Print("Print(Model ", model_macd.Name(), " with period = ", model_macd.Period(), 
               " on symbol ", model_macd.Symbol(), " creation has failed");
      }
      // 1.4 AUDUSD H4; FMA=14; SMA=15; 
      model_macd = new cmodel_macd; // Initialize the pointer by the model MACD
      if(model_macd.Init(129475, "Model macd H4", "AUDUSD", PERIOD_H4, 0.0, 14,15,9))
      { 
         Print("Print(Model ", model_macd.Name(), " with period = ", model_macd.Period(), 
         " on symbol ", model_macd.Symbol(), " successfully created");
         list_model.Add(model_macd);// Load the model into the list of models
      }
      else{// Loading of parameters was unsuccessful
         Print("Print(Model ", model_macd.Name(), " with period = ", model_macd.Period(), 
              " on symbol ", model_macd.Symbol(), " creation has failed");
      }
      // 1.5 GBPUSD H6; FMA=20; SMA=33; 
      model_macd = new cmodel_macd; // Initialize the pointer by the model MACD
      if(model_macd.Init(129475, "Model macd H6", "GBPUSD", PERIOD_H6, 0.0, 20,33,9))
      { 
         Print("Print(Model ", model_macd.Name(), " with period = ", model_macd.Period(), 
              " on symbol ", model_macd.Symbol(), " successfully created");
         list_model.Add(model_macd);// Load the model into the list of models
      }
      else
      {// Loading of parameters was  unsuccessful
         Print("Print(Model ", model_macd.Name(), " with period = ", model_macd.Period(), 
               " on symbol ", model_macd.Symbol(), " creation has failed");
      }
      // 1.6 GBPUSD H12; FMA=12; SMA=30; 
      model_macd = new cmodel_macd; // Initialize the pointer by the model MACD
      if(model_macd.Init(129475, "Model macd H6", "GBPUSD", PERIOD_H12, 0.0, 12,30,9))
      { 
         Print("Print(Model ", model_macd.Name(), " with period = ", model_macd.Period(), 
              " on symbol ", model_macd.Symbol(), " successfully created");
         list_model.Add(model_macd);// Load the model into the list of models
      }
      else
      {// Loading of parameters was unsuccessful
         Print("Print(Model ", model_macd.Name(), " with period = ", model_macd.Period(), 
              " on symbol ", model_macd.Symbol(), " creation has failed");
      }
   }
//----------------------------------------------------------------------------------------------
//-------------------------------------BOLLINGER DEFAULT----------------------------------------
   if(bollinger_default==true&&bollinger_best==false)
   {
      model_bollinger = new cmodel_bollinger;
      if(model_bollinger.Init(1829374,"Bollinger",_Symbol,PERIOD_CURRENT,0,
                             period_bollinger,dev_bollinger,0,14,k_ATR))
      {
         Print("Model ", model_bollinger.Name(), " successfully created");
         list_model.Add(model_bollinger);
      }
   }
//----------------------------------------------------------------------------------------------
//--------------------------------------BOLLLINGER BEST-----------------------------------------
   if(bollinger_best==true&&bollinger_default==false)
   {
      //2.1 Symbol: EURUSD M30; period: 15; deviation: 2,75; k_ATR=2,75;
      model_bollinger = new cmodel_bollinger;
      if(model_bollinger.Init(1829374,"Bollinger","EURUSD",PERIOD_M30,0,15,2.75,0,14,2.75))
      {
         Print("Model ", model_bollinger.Name(), "Period: ", model_bollinger.Period(),
              ". Symbol: ", model_bollinger.Symbol(), " successfully created");
         list_model.Add(model_bollinger);
      }
      //2.2 Symbol: EURUSD H4; period: 30; deviation: 2.0; k_ATR=2.25;
      model_bollinger = new cmodel_bollinger;
      if(model_bollinger.Init(1829374,"Bollinger","EURUSD",PERIOD_H4,0,30,2.00,0,14,2.25))
      {
         Print("Model ", model_bollinger.Name(), "Period: ", model_bollinger.Period(),
         ". Symbol: ", model_bollinger.Symbol(), " successfully created");
         list_model.Add(model_bollinger);
      }
      //2.3 Symbol: GBPUSD M15; period: 18; deviation: 2.25; k_ATR=3.0;
      model_bollinger = new cmodel_bollinger;
      if(model_bollinger.Init(1829374,"Bollinger","GBPUSD",PERIOD_M15,0,18,2.25,0,14,3.00))
      {
         Print("Model ", model_bollinger.Name(), "Period: ", model_bollinger.Period(),
         ". Symbol: ", model_bollinger.Symbol(), " successfully created");
         list_model.Add(model_bollinger);
      }
      //2.4 Symbol: GBPUSD H1; period: 27; deviation: 2.25; k_ATR=3.75;
      model_bollinger = new cmodel_bollinger;
      if(model_bollinger.Init(1829374,"Bollinger","GBPUSD",PERIOD_H1,0,27,2.25,0,14,3.75))
      {
         Print("Model ", model_bollinger.Name(), "Period: ", model_bollinger.Period(),
         ". Symbol: ", model_bollinger.Symbol(), " successfully created");
         list_model.Add(model_bollinger);
      }
      //2.5 Symbol: USDCAD M15; period: 18; deviation: 2.5; k_ATR=2.00;
      model_bollinger = new cmodel_bollinger;
      if(model_bollinger.Init(1829374,"Bollinger","USDCAD",PERIOD_M15,0,18,2.50,0,14,2.00))
      {
         Print("Model ", model_bollinger.Name(), "Period: ", model_bollinger.Period(),
         ". Symbol: ", model_bollinger.Symbol(), " successfully created");
         list_model.Add(model_bollinger);
      }
      //2.6 Symbol: USDCAD M15; period: 21; deviation: 2.5; k_ATR=3.25;
      model_bollinger = new cmodel_bollinger;
      if(model_bollinger.Init(1829374,"Bollinger","USDCAD",PERIOD_H2,0,21,2.50,0,14,3.25))
      {
         Print("Model ", model_bollinger.Name(), "Period: ", model_bollinger.Period(),
         ". Symbol: ", model_bollinger.Symbol(), " successfully created");
         list_model.Add(model_bollinger);
      }
   }
//----------------------------------------------------------------------------------------------
}

Şimdi on iki modeli aynı anda test edelim:


Sonuçların sermayelendirilmesi

Ortaya çıkan grafik etkileyicidir. Ancak önemli olan sonuç değil, tüm modellerin aynı anda alım satım yapması, hepsinin kendi koruyucu stoplarını kullanması ve birbirinden bağımsız olmasıdır. Şimdi ortaya çıkan grafiği sermayelendirmeye çalışalım. Bunun için sermayelendirmenin standart fonksiyonlarını kullanacağız: sabit oranlı yöntem ve Ryan Jones tarafından önerilen yöntem.

Sözde optimal f, sabit oranlı yöntemin özel bir durumudur. Bu yöntemin temelinde her işleme hesabın belirli bir yüzdesine eşit olan bir zarar limiti verilmesi yatar. Koruyucu sermayelendirme stratejileri genellikle %2'lik bir zarar limiti uygular. Yani 10,000 dolarda pozisyon büyüklüğü, Zarar Durdur uygulandıktan sonra kayıp 200 doları geçmeyecek şekilde hesaplanır. Bununla birlikte, riskteki artıştaki nihai denge büyümesinin belirli bir fonksiyonu vardır. Bu fonksiyon çan benzeri bir şekle sahiptir. Yani ilk başta riskin artmasıyla birlikte toplam kârda bir artış olur. Ancak her işlem için bir risk eşiği vardır ve bundan sonra toplam kâr bakiyesi azalmaya başlar. Bu eşik, sözde optimal f'dir.

Bu makale, sermaye yönetimi konusuna yönelik hazırlanmamıştır; sabit oran yöntemini kullanmak için bilmemiz gereken tek şey koruyucu stopların seviyesi ve riske atabileceğimiz hesabın yüzdesidir. Ryan Jones formülü farklı şekilde yapılandırılmıştır. Çalışması için sabit koruyucu stopların kullanılması gerekli değildir. Önerilen modellerden ilki (MACD modeli) oldukça ilkel olduğundan ve koruyucu stopları olmadığından dolayı bu modelin sermayelendirmesi için bu yöntemi kullanacağız. Bollinger bantlarına dayanan model için sabit oran yöntemini kullanacağız.

Sermayelendirme formülünü kullanmaya başlamak için temel modelde yer alan m_delta değişkenini girmemiz gerekiyor.

Sabit oran formülünü kullanırken bu her işlem için risk yüzdesine eşit olmalıdır. Ryan Jones yöntemini kullanırken bu sözde delta artışına, yani daha yüksek bir pozisyon hacmi seviyesine ulaşmak için kazanılması gereken para miktarına eşittir.

Aşağıda sermayelendirme grafiği verilmiştir:


Görülebileceği gibi tüm modellerin kendi sermayelendirme formülleri vardır (sabit-oran yöntemi veya Ryan Jones yöntemi).

Verilen örnekte tüm modeller için aynı maksimum risk ve delta değerlerini kullandık. Ancak her birinde sermayelendirme için farklı parametreler seçebiliriz. Modellerde yapılacak ince ayarlamalar, bu makaledeki konuların kapsamına dâhil değildir.


Bekleyen emirlerle çalışma

Sunulan alım satım modelleri sözde bekleyen emirleri kullanmaz. Bunlar yalnızca belirli şartlar ve koşullar mevcut olduğunda gerçekleştirilen emirlerdir.

Normalde, bekleyen emirleri kullanan herhangi bir alım satım stratejisi piyasadaki emirlerin kullanımına göre ayarlanabilir. Bunun yerine, bekleyen emirlere daha güvenilir bir sistem çalışması için ihtiyaç duyulur çünkü robotun veya üzerinde çalıştığı ekipmanın arızalanması durumunda, bekleyen emirler hâlâ koruyucu stoplar uygular ya da tam tersi olarak önceden belirlenmiş fiyatlar doğrultusunda piyasaya girer.

Önerilen alım satım modeli bu durumda sunumlarının kontrol süreci çok daha karmaşık olmasına rağmen bekleyen alım satım emirleriyle çalışmanıza izin verir. Bu emirlerle çalışmak için CTableOrders sınıfının aşırı yüklemeli Add (COorderInfo & order_info, double stop_loss, double take_profit) yöntemini kullanırız. Bu durumda bu sınıfın m_type değişkeni bekleyen emirin uygun türünü içerecektir (örneğin ORDER_TYPE_BUY_STOP veya ORDER_TYPE_SELL_LIMIT)

Daha sonrasında, bekleyen emir verildiği zaman tetiklendiği anı “yakalamanız” gerekir, aksi takdirde geçerlilik süresi sona erer. Bu o kadar basit değildir çünkü sadece Alım Satım olayını kontrol etmek yeterli olmaz, aynı zamanda onu neyin tetiklediğini bilmek de gerekir.

MQL5'in dili gelişmektedir ve şu anda Alım Satım olayını açıklayacak özel bir hizmet yapısının buna dâhil edilmesi sorunu çözülmektedir. Ama şimdilik geçmiş emirlerin listesini görmemiz gerekiyor. Geçmiş emir tablosundaki bekleyen emir ile aynı fişe sahip bir emir bulunursa tabloya yansıtılması gereken bazı olaylar meydana gelir. 

Temel modelin kodu özel bir CModel:: ReplaceDelayedOrders yöntemini içerir Bu yöntem aşağıdaki algoritma ile çalışır. İlk olarak emir tablosunda aktif olan tüm emirler kontrol edilir. Bu emirleri fişi geçmiş (HistoryOrderGetTicket()) emirlerin fişi ile karşılaştırılır. Geçmiş emirin fişi emir tablosundaki bekleyen emir ile çakışıyor ancak emir durumu uygulanıyor şeklindeyse (ORDER_STATE_PARTIAL veyaORDER_STATE_FILLED) emir tablosundaki bekleyen emirlerin durumu da işlem için değiştirilir.

Ayrıca bu emir Zarar Durdur ve Kâr Et çalışmasını taklit eden bekleyen emirlerle bağlantılı değilse bu emir verilir ve emir fişleri uygun tablo değerlerine (TicketSL, TicketTP) girilir. Ve koruyucu stop ve kâr seviyelerini taklit eden bekleyen emirler m_sl ve m_tp değişkenleri yardımıyla önceden belirlenen fiyattan verilir. Yani bu fiyatların ReplaceDelayedOrders yöntemi çağrıldığı anda bilinmesi gerekir.

Yöntemin, gerekli bekleyen emirlerin Zarar Durdur ve Kâr Et türünde olduğu piyasaya bir emir verildiğinde durumla başa çıkacak şekilde uyarlanmış olması kayda değerdir.

Genel olarak, önerilen yöntemin çalışması önemsiz değildir ve kullanımı biraz bilgi gerektirir:

bool CModel::ReplaceDelayedOrders(void)
{
   if(m_symbol_info.TradeMode()==SYMBOL_TRADE_MODE_DISABLED)return(false);
   CTableOrders *t;
   int total_elements;
   int history_orders=HistoryOrdersTotal();
   ulong ticket;
   bool rez=false;
   long request;
   total_elements=ListTableOrders.Total();
   int try=0;
   if(total_elements==0)return(false);
   // View every order in the table
   for(int i=total_elements-1;i>=0;i--)
   {
      if(CheckPointer(ListTableOrders)==POINTER_INVALID)continue;
      t=ListTableOrders.GetNodeAtIndex(i);
      if(CheckPointer(t)==POINTER_INVALID)continue;
      switch(t.Type())
      {
         case ORDER_TYPE_BUY:
         case ORDER_TYPE_SELL:
            for(int b=0;i<history_orders;b++)
            {
               ticket=HistoryOrderGetTicket(b);
               // If the ticket of the historical order is equal to one of the tickets 
               // Stop Loss or Take Profit, then the order was blocked, and it needs to be
               // deleted from the table of orders
               if(ticket==t.TicketSL()||ticket==t.TicketTP())
               {
                  ListTableOrders.DeleteCurrent();
               }
            }
            // If the orders, imitating the Stop Loss and Take Profit are not found in the history,
            // then perhaps they are not yet set. Therefore, they need to be inputted,
            // using the process for pending orders below:
            // the cycle  keeps going, the exit 'break' does not exist!!!
         case ORDER_TYPE_BUY_LIMIT:
         case ORDER_TYPE_BUY_STOP:
         case ORDER_TYPE_BUY_STOP_LIMIT:
         case ORDER_TYPE_SELL_LIMIT:
         case ORDER_TYPE_SELL_STOP:
         case ORDER_TYPE_SELL_STOP_LIMIT:
            for(int b=0;i<history_orders;b++)
            {
               ticket=HistoryOrderGetTicket(b);
               // If the ticket of the historical order is equal to the ticket of the pending order 
               // then the pending order has worked and needs to be put out
               // the pending orders, imitating the work of Stop Loss and Take Profit.
               // It is also necessary to change the pending status of the order in the table
               // of orders for the executed (ORDER_TYPE_BUY или ORDER_TYPE_SELL)
               m_order_info.InfoInteger(ORDER_STATE,request);
               if(t.Ticket()==ticket&&
                  (request==ORDER_STATE_PARTIAL||request==ORDER_STATE_FILLED))
                  {
                  // Change the status order in the table of orders:
                  m_order_info.InfoInteger(ORDER_TYPE,request);
                  if(t.Type()!=request)t.Type(request);
                  //------------------------------------------------------------------
                  // Put out the pending orders, imitating Stop Loss an Take Profit:
                  // The level of pending orders, imitating Stop Loss and Take Profit
                  // should be determined earlier. It is also necessary to make sure that
                  // the current order is not already linked with the pending order, imitating Stop Loss
                  // and Take Profit:
                  if(t.StopLoss()!=0.0&&t.TicketSL()==0)
                    {
                     // Try to put out the pending order:
                     switch(t.Type())
                     {
                        case ORDER_TYPE_BUY:
                           // Make three attempts to put out the pending order
                           for(try=0;try<3;try++)
                           {
                              m_trade.SellStop(t.VolumeInitial(),t.StopLoss(),m_symbol,0.0,0.0,0,0,"take-profit for buy");
                              if(m_trade.ResultRetcode()==TRADE_RETCODE_PLACED||m_trade.ResultRetcode()==TRADE_RETCODE_DONE)
                              {
                                 t.TicketTP(m_trade.ResultDeal());
                                 break;
                              }
                           }
                        case ORDER_TYPE_SELL:
                           // Make three attempts to put up a pending order
                           for(try=0;try<3;try++)
                           {
                              m_trade.BuyStop(t.VolumeInitial(),t.StopLoss(),m_symbol,0.0,0.0,0,0,"take-profit for buy");
                              if(m_trade.ResultRetcode()==TRADE_RETCODE_PLACED||m_trade.ResultRetcode()==TRADE_RETCODE_DONE)
                              {
                                 t.TicketTP(m_trade.ResultDeal());
                                 break;
                              }
                           }
                     }
                  }
                  if(t.TakeProfit()!=0.0&&t.TicketTP()==0){
                     // Attempt to put out the pending order, imitating Take Profit:
                     switch(t.Type())
                     {
                        case ORDER_TYPE_BUY:
                           // Make three attempts to put out the pending order
                           for(try=0;try<3;try++)
                           {
                              m_trade.SellLimit(t.VolumeInitial(),t.StopLoss(),m_symbol,0.0,0.0,0,0,"take-profit for buy");
                              if(m_trade.ResultRetcode()==TRADE_RETCODE_PLACED||m_trade.ResultRetcode()==TRADE_RETCODE_DONE)
                              {
                                 t.TicketTP(m_trade.ResultDeal());
                                 break;
                              }
                           }
                           break;
                        case ORDER_TYPE_SELL:
                           // Make three attempts to put out the pending order
                           for(try=0;try<3;try++)
                           {
                              m_trade.BuyLimit(t.VolumeInitial(),t.StopLoss(),m_symbol,0.0,0.0,0,0,"take-profit for buy");
                              if(m_trade.ResultRetcode()==TRADE_RETCODE_PLACED||m_trade.ResultRetcode()==TRADE_RETCODE_DONE)
                              {
                                 t.TicketTP(m_trade.ResultDeal());
                                 break;
                              }
                           }
                     }
                  }
               }
            }
            break;
         
      }
   }
   return(true);
}

Bu yöntemi kullanarak, bekleyen emirler doğrultusunda rahatlıkla bir model oluşturabilirsiniz.


Sonuç

Ne yazık ki önerilen yaklaşımın tüm ince ayrıntılarını, zorluklarını ve faydalarını tek bir makalede ele almak imkânsızdır. Modellerin mevcut durumu hakkında gerekli tüm bilgileri saklamanıza, kaydetmenize ve veri dosyalarından almanıza izin veren bir yöntem olan verilerin serileştirilmesini ele almadık. Sentetik alış satış farklarının alım satımına dayalı alım satım modelleri bizim kapsamımızın dışında kalmıştır. Bunlar çok ilginç konular ve kesinlikle önerilen kavramlara yönelik etkili çözümlere sahipler.

Yapmayı başardığımız temel şey tamamen dinamik ve yönetilebilir bir veri yapısı geliştirmektir. Bağlı listeler kavramı bunları etkin bir şekilde yönetir, alım satım taktiklerini bağımsız ve bireysel olarak özelleştirilebilir bir hale getirir. Bu yaklaşımın bir diğer çok önemli avantajı da tamamen evrensel olmasıdır.

Örneğin, temelde iki alım satım EA'sı oluşturmak ve bunları aynı araca yerleştirmek yeterlidir. Her ikisi de otomatik olarak yalnızca kendi emirleriyle ve yalnızca kendi sermaye yönetimi sistemleriyle çalışacaktır. Böylece aşağı doğru bir uyumluluk desteği oluşur. Tek bir EA'da aynı anda yapılabilenler birkaç robota dağıtılabilir. Bu özellik net pozisyonlarda çalışırken son derece önemlidir.

Açıklanan model sadece bir teori değildir. Gelişmiş bir yardımcı fonksiyonlar aygıtını, sermaye yönetimine ve marjinal gereksinimleri kontrol etmeye yönelik fonksiyonları içerir. Emir gönderme sistemi, etkileri gerçek alım satımda sıklıkla görülen sözde tekliflere ve kaymalara karşı dirençlidir.

Alım satım motoru, pozisyonun maksimum boyutunu ve maksimum işlem hacmini belirler. Özel bir algoritma da alım satım taleplerini her biri ayrı ayrı işlenen birkaç bağımsız işleme ayırır. Ayrıca sunulan modelOtomatik Alım Satım Şampiyonası 2010’da kendini kanıtlamıştır: tüm işlemler doğru ve alım satım planına uygun olarak gerçekleştirilir, Şampiyonalarda sunulan alım satım modelleri farklı düzeydeki risklerle ve farklı para yönetimi sistemleriyle yönetilir.

Sunulan yaklaşım, Şampiyonalara katılımın yanı sıra çeşitli araçlar ve zaman dilimleri üzerinde çeşitli alım satım taktiklerine yönelik paralel operasyon için neredeyse eksiksiz bir çözümdür. Bu yaklaşıma aşina olmanın tek zorluğu yaklaşımın karmaşık olmasıdır. 

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

Ekli dosyalar |
files-en.zip (18.6 KB)
Mikro, Orta ve Ana Eğilimlerin Göstergeleri Mikro, Orta ve Ana Eğilimlerin Göstergeleri
Bu makalenin amacı, James Hyerczyk'in “Pattern, Price & Time: Use Gann Theory in Trading Systems” (Model, Fiyat ve Zaman: Alım Satım Sistemlerinde Gann Teorisini Kullanmak) adlı kitabında göstergelere ve Uzman Danışmanlara yönelik verilen bazı fikirler doğrultusunda alım satım otomasyonunun olanaklarını ve analizini araştırmaktır. Kapsamlı bir makale olmamakla birlikte burada sadece Gann teorisinin ilk bölümünü, yani Modeli araştırıyoruz.
Çizim Kanalları - İçeriden ve Dışarıdan Görünüm Çizim Kanalları - İçeriden ve Dışarıdan Görünüm
Kanalların piyasa analizi ve hareketli ortalamalardan sonra alım satım kararları almak için en popüler araç olduğunu söylersem sanırım abartmış olmam. Kanalları ve bileşenlerini kullanan alım satım stratejileri yığınına derinlemesine dalmadan, istemci terminalinin ekranında üç uçdeğer tarafından belirlenen bir kanal çizen bir göstergenin matematiksel temelini ve pratik uygulamasını açıklayacağız.
Grafiklerin Analizine Ekonometrik Yaklaşım Grafiklerin Analizine Ekonometrik Yaklaşım
Bu makale ekonometrik analiz yöntemlerini, otokorelasyon analizini ve özellikle koşullu varyans analizini açıklamaktadır. Burada açıklanan yaklaşımın faydası nedir? Doğrusal olmayan GARCH modellerinin kullanımı, analiz edilen serilerin matematiksel açıdan resmi olarak temsil edilmesine ve belirli sayıda adım için bir tahmin oluşturulmasına olanak tanır.
MetaTrader 5’te Paralel Hesaplamalar MetaTrader 5’te Paralel Hesaplamalar
İnsanlık tarihi boyunca zaman büyük bir değer olmuştur ve bunu gereksiz şekilde israf etmemeye çalışıyoruz. Bu makale, bilgisayarınız çok çekirdekli bir işlemciye sahipse Uzman Danışmanınızın (EA) çalışmasını nasıl hızlandıracağınızı açıklayacaktır. Ayrıca, önerilen yöntemin uygulaması MQL5 dışında başka hiçbir dilin bilinmesini gerektirmez.