English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Italiano
Belirtilen Sihirli Sayıya Göre Toplam Pozisyon Hacmini Hesaplamak İçin Optimum Yöntem

Belirtilen Sihirli Sayıya Göre Toplam Pozisyon Hacmini Hesaplamak İçin Optimum Yöntem

MetaTrader 5Alım-satım sistemleri | 15 Aralık 2021, 10:39
139 0
Dmitry Fedoseev
Dmitry Fedoseev

Giriş

MetaTrader 5 istemci terminali, birkaç Expert Advisor'ın tek bir sembolle paralel çalışmasına olanak tanır. Bu, son derece basittir - Yalnızca birkaç grafik açın ve bunlara Expert Advisor'lar ekleyin. Her bir Expert Advisor aynı sembolle çalışan diğer Expert Advisor'lardan bağımsız olarak çalışsa daha iyi olurdu (farklı sembollerle çalışan Expert Advisor'larda böyle bir sorun yoktur).

Her şeyden önce, bu, bir Expert Advisor'ın, Strateji test Cihazındaki test performansı ve optimizasyonu ile tam uyum içinde alım satım yapmasına izin verecektir. Pozisyon açma koşulları, halihazırda açılmış pozisyonun boyutuna veya var olmamasına bağlı olabilir. Birkaç Expert Advisor aynı sembolle çalışırsa birbirini etkiler.

İkinci ve muhtemelen daha önemli olan şey, Expert Advisor'ların Expert Advisor'larda uygulanan alım satım stratejilerine bağlı olarak, farklı para yönetim sistemleri kullanmasına olanak tanımasıdır. Ve son olarak her bir Expert Advisor'ın sonuçlarını izleme ve gerekirse kapatma imkanıdır.

1. Pozisyon Hacmi Hesabının Genel Prensibi

Bir talimat açtığınızda, OrderSend() işlevine iletilen MqlTradeRequest yapısındaki sihirli değişkenin değerini belirterek bunu sihirli bir sayı ile işaretleyebilirsiniz. Talimat gerçekleştiğinde, yatırım da talimat sihirli sayısı ile işaretlenir. Ayrıca, geçmişteki yatırımları analiz ederek, farklı Expert Advisor'lar tarafından açılan yatırımları görebiliriz.

Toplam pozisyonu hesaplama yöntemi oldukça basittir: Örneğin, 0,1 hacimli bir alış yatırımı yaparsanız, sonra başka bir 0,1 alır ve 0,1 satarsanız toplam pozisyonun hacmi 0,1+0,1-0,1=+0,1'e eşit olacaktır. Alış yatırımlarının hacimlerini toplar ve satış yatırımlarının hacimlerini çıkarırız ve toplam pozisyonun hacmini elde ederiz.

Hesaplamalara toplam pozisyonun hacmi 0'a eşit olduğunda başlamak önemlidir. Bu tür ilk ve en bariz nokta hesap açılış anıdır. Başka bir deyişle, HistorySelect() işlevini 0'a eşit (mümkün olan en düşük süre) olan ilk parametre ve ikinci parametrenin TimeCurrent() değeriyle (bir sunucunun bilinen son zamanı) kullanarak hesabın tüm yatırım geçmişini talep edebilirsiniz:

HistorySelect(0,TimeCurrent()); // load all history

Ardından, baştan sona kadar tüm tarihi gözden geçirin, alış yatırımlarının hacimlerini ekleyin ve belirtilen sihirli sayı ile her bir yatırım için satış yatırımlarının hacimlerini çıkarın. Bu, aynı zamanda bir çözümdür, ancak pratikte yatırımların geçmişi oldukça büyük olabilir. Bu, bir Expert Advisor'ın hızını, özellikle test ve optimizasyon sırasında, (böyle bir Expert Advisor'ın pratik kullanımını imkansız hale getirecek şekilde) önemli ölçüde etkileyebilir. Toplam net pozisyonun hacmi sıfıra eşit olduğunda, yatırımlar geçmişindeki en son anı bulmamız gerekiyor.

Bunu yapmak için, ilk olarak tüm geçmişi gözden geçirmeli ve toplam net pozisyonun hacminin sıfır olduğu son noktayı bulmalıyız. Bu noktayı bularak, belirli bir değişkende (sabit pozisyon süresi) saklıyoruz. Daha sonra, Expert Advisor, saklanan noktanın zamanından itibaren yatırımların geçmişini gözden geçirecektir. Daha iyi bir çözüm, bu noktayı bir Expert Advisor değişkeni yerine istemci terminalinin genel bir değişkeninde saklamaktır; zira böyle bir durumda Expert Advisor ayrılırken yok edilecektir.

Bu tip bir durumda, Expert Advisor başlatıldığında dahi, tüm yatırım geçmişi yerine gerekli minimum geçmişi yüklemeniz gerekir. Aynı sembol üzerinde alım satım işlemi yapabilen birçok Expert Advisor vardır; bu nedenle bu genel değişkeni (sıfır hacmin son noktasının saklanan zamanı ile) tüm Expert Advisor'larla paylaşacağız.

Ana konudan uzaklaşalım ve birkaç Expert Advisor'ın aynı sembolle (belki farklı parametrelerle) çalışmasına izin veren ve farklı Expert Advisor örnekleri tarafından oluşturulan adların çakışmasını önleyen istemci terminalinin genel değişkenlerinin kullanımını ele alalım.

2. İstemci Terminalinin Genel Değişkenleri

MQL5 dili, bir mql5-programı hakkında farklı bilgiler elde etmeyi sağlayan MQLInfoString() işlevine sahiptir.

Dosya adı hakkında bilgi edinmek için bu işlevi MQL_PROGRAM_NAME tanımlayıcısıyla çağırın:

MQL5InfoString(MQL_PROGRAM_NAME); // Expert Advisor name

Bu nedenle, genel değişkenlerin adlarını bir Expert Advisor adıyla başlatıyoruz. Bir Expert Advisor birkaç sembolle çalışabilir; bu, bir sembolün (Symbol) adını eklememiz gerektiği anlamına gelir. Expert Advisor'lar aynı sembolle ancak farklı zaman dilimlerinde (farklı ayarlarla) çalışabilirler, bu durumlar için sihirli sayıyı kullanmamız gerekir. Bu nedenle, sihirli sayıyı da ekliyoruz.

Örneğin, Expert Advisor'ın Magic_N değişkeninde saklanan sihirli bir sayısı varsa onu genel değişkenin adına ekliyoruz.

Tüm genel değişkenlerin adları aşağıdaki gibi görünecektir:

gvp=MQLInfoString(MQL_PROGRAM_NAME)+"_"+_Symbol+"_"+IntegerToString(Magic_N)+"_"; // name of an Expert Advisor and symbol name 
                                                                            // and its magic number

Burada gvp (Genel Değişken Öneki), ortak değişkenler bölümünde bildirilen bir dize değişkenidir.

Programlamada kullanıldıkları için genel değişkenlerin karıştırılmasını önlemek için terminolojiyi netleştirmek istiyorum (genel değişkenler tüm işlevlerin içinde görünür, işlevlerin yerel değişkenleri yalnızca işlevin içinde görünür).

Ama burada farklı bir durum var. "Genel değişkenler" terimi, istemci terminalinin genel değişkenleri anlamına gelir (bir dosyada saklanan özel değişkenler, bunlar GlobalVariable...() işlevleri tarafından kullanılabilirler). Genel değişkenler hakkında konuşurken (programlamada kullanıldığı gibi), "ortak değişkenler" terimini kullanacağız. Yerel değişkenler terimi, yerel değişkenler anlamına gelir.

Genel değişkenler faydalıdır; zira bir Expert Advisor'ın (Expert Advisor'ın, istemci terminalinin, bilgisayarın yeniden başlatılması) sıfırlanmasından sonra değerlerini saklarlar, ancak test modunda tüm değişkenleri (veya optimizasyon sırasında önceki bir geçişi) temizlemek gerekir. Gerçek işlemlerde kullanılan genel değişkenler, test sırasında oluşturulan genel değişkenlerden ayrılmalıdır; bunların testten sonra silinmesi gerekir. Ancak Expert Advisor tarafından oluşturulan genel değişkenleri değiştirmemeli veya silmemelisiniz.

AccountInfoInteger() işlevini kullanarak ve onu ACCOUNT_TRADE_MODE tanımlayıcısıyla çağırarak, mevcut modu ayırt edebilirsiniz: Test cihazı, demo veya gerçek hesap.

Genel değişkenlere bir önek ekleyelim: "d" önekini demo hesaplarda çalışırken, "r" önekini gerçek hesaplarda çalışırken, "t" önekini Strateji Test Cihazında çalışırken kullanalım:

gvp=MQLInfoString(MQL_PROGRAM_NAME)+"_"+_Symbol+"_"+IntegerToString(Magic_N)+"_"; // name of an Expert Advisor, symbol name
                                                                                  // and the Expert Advisor magic number
if(AccountInfoInteger(ACCOUNT_TRADE_MODE)==ACCOUNT_TRADE_MODE_DEMO))
  {
   gvp=gvp+"d_"; // demo account
  }
if(AccountInfoInteger(ACCOUNT_TRADE_MODE)==ACCOUNT_TRADE_MODE_REAL)
  {
   gvp=gvp+"r_"; // real
  }
if(MQL5InfoInteger(MQL_TESTER))
  {
   gvp=gvp+"t_"; // testing
  } 

İşlev, Expert Advisor'ın OnInit() işlevinden çağrılmalıdır.

Yukarıda bahsedildiği gibi, genel değişkenler test yapılırken silinmelidir; başka bir deyişle bir Expert Advisor'ın OnDeinit() işlevine genel değişkenleri silen işlevi eklememiz gerekir:

void fDeleteGV()
  {
   if(MQL5InfoInteger(MQL_TESTER)) // Testing mode
     {
      for(int i=GlobalVariablesTotal()-1;i>=0;i--) // Check all global variables (from the end, not from the begin)
        {
         if(StringFind(GlobalVariableName(i),gvp,0)==0) // search for the specified prefix
           {
            GlobalVariableDel(GlobalVariableName(i)); // Delete variable
           }
        }
     }
  }

Şu anda MetaTrader 5'te testi yarıda kesme imkansızdır; yani, OnDeinit() işlevinin yürütülmesi garanti edilmez, ancak gelecekte bu, ortaya çıkabilir. Strateji Test Cihazının kesintiye uğramasından sonra OnDeinit() işlevinin yürütülüp yürütülmeyeceğini bilmiyoruz; bu nedenle, OnInit() işlevinin içinde çalışan Expert Advisor'ın başlangıcındaki genel değişkenleri sileriz.

OnInit() ve OnDeinit() işlevlerinin aşağıdaki kodunu alacağız:

int OnInit()
  {
   fCreateGVP(); // Creating a prefix for the names of global variables of the client terminal
   fDeleteGV();  // Delete global variables when working in Tester
   return(0);
  }

void OnDeinit(const int reason)
  {
   fDeleteGV();  // Delete global variables when working in tester
  }

Ayrıca, genel değişkenler oluşturma için kısa adlarla işlevler oluşturarak genel değişkenlerin kullanımını basitleştirebiliriz GlobalVariableSet(gvp+... yerine).

Genel değişkenin değerini ayarlama işlevi:

void fGVS(string aName,double aValue)
  {
   GlobalVariableSet(gvp+aName,aValue);
  }

Genel değişkenin değerini alma işlevi:

double fGVG(string aName)
  {
   return(GlobalVariableGet(gvp+aName));
  }

Genel değişkeni silme işlevi:

void fGVD(string aName)
  {
   GlobalVariableDel(gvp+aName);
  }

Genel değişkenleri tartıştık fakat hepsi bu kadar değil.

Bir sembol için genel değişkenler oluşturma ve hesapta ve Strateji Test Cihazında bunların farklı şekilde işlemelerini sağlama imkanı yaratmamız gerekiyor. Bu genel değişkenlerin adları bir Expert Advisor'ın adına ve sihirli sayısına bağlı olmamalıdır.

Genel bir değişken öneki için "Commom_gvp" adında başka bir değişken bildirelim. Daha sonra bir hesapla çalışırken bu, "COMMON" değerine sahip olacak ve Strateji Test Cihazı ile çalışırken gvp değişkeni ile aynı değere sahip olacaktır (değişkeni strateji geriye dönük test sürecinden sonra veya önce silmek için).

Son olarak, genel değişken öneklerini hazırlama işlevi aşağıdaki forma sahiptir:

void fCreateGVP()
  {
   gvp=MQL5InfoString(MQL_PROGRAM_NAME)+"_"+_Symbol+"_"+IntegerToString(Magic_N)+"_";
   Commom_gvp="COMMOM_"; // Prefix for common variables for all Expert Advisors
   if(AccountInfoInteger(ACCOUNT_TRADE_MODE)==ACCOUNT_TRADE_MODE_DEMO)
     {
      gvp=gvp+"d_";
     }
   if(AccountInfoInteger(ACCOUNT_TRADE_MODE)==ACCOUNT_TRADE_MODE_REAL)
     {
      gvp=gvp+"r_";
     }
   if(MQLInfoInteger(MQL_TESTER))
     {
      gvp=gvp+"t_";
      Commom_gvp=gvp; // To be used in tester, the variables with such a prefix 
                      // will be deleted after the testing
     }
  }

Genel değişkenlerin öneklerinin ekstra bilgiler içerdiği düşünülebilir. Demo ve gerçek hesapların ayrılması ve test sırasında "t" öneki, "t" karakteri eklenerek yapılabilmesine rağmen Expert Advisor'ımızın Strateji Test Cihazında çalıştığını gösterir. Ama ben bu şekilde yaptım. Geleceği ve Expert Advisor'ların çalışmalarını analiz etmek için gerekli olabilecek şeyleri bilmiyoruz.

Fazla mal göz çıkarmaz derler.

Yukarıda sunulan işlevler, istemci terminalinin tek bir hesapla çalıştığı anlamına gelir; çalışması sırasında herhangi bir hesap değişikliği yoktur. Expert Advisor'ın çalışması sırasında hesap değiştirmek yasaktır. Elbette, gerekirse genel değişkenlerin adlarına bir hesap numarası eklenerek bu sorun çözülebilir.

Çok önemli başka bir husus daha var! Genel değişken adının uzunluğu 63 sembolle sınırlıdır. Bu nedenle Expert Advisor'larınıza uzun adlar vermeyin.

Genel değişkenler konusunu bitirdik, şimdi makalenin ana konusunu yani belirli bir sihirli sayı ile pozisyon hacminin hesaplanması konusunu ele alma zamanı.

3. Pozisyon Hacmini Hesaplama

İlk olarak GlobalVariableCheck() işlevini kullanarak sıfır hacim pozisyonunun son zamanı hakkında bilgi içeren genel bir değişken olup olmadığını kontrol edelim (basit olması için açılmış pozisyon yoksa bunu "sıfır pozisyon" durumu olarak adlandırıyoruz).

Böyle bir değişken varsa - yatırımların geçmişini değişkende saklanan zamandan başlayarak yükleyelim, aksi takdirde tüm geçmişi yükleyeceğiz:

if(GlobalVariableCheck(Commom_gvp+sSymbol+"_HistStTm")) // Saved time of a "zero" total position
  {
   pLoadHistoryFrom=(datetime)GlobalVariableGet(Commom_gvp+pSymbol+"_HistStTm"); // initial date setting 
                                                                             // select only the history needed
  }
else
 {
   GlobalVariableSet(Commom_gvp+sSymbol+"_HistStTm",0);
 }
if(!HistorySelect(sLoadHistoryFrom,TimeCurrent())) // Load the necessary part of the deal history
  { 
   return(false);
  } 

Ardından, bir sembol için toplam net pozisyonun hacmini tanımlarız:

double CurrentVolume=fSymbolLots(pSymbol);

Bir pozisyonun hacmi, fSymbolLots() işlevi kullanılarak belirlenir.

Bir pozisyonun hacmini elde etmenin birkaç yolu vardır: Örneğin, bu, PositionSelect() işlevi kullanılarak yapılabilir. İşlev false değerini döndürürse bu, herhangi bir pozisyon olmadığı anlamına gelir (hacmi sıfıra eşittir). İşlev true değerini döndürürse hacim, POSITION_VOLUME tanımlayıcılı PositionGetDouble() işlevi kullanılarak elde edilebilir. Pozisyon türü (alış veya satış), POSITION_TYPE tanımlayıcılı PositionGetInteger() işlevi kullanılarak belirlenir. İşlev, uzun pozisyonlar için pozitif, kısa pozisyonlar için negatif bir değer döndürür.

Tam işlev aşağıdaki gibi görünür:

double fSymbolLots(string aSymbol)
  {
   if(PositionSelect(aSymbol,1000)) // the position has been selected successfully, so it exists
     {
      switch(PositionGetInteger(POSITION_TYPE)) // It returns the positive or negative value dependent on the direction
        {
         case POSITION_TYPE_BUY:
            return(NormalizeDouble(PositionGetDouble(POSITION_VOLUME),2));
            break;
         case POSITION_TYPE_SELL:
            return(NormalizeDouble(-PositionGetDouble(POSITION_VOLUME),2));
            break;
        }
     }
   else
     {
      return(0);
     }
  }

Alternatif olarak, sembolün toplam pozisyonunun hacmini tüm pozisyonlar arasında döngü yaparak belirleyebilirsiniz, pozisyon sayısı PositionsTotal() işleviyle belirlenir. Ardından, PositionGetSymbol() işlevini kullanarak gerekli sembolü bulun ve pozisyonun hacmini ve yönünü belirleyin (POSITION_VOLUME tanımlayıcılı PositionGetDouble() ile ve POSITION_TYPE tanımlayıcılı PositionGetInteger() işlevi).

Bu durumda, hazır işlevi şu şekilde olacaktır: 

double fSymbolLots(string aSymbol)
  {
   double TmpLots=0;
   for(int i=0;i<PositionsTotal();i++) // Go through all positions
     {
      if(PositionGetSymbol(i)==aSymbol) // we have found a position with specified symbol
        {
         TmpLots=PositionGetDouble(POSITION_VOLUME);
         if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
           {
            TmpLots*=-1; // the sign is dependent on the position type           }
         break;
        }
     }
   TmpLots=NormalizeDouble(TmpLots,2);
   return(TmpLots);
  }

Mevcut hacmi belirledikten sonra, hacimlerin toplamı hacme eşit olana kadar, baştan sona yatırımların geçmişini inceleyeceğiz.

Seçilen geçmiş ща yatırımlarının uzunluğu HistoryDealsTotal() işlevi kullanılarak belirlenir, bilet, her yatırım için HistoryDealGetTicket() işlevi kullanılarak belirlenir, yatırım verileri HistoryDealGetInteger() işlevi kullanılarak ayıklanır (yatırım türü için DEAL_TYPE tanımlayıcısı) ve HistoryDealGetDouble() (yatırım hacmi için DEAL_VOLUME tanımlayıcısı):

double Sum=0; 
int FromI=0;
int FromTicket=0;
for(int i=HistoryDealsTotal()-1;i>=0;i--) // go through all the deals from the end to the beginning 
  {
   ulong ticket=HistoryDealGetTicket(i); // Get ticket of the deal
   if(ticket!=0)
     {
      switch(HistoryDealGetInteger(ticket,DEAL_TYPE)) // We add or subtract the volume depending on deal direction
        {
         case DEAL_TYPE_BUY:
            Sum+=HistoryDealGetDouble(ticket,DEAL_VOLUME);
            Sum=NormalizeDouble(Sum,2);
            break;
         case DEAL_TYPE_SELL:
            Sum-=HistoryDealGetDouble(ticket,DEAL_VOLUME);
            Sum=NormalizeDouble(Sum,2);
            break;
        }
      if(CurrentVolume==Sum) // all the deals has scanned
        {
         sLoadHistoryFrom=HistoryDealGetInteger(ticket,DEAL_TIME); // Save the time of a "zero" position
         GlobalVariableSet(Commom_gvp+aSymbol+"_HistStTm",sLoadHistoryFrom);
         FromI=i; // Save the index
         break;
        }
     }
  }

Bu noktayı bulduğumuzda, zamanı, yatırımların geçmişini yüklerken kullanılacak olan genel değişkende saklarız (geçmişteki yatırım indeksi FromI değişkeninde saklanır).

FromI indeksiyle yatırımdan önce, sembol üzerindeki toplam pozisyon sıfıra eşitti.

Şimdi FromI'den geçmişin sonuna doğru gidiyoruz ve belirtilen sihirli sayı ile yatırımların hacmini sayıyoruz:

static double sVolume=0;
static ulong sLastTicket=0;
for(int i=FromI;i<HistoryDealsTotal();i++) // from the first deal until the end
  {
   ulong ticket=HistoryDealGetTicket(i);   // Get deal ticket
   if(ticket!=0)
     {
      if(HistoryDealGetString(ticket,DEAL_SYMBOL)==aSymbol) // Specified symbol
        {
         long PosMagic=HistoryDealGetInteger(ticket,DEAL_MAGIC);
         if(PosMagic==aMagic || aMagic==-1) // Specified magic
           {
            switch(HistoryDealGetInteger(ticket,DEAL_TYPE)) // add or subtract the deal volumes 
                                                       // depending on the deal type
              {
               case DEAL_TYPE_BUY:
                  sVolume+=HistoryDealGetDouble(ticket,DEAL_VOLUME);
                  sLastTicket=ticket;
                  break;
               case DEAL_TYPE_SELL:
                  sVolume-=HistoryDealGetDouble(ticket,DEAL_VOLUME);
                  sLastTicket=ticket;
                  break;
              }
           }
        }
     }
  } 

Döngünün bitiminden sonra, belirtilen sihirli sayıya göre geçerli bir pozisyonun hacmine sahip olacağız; belirtilen sihirli sayı ile son yatırımın bileti, sLastTicket değişkeninde saklanacaktır, yatırımın yürütülmesinden sonra belirtilen sihirli sayı ile bir pozisyonun toplam hacmi sVolume'a eşit olacaktır. İşlevin ön çalışması tamamlandı.

sLoadHistoryFrom, sLastTicket ve sVolume değişkenleri statik olarak bildirilir (değerlerini işlevin tamamlanmasından sonra saklaçrlar), bu değerler işlevin her çağrısı için ayrıca kullanılacaktır.

Zamana sahibiz (yatırımlar geçmişinin başlangıç noktası); yatırım bileti, yürütülmesinden sonra toplam pozisyonun hacmi (belirtilen sembolle) cari evcut değere sahip olacaktır.

Sıfır hacim pozisyonunun zamanı olduğu için, mevcut zamandan saklanan zamana kadar geçmişe gitmek ve yatırım hacimleri toplamasını yapmak ve son yatırımın hacmini ve biletini saklamak yeterlidir.

Dolayısıyla, Expert Advisor'ın toplam pozisyonunun hesaplanması, birkaç son yatırımın işlenmesidir:

if(!HistorySelect(sLoadHistoryFrom,TimeCurrent())) // Request for the deals history up to the current time
  {
   return(false);
  }
for(int i=HistoryDealsTotal()-1;i>=0;i--) // Loop from the end
  {
   ulong ticket=HistoryDealGetTicket(i); // Get ticke
   if(ticket!=0)
     {
      if(ticket==sLastTicket) // We have found the already calculated deal, save the ticket and break
        {
         sLastTicket=HistoryDealGetTicket(HistoryDealsTotal()-1);
         break;
        }
      switch(HistoryDealGetInteger(ticket,DEAL_TYPE)) // Add or subtract deal volume depending on deal type      
        {
         case DEAL_TYPE_BUY:
            sVolume+=HistoryDealGetDouble(ticket,DEAL_VOLUME);
            break;
         case DEAL_TYPE_SELL:
            sVolume-=HistoryDealGetDouble(ticket,DEAL_VOLUME);
            break;
        }
     }
  }

İşlevin algoritması aşağıdaki gibi sunulabilir:


Tam işlev:

bool fGetPositionVolume(string aSymbol,int aMagic,double aVolume)
  {
   static bool FirstStart=false;
   static double sVolume=0;
   static ulong sLastTicket=0;
   static datetime sLoadHistoryFrom=0;
   // First execution of function when Expert Advisor has started
   if(!FirstStart)
     {
      if(GlobalVariableCheck(Commom_gvp+aSymbol+"_HistStTm"))
        {
         sLoadHistoryFrom=(datetime)GlobalVariableGet(Commom_gvp+aSymbol+"_HistStTm");
        }
      else
        {
         GlobalVariableSet(Commom_gvp+aSymbol+"_HistStTm",0);
        }
      if(!HistorySelect(sLoadHistoryFrom,TimeCurrent())) // Return if unsuccessful, 
                                                      // we will repeat on the next tick
        {
         return(false);
        }
      double CurrentVolume=fSymbolLots(aSymbol); // Total volume
      double Sum=0;
      int FromI=0;
      int FromTicket=0;
      // Search the last time when position volume was equal to zero
      for(int i=HistoryDealsTotal()-1;i>=0;i--)
        {
         ulong ticket=HistoryDealGetTicket(i);
         if(ticket!=0)
           {
            switch(HistoryDealGetInteger(ticket,DEAL_TYPE))
              {
               case DEAL_TYPE_BUY:
                  Sum+=HistoryDealGetDouble(ticket,DEAL_VOLUME);
                  Sum=NormalizeDouble(Sum,2);
                  break;
               case DEAL_TYPE_SELL:
                  Sum-=HistoryDealGetDouble(ticket,DEAL_VOLUME);
                  Sum=NormalizeDouble(Sum,2);
                  break;
              }
            if(CurrentVolume==Sum)
              {
               sLoadHistoryFrom=HistoryDealGetInteger(ticket,DEAL_TIME);
               GlobalVariableSet(Commom_gvp+aSymbol+"_HistStTm",sLoadHistoryFrom);
               FromI=i;
               break;
              }
           }
        }
      // Calculate the volume of position with specified magic number and symbol
      for(int i=FromI;i<HistoryDealsTotal();i++)
        {
         ulong ticket=HistoryDealGetTicket(i);
         if(ticket!=0)
           {
            if(HistoryDealGetString(ticket,DEAL_SYMBOL)==aSymbol)
              {
               long PosMagic=HistoryDealGetInteger(ticket,DEAL_MAGIC);
               if(PosMagic==aMagic || aMagic==-1)
                 {
                  switch(HistoryDealGetInteger(ticket,DEAL_TYPE))
                    {
                     case DEAL_TYPE_BUY:
                        sVolume+=HistoryDealGetDouble(ticket,DEAL_VOLUME);
                        sLastTicket=ticket;
                        break;
                     case DEAL_TYPE_SELL:
                        sVolume-=HistoryDealGetDouble(ticket,DEAL_VOLUME);
                        sLastTicket=ticket;
                        break;
                    }
                 }
              }
           }
        }
      FirstStart=true;
     }

   // Recalculate the volume of a position (with specified symbol and magic)
   // for the deals, after the zero position time
   if(!HistorySelect(sLoadHistoryFrom,TimeCurrent()))
     {
      return(false);
     }
   for(int i=HistoryDealsTotal()-1;i>=0;i--)
     {
      ulong ticket=HistoryDealGetTicket(i);
      if(ticket!=0)
        {
         if(ticket==sLastTicket)
           {
            sLastTicket=HistoryDealGetTicket(HistoryDealsTotal()-1);
            break;
           }
         switch(HistoryDealGetInteger(ticket,DEAL_TYPE))
           {
            case DEAL_TYPE_BUY:
               sVolume+=HistoryDealGetDouble(ticket,DEAL_VOLUME);
               break;
            case DEAL_TYPE_SELL:
               sVolume-=HistoryDealGetDouble(ticket,DEAL_VOLUME);
               break;
           }
        }
     }
   aVolume=NormalizeDouble(sVolume,2);;
   return(true);
  }

Sembol ve sihirli sayı, pozisyon hacmini döndüren işleve iletilir. Başarılıysa true, başarısızsa false değerini döndürür.

Başarılı olduğunda, istenen hacmi, işleve referansla iletilen aVolume değişkenine döndürür. İşlevde bildirilen statik değişkenler, bu işlevin farklı parametrelerle (sembol ve sihirli sayı) kullanılmasına izin vermez.

MQL4 durumunda bu sorun, bu işlevin farklı bir adla bir kopyasının oluşturulması ve diğer "sembol-sihirli sayı" çifti için çağrılması veya FirstStart, sVolume, sLastTicket, sLoadHistoryFrom değişkenlerinin her bir "sembol-sihirli sayı" çifti için ortak değişkenler olarak bildirilmesiyle ve bunların işleve iletilmesiyle çözülebilir.

Bu, aynı şekilde MQL5'te de uygulanabilir fakat MQL5'in çok daha kullanışlı bir özelliği var - sınıfların kullanımının mantıklı olduğu durumda sınıflar. Sınıfları kullanırken, her bir sembol-sihirli sayı çifti için bir sınıf örneği oluşturmak gerekir, veriler her sınıf örneğinde saklanacaktır.

Bir PositionVolume sınıfı bildirelim. İşlev içinde static olarak bildirilen tüm değişkenler özel olarak bildirilecektir, Volume değişkeni dışında bunları doğrudan Expert Advisor'dan kullanmayacağız. Ancak buna yalnızca hacim hesaplama işlevinin yürütülmesinden sonra ihtiyacımız olacak. Ayrıca Symbol ve Magic değişkenlerini de bildiririz - Bunları işleve iletmek pratik değildir; bunu, yalnızca sınıf örneğini başlatırken bir kez yapın.

Sınıfın iki genel işlevi olacaktır: Başlatma işlevi ve pozisyon hacmini hesaplama işlevi. Ayrıca pozisyonun toplam hacmini belirlemek için özel bir işlevi olacaktır:

class PositionVolume
  {
private:
   string            pSymbol;
   int               pMagic;
   bool              pFirstStart;
   ulong             pLastTicket;
   double            pVolume;
   datetime         pLoadHistoryFrom;
   double            SymbolLots();
public:
   void Init(string aSymbol,int aMagic)
     {
      pSymbol=aSymbol;
      pMagic=aMagic;
      pFirstStart=false;
      pLastTicket=0;
      pVolume=0;
     }
   bool              GetVolume(double  &aVolume);
  }; 
bool PositionVolume::GetVolume(double  &aVolume)
  {
   if(!pFirstStart)
     {
      if(GlobalVariableCheck(Commom_gvp+pSymbol+"_HistStTm"))
        {
         pLoadHistoryFrom=(datetime)GlobalVariableGet(Commom_gvp+pSymbol+"_HistStTm");
        }
      else
        {
         GlobalVariableSet(Commom_gvp+pSymbol+"_HistStTm",0);
        }
      if(!HistorySelect(pLoadHistoryFrom,TimeCurrent()))
        {
         return(false);
        }
      double CurrentVolume=fSymbolLots(pSymbol);
      double Sum=0;
      int FromI=0;
      int FromTicket=0;
      for(int i=HistoryDealsTotal()-1;i>=0;i--)
        {
         ulong ticket=HistoryDealGetTicket(i);
         if(ticket!=0)
           {
            switch(HistoryDealGetInteger(ticket,DEAL_TYPE))
              {
               case DEAL_TYPE_BUY:
                  Sum+=HistoryDealGetDouble(ticket,DEAL_VOLUME);
                  Sum=NormalizeDouble(Sum,2);
                  break;
               case DEAL_TYPE_SELL:
                  Sum-=HistoryDealGetDouble(ticket,DEAL_VOLUME);
                  Sum=NormalizeDouble(Sum,2);
                  break;
              }
            if(CurrentVolume==Sum)
              {
               pLoadHistoryFrom=HistoryDealGetInteger(ticket,DEAL_TIME);
               GlobalVariableSet(Commom_gvp+pSymbol+"_HistStTm",pLoadHistoryFrom);
               FromI=i;
               break;
              }
           }
        }
      for(int i=FromI;i<HistoryDealsTotal();i++)
        {
         ulong ticket=HistoryDealGetTicket(i);
         if(ticket!=0)
           {
            if(HistoryDealGetString(ticket,DEAL_SYMBOL)==pSymbol)
              {
               long PosMagic=HistoryDealGetInteger(ticket,DEAL_MAGIC);
               if(PosMagic==pMagic || pMagic==-1)
                 {
                  switch(HistoryDealGetInteger(ticket,DEAL_TYPE))
                    {
                     case DEAL_TYPE_BUY:
                        pVolume+=HistoryDealGetDouble(ticket,DEAL_VOLUME);
                        pLastTicket=ticket;
                        break;
                     case DEAL_TYPE_SELL:
                        pVolume-=HistoryDealGetDouble(ticket,DEAL_VOLUME);
                        pLastTicket=ticket;
                        break;
                    }
                 }
              }
           }
        }
      pFirstStart=true;
     }
   if(!HistorySelect(pLoadHistoryFrom,TimeCurrent()))
     {
      return(false);
     }
   for(int i=HistoryDealsTotal()-1;i>=0;i--)
     {
      ulong ticket=HistoryDealGetTicket(i);
      if(ticket!=0)
        {
         if(ticket==pLastTicket)
           {
            break;
           }
         if(HistoryDealGetString(ticket,DEAL_SYMBOL)==pSymbol)
           {
            long PosMagic=HistoryDealGetInteger(ticket,DEAL_MAGIC);
            if(PosMagic==pMagic || pMagic==-1)
              {
               switch(HistoryDealGetInteger(ticket,DEAL_TYPE))
                 {
                  case DEAL_TYPE_BUY:
                     pVolume+=HistoryDealGetDouble(ticket,DEAL_VOLUME);
                     break;
                  case DEAL_TYPE_SELL:
                     pVolume-=HistoryDealGetDouble(ticket,DEAL_VOLUME);
                     break;
                 }
              }
           }
        }
     }
   if(HistoryDealsTotal()>0)
     {
      pLastTicket=HistoryDealGetTicket(HistoryDealsTotal()-1);
     }
   pVolume=NormalizeDouble(pVolume,2);
   aVolume=pVolume;
   return(true);
  }

double PositionVolume::SymbolLots()
  {
   double TmpLots=0;
   for(int i=0;i<PositionsTotal();i++)
     {
      if(PositionGetSymbol(i)==pSymbol)
        {
         TmpLots=PositionGetDouble(POSITION_VOLUME);
         if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
           {
            TmpLots*=-1;
           }
         break;
        }
     }
   TmpLots=NormalizeDouble(TmpLots,2);
   return(TmpLots);
  }

Her bir sembol-sihirli sayı çifti için bu sınıfı kullanırken, bir sınıf örneği oluşturmak gerekir:

PositionVolume PosVol11;
PositionVolume PosVol12;
PositionVolume PosVol21;
PositionVolume PosVol22;

Bu, bir Expert Advisor'ın OnInit() işlevinde başlatılmalıdır, örneğin:

PosVol11.Init(Symbol_1,Magic_1); 
PosVol12.Init(Symbol_1,Magic_2);
PosVol21.Init(Symbol_2,Magic_1); 
PosVol22.Init(Symbol_2,Magic_2);   

Bundan sonra, belirtilen sembol ve sihirli sayı ile bir pozisyonun hacmini elde etmek mümkündür. Karşılık gelen sınıf örneğinin GetVolume işlevini çağıralım.

Başarılı olursa, true değerini döndürür ve değeri, işlevin parametresi olarak referansla iletilen değişkene koyar:

double Vol11;
double Vol12;
double Vol21;
double Vol22;
PosVol11.GetVolume(Vol11);
PosVol12.GetVolume(Vol12);
PosVol21.GetVolume(Vol21);
PosVol22.GetVolume(Vol22);

Konunun burada bittiği düşünülebilir ama daha kontrol testi var.

4. Kontrol Testi

İşlevin çalışmasını test etmek için, aynı anda dört pozisyonla çalışan bir Expert Advisor kullandık:

  1. Sihirli sayı 1 ile EURUSD üzerinde 14 dönemi olan RSI göstergesi kullanımı,
  2. Sihirli sayı 2 ile EURUSD üzerinde 21 dönemi olan RSI göstergesi kullanımı,
  3. Sihirli sayı 1 ile GBPUSD üzerinde 14 dönemi olan RSI göstergesi kullanımı,
  4. Sihirli sayı 2 ile GBPUSD üzerinde 21 dönemi olan RSI göstergesi kullanımı.

1 sihirli sayılı Expert Advisor 0,1 lot hacminde, 2 sihirli sayılı Expert Advisor 0,2 lota eşit hacimde alım satım işlemi yaptı.

Yatırım hacmi, yatırım yapılırken Expert Advisor değişkenlerine eklenir, yatırımdan önce ve sonra her pozisyonun hacmi yukarıda sunulan işlev kullanılarak belirlenir. 

Hacimlerin hesaplanmasında bir hata varsa işlev bir mesaj oluşturur.

Expert Advisor'ın kodu makalenin ekinde bulunabilir (dosya adı: ePosVolTest.mq5).

Sonuç

Bir Expert Advisor için birçok işleve ihtiyaç vardır ve bunların her aşamada kullanıma elverişli şekilde uygulanması gerekir. Bu işlevler, bilgi işlem kaynaklarının en iyi kullanımı açısından yazılmalıdır.

Bu makalede önerilen pozisyon hacminin hesaplanma yöntemi bu koşulları karşılar. Başlatıldığında yalnızca gerekli minimum yatırım geçmişini yükler. Çalışırken, en son yatırımları kullanarak pozisyonun mevcut hacmini tekrar hesaplar.

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

Ekli dosyalar |
eposvoltest.mq5 (17.98 KB)
Karı Geri Çekme İşlemlerini Modellemek için TesterWithdrawal() İşlevini Kullanma Karı Geri Çekme İşlemlerini Modellemek için TesterWithdrawal() İşlevini Kullanma
Bu makalede, işlem sırasında varlıkların belirli bir bölümünün geri çekilmesini gerektiren alım satım sistemlerindeki riskleri tahmin etmek için TesterWithDrawal() işlevinin kullanımı açıklanmaktadır. Ayrıca, bu işlevin strateji test cihazında hisse senedi düşüşü hesaplama algoritması üzerindeki etkisi de açıklanmaktadır. Bu işlev, Expert Advisor'larınızın parametresini optimize ederken kullanışlıdır.
MQL5 Nesne Yönelimli Programlama Yaklaşımını Kullanarak Expert Advisor Yazma MQL5 Nesne Yönelimli Programlama Yaklaşımını Kullanarak Expert Advisor Yazma
Bu makalede, "Yeni Başlayanlar için MQL5'te Expert Advisor yazmak için Adım Adım Kılavuz" adlı makalede yaptığımız şeyi yapmak için (Basit bir Expert Advisor oluşturma) nesne yönelimli yaklaşıma odaklanılmaktadır. Birçok kişi bunun zor olduğunu düşünüyor, ancak bu makaleyi okumayı bitirdiğinizde, nesne yönelimi temelinde kendi Expert Advisor'ınızı yazabileceğinizi garanti ediyorum.
Expert Advisor yazarken MQL5 Standard Alım Satım Sınıfı kitaplıklarının kullanımı Expert Advisor yazarken MQL5 Standard Alım Satım Sınıfı kitaplıklarının kullanımı
Bu makalede, bir alım satım işlemi yapmadan önce pozisyon kapatma ve değiştirme, bekleyen talimat verme ve Marjın silinmesi ve doğrulanmasını uygulayan Expert Advisor'ları yazarken MQL5 Standart Kitaplık Alım Satım Sınıflarının başlıca işlevlerinin nasıl kullanılacağı açıklanmaktadır. Ayrıca, talimat ve yatırım ayrıntılarını elde etmek için Alım Satım sınıflarının nasıl kullanılabileceğini de gösterdik.
Adlandırılmış Kanalları kullanarak MetaTrader 5 terminalleri arasında iletişim kurmak için DLL içermeyen bir çözüm Adlandırılmış Kanalları kullanarak MetaTrader 5 terminalleri arasında iletişim kurmak için DLL içermeyen bir çözüm
Makalede, adlandırılmış kanallar kullanılarak MetaTrader 5 istemci terminalleri arasında İşlemler Arası İletişimin nasıl uygulanacağı açıklanmaktadır. Adlandırılmış kanalların kullanımı için CNamedPipes sınıfı geliştirilmiştir. Kullanımını test etmek ve bağlantı verimini ölçmek için tick göstergesi, sunucu ve istemci script dosyaları sunulur. Adlandırılmış kanalların kullanılması, gerçek zamanlı fiyat teklifleri için yeterlidir.