English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Italiano
MetaTrader 5’te Paralel Hesaplamalar

MetaTrader 5’te Paralel Hesaplamalar

MetaTrader 5Örnekler | 16 Aralık 2021, 14:28
141 0
ds2
ds2


İşlemci paralelliğine giriş

Neredeyse tüm modern bilgisayarlar, birden çok işlemci çekirdeğinin varlığı nedeniyle, birçok görevi eş zamanlı olarak gerçekleştirebilmektedir. Bunların sayısı her geçen yıl artmaktadır - 2, 3, 4, 6 çekirdek... Intel yakın zamanda çalışan, deneysel bir 80 çekirdekli işlemciyi (evet, yazım hatası yok - seksen çekirdek - ne yazık ki, bu bilgisayar, işlemci sadece teknolojinin potansiyel kapasitesini çalışma amacıyla üretildiği için, mağazalarda görünmeyecek) tanıttı.

Tüm bilgisayar kullanıcıları (ve hatta tüm acemi programcılar) bunun nasıl çalıştığını anlamaz. Bu nedenle, biri kesinlikle şu soruyu soracaktır: Daha önce bile (tek çekirdekle) bilgisayar çok sayıda programı eş zamanlı çalıştırabilirken ve tümü çalışırken neden bu kadar çok çekirdekli işlemciye ihtiyacımız var? Güvenin, bu böyle değildir. Aşağıdaki diyagrama bakalım.

Şekil 1. Uygulamaların paralel yürütülmesi

Şekil 1. Uygulamaların paralel yürütülmesi

Durum A diyagram üzerinde tek çekirdekli bir işlemcide tek bir program çalıştırıldığında ne olduğunu göstermektedir. İşlemci tüm zamanını bunun uygulamasına ayırır ve program zaman (T) içerisinde belirli miktarda çalışma gerçekleştirir.

Durum B - 2 program başlatılır. Fakat işlemci, fiziksel olarak, zamanda herhangi bir noktada, çekirdeklerinden biri sadece bir komutu yürütebilecek şekilde düzenlenmiştir, bu nedenle sürekli olarak iki program arasında geçiş yapar: Önce birincinin birazını, ardından ikincinin birazını yürütecektir vb. Bu saniyede birçok kez, çok hızlı şekilde gerçekleşir, dolayısıyla işlemci her iki programı eş zamanlı yürütüyor gibi görünür. Buna karşın gerçekte, bunların yürütülmesi, her bir programın işlemcide ayrı şekilde yürütülmesinden iki kat daha uzun sürer.

Durum C, bir işlemcideki çekirdeklerin sayısı çalışan programların sayısına karşılık gelirse bu sorunun etkili bir şekilde çözüldüğünü gösterir. Her bir program, hizmetinde ayrı bir çekirdeğe sahiptir ve bunun yürütme hızı durum A’daki gibi artar.

Durum D çok sayıda kullanıcının ortak yanılgısına bir yanıttır. Bunlar, bir program çok çekirdekli bir işlemcide çalışıyorsa bunun birkaç kat daha hızlı yürütüldüğüne inanmaktadırlar. Genel olarak, bu doğru olamaz çünkü işlemci programı bağımsız olarak ayrı parçalara bölemez ve bunların tümünü eş zamanlı olarak yürütemez.

Örneğin, program öncelikle bir parola isterse ve ardından doğrulamaları gerçekleştirilirse, aynı anda bir çekirdek üzerinde parola isteği ve bir diğerinde doğrulama gerçekleştirilmesi kabul edilemez. Başlangıçta, parola henüz girilmediği için doğrulama hiçbir zaman başarılı olamayacaktır.

İşlemci programcının uyguladığım tasarımların tümünü veya programın tüm çalışma mantığını bilmez ve bu nedenle, çekirdekler arasında programı bağımsız olarak ayıramaz. Dolayısıyla tek bir programı çok çekirdekli bir sistemde çalıştırırsak bu sadece bir çekirdeği kullanacaktır ve tek çekirdekli bir işlemcide çalıştırılıyormuş gibi aynı hızda yürütülecektir.

Durum E programın tüm çekirdeklerini kullanması ve daha hızlı yürütülmesi için yapılması gerekenleri açıklar. Programcı programın mantığını bildiği için, geliştirilmesi sırasında bir şekilde aynı anda yürütülebilen bu program parçalarını işaretlemelidir. Yürütülmesi sırasında program bu bilgileri işlemciye iletecektir ve işlemci ardından programı gerekli sayıda çekirdeğe tahsis edecektir.


MetaTrader’da Paralellik

Önceki bölümde, tüm CPU çekirdeklerini kullanmak ve programın yürütülmesini hızlandırmak için yapılması gerekenleri belirledik: Bir şekilde programın paralelleştirilebilir kodunu ayrı dişlere tahsis etmemiz gerekir. Çok sayıda programlama dilinde, bunun için özel sınıflar veya operatörler vardır. Fakat MQL5 dilinde bu tür bir yerleşik araç yoktur. O halde ne yapabiliriz?

Bu sorunu çözmenin iki yolu vardır:

1. DLL kullanın 2. MetaTrader’ın dil dışı kaynaklarını kullanın
Paralelleştirme için yerleşik bir araca sahip bir dilde bir DLL oluşturarak MQL5-EA’da da paralelleştirme elde edeceğiz. MetaTrader geliştiricilerinden gelen bilgilere göre, istemci terminalinin mimarisi çok zincirlidir. Dolayısıyla, belirli koşullarda, gelen piyasa verileri ayrı zincirlerde işlenir. Bu yüzden, programımızın kodunu bir dizi EA veya göstergeye ayırmanın bir yolunu bulabilirsek MetaTrader yürütülmesi için bir dizi CPU çekirdeğini kullanabilecektir.


Birinci yöntemi bu makalede tartışmayacağız. DLL’de istediğimiz her şeyi uygulayabileceğimiz açıktır. Sadece standart MetaTrader araçlarını içerecek ve MQL5 dışında hiçbir dil kullanımını gerektirmeyecek bir çözüm bulmaya çalışacağız.

Ve şimdi, ikinci yöntem hakkında daha fazlası. Birden fazla çekirdeğin MetaTrader’da tam olarak nasıl destekleneceğini bulmak için bir dizi deney gerçekleştirmemiz gerekecektir. Bunu yapmak için, CPU’yu ağır şekilde yüklenen, devam eden herhangi bir işi gerçekleştirecek bir test göstergesi ve bir test EA’sı oluşturalım.

Aşağıdaki i-flood göstergesini:

//+------------------------------------------------------------------+
//|                                                      i-flood.mq5 |
//+------------------------------------------------------------------+
#property indicator_chart_window

input string id;
//+------------------------------------------------------------------+
void OnInit()
  {
   Print(id,": OnInit");
  }
//+------------------------------------------------------------------+
int OnCalculate(const int rt,const int pc,const int b,const double &p[])
  {
   Print(id,": OnCalculate Begin");
   
   for (int i=0; i<1e9; i++)
     for (int j=0; j<1e1; j++);
     
   Print(id,": OnCalculate End");
   return(0);   
  }
//+------------------------------------------------------------------+

Ve buna benzer e-flood EA’sını yazdım:

//+------------------------------------------------------------------+
//|                                                      e-flood.mq5 |
//+------------------------------------------------------------------+
input string id;
//+------------------------------------------------------------------+
void OnInit()
  {
   Print(id,": OnInit");
  }
//+------------------------------------------------------------------+
void OnTick()
  {
   Print(id,": OnTick Begin");
   
   for (int i=0; i<1e9; i++)
     for (int j=0; j<1e1; j++);
     
   Print(id,": OnTick End");
  }
//+------------------------------------------------------------------+

Ayrıca, grafik pencerelerinin çeşitli kombinasyonlarını (bir grafik, aynı sembole sahip iki grafik, farklı sembollere sahip iki grafik) açarak ve bunların üzerine bu göstergenin veya EA’nın bir veya iki kopyasını yerleştirerek terminalin CPU çekirdeklerini nasıl kullandığını gözlemleyebiliriz.

Bu göstergeler ve EA ayrıca günlüğe mesajlara gönderir ve bunların görünüş sırasını gözlemlemek ilginçtir. Bunları kendiniz oluşturamayacağınız için bu günlükleri sağlamayacağım, fakat bu makalede, terminal tarafından kaç tane çekirdeğin hangi grafik kombinasyonlarında kullanıldığını bulmakla ilgileniyoruz.

Windows “Görev Yöneticisi” aracılığıyla çalışan çekirdeklerin sayısını ölçebiliriz.

Şekil 2. CPU Çekirdekleri

Şekil 2. CPU Çekirdekleri


Tüm ölçümlerin sonuçları aşağıdaki tabloda toplanır:


kombinasyon
 Terminalin içerikleri
CPU kullanımı
1
Bir grafik üzerinde 2 gösterge 1 çekirdek
2
Farklı grafikler üzerinde 2 gösterge, aynı çift 1 çekirdek
3
Farklı grafikler üzerinde 2 gösterge, farklı çiftler 2 çekirdek
4
Aynı grafik üzerinde 2 EA - bu durum imkansızdır -
5
Farklı grafikler üzerinde 2 EA, aynı çift 2 çekirdek
6
Farklı grafikler üzerinde 2 EA, farklı çiftler 2 çekirdek
7
Farklı çiftler üzerinde 2 gösterge, EA tarafından oluşturulmuştur 2 çekirdek


7. kombinasyon çok sayıda alım satım stratejisinde kullanılan bir gösterge oluşturmak için ortak bir yoldur.

Tek özel özellik olarak, kombinasyonlar 1 ve 2 aynı çift üzerinde gösterge koymanın mantıksız olduğunu gösterdiği için iki farklı para birimi çifti üzerinde iki gösterge oluşturdum. Bu kombinasyon için i-flood’un iki kopyasını üreten EA e-flood-başlatıcıyı kullandım:

//+------------------------------------------------------------------+
//|                                              e-flood-starter.mq5 |
//+------------------------------------------------------------------+
void OnInit() 
  {
   string s="EURUSD";
   for(int i=1; i<=2; i++) 
     {
      Print("Indicator is created, handle=",
            iCustom(s,_Period,"i-flood",IntegerToString(i)));
      s="GBPUSD";
     }
  }
//+------------------------------------------------------------------+

Dolayısıyla, çekirdeklerin tüm hesaplamaları gerçekleştirildi ve şimdi MetaTrader’ın hangi kombinasyonlar için birden çok çekirdek kullandığını biliyoruz. Sonra, paralel hesaplamaların fikirlerini uygulamak için bu bilgiyi uygulamaya çalışacağız.


Bir paralel sistem tasarlıyoruz

Paralel sistem için alım satım terminaline ilişkin olarak, birlikte bazı ortak görevleri birlikte yürüten, örneğin, alım satım yapan veya grafik üzerine çizen bir grup gösterge veya EA’yı (veya her ikisinin bir karışımını) kastediyoruz. Bu grubun bir büyük gösterge olarak veya bir büyük EA olarak çalıştığını kastediyoruz. Fakat aynı zamanda hesaplama yükünü mevcut işlemci çekirdekleri arasında dağıtır.

Bu tür bir sistem iki tip yazılım bileşeninden oluşur:

  • CM - hesaplama modülü. Bunların sayısı 2’den işlemci çekirdeklerinin sayısına kadar olabilir. Paralelleştirilmesi gereken kodun tamamı CM’ye yerleştirilir. Önceki bölümde öğrendiğimiz gibi, CM bir gösterge olarak ve ayrıca bir EA olarak uygulanabilir - herhangi bir uygulama şekli için tüm işlemci çekirdeklerini kullanan bir kombinasyon vardır;
  • MM - ana modül. Sistemin temel fonksiyonlarını gerçekleştirir. Bu yüzden, MM bir göstergeyse, grafik üzerinde çizimi gerçekleştirir ve MM bir Ea ise, alım satım fonksiyonlarını gerçekleştirir. MM ayrıca tüm CM’leri yönetir.

Örneğin, bir MM EA ve bir 2 çekirdekli işlemci için, sistemin çalışma şeması aşağıdaki gibi olacaktır:

Şekil 3. 2 CPU çekirdekli sistemin şeması.

Şekil 3. 2 CPU çekirdekli sistemin şeması.

Bizim geliştirdiğimiz sistemin belirli bir andaki prosedürde gerekli şeyi çağırabileceğiniz geleneksel bir program olmadığı anlaşılmalıdır. MM ve CM, EA’lar veya göstergelerdir, diğer bir deyişle, bunlar bağımsız ve tek başına programlardır. Bunlar arasında doğrudan hiçbir bağlantı yoktur, bunlar bağımsız olarak çalışır ve doğrudan birbirleriyle iletişim kuramazlar.

Bu programlardan herhangi birinin yürütülmesi sadece herhangi bir etkinlik terminalinde görünme (örneğin, tekliflerin gelişi veya bir zamanlayıcı tiki) ile başlar. Ve etkinlikler arasında, bu programların birbirlerine iletmek istediği tüm veriler dışında bir yerde, herkesin erişebileceği bir yerde (buna “Veri Değişim Tamponu” diyelim) saklanmalıdır. Dolayısıyla yukarıdaki şema terminalde aşağıdaki gibi uygulanır:

Şekil 4. Uygulama ayrıntıları

Şekil 4. Uygulama ayrıntıları

Bu sistemin uygulanması için aşağıdaki soruları yanıtlamamız gerekiyor:

  • önceki bölümde bulunan çoklu çekirdek kombinasyonlarından hangisini sistemimizde kullanacağız?
  • sistem birkaç EA veya göstergeden oluştuğu için, bunlar arasında (iki taraflı) veri değişimini nasıl daha iyi düzenleyebiliriz (yani, pano verileri fiziksel olarak nasıl olacak)?
  • bunların eylemlerinin koordinasyonunu ve senkronizasyonunu nasıl düzenleyebiliriz?

Bu soruların her biri için birden fazla cevap vardır ve bunların tümü aşağıda sağlanmıştır. Uygulamada, spesifik seçenekler özel duruma göre seçilmelidir. Bunu sonraki bölümde yapacağız. Bu sırada, tüm olası cevapları göz önünde bulunduralım.

Kombinasyon

Terminalde ek pencere açma ve bunları EA’lar veya göstergeler üzerine yerleştirme gereksinimi olmadığı için Kombinasyon 7 düzenli pratik kullanım için en uygun olandır (tüm diğer kombinasyonlar önceki bölümde listelenmiştir). Tüm sistem tek bir pencerede bulunur ve tüm göstergeler (CM-1 ve CM-2) EA (MM) tarafından otomatik olarak oluşturulur. Ekstra pencerelerin ve manuel eylemlerin olmayışı yatırımcı için kafa karışıklığını ve bu tür karışıklık hatalarını ortadan kaldırmıştır.

Bazı alım satım stratejilerinde, diğer kombinasyonlar daha faydalı olabilir. Örneğin, bunlardan herhangi biri temelinde, “istemci-sunucu” ilkesinde çalışan tüm yazılım sistemlerini oluşturabiliriz. Burada aynı CM’ler birkaç MM için ortak olacaktır. Bu tür ortak CM’ler sadece bir “bilgisayarın” ikincil görevini gerçekleştirmekle kalmaz, fakat ayrıca tüm strateji bilgilerinin bir tür birleştirilmiş halini veya hatta bunların kolektif çalışmasının koordinatörlerini depolayan bir “sunucu” olabilir. Bir CM sunucusu, örneğin, istenen genel risk seviyesini korurken bazı strateji portföyünde ve para birimi çiftlerinde araçların dağılımını merkezi olarak kontrol edebilir.

Veri değişimi

Bilgileri 3 yoldan herhangi birini kullanarak MM ve CM arasında iletebiliriz:

  1. terminalin global değişkenleri;
  2. dosyalar;
  3. Gösterge tamponları.

Küçük sayıda nümerik değişken aktarılırken 1. yöntem optimumdur. Metin verilerini aktarma gereksinimi varsa global değişkenler sadece double tipe sahip olduğu için bunların bir şekilde sayılar halinde kodlanması gerekecektir.

Herhangi bir şey dosyalara yazılabildiği için alternatif 2. yöntemdir. Ve bu büyük miktarlarda veri aktarmanız gereken durumlarda uygun (ve muhtemelen 1.’den daha hızlı) bir yöntemdir.

Üçüncü yöntem, MM ve CM gösterge olduğunda uygundur. Sadece double tipli veriler aktarılabilir, fakat büyük nümerik dizileri aktarmak için daha uygundur. Fakat bir dezavantaj vardır: yeni bir çubuk oluşumu sırasında, tamponlardaki elemanların numaralandırılması kaydırılır. MM ve CM farklı para birimi çiftlerinde olduğu için yeni çubuklar eş zamanlı olarak görünmeyecektir. Bu kaymaları hesaba katmalıyız.

Senkronizasyon

Terminal MM için bir fiyat aldığında ve bunu işlemeye başladığında kontrolü hemen CM’ye aktaramaz. Sadece (yukarıdaki diyagramda gösterildiği gibi) bir görev (bunu global değişkenlere, bir dosyaya veya bir gösterge tamponuna yerleştirerek) oluşturabilir ve CM’nin yürütülmesini bekleyebilir. Tüm CM’ler farklı para birimi çiftlerinde bulunduğu için bekleme biraz zaman alabilir. Bunun nedeni bir çift fiyat alabilirken diğerinin henüz almamış olmasıdır ve bu, birkaç saniye veya hatta dakika içinde gelecektir (örneğin, bu likit olmayan çiftlerde geceleyin meydana gelebilir).

Dolayısıyla, CM’nin kontrolü alması için fiyatlara bağlı olan OnTick ve OnCalculate etkinliklerini kullanmamalıyız. Bunların yerine, belirtilen sıklıkta (örneğin, 1 saniye) yürütülen OnTimer etkinliğini (MQL5 yeniliği) kullanmamız gerekir. Bu durumda, sistemdeki gecikmeler son derece sınırlandırılacaktır.

Ayrıca, OnTimer yerine, periyodik işleme tekniğini kullanabiliriz: yani OnInit veya OnCalculate içinde CM için sonsuz bir döngü koyabiliriz. Bu yinelemelerin her biri bir zamanlayıcının tikine benzer.

Uyarı. Bazı deneyler gerçekleştirdim ve Kombinasyon 7’yi kullanırken, zamanlayıcıların başarıyla oluşturulmasına rağmen (bir nedenle) OnTimer etkinliğinin göstergelerde çalışmadığını buldum.

OnInit ve OnCalculate içinde sonsuz döngülerde dikkatli olmalısınız: Bir CM göstergesi MM-EA ile aynı para birimi çiftinde bulunsa bile fiyat grafik üzerinde hareketi durdurur ve EA çalışmayı durdurur (OnTick etkinliklerini oluşturmaya son verir). Terminalin geliştiricileri bu davranışın nedenlerini açıklamıştır.

Geliştiricilerden: Komut dosyaları ve EA’lar kendi ayrı zincirlerinde çalışırken tek bir sembol üzerindeki tüm göstergeler aynı zincirde çalışır. Göstergelerle aynı akışta, bu sembol üzerindeki diğer tüm eylemler de ardışık olarak yürütülür: tiklerin işlenmesi, geçmişin senkronizasyonu ve göstergelerin hesaplanması. Bu yüzden gösterge bir sonsuz eylem gerçekleştirirse sembolü için diğer tüm etkinlikler hiçbir zaman yürütülmez.

Program Yürütme Not
Komut dosyası Kendi zincirinde, komut dosyaları kadar çok sayıda yürütme zinciri vardır Bir periyodik işleme komut dosyası diğer programların çalışmasını bozmaz
Uzman Danışman (EA) Kendi zincirinde, EA’lar kadar çok sayıda yürütme zinciri vardır Bir periyodik işleme komut dosyası diğer programların çalışmasını bozmaz
Gösterge Bir sembol üzerinde tüm göstergeler için bir yürütme zinciri. Göstergeleri olan semboller kadar çok sayıda yürütme zinciri vardır Bir göstergedeki sonsuz bir döngü bu sembol üzerindeki diğer tüm göstergelerin çalışmasını durduracaktır


Bir test Uzman Danışmanı (EA) oluşturma

Paralelleştirilmesi mantıklı olan bir alım satım stratejisi ve bunun için uygun bir algoritma seçelim.

Örneğin, bu basit bir strateji olabilir: sırayı son çubukların N'sinden derlemek ve geçmişte buna en benzer diziyi bulmak. Fiyatın geçmişte nereye kaydığını bilerek ilgili sözleşmeyi açarız.

Sıranın uzunluğu görece kısaysa bu strateji MetaTrader 5’te çok hızlı - saniyeler içinde - çalışacaktır. Büyük bir uzunluk alırsak - örneğin, son 24 saat için M1 zaman diliminin tüm çubukları (1440 çubuk olacaktır) - ve bir yıl öncesine kadar geçmişte geri arama yaparsak (yaklaşık 375.000 çubuk), bu önemli miktarda zaman gerektirecektir. Yine de, bu arama kolayca paralelleştirilebilir: Geçmişi mevcut işlemci çekirdeklerinin sayısı üzerinde eşit parçalara bölmek ve her bir çekirdeği spesifik bir konumu aramaya atamak yeterlidir.

Paralel sistemin parametreleri aşağıdaki gibi olacaktır:

  • MM - modelli alım satım stratejisini uygular;
  • paralel hesaplama Ea’dan otomatik olarak (yani, Kombinasyon 7 kullanılarak) oluşturulan CM göstergelerinde yapılır;
  • CM göstergelerindeki hesaplama kodu OnInit içinde sonsuz bir döngüye yerleştirilir;
  • MM-EA ve CM göstergeleri arasındaki veri değişimi - terminalin global değişkenleri yoluyla yapılır.

Geliştirme ve sonrasında kullanım kolaylığı açısından, EA’yı, ayarlara bağlı olarak, bir paralel (göstergelerdeki hesaplamalar ile) ve bir olağan (yani göstergeler kullanılmadan) EA olarak çalışabilecek şekilde oluşturacağız. Elde edilen e-MultiThread Uzman Danışmanın (EA) kodu:

//+------------------------------------------------------------------+
//|                                                e-MultiThread.mq5 |
//+------------------------------------------------------------------+
input int Threads=1; // How many cores should be used
input int MagicNumber=0;

// Strategy parameters
input int PatternLen  = 1440;   // The length of the sequence to analyze (pattern)
input int PrognozeLen = 60;     // Forecast length (bars)
input int HistoryLen  = 375000; // History length to search

input double Lots=0.1;
//+------------------------------------------------------------------+
class IndData
  {
public:
   int               ts,te;
   datetime          start_time;
   double            prognoze,rating;
  };

IndData Calc[];
double CurPattern[];
double Prognoze;
int  HistPatternBarStart;
int  ExistsPrognozeLen;
uint TicksStart,TicksEnd;
//+------------------------------------------------------------------+
#include <ThreadCalc.mqh>
#include <Trade\Trade.mqh>
//+------------------------------------------------------------------+
int OnInit()
  {

   double rates[];

//--- Make sure there is enough history
   int HistNeed=HistoryLen+Threads+PatternLen+PatternLen+PrognozeLen-1;
   if(TerminalInfoInteger(TERMINAL_MAXBARS)<HistNeed)
     {    
      Print("Change the terminal setting \"Max. bars in chart\" to the value, not lesser than ",
            HistNeed," and restart the terminal");
      return(1);      
     }
   while(Bars(_Symbol,_Period)<HistNeed)
     {      
      Print("Insufficient history length (",Bars(_Symbol,_Period),") in the terminal, upload...");
      CopyClose(_Symbol,_Period,0,HistNeed,rates);
     }
   Print("History length in the terminal: ",Bars(_Symbol,_Period));

//--- For a multi-core mode create computational indicators
   if(Threads>1)
     {
      GlobalVarPrefix="MultiThread_"+IntegerToString(MagicNumber)+"_";
      GlobalVariablesDeleteAll(GlobalVarPrefix);

      ArrayResize(Calc,Threads);

      // Length of history for each core
      int HistPartLen=MathCeil(HistoryLen/Threads);
      // Including the boundary sequences
      int HistPartLenPlus=HistPartLen+PatternLen+PrognozeLen-1;

      string s;
      int snum=0;
      // Create all computational indicators
      for(int t=0; t<Threads; t++)
        {      
         // For each indicator - its own currency pair,
         // it should not be the same as for the EA
         do
            s=SymbolName(snum++,false);
         while(s==_Symbol);

         int handle=iCustom(s,_Period,"i-Thread",
                            GlobalVarPrefix,t,_Symbol,PatternLen,
                            PatternLen+t*HistPartLen,HistPartLenPlus);

         if(handle==INVALID_HANDLE) return(1);
         Print("Indicator created, pair ",s,", handle ",handle);
        }
     }

   return(0);
  }
//+------------------------------------------------------------------+
void OnTick()
  {
   TicksStart=GetTickCount();

   // Fill in the sequence with the last bars
   while(CopyClose(_Symbol,_Period,0,PatternLen,CurPattern)<PatternLen) Sleep(1000);

   // If there is an open position, measure its "age"
   // and modify the forecast range for the remaining 
   // planned life time of the deal
   CalcPrognozeLen();

   // Find the most similar sequence in the history
   // and the forecast of the movement of its price on its basis
   FindHistoryPrognoze();

   // Perform the necessary trade actions
   Trade();

   TicksEnd=GetTickCount();
   // Debugging information in
   PrintReport();
  }
//+------------------------------------------------------------------+
void FindHistoryPrognoze()
  {
   Prognoze=0;
   double MaxRating;

   if(Threads>1)
     {
      //--------------------------------------
      // USE COMPUTATIONAL INDICATORS
      //--------------------------------------
      // Look through all of the computational indicators 
      for(int t=0; t<Threads; t++)
        {
         // Send the parameters of the computational task
         SetParam(t,"PrognozeLen",ExistsPrognozeLen);
         // "Begin computations" signal 
         SetParam(t,"Query");
        }

      for(int t=0; t<Threads; t++)
        {
         // Wait for results
         while(!ParamExists(t,"Answer"))
            Sleep(100);
         DelParam(t,"Answer");

         // Obtain results
         double progn        = GetParam(t, "Prognoze");
         double rating       = GetParam(t, "Rating");
         datetime time[];
         int start=GetParam(t,"PatternStart");
         CopyTime(_Symbol,_Period,start,1,time);
         Calc [t].prognoze   = progn;
         Calc [t].rating     = rating;
         Calc [t].start_time = time[0];
         Calc [t].ts         = GetParam(t, "TS");
         Calc [t].te         = GetParam(t, "TE");

         // Select the best result
         if((t==0) || (rating>MaxRating))
           {
            MaxRating = rating;
            Prognoze  = progn;
           }
        }
     }
   else
     {
      //----------------------------
      // INDICATORS ARE NOT USED
      //----------------------------
      // Calculate everything in the EA, into one stream
      FindPrognoze(_Symbol,CurPattern,0,HistoryLen,ExistsPrognozeLen,
                   Prognoze,MaxRating,HistPatternBarStart);
     }
  }
//+------------------------------------------------------------------+
void CalcPrognozeLen()
  {
   ExistsPrognozeLen=PrognozeLen;

   // If there is an opened position, determine 
   // how many bars have passed since its opening
   if(PositionSelect(_Symbol))
     {
      datetime postime=PositionGetInteger(POSITION_TIME);
      datetime curtime,time[];
      CopyTime(_Symbol,_Period,0,1,time);
      curtime=time[0];
      CopyTime(_Symbol,_Period,curtime,postime,time);
      int poslen=ArraySize(time);
      if(poslen<PrognozeLen)
         ExistsPrognozeLen=PrognozeLen-poslen;
      else
         ExistsPrognozeLen=0;
     }
  }
//+------------------------------------------------------------------+
void Trade()
  {

   // Close the open position, if it is against the forecast
   if(PositionSelect(_Symbol))
     {
      long type=PositionGetInteger(POSITION_TYPE);
      bool close=false;
      if((type == POSITION_TYPE_BUY)  && (Prognoze <= 0)) close = true;
      if((type == POSITION_TYPE_SELL) && (Prognoze >= 0)) close = true;
      if(close)
        {
         CTrade trade;
         trade.PositionClose(_Symbol);
        }
     }

   // If there are no position, open one according to the forecast
   if((Prognoze!=0) && (!PositionSelect(_Symbol)))
     {
      CTrade trade;
      if(Prognoze > 0) trade.Buy (Lots);
      if(Prognoze < 0) trade.Sell(Lots);
     }
  }
//+------------------------------------------------------------------+
void PrintReport()
  {
   Print("------------");
   Print("EA: started ",TicksStart,
         ", finished ",TicksEnd,
         ", duration (ms) ",TicksEnd-TicksStart);
   Print("EA: Forecast on ",ExistsPrognozeLen," bars");

   if(Threads>1)
     {
      for(int t=0; t<Threads; t++)
        {
         Print("Indicator ",t+1,
               ": Forecast ", Calc[t].prognoze,
               ", Rating ", Calc[t].rating,
               ", sequence from ",TimeToString(Calc[t].start_time)," in the past");
         Print("Indicator ",t+1,
               ": started ",  Calc[t].ts,
               ", finished ",   Calc[t].te,
               ", duration (ms) ",Calc[t].te-Calc[t].ts);
        }
     }
   else
     {
      Print("Indicators were not used");
      datetime time[];
      CopyTime(_Symbol,_Period,HistPatternBarStart,1,time);
      Print("EA: sequence from ",TimeToString(time[0])," in the past");
     }

   Print("EA: Forecast ",Prognoze);
   Print("------------");
  }
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   // Send the "finish" command to the indicators
   if(Threads>1)
      for(int t=0; t<Threads; t++)
         SetParam(t,"End");
  }
//+------------------------------------------------------------------+

Uzman Danışman (EA) tarafından kullanılan i-Thread hesaplama göstergesinin kodu:

//+------------------------------------------------------------------+
//|                                                     i-Thread.mq5 |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1

//--- input parameters
input string VarPrefix;  // Prefix for global variables (analog to MagicNumber)
input int    ThreadNum;  // Core number (so that indicators on different cores could
                        // differentiate their tasks from the tasks of the "neighboring" cores)
input string DataSymbol; // On what pair is the MM-EA working
input int    PatternLen; // Length of the sequence for analysis
input int    BarStart;   // From which bar in the history the search for a similar sequence began
input int    BarCount;   // How many bars of the history to perform a search on

//--- indicator buffers
double Buffer[];
//---
double CurPattern[];

//+------------------------------------------------------------------+
#include <ThreadCalc.mqh>
//+------------------------------------------------------------------+
void OnInit()
  {
   SetIndexBuffer(0,Buffer,INDICATOR_DATA);

   GlobalVarPrefix=VarPrefix;

   // Infinite loop - so that the indicator always "listening", 
   // for new commands from the EA
   while(true)
     {
      // Finish the work of the indicator, if there is a command to finish
      if(ParamExists(ThreadNum,"End"))
         break;

      // Wait for the signal to begin calculations
      if(!ParamExists(ThreadNum,"Query"))
        {
         Sleep(100);
         continue;
        }
      DelParam(ThreadNum,"Query");

      uint TicksStart=GetTickCount();

      // Obtain the parameters of the task
      int PrognozeLen=GetParam(ThreadNum,"PrognozeLen");

      // Fill the sequence from the last bars
      while(CopyClose(DataSymbol,_Period,0,PatternLen,CurPattern)
            <PatternLen) Sleep(1000);

      // Perform calculations
      int HistPatternBarStart;
      double Prognoze,Rating;
      FindPrognoze(DataSymbol,CurPattern,BarStart,BarCount,PrognozeLen,
                   Prognoze,Rating,HistPatternBarStart);

      // Send the results of calculations
      SetParam(ThreadNum,"Prognoze",Prognoze);
      SetParam(ThreadNum,"Rating",Rating);
      SetParam(ThreadNum,"PatternStart",HistPatternBarStart);
      SetParam(ThreadNum,"TS",TicksStart);
      SetParam(ThreadNum,"TE",GetTickCount());
      // Signal "everything is ready"
      SetParam(ThreadNum,"Answer");
     }
  }
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
   // The handler of this event is required
   return(0);
  }
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   SetParam(ThreadNum,"End");
  }
//+------------------------------------------------------------------+

Uzman Danışman (EA) ve gösterge bir ortak ThreadCalc.mqh kitaplığı kullanır.

Kodu buradadır:

//+------------------------------------------------------------------+
//|                                                   ThreadCalc.mqh |
//+------------------------------------------------------------------+
string GlobalVarPrefix;
//+------------------------------------------------------------------+
// It finds the price sequence, most similar to the assigned one.
// in the specified range of the history
// Returns the estimation of similarity and the direction 
// of the further changes of prices in history.
//+------------------------------------------------------------------+
void FindPrognoze(
                  string DataSymbol,    // symbol
                  double  &CurPattern[],// current pattern
                  int BarStart,         // start bar
                  int BarCount,         // bars to search
                  int PrognozeLen,      // forecast length

                  // RESULT
                  double  &Prognoze,        // forecast (-,0,+)
                  double  &Rating,          // rating
                  int  &HistPatternBarStart // starting bar of the found sequence
                  ) 
  {

   int PatternLen=ArraySize(CurPattern);

   Prognoze=0;
   if(PrognozeLen<=0) return;

   double rates[];
   while(CopyClose(DataSymbol,_Period,BarStart,BarCount,rates)
         <BarCount) Sleep(1000);

   double rmin=-1;
   // Shifting by one bar, go through all of the price sequences in the history
   for(int bar=BarCount-PatternLen-PrognozeLen; bar>=0; bar--) 
     {
      // Update to eliminate the differences in the levels of price in the sequences
      double dr=CurPattern[0]-rates[bar];

      // Calculate the level of differences between the sequences - as a sum 
      // of squares of price deviations from the sample values
      double r=0;
      for(int i=0; i<PatternLen; i++)
         r+=MathPow(MathAbs(rates[bar+i]+dr-CurPattern[i]),2);

      // Find the sequence with the least difference level
      if((r<rmin) || (rmin<0)) 
        {
         rmin=r;
         HistPatternBarStart   = bar;
         int HistPatternBarEnd = bar + PatternLen-1;
         Prognoze=rates[HistPatternBarEnd+PrognozeLen]-rates[HistPatternBarEnd];
        }
     }
   // Convert the bar number into an indicator system of coordinates
   HistPatternBarStart=BarStart+BarCount-HistPatternBarStart-PatternLen;

   // Convert the difference into the rating of similarity
   Rating=-rmin;
  }
//====================================================================
// A set of functions for easing the work with global variables.
// As a parameter contain the number of computational threads 
// and the names of the variables, automatically converted into unique
// global names.
//====================================================================
//+------------------------------------------------------------------+
string GlobalParamName(int ThreadNum,string ParamName) 
  {
   return GlobalVarPrefix+IntegerToString(ThreadNum)+"_"+ParamName;
  }
//+------------------------------------------------------------------+
bool ParamExists(int ThreadNum,string ParamName) 
  {
   return GlobalVariableCheck(GlobalParamName(ThreadNum,ParamName));
  }
//+------------------------------------------------------------------+
void SetParam(int ThreadNum,string ParamName,double ParamValue=0) 
  {
   string VarName=GlobalParamName(ThreadNum,ParamName);
   GlobalVariableTemp(VarName);
   GlobalVariableSet(VarName,ParamValue);
  }
//+------------------------------------------------------------------+
double GetParam(int ThreadNum,string ParamName) 
  {
   return GlobalVariableGet(GlobalParamName(ThreadNum,ParamName));
  }
//+------------------------------------------------------------------+
double DelParam(int ThreadNum,string ParamName) 
  {
   return GlobalVariableDel(GlobalParamName(ThreadNum,ParamName));
  }
//+------------------------------------------------------------------+

Çalışması sırasında bir işlemciden fazlasını kullanabilen alım satım sistemimiz hazır!

Bunu kullanırken, bu örnekte sonsuz döngülere sahip CM göstergelerini kullandığımızı unutmamalısınız.

Terminalde, bu sistemin yanında, başka programları çalıştırmayı planlıyorsanız, bunları CM göstergeleri tarafından kullanılmayan para birimi çiftleri üzerinde kullandığınızdan emin olmalısınız. Bu tür bir çakışmayı önlemenin iyi bir yolu, sistemi MM-EA’nın giriş parametrelerinde CM göstergeleri için para birimi çiftlerini doğrudan belirtebileceğiniz şekilde modifiye etmektir.


EA’nın çalışma hızının ölçülmesi

Düzenli mod

EURUSD M1 grafiğini açın ve önceki bölümde oluşturulan Uzman Danışmanımızı (EA) başlatın. Ayarlarda, modellerin uzunluğunu 24 saat olarak (1440 min. çubuk) ve geçmişte arama derinliğini 1 yıl olarak (375.000 çubuk) belirtin.

Şekil 4. Uzman Danışman (EA) giriş parametreleri

Şekil 4. Uzman Danışman (EA) için giriş parametreleri

Parametre “Zincirleri” seti 1’e eşittir. Bu, tüm EA hesaplamalarının bir zincirde (tek bir çekirdek üzerinde) üretilebileceği anlamına gelir. Bu sırada, hesaplama göstergelerini kullanmayacaktır, fakat her şeyi kendisi hesaplayacaktır. Temel olarak, düzenli bir EA’nın çalışma ilkesi yoluyla.

Yürütme günlüğü:

Şekil 6. Uzman Danışman (EA) Günlüğü

Şekil 6. Uzman Danışman (EA) Günlüğü (1 zincir)


Paralel mod

Şimdi bu EA’yı ve bunun tarafından açılan pozisyonu silelim. EA’yı tekrar, fakat bu kez, “Zincirler” parametresi 2’ye eşit olarak ekleyin.

Şimdi EA’nın kendi çalışmasında iki işlemci çekirdeğini kullanan 2 hesaplama göstergesi oluşturması ve kullanması gerekecektir. Yürütme günlüğü:

Şekil 7. Uzman Danışman (EA) Günlüğü (2 zincir)

Şekil 7. Uzman Danışman (EA) Günlüğü (2 zincir)


Hız Karşılaştırma

Bu günlüklerin ikisini de analiz ederek EA’nın yaklaşık yürütme süresinin aşağıdaki gibi olduğu sonucuna varırız:

  • düzenli modda - 52 saniye;
  • 2 çekirdekli modda - 27 saniye.

Dolayısıyla, 2 çekirdekli CPU üzerinde paralelleştirme gerçekleştirerek bir EA’nın hızını 1,9 kat artırabildik. Büyük miktarda çekirdeğe sahip bir işlemci kullanılırken yürütme hızının çekirdeklerin sayısına oranla daha da artacağı varsayılabilir.

Çalışma doğruluğu kontrolü

Yürütme süresine ek olarak, günlükler tüm ölçümlerin doğru şekilde gerçekleştirildiğini doğrulamamızı sağlayan ek bilgiler sağlar. Çizgiler EA: Çalışma başlangıcı ... çalışma sonu ... " ve "Gösterge ...: Çalışma başlangıcı ... çalışma sonu ..." göstergelerin hesaplamalarına EA bunlara komutu vermeden başlamadığını gösterir.

Ayrıca paralel modda EA’nın başlatılması sırasında alım satım stratejisinin ihlal edilmediğini doğrulayalım. Günlüklere göre, EA’nın paralel modda başlatılmasının, düzenli moda başlatılmasından neredeyse hemen sonra yapıldığı açıktır. Yani, her iki durumda, piyasa durumları benzerdi. Günlükler, her iki durumda modellerin geçmişinde bulunan tarihlerin de çok benzer olduğunu gösterir. Dolayısıyla her şey yolunda: Stratejinin algoritması her iki durumda da eşit şekilde çalışıyor.

Günlüklerin durumlarında açıklanan modeller buradadır. EA düzenli modda çalıştırılırken mevcut piyasa durumu (uzunluk - 1440 dakika çubuğu) böyleydi:

Şekil 8. Mevcut piyasa durumu

Şekil 8. Mevcut piyasa durumu

EA geçmişte aşağıdaki benzer modeli buldu:

Şekil 9. Benzer piyasa durumu

Şekil 9. Benzer piyasa durumu

EA’yı paralel modda çalıştırırken aynı model “Gösterge 1” tarafından bulundu. Günlükteki gibi “Gösterge 2” geçmişin diğer yarım yılındaki modelleri arıyordu, bu nedenle farklı bir benzer model buldu:

Şekil 10. Benzer piyasa durumu

Şekil 10. Benzer piyasa durumu

Ve paralel modda EA çalışması sırasında MetaTrader 5’teki global değişkenler böyle görünür:

Şekil 11. Global değişkenler

Şekil 11. Global değişkenler

EA ve göstergeler arasında global değişkenler yoluyla veri değişimi başarıyla uygulandı.


Sonuç

Bu çalışmada, MetaTrader 5’in standart araçları yoluyla zengin algoritmaların paralelleştirilebileceği bulunmuştur. Ve bu soruna bulunan çözüm gerçek dünyadaki alım satım stratejilerinde kullanım için uygundur.

Bu program, çok çekirdekli bir sistemde, orantısal olarak gerçekten daha hızlı çalışmaktadır. İşlemcideki çekirdeklerin sayısı her yıl artmaktadır ve bu MetaTrader kullanan yatırımcıların bu donanım kaynaklarını etkili bir şekilde kullanma fırsatına sahip olması iyi bir şeydir. Piyasayı hala gerçek zamanlı olarak analiz edebilecek daha zengin alım satım stratejilerini güvenli şekilde oluşturabiliriz.


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

Ekli dosyalar |
i-flood.mq5 (0.7 KB)
e-flood.mq5 (0.58 KB)
i-thread.mq5 (3.1 KB)
threadcalc.mqh (3.93 KB)
Ç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.
CChartObject sınıfına dayalı yeni GUI pencere öğelerinin tasarlanması ve uygulanması CChartObject sınıfına dayalı yeni GUI pencere öğelerinin tasarlanması ve uygulanması
GUI arayüzlü yarı otomatik Uzman Danışman (EA) üzerine önceki makaleyi yazdıktan sonra arayüzün daha karmaşık göstergeler ve Uzman Danışman (EA) için bazı yeni işlevlerle geliştirilmesinin istendiği ortaya çıktı. MQL5 standart kitaplık sınıfları ile tanıştıktan sonra yeni pencere öğelerini uyguladım. Bu makale, göstergelerde ve Uzman Danışmanlarda (EA) kullanılabilecek yeni MQL5 GUI pencere öğelerini tasarlamaya ve uygulamaya yönelik bir işlemi açıklamaktadır. Makalede sunulan pencere öğeleri CChartObjectSpinner, CChartObjectProgressBar ve CChartObjectEditTable’dır.
Alım Satım Modellerine Dayalı Çoklu Uzman Danışmanlar Oluşturma Alım Satım Modellerine Dayalı Çoklu Uzman Danışmanlar Oluşturma
MQL5'te nesne yönelimli yaklaşımı kullanmak, çoklu para birimli/çoklu sistemli/çoklu zaman dilimli Uzman Danışmanların oluşturulmasını büyük ölçüde basitleştirir. Tek bir EA ile 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.
Önceden tanımlanmış risk ve R/R oranına dayalı etkileşimli yarı otomatik sürükle ve bırak Uzman Danışman (EA) oluşturma Önceden tanımlanmış risk ve R/R oranına dayalı etkileşimli yarı otomatik sürükle ve bırak Uzman Danışman (EA) oluşturma
Bazı yatırımcılar tüm alım-satımları otomatik olarak yürütür ve bazıları birkaç göstergenin çıkışına bağlı olarak otomatik ve manuel alım-satımları karıştırır. Sonraki grubun bir üyesi olarak risk ve ödül seviyelerini doğrudan grafikten dinamik olarak değerlendirmek için etkileşimli bir araca ihtiyacım vardı. Bu makale, önceden tanımlanmış hisse senedi riski ve R/R oranı ile etkileşimli yarı otomatik bir Uzman Danışman (EA) uygulamanın bir yolunu sunacaktır. Uzman Danışman (EA) riski, RR ve lot boyutu parametreleri EA paneli üzerinde çalışma süresi boyunca değiştirilebilir.