English Русский 中文 Español Deutsch 日本語 Português
preview
MetaTrader'da çoklu robot: Tek bir grafikten birden fazla robot başlatma

MetaTrader'da çoklu robot: Tek bir grafikten birden fazla robot başlatma

MetaTrader 5Örnekler | 27 Ağustos 2024, 13:30
23 0
Evgeniy Ilin
Evgeniy Ilin

İçindekiler


Giriş

Finansal piyasalar dünyasında, otomatik alım-satım sistemleri karar verme sürecinin ayrılmaz bir parçası haline gelmiştir. Bu sistemler önceden tanımlanmış kurallar ve algoritmalar kullanarak piyasayı analiz etmek, giriş ve çıkış kararları vermek ve işlemleri gerçekleştirmek üzere yapılandırılabilir. Ancak, birden fazla grafikte robotları kurmak ve çalıştırmak zaman alıcı bir görev olabilir. Her robotun her grafik için ayrı ayrı yapılandırılması gerekir, bu da ek çaba gerektirir.

Bu makalede, MetaTrader 4 ve 5'te birden fazla grafik için evrensel bir robot oluşturmanıza olanak tanıyan basit bir şablon uygulamamı göstereceğim. Şablonumuz, robotu bir grafiğe eklemenize izin verecek, grafiklerin geri kalanı ise Uzman Danışman içinde işlenecektir. Böylece şablonumuz, birden fazla grafikte robot kurma ve çalıştırma sürecini büyük ölçüde basitleştirerek yatırımcıların zamandan ve emekten tasarruf etmesini sağlar. Bu makalede, fikirden teste kadar MQL5'te böyle bir robot oluşturma sürecini ayrıntılı olarak inceleyeceğiz.


Problem tanımı ve uygulanabilirlik sınırları

Bu fikir aklıma çok uzun zaman önce gelmedi, ancak profesyonel satıcıların benzer kararlarını uzun zamandır gözlemliyordum. Başka bir deyişle, bu alanda bir fikir ortaya atan ne ilk ne de son kişiyim, ancak her zaman olduğu gibi, programcının bu tür kararlara varmaya başlaması için bazı koşulların ortaya çıkması gerekiyor. MQL5 mağazasında bu tür Uzman Danışmanların geliştirilmesinin ana nedeni, kullanıcı rahatlığı isteğidir. Ancak benim durumumda biraz daha farklı bir motivasyon vardı. Motivasyonum, ilk olarak ya ‘birkaç enstrüman için aynı anda birkaç stratejiyi’ ya da ‘aynı stratejiyi, ancak çok dövizli özelliklerini görmek için’ test etmem gerektiğiydi.

Ek olarak, sınayıcıda bir stratejiyi test ederken, özellikle çok dövizli modda çok önemli bir faktör genel karlılık eğrisidir. Bu eğri, geçmiş veriler üzerinde geriye dönük test yaparken otomatik alım-satım sistemleri için yapılacak herhangi bir değerlendirmenin temelini oluşturur. Alım-satım sistemlerini tek bir enstrüman üzerinde ayrı ayrı test ederken, bu tür raporları daha sonra birleştirmek oldukça zordur. En azından MetaTrader 5 için bu tür araçların farkında değilim. Terminalin dördüncü sürümüne gelince, bu tür manipülasyonlar için resmi olmayan bir araç vardır. Bunu en az bir makalede kullandım, ancak elbette böyle bir yaklaşım tercih edilmez.

Test sürecine ek olarak, otomatik alım-satımın kendisi ve her biri kendi grafiğinde bağımsız olarak çalışan benzer Uzman Danışmanların senkronizasyonu konusunda da aynı derecede önemli bir süreç vardır. Bu tür çok fazla grafik varsa, ek bilgisayar kaynakları gerektirerek programın çalışma performansını yavaşlatabilir veya kötüleştirebilir ve nihai alım-satım sonucu üzerinde zarar oluşturan bir etkiye sahip olabilecek beklenmedik hatalara ve diğer hoş olmayan olaylara yol açabilir. Bu tür her bir Uzman Danışman için benzersiz emir kimlikleri, yüksek frekanslı sunucu isteklerine karşı koruma ve ilk bakışta belli olmayan birçok başka şey bulmamız gerekir.

Uzman Danışmanın grafiksel kısmının işlenmesi ayrı ve çok hassas bir konudur. Artık az ya da çok becerikli tüm Uzman Danışman geliştiricileri, Uzman Danışmanın bağlı olduğu grafik üzerinde bazı görsel ifadelerin en azından minimal bir versiyonunu oluşturmaktadır. Bu şekilde Uzman Danışman daha ciddi görünür ve daha fazla güven verir ve son olarak, neredeyse her zaman, kullanıcıya grafikte bazı bilgilerin gösterilmesi, Uzman Danışman alım-satım süreci üzerinde daha etkili kontrol sağlar. Ayrıca, gerektiğinde manuel kontrol için öğeler eklemek de mümkündür. Tüm bunlara kullanıcı arayüzü denir. Bu tür Uzman Danışmanları grafiklere dağıtırken, arayüzlerdeki hem grafiksel, hem metinsel hem de sayısal bilgilerin güncellenmesi üzerindeki yük katlanarak artar. Elbette, çoklu şablon kullanırken, terminal için minimum miktarda kaynak gerektiren bir arayüze sahip oluyoruz.

Elbette böyle bir şablon tüm sorunları çözmüyor, ancak yine de projelerimde bana çok yardımcı oluyor. Farklı robotlar kullanıyorum ve genel olarak tüm yaklaşımların var olma hakkı var, ancak birçok acemi programcının bu modeli yararlı bulabileceğini düşünüyorum. Tamamen kopyalamak gerekli değildir, ancak dilerseniz ihtiyaçlarınıza göre kolayca ayarlayabilirsiniz. Amacım size olağanüstü bir şey sunmak değil, böyle bir sorunu çözme seçeneklerinden birini göstermek ve açıklamak.


Çoklu robot kullanımı açısından MetaTrader 4 ve MetaTrader 5 terminalleri arasındaki farklar

MetaTrader 5 hakkında sevdiğim şey, sahip olduğu sınayıcı aracının gücüdür: yukarıda belirtilen Uzman Danışman geliştirme yaklaşımını kullanmanız koşuluyla, aynı anda birden fazla enstrüman üzerinde test etmeniz için ihtiyacınız olan tüm özellikleri size sunar. Sınayıcı, fiyatları zamana göre otomatik olarak senkronize eder ve size zaman ölçeğinde net bir şekilde senkronize edilmiş bir kârlılık eğrisi sağlar. MetaTrader 4'ün böyle bir işlevi yoktur. Bence en büyük dezavantajı bu. Bununla birlikte, MetaQuotes'un dördüncü terminali desteklemek için elinden gelenin en iyisini yaptığını ve popülaritesinin hala yüksek olduğunu belirtmek gerekir. MetaTrader 4'ün aktif bir kullanıcısı olarak, bu eksikliklerin göründükleri kadar önemli olmadığını söyleyebilirim.

MQL4 dili yakın zamanda MQL5 olarak güncellenmiştir. Bu, bizimki gibi benzer şablonlar yazarken kodda minimum farklılığa sahip olacağımız anlamına gelir. Her iki terminal için de bir şeyler uygulamaya çalışmak benim iyi bir alışkanlığımdır, bu nedenle her iki terminal için de bir şablon elde edeceksiniz. Eski terminalde yapılan bu tür iyileştirmeler, diğer şeylerin yanı sıra, gerçekten ihtiyaç duyduğumuz aşağıdaki fonksiyonları kullanmamıza olanak tanır:

  • CopyClose - çubuk kapanış fiyatları için talep
  • CopyOpen - çubuk açılış fiyatları için talep
  • CopyHigh - çubuk yüksek fiyatları için talep
  • CopyLow - çubuk düşük fiyatları için talep
  • CopyTime - çubuk açılış zamanı için talep
  • SymbolInfoTick - sembole son gelen tik talebi
  • SymbolInfoInteger - tamsayılar ve numaralandırılmış listelerle tanımlanabilen sembol verileri için talep
  • SymbolInfo******* - ihtiyacımız olan diğer fonksiyonlar

Bu özellikler hem MQL4 hem de MQL5'te mevcuttur. Bu fonksiyonlar, herhangi bir sembol ve zaman dilimi için çubuk verilerini almanıza olanak sağlar. Bu nedenle, dördüncü ve beşinci sürümlerin sınayıcıları arasındaki tek hoş olmayan fark, dördüncü terminaldeki bu fonksiyonların yalnızca testin yapıldığı mevcut grafik için çalışacağı ve taleplerin geri kalanının MetaTrader 4 sınayıcının özellikleri nedeniyle veri olmadığını size bildireceği gerçeğidir. Bu nedenle, şablonumuzu test ederken, yalnızca seçilen sembol üzerinde alım-satım yapacaksınız ve tek bir robot için kâr eğrilerinden yalnızca birini alacaksınız.

Beşinci terminalde, talep edilen tüm semboller üzerinde alım-satım yapacaksınız ve ortak bir kârlılık çizgisi alacaksınız. Alım-satımda kullanmaya gelince, her iki terminalde de böyle bir robotla doğrudan alım-satım yaparken, şablonun tam performansını elde edeceksiniz. Başka bir deyişle, fark sadece sınayıcıdadır. Ancak bu gibi durumlarda bile, bir Uzman Danışman oluştururken MetaTrader 5 sürümüyle başlamanın daha iyi olduğunu anlayabilirsiniz. Gerekli tüm testlerden sonra, MetaTrader 4 için versiyonu hızlı bir şekilde oluşturabilirsiniz.

Elbette, değinmediğim bir dizi farklılık var. Sadece bazılarının önemini vurgulamak istiyorum, çünkü böyle bir şablon için ayrıntılı bir yapı oluştururken bu nüansların bilinmesi gerekir. MetaTrader 5 kesinlikle önceki sürümünden daha iyidir, ancak yine de dördüncü terminalden kurtulma niyetim yok, çünkü birçok durumda, hesaplama kaynaklarına olan talebi beşinci sürüme kıyasla çok fazla değil. Her iki araç da hala iyi durumda.


Evrensel bir şablon oluşturmanın incelikleri

Böyle bir şablon oluşturmak için terminalin nasıl çalıştığını, bir Uzman Danışmanın ne olduğunu ve bir MetaTrader grafiğinin ne olduğunu anlamalısınız. Ayrıca, her grafiğin ayrı bir nesne olduğunu anlamanız gerekir. Bu tür grafiklerin her biri birkaç gösterge ve yalnızca bir Uzman Danışman ile ilişkilendirilebilir. Birbirinin aynısı olan birkaç grafik olabilir. Bir sembol zaman diliminde birkaç farklı Uzman Danışman çalıştırmak veya bir Uzman Danışmanın birden çok kopyasını farklı ayarlarla çalıştırmak için genellikle birkaç grafik oluşturulur. Bu nüansları anladığımızda, şablonumuz lehine birden fazla grafikten vazgeçmek için tüm bunları şablonumuzun içinde uygulamamız gerektiği sonucuna varmalıyız. Bu bir diyagram şeklinde gösterilebilir:

objects structure

Ayrı olarak, tikler hakkında da konuşulmalıdır. Bu yaklaşımın dezavantajı, her grafik için yeni bir tik işleyiciye abone olamayacağımızdır. Robotumuzun üzerinde çalıştığı grafikten tikler uygulamamız veya zamanlayıcıyı kullanmamız gerekecektir. Nihayetinde bu, tik robotları için aşağıdaki gibi tatsız anlar anlamına gelecektir:

  • Özel OnTick işleyicileri yazmamız gerekecektir
  • Bu işleyicilerin OnTimer'ın bir türevi olarak uygulanması gerekecektir
  • OnTimer bir gecikmeyle çalıştığı için tikler mükemmel olmayacaktır (gecikmenin değeri önemli değildir, ancak varlığı önemlidir)
  • Tikleri almak için SymbolInfoTick fonksiyonuna ihtiyacımız vardır

Bence her milisaniyeyi sayanlar için bu can sıkıcı bir şey olabilir, özellikle de arbitrajı sevenler için. Ancak ben şablonumda bunu kullanmıyorum. Farklı sistemler inşa ettiğim yıllar boyunca, çubuk alım-satımı paradigmasına ulaştım. Bu, alım-satım işlemlerinin ve diğer hesaplamaların çoğunlukla yeni bir çubuk göründüğünde gerçekleştiği anlamına gelir. Bu yaklaşımın bir dizi belirgin avantajı vardır:

  • Yeni bir çubuğun başlangıcını belirlemedeki yanlışlık alım-satımı önemli ölçüde etkilemez
  • Çubuğun süresi ne kadar uzun olursa, bu etki o kadar az olur.
  • Çubuklar şeklinde ayrıklaştırma, test hızında birkaç kat artış sağlar
  • Hem gerçek tikler üzerinde hem de yapay tikler üzerinde test yaparken aynı kalitede test yapma imkanı sunar

Bu yaklaşım, Uzman Danışmanların oluşturulmasına yönelik belirli bir paradigmayı öğretmektedir. Bu paradigma, tik Uzman Danışmanlarıyla ilgili birçok sorunu ortadan kaldırır, test sürecini hızlandırır, ana unsur olan daha yüksek bir matematiksel kâr beklentisi sağlar ve ayrıca zaman ve hesaplama gücü açısından büyük tasarruf getirir. Sanırım daha birçok avantaj bulabiliriz, ancak bu makale bağlamında bu kadarının yeterli olduğunu düşünüyorum.

Şablonumuzu uygulamak için, işlem terminalinin çalışma alanının tüm yapısını şablonumuzun içine yerleştirmek gerekli değildir. Her robot için ayrı bir grafik uygulamak yeterlidir. Bu en uygun yapı değildir, ancak her bir enstrümanın enstrümanlar listesinde yalnızca bir kez bulunacağını kabul edersek, bu optimizasyon gerekli olmayacaktır. Şöyle görünecektir:

our realization

Grafiklerin uygulanması için en basit yapıyı uyguladık. Şimdi böyle bir şablonun girdilerini ve daha da önemlisi, MQL5 dilinin izin verilen olanakları dahilinde her durum için dinamik grafik ve Uzman Danışman sayısını nasıl dikkate alacağımızı düşünmenin zamanı geldi. Bu problemi çözmenin tek yolu dizge (string) girdi değişkenleri kullanmaktır. Bir dizge çok büyük miktarda veri depolamamıza olanak tanır. Aslında, böyle bir şablon için gerekli tüm parametreleri tanımlamak amacıyla, girdi verilerinde dinamik dizilere ihtiyacımız olacaktır. Elbette, çok az kişi bu tür olanakları kullanacağı için kimse bu tür şeyleri uygulamayacaktır. Dizge, içine istediğimiz her şeyi koyabileceğimiz dinamik dizimizdir. O zaman kullanalım. En basit şablonum için aşağıdaki gibi üç değişken kullanmaya karar verdim:

  • Charts - grafiklerimiz (liste)
  • Chart Lots - alım-satım için lotlar (liste)
  • Chart Timeframes - grafik zaman dilimleri (liste)

Genel olarak, tüm bu verileri tek bir dizgede birleştirebiliriz, ancak o zaman yapısı karmaşık olacak ve potansiyel bir kullanıcının verileri nasıl doğru bir şekilde tanımlayacağını bulması zor olacaktır. Buna ek olarak, doldururken hata yapmak çok kolay olacaktır ve kullanırken çok tatsız şeyler yaşayabiliriz; bu verileri dizgelerden çıkaracak olan dönüştürme fonksiyonunun inanılmaz karmaşıklığından bahsetmiyorum bile. Satıcılar arasında benzer çözümler gördüm ve genel olarak her şeyi doğru yaptılar. Tüm veriler virgüllerle ayrılarak listelenir. Uzman Danışmanın başlangıcında, bu veriler özel fonksiyonlar kullanılarak bir dizgeden alınır ve daha sonra kodda kullanılan ilgili dinamik dizilere doldurulur. Biz de bu yolu takip edeceğiz. Aynı numaralandırma kurallarına sahip daha fazla benzer dizge ekleyebiliriz. Ayırıcı olarak ":" kullanmaya karar verdim. Virgül kullanırsak, Chart Lots gibi double türündeki dizilerle nasıl başa çıkılacağı net değildir. Bu tür dizge değişkenlerinden daha fazlasını eklemek ve genel olarak daha eksiksiz ve çok yönlü bir şablon oluşturmak mümkündür, ancak benim buradaki görevim yalnızca bunun nasıl uygulanacağını göstermek ve size hızlı ve kolay bir şekilde değiştirebileceğiniz şablonun ilk sürümünü vermektir.

Bu tür dizileri uygulamak yeterli değildir, ortak değişkenleri de uygulamak gerekir, örneğin:

  • Work Timeframe For Unsigned - belirtilmediği yerde grafik zaman dilimi
  • Fix Lot For Unsigned - belirtilmediği yerde lot miktarı

Charts listesi doldurulmalıdır. Aynı eylem Chart Lots ve Chart Timeframes için isteğe bağlıdır. Örneğin, tüm grafikler için tek lot ve tüm grafikler için aynı zaman dilimini ayarlayabiliriz. Şablonumuzda da benzer bir işlevsellik uygulanacaktır. Bu tür şablonlar temelinde oluşturulan bir Uzman Danışmanın girdi parametrelerini ayarlarken netlik sağlamak için mümkün olan her yerde bu tür uygulama kurallarının uygulanması arzu edilir. 

Şimdi böyle bir modelin minimal bir uygulaması için birkaç önemli değişken daha tanımlayalım:

  • Last Bars Count - her bir grafik için sakladığımız son çubuk sayısı
  • Deposit For Lot - belirtilen lotun kullanımı için mevduat
  • First Magic - Uzman Danışmanın işlemleri için benzersiz kimlik

İlk değişkenin oldukça açık olduğunu düşünüyorum. İkinci değişkeni kavramak çok daha zordur. Uzman Danışmanlarımdaki otomatik lotu bu şekilde düzenliyorum. Bunu "0" olarak ayarlarsam, algoritmaya yalnızca ilgili dizgede veya yukarıda ele aldığımız paylaşılan bir değişkende belirtilen sabit lotla işlem yapması gerektiğini bildiririm. Aksi takdirde, ayarlarda belirtilen lotun uygulanabilmesi için gerekli mevduatı ayarlıyorum. Daha küçük veya daha büyük bir mevduat ile bu lot değerinin şu denkleme göre değiştiğini anlamak kolaydır:

  • Lot = Girdide lot * (Mevcut mevduat / Lot için mevduat)

Sanırım artık her şey açıklığa kavuşmuş olmalı. Sabit bir lot istiyorsak, sıfır olarak ayarlarız ve diğer durumlarda girdi ayarlarındaki mevduatı risklere göre ayarlarız. Bence basit ve şık. Gerekirse, otomatik lot için risk değerlendirme yaklaşımını değiştirebilirsiniz, ancak ben şahsen bu seçeneği seviyorum, üzerinde fazla düşünmenin bir anlamı yok.

Senkronizasyondan ve özellikle de Uzman Danışman sihirli sayısının ayarlanması konusundan bahsetmeye değer. Uzman Danışmanlarla veya hatta karma bir biçimde işlem yaparken, kendine saygı duyan tüm programcılar bu özel değişkene özellikle dikkat eder. Mesele şu ki, birden fazla Uzman Danışman kullanırken, bu tür Uzman Danışmanların her birinin benzersiz bir kimliğe sahip olmasını sağlamak çok önemlidir. Aksi takdirde, emirlerle, işlemlerle veya pozisyonlarla çalışırken tam bir karmaşa yaşarsınız ve stratejileriniz doğru çalışmayı durdurur ve çoğu durumda tamamen çalışmayı durdurur. Umarım nedenini açıklamam gerekmiyor. Bir Uzman Danışman grafiğe her yerleştirildiğinde, bu kimlikleri yapılandırmamız ve tekrar etmediklerinden emin olmamız gerekir. Tek bir hata bile feci sonuçlara yol açabilir. Ek olarak, Uzman Danışmanın bulunduğu grafiği yanlışlıkla kapatırsanız, yeniden yapılandırmanız gerekecektir. Sonuç olarak, hata yapma olasılığı büyük ölçüde artar. Ayrıca, diğer birçok açıdan da çok can sıkıcıdır. Örneğin, grafiği kapattınız ve orada hangi kimliğin kullanıldığını unuttunuz. Bu durumda, aramak için işlem geçmişini incelemeniz gerekecektir. Kimlik olmadan, yeni başlatılan Uzman Danışman yanlış çalışabilir ve daha birçok tatsız şey olabilir.

Benimki gibi bir şablon kullanmak bizi bu tür bir kontrolden kurtarır ve olası hataları en aza indirir çünkü Uzman Danışman ayarlarında yalnızca başlangıç kimliğini ayarlamamız gerekirken, diğer kimlikler bir artış kullanılarak otomatik olarak oluşturulacak ve ilgili Uzman Danışman kopyalarına atanacaktır. Bu işlem her yeniden başlatmada otomatik olarak gerçekleşecektir. Sadece bir başlangıç kimliğini hatırlamak, yolun ortasında rastgele bir kimliği hatırlamaktan çok daha kolaydır.


Evrensel bir şablon yazma

Şablonu uygulama zamanı geldi. Aşırı öğeleri atlamaya çalışacağım, böylece bu şablona ihtiyaç duyan herkes indirebilir ve geri kalanını kaynak kodunda görebilir. Burada sadece fikirlerimizle doğrudan ilgili olan şeyleri göstereceğim. Durma seviyeleri ve diğer parametreler kullanıcılar tarafından tanımlanır. Uygulamamı kaynak kodunda bulabilirsiniz. İlk olarak, kesinlikle ihtiyaç duyacağımız girdi değişkenlerimizi tanımlayalım:

//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
input string SymbolsE="EURUSD:GBPUSD:USDCHF:USDJPY:NZDUSD:AUDUSD:USDCAD";//Charts
input string LotsE="0.01:0.01:0.01:0.01:0.01:0.01:0.01";//Chart Lots
input string TimeframesE="H1:H1:H1:H1:H1:H1:H1";//Chart Timeframes
input int LastBars=10;//Last Bars Count
input ENUM_TIMEFRAMES TimeframeE=PERIOD_M1;//Work Timeframe For Unsigned
input double RepurchaseLotE=0.01;//Fix Lot For Unsigned
input double DepositForRepurchaseLotE=0.00;//Deposit For Lot (if "0" then fix)
input int MagicE=156;//First Magic

Burada, tıpkı paylaşılan değişkenler örneğinde olduğu gibi, dinamik dizilerimizi yansıtan dizge değişkenlerini doldurmanın bir örneğini görebilirsiniz. Bu arada, bu kod hem MQL4'te hem de MQL5'te aynı görünecektir. Her şeyi olabildiğince benzer yapmaya çalıştım.

Şimdi dizge verilerimizi nasıl alacağımıza karar verelim. Bu, ilgili fonksiyon tarafından yapılacaktır, ancak önce fonksiyonumuzun dizgelerden elde edilen verileri ekleyeceği diziler oluşturacağız:

//+------------------------------------------------------------------+
//|Arrays                                                            |
//+------------------------------------------------------------------+
string S[];// Symbols array
double L[];//Lots array
ENUM_TIMEFRAMES T[];//Timeframes array

Aşağıdaki fonksiyon bu dizileri doldurur:

//+------------------------------------------------------------------+
//| Fill arrays                                                      |
//+------------------------------------------------------------------+
void ConstructArrays()
   {
      int SCount=1;
      for (int i = 0; i < StringLen(SymbolsE); i++)//calculation of the number of tools
         {
         if (SymbolsE[i] == ':')
            {
            SCount++;
            }
         }
      ArrayResize(S,SCount);//set the size of the character array
      ArrayResize(CN,SCount);//set the size of the array to use bars for each character
      int Hc=0;//found instrument index
      for (int i = 0; i < StringLen(SymbolsE); i++)//building an array of tools
         {
         if (i == 0)//if we just started
            {
            int LastIndex=-1;
            for (int j = i; j < StringLen(SymbolsE); j++)
               {
               if (StringGetCharacter(SymbolsE,j) == ':')
                  {
                  LastIndex=j;
                  break;
                  }
               }
            if (LastIndex != -1)//if no separating colon was found
               {
               S[Hc]=StringSubstr(SymbolsE,i,LastIndex);
               Hc++;
               }
            else
               {
               S[Hc]=SymbolsE;
               Hc++;
               }
            }          
         if (SymbolsE[i] == ':')
            {
            int LastIndex=-1;
            for (int j = i+1; j < StringLen(SymbolsE); j++)
               {
               if (StringGetCharacter(SymbolsE,j) == ':')
                  {
                  LastIndex=j;
                  break;
                  }
               }
            if (LastIndex != -1)//if no separating colon was found
               {
               S[Hc]=StringSubstr(SymbolsE,i+1,LastIndex-(i+1));
               Hc++;
               }
            else
               {
               S[Hc]=StringSubstr(SymbolsE,i+1,StringLen(SymbolsE)-(i+1));
               Hc++;
               }               
            }
         }
      for (int i = 0; i < ArraySize(S); i++)//assignment of the requested number of bars
         {
         CN[i]=LastBars;
         }
      ConstructLots();
      ConstructTimeframe();         
   }

Kısacası, burada ayırıcılar sayesinde dizideki veri miktarı hesaplanır. İlk diziye bağlı olarak, diğer tüm dizilerin büyüklüğü semboller içeren diziye benzer şekilde ayarlanır, devamında önce semboller doldurulur ve ardından ConstructLots ve ConstructTimeframe fonksiyonları kullanılır. Bunların uygulanması, bazı farklılıklarla birlikte bu fonksiyonun uygulanmasına benzerdir. Bunların uygulamasını kaynak kodunda görebilirsiniz. Tekrarlayan kodu göstermemek için bunları makaleye eklemedim.

Şimdi sanal grafik ve ona bağlı sanal robot için ilgili sınıfları oluşturmamız gerekiyor. Sanal grafiklerin ve Uzman Danışmanların dizilerde saklanacağını tanımlayarak başlayalım:

//+------------------------------------------------------------------+
//| Charts & experts pointers                                        |
//+------------------------------------------------------------------+
Chart *Charts[];
BotInstance *Bots[];

Grafik sınıfından başlayalım:

//+------------------------------------------------------------------+
//| Chart class                                                      |
//+------------------------------------------------------------------+
class Chart
   {
   public:
   datetime TimeI[];
   double CloseI[];
   double OpenI[];
   double HighI[];
   double LowI[];
   string BasicSymbol;//the base instrument that was extracted from the substring
   double ChartPoint;//point size of the current chart
   double ChartAsk;//Ask
   double ChartBid;//Bid
   datetime tTimeI[];//auxiliary array to control the appearance of a new bar
   static int TCN;//tcn
   string CurrentSymbol;//symbol
   ENUM_TIMEFRAMES Timeframe;//timeframe
   int copied;//how much data is copied
   int lastcopied;//last amount of data copied
   datetime LastCloseTime;//last bar time
   MqlTick LastTick;//last tick fos this instrument
   
   Chart()
      {
      ArrayResize(tTimeI,2);
      }
   
   void ChartTick()//this chart tick
      {
      SymbolInfoTick(CurrentSymbol,LastTick);
      ArraySetAsSeries(tTimeI,false);
      copied=CopyTime(CurrentSymbol,Timeframe,0,2,tTimeI);
      ArraySetAsSeries(tTimeI,true);
      if ( copied == 2 && tTimeI[1] > LastCloseTime )
         {
         ArraySetAsSeries(CloseI,false);                        
         ArraySetAsSeries(OpenI,false);                           
         ArraySetAsSeries(HighI,false);                        
         ArraySetAsSeries(LowI,false);                              
         ArraySetAsSeries(TimeI,false);                                                            
         lastcopied=CopyClose(CurrentSymbol,Timeframe,0,Chart::TCN+2,CloseI);
         lastcopied=CopyOpen(CurrentSymbol,Timeframe,0,Chart::TCN+2,OpenI);   
         lastcopied=CopyHigh(CurrentSymbol,Timeframe,0,Chart::TCN+2,HighI);   
         lastcopied=CopyLow(CurrentSymbol,Timeframe,0,Chart::TCN+2,LowI);
         lastcopied=CopyTime(CurrentSymbol,Timeframe,0,Chart::TCN+2,TimeI);
         ArraySetAsSeries(CloseI,true);
         ArraySetAsSeries(OpenI,true);
         ArraySetAsSeries(HighI,true);                        
         ArraySetAsSeries(LowI,true);
         ArraySetAsSeries(TimeI,true);         
         LastCloseTime=tTimeI[1];
         }
      ChartBid=LastTick.bid;
      ChartAsk=LastTick.ask;
      ChartPoint=SymbolInfoDouble(CurrentSymbol,SYMBOL_POINT);
      }
   };
int Chart::TCN = 0;

Sınıf, tiklerin ve çubukların güncellenmesini kontrol eden tek bir fonksiyonun yanı sıra belirli bir grafiğin bazı gerekli parametrelerini tanımlamak için gerekli alanlara sahiptir. Orada bazı parametreler eksiktir. İsterseniz, örneğin ChartPoint'i güncellemek gibi eksik olanları ekleyebilirsiniz. Çubuk dizileri MQL4 tarzında yapılır. MQL4'te önceden belirlenmiş dizilerle çalışmayı hala çok uygun buluyorum. Sıfır çubuğunun mevcut çubuk olduğunu bildiğinizde çok kullanışlıdır. Her neyse, bu sadece benim görüşüm. Kendinize ait olanı takip etmekte özgürsünüz.

Şimdi ayrı bir sanal Uzman Danışman sınıfını tanımlamamız gerekiyor:

//+------------------------------------------------------------------+
//| Bot instance class                                               |
//+------------------------------------------------------------------+
class BotInstance//expert advisor object
   {
   public:
   CPositionInfo  m_position;// trade position object
   CTrade         m_trade;// trading object   
   ///-------------------this robot settings----------------------
   int MagicF;//Magic
   string CurrentSymbol;//Symbol
   double CurrentLot;//Start Lot
   int chartindex;//Chart Index
   ///------------------------------------------------------------   
      
   
   ///constructor
   BotInstance(int index,int chartindex0)//load all data from hat using index, + chart index
      {
      chartindex=chartindex0;
      MagicF=MagicE+index;
      CurrentSymbol=Charts[chartindex].CurrentSymbol;
      CurrentLot=L[index];
      m_trade.SetExpertMagicNumber(MagicF);
      }
   ///
   
   void InstanceTick()//bot tick
      {
      if ( bNewBar() ) Trade();
      }
      
   private:
   datetime Time0;
   bool bNewBar()//new bar
      {
      if ( Time0 < Charts[chartindex].TimeI[1] && Charts[chartindex].ChartPoint != 0.0 )
         {
         if (Time0 != 0)
            {
            Time0=Charts[chartindex].TimeI[1];
            return true;
            }
         else
            {
            Time0=Charts[chartindex].TimeI[1];
            return false;
            }
         }
      else return false;
      }
      
   //////************************************Main Logic********************************************************************
   void Trade()//main trade function
      {
      //Close[0]   -->   Charts[chartindex].CloseI[0] - example of access to data arrays of bars of the corresponding chart
      //Open[0]   -->   Charts[chartindex].OpenI[0] -----------------------------------------------------------------------
      //High[0]   -->   Charts[chartindex].HighI[0] -----------------------------------------------------------------------
      //Low[0]   -->   Charts[chartindex].LowI[0] -------------------------------------------------------------------------
      //Time[0]   -->   Charts[chartindex].TimeI[0] -----------------------------------------------------------------------      

      if ( true )
         {
            CloseBuyF();
            //CloseSellF();       
         }
      if ( true )
         {
            BuyF();
            //SellF(); 
         }

      }
      
   double OptimalLot()//optimal lot calculation
      {
      if (DepositForRepurchaseLotE != 0.0) return CurrentLot * (AccountInfoDouble(ACCOUNT_BALANCE)/DepositForRepurchaseLotE);
      else return CurrentLot;
      }
      
   //here you can add functionality or variables if the trading function turns out to be too complicated
   //////*******************************************************************************************************************
   
   ///trade functions
   int OrdersG()//the number of open positions / orders of this virtual robot
      {
      ulong ticket;
      bool ord;
      int OrdersG=0;
      for ( int i=0; i<PositionsTotal(); i++ )
         {
         ticket=PositionGetTicket(i);
         ord=PositionSelectByTicket(ticket);      
         if ( ord && PositionGetInteger(POSITION_MAGIC) == MagicF && PositionGetString(POSITION_SYMBOL) == CurrentSymbol )
            {
            OrdersG++;
            }
         }
      return OrdersG;
      }
   
   /////////********/////////********//////////***********/////////trade function code block
   void BuyF()//buy market
      {
      double DtA;
      double CorrectedLot;
   
      DtA=double(TimeCurrent())-GlobalVariableGet("TimeStart161_"+IntegerToString(MagicF));//unique bot marker last try datetime
      if ( (DtA > 0 || DtA < 0) )
         {
         CorrectedLot=OptimalLot(Charts[chartindex]);
         if ( CorrectedLot > 0.0 )
            {
            //try buy logic
            }            
         }
      }
      
   void SellF()//sell market
      {
      //Same logic
      }

   void CloseSellF()//close sell position
      {
      ulong ticket;
      bool ord;
      for ( int i=0; i<PositionsTotal(); i++ )
         {
         ticket=PositionGetTicket(i);
         ord=PositionSelectByTicket(ticket);      
         if ( ord && PositionGetInteger(POSITION_MAGIC) == MagicF && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL 
         && PositionGetString(POSITION_SYMBOL) == Charts[chartindex].CurrentSymbol )
            {
            //Close Sell logic
            }
         }    
      }
      
   void CloseBuyF()//close buy position
      {
      //same logic 
      }        
      
   bool bOurMagic(ulong ticket,int magiccount)//whether the magic of the current deal matches one of the possible magics of our robot
      {
      int MagicT[];
      ArrayResize(MagicT,magiccount);
      for ( int i=0; i<magiccount; i++ )
         {
         MagicT[i]=MagicE+i;
         }
      for ( int i=0; i<ArraySize(MagicT); i++ )
         {
         if ( HistoryDealGetInteger(ticket,DEAL_MAGIC) == MagicT[i] ) return true;
         }
      return false;
      }
   /////////********/////////********//////////***********/////////end trade function code block
   };

Kod miktarını azaltmak için tekrarlayan mantığın bir kısmını kaldırdım. Bu, Uzman Danışmanınızın tüm algoritmasının uygulanacağı sınıftır. Bu sınıfta bulunan ana işlevsellik:

  • Trade() - ilgili grafik için çubuk işleyicide çağrılan ana alım-satım fonksiyonu
  • BuyF() - piyasa fiyatından alış fonksiyonu
  • SellF() - piyasa fiyatından satış fonksiyonu
  • CloseBuyF() - piyasa fiyatından alış pozisyonlarını kapatma fonksiyonu
  • CloseSellF() - piyasa fiyatından satış pozisyonlarını kapatma fonksiyonu

Bu, çubuklarla alım-satımı temsil etmek için minimum fonksiyon setidir. Bu temsil için, herhangi bir pozisyon açmamız ve bir sonraki çubukta kapatmamız yeterlidir. Bu makale çerçevesinde bu kadarı kâfidir. Bu sınıfta anlayışa katkıda bulunması gereken bazı ek fonksiyonlar vardır:

  • OrdersG() - grafiğe bağlı belirli bir sembol üzerinde açık olan pozisyonları sayma
  • OptimalLot() - alım-satım fonksiyonuna göndermeden önce lotu hazırlama (sabit bir lot seçme veya otomatik bir lot hesaplama)
  • bOurMagic() - geçmişteki işlemlerin kabul edilebilir işlemler listesine uygunluğunu kontrol etme (yalnızca kendi geçmişini filtrelemek için)

Bu fonksiyonlar alım-satım mantığını uygulamak için gerekli olabilir. Yeni çubuk işleyicisini de hatırlatmak fayda var:

  • InstanceTick() - ayrı bir Uzman Danışman örneğinde tik simülasyonu
  • bNewBar() - yeni çubuğun ortaya çıkışını kontrol etmek için dayanak fonksiyonu (InstanceTick içinde kullanılır)

Dayanak fonksiyonu yeni bir çubuk gösterirse, Trade fonksiyonu tetiklenir. Bu, ana alım-satım mantığının ayarlanacağı fonksiyondur. İlgili grafikle bağlantı, örnek oluşturulduğunda atanan chartindex değişkeni kullanılarak gerçekleştirilir. Böylece her Uzman Danışman örneği, hangi grafikten fiyat alması gerektiğini bilir. 

Şimdi sanal grafikleri ve Uzman Danışmanları oluşturma sürecini ele alalım. Önce sanal grafikler oluşturulur:

//+------------------------------------------------------------------+
//| Creation of graph objects                                        |
//+------------------------------------------------------------------+
void CreateCharts()
   {
   bool bAlready;
   int num=0;
   string TempSymbols[];
   string Symbols[];
   ConstructArrays();//array preparation
   int tempcnum=CN[0];
   Chart::TCN=tempcnum;//required number of stored bars for all instruments
   for (int j = 0; j < ArraySize(Charts); j++)//fill in all the names and set the dimensions of all time series, each graph
      {
      Charts[j] = new Chart();
      Charts[j].lastcopied=0;
      ArrayResize(Charts[j].CloseI,tempcnum+2);//assign size to character arrays
      ArrayResize(Charts[j].OpenI,tempcnum+2);//----------------------------------
      ArrayResize(Charts[j].HighI,tempcnum+2);//----------------------------------
      ArrayResize(Charts[j].LowI,tempcnum+2);//-----------------------------------
      ArrayResize(Charts[j].TimeI,tempcnum+2);//----------------------------------
      Charts[j].CurrentSymbol = S[j];//symbol
      Charts[j].Timeframe = T[j];//timeframe
      }
   ArrayResize(Bots,ArraySize(S));//assign a size to the array of bots      
   }

Grafikler oluşturduktan ve dizinin büyüklüğünü sanal Uzman Danışmanlarla ayarladıktan sonra, Uzman Danışmanların örneklerini oluşturmamız ve sanal Uzman Danışmanların grafiklerle bağlantısını uygulamamız gerekir:

//+------------------------------------------------------------------+
//| create and hang all virtual robots on charts                     |
//+------------------------------------------------------------------+
void CreateInstances()
   {
   for (int i = 0; i < ArraySize(S); i++)
      {
      for (int j = 0; j < ArraySize(Charts); j++)
         {
         if ( Charts[j].CurrentSymbol == S[i] )
            {
            Bots[i] = new BotInstance(i,j);
            break;
            } 
         }
      }
   }

Bağlantı, sanal Uzman Danışman oluşturulurken her bir örnekte ayarlanan "j" indeksi kullanılarak gerçekleştirilir. Yukarıda gösterilen ilgili değişken burada vurgulanmıştır. Elbette tüm bunlar birçok şekilde ve çok daha zarif bir şekilde yapılabilir, ancak bence asıl önemli olan genel fikrin net olmasıdır.

Geriye kalan tek şey, her grafikte ve onunla ilişkili Uzman Danışmanda tiklerin nasıl simüle edildiğini göstermektir:

//+------------------------------------------------------------------+
//| All bcharts & all bots tick imitation                            |
//+------------------------------------------------------------------+
void AllChartsTick()
   {
   for (int i = 0; i < ArraySize(Charts); i++)
      {
      Charts[i].ChartTick();
      }
   }

void AllBotsTick()
   {
   for (int i = 0; i < ArraySize(S); i++)
      {
      if ( Charts[Bots[i].chartindex].lastcopied >= Chart::TCN+1 ) Bots[i].InstanceTick();
      }
   }

Belirtmek istediğim tek şey, bu şablonun çok daha ciddi amaçlar için tasarlanmış olan daha karmaşık şablonumun yeniden işlenmesiyle elde edildiği, bu nedenle burada ve orada aşırı öğeler olabileceğidir. İsterseniz bunları kolayca kaldırabilir ve kodu daha düzenli hale getirebilirsiniz.

Şablona ek olarak, örneğin freelance veya başka amaçlarla bir sipariş yazarken de kullanışlı olabileceğini düşündüğüm basit bir arayüz var:


Bu arayüzde boş alan bıraktım, yeterli alanınız olmaması durumunda üç giriş için yeterli olacaktır. Gerekirse kolayca genişletebilir veya yapısını tamamen değiştirebilirsiniz. Bu örnekte eksik olan üç alanı eklemek istiyorsak, kodda aşağıdaki yerleri bulmamız gerekir:

//+------------------------------------------------------------------+
//| Reserved elements                                                |
//+------------------------------------------------------------------+

   "template-UNSIGNED1",//UNSIGNED1
   "template-UNSIGNED2",//UNSIGNED2
   "template-UNSIGNED3",//UNSIGNED3

   //LabelCreate(0,OwnObjectNames[13],0,x+Border+2,y+17+Border+20*5+20*5+23,corner,"","Arial",11,clrWhite,0.0,ANCHOR_LEFT);//UNSIGNED1
   //LabelCreate(0,OwnObjectNames[14],0,x+Border+2,y+17+Border+20*5+20*5+23+20*1,corner,"","Arial",11,clrWhite,0.0,ANCHOR_LEFT);//UNSIGNED2
   //LabelCreate(0,OwnObjectNames[15],0,x+Border+2,y+17+Border+20*5+20*5+23+20*2,corner,"","Arial",11,clrWhite,0.0,ANCHOR_LEFT);//UNSIGNED3

   ////////////////////////////
   //TempText="UNSIGNED1 : ";
   //TempText+=DoubleToString(NormalizeDouble(0.0),3);   
   //ObjectSetString(0,OwnObjectNames[13],OBJPROP_TEXT,TempText);
   //TempText="UNSIGNED2 : ";
   //TempText+=DoubleToString(NormalizeDouble(0.0),3);   
   //ObjectSetString(0,OwnObjectNames[14],OBJPROP_TEXT,TempText);
   //TempText="UNSIGNED3 : ";
   //TempText+=DoubleToString(NormalizeDouble(0.0),3);
   //ObjectSetString(0,OwnObjectNames[15],OBJPROP_TEXT,TempText);
   ///////////////////////////
 

İlk üç girdi arayüzdeki yeni elemanların adlarını atar, ikinci üç girdi Uzman Danışmanın başlangıcında arayüz oluşturulurken kullanılır, son üç girdi ise arayüzdeki bilgileri güncellemek için fonksiyonda kullanılır. Şimdi her iki şablonun performansını test etme zamanı. Sınayıcı görselleştiricisi görsel bir sunum için yeterli olacaktır. Sadece MetaTrader 5 seçeneğini göstereceğim, çünkü görselleştiricisi orada çok daha iyidir. Ayrıca, çalışmanın sonucu, verimliliği doğrulamak için gereken her şeyi açıkça gösterecektir:

checking using MetaTrader 5 tester visualization


Gördüğünüz gibi, majör forex çiftleri için yedi grafiğin tümünü yükledik. Görselleştirme günlüğü, listelenen tüm semboller için alım-satımın devam ettiğini gösteriyor. Alım-satım, gerektiği şekilde bağımsız olarak gerçekleştirilmektedir. Başka bir deyişle, Uzman Danışmanlar her biri kendi grafiğinde alım-satım yapmakta ve birbirleriyle hiç bir etkileşime girmemektedir


Sonuç

Bu makalede, MetaTrader 4 ve MetaTrader 5 terminalleri için evrensel şablonlar oluşturmanın ana nüanslarını inceledik, basit ama çalışan bir şablon oluşturduk, çalışmasının en önemli noktalarını analiz ettik ve ayrıca MetaTrader 5 sınayıcı görselleştiricisini kullanarak uygulanabilirliğini doğruladık. Sanırım, böyle bir şablonun o kadar da karmaşık olmadığı artık oldukça açık. Genel olarak, bu tür şablonların çeşitli versiyonlarını yapabilirsiniz ve bu tür şablonların uygulanabilir kalırken tamamen farklı olabileceği açıktır. Önemli olan, bu tür yapıları inşa etmenin temel nüanslarını anlamaktır. Gerekirse, şablonlar üzerinde kişisel kullanım için yeniden çalışabilirsiniz.


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

Ekli dosyalar |
MultiTemplate.mq4 (93.94 KB)
MultiTemplate.mq5 (91.41 KB)
Swaplar (Bölüm I): Kilitleme ve sentetik pozisyonlar Swaplar (Bölüm I): Kilitleme ve sentetik pozisyonlar
Bu makalede klasik swap alım-satım yöntemleri konseptini genişletmeye çalışacağım. Bu kavramın neden özel bir ilgiyi hak ettiği ve üzerinde çalışılması gerektiği sonucuna vardığımı açıklayacağım.
Sıfırdan bir alım-satım Uzman Danışmanı geliştirme (Bölüm 31): Geleceğe doğru (IV) Sıfırdan bir alım-satım Uzman Danışmanı geliştirme (Bölüm 31): Geleceğe doğru (IV)
Uzman Danışmanımızdan farklı parçalar çıkarmaya devam ediyoruz. Bu, bu serideki son makaledir. Çıkarılacak son şey ise ses sistemi. Bu makale serisini takip etmediyseniz bu biraz kafa karıştırıcı olabilir.
Örneklerle formasyonlar (Bölüm I): Çoklu tepe Örneklerle formasyonlar (Bölüm I): Çoklu tepe
Bu, algoritmik alım-satım çerçevesinde terse dönüş formasyonlarıyla ilgili bir serinin ilk makalesidir. Çift tepe ve çift dip formasyonlarından kaynaklanan en ilginç formasyon ailesiyle başlayacağız.
Sıfırdan bir alım-satım Uzman Danışmanı geliştirme (Bölüm 30): Bir gösterge olarak alım-satım arayüzü? Sıfırdan bir alım-satım Uzman Danışmanı geliştirme (Bölüm 30): Bir gösterge olarak alım-satım arayüzü?
Bugün yine alım-satım arayüzünü kullanacağız, ancak bu sefer grafikte mevcut olabilecek veya olmayabilecek bir gösterge şeklinde olacaktır.