English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Italiano
MQL5 Tarif Defteri Alım Satım Seviyelerini Ayarlarken/Değiştirirken Hatalardan Nasıl Kaçınılır?

MQL5 Tarif Defteri Alım Satım Seviyelerini Ayarlarken/Değiştirirken Hatalardan Nasıl Kaçınılır?

MetaTrader 5Örnekler | 13 Ocak 2022, 09:31
58 0
Anatoli Kazharski
Anatoli Kazharski

Giriş

"MQL5 Tarif Defteri: Pozisyon Parametrelerini MetaTrader 5 Strateji Test Cihazında Analiz Etme" isimli serinin önceki makalesinden Uzman Danışman üzerindeki çalışmamızın devamında, bunu birçok faydalı fonksiyon ile geliştirecek ve mevcut olanları iyileştirip optimize edeceğiz.

Alım satım seviyelerini ayarlarken/değiştirirken (Zarar Durdur, Kâr Al ve bekleyen emirler) ortaya çıkan hatalarla ilgili yeni başlayanlardan gelen sorular, MQL programlama forum(lar)ında hiç de nadir görünmez. Çoğunuzun [Geçersiz stop noktaları] ile biten günlük mesajına zaten aşina olduğunuza inanıyorum. Bu makalede, bir pozisyon açmadan/değiştirmeden önce alım satım seviyesi değerlerini normalleştiren ve doğruluğunu kontrol eden fonksiyonlar oluşturacağız.

Uzman Danışman, bu sefer MetaTrader 5 Strateji Test Cihazında optimize edilebilecek harici parametrelere sahip olacak ve bazı yönlerden basit bir alım sistemine benzeyecektir. Gerçek bir alım satım sistemi geliştirmeden önce kesinlikle kat etmemiz gereken uzun bir yol var. Ama Roma bir günde kurulmadı. Yani daha yapacak çok işimiz var.

Makale ilerledikçe mevcut fonksiyonlarda kod optimizasyonu dikkate alınacaktır. Standart tanımlayıcılar kullanılarak elde edilemeyen bazı pozisyon özelliklerine hala bakmamız gerektiğinden bilgi paneli bu noktada ele alınmayacaktır (işlemler geçmişinin kullanılması gereklidir). Yine de bu konu, serinin aşağıdaki makalelerinden birinde ele alınacaktır.


Uzman Danışman Geliştirme

Öyleyse başlayalım. Her zamanki gibi, dosyanın başına ek numaralandırmalar, değişkenler, diziler ve yardımcı fonksiyonlar ekleyerek başlıyoruz. Sembol özelliklerini kolayca elde etmemizi sağlayacak bir fonksiyona ihtiyacımız olacak. Pozisyon özelliklerini almak için de aynı basit yaklaşım gerekli olacaktır.

Önceki makalelerde GetPositionProperties fonksiyonunda global değişkenlere tüm pozisyon özelliklerinin bir kerede atandığını görmüştük. Bu sefer her bir özelliği ayrı ayrı elde etme imkanı sağlamaya çalışacağız. Aşağıda, yukarıdakilerin uygulanması için iki numaralandırma yer almaktadır. Fonksiyonların kendileri biraz daha sonra gözden geçirilecektir.

//--- Enumeration of position properties
enum ENUM_POSITION_PROPERTIES
  {
   P_SYMBOL        = 0,
   P_MAGIC         = 1,
   P_COMMENT       = 2,
   P_SWAP          = 3,
   P_COMMISSION    = 4,
   P_PRICE_OPEN    = 5,
   P_PRICE_CURRENT = 6,
   P_PROFIT        = 7,
   P_VOLUME        = 8,
   P_SL            = 9,
   P_TP            = 10,
   P_TIME          = 11,
   P_ID            = 12,
   P_TYPE          = 13,
   P_ALL           = 14
  };
//--- Enumeration of symbol properties
enum ENUM_SYMBOL_PROPERTIES
  {
   S_DIGITS       = 0,
   S_SPREAD       = 1,
   S_STOPSLEVEL   = 2,
   S_POINT        = 3,
   S_ASK          = 4,
   S_BID          = 5,
   S_VOLUME_MIN   = 6,
   S_VOLUME_MAX   = 7,
   S_VOLUME_LIMIT = 8,
   S_VOLUME_STEP  = 9,
   S_FILTER       = 10,
   S_UP_LEVEL     = 11,
   S_DOWN_LEVEL   = 12,
   S_ALL          = 13
  };

ENUM_SYMBOL_PROPERTIES numaralandırması tüm sembol özelliklerini içermez, ancak gerektiğinde herhangi bir zamanda eklenebilir. Numaralandırma ayrıca, hesaplaması diğer sembol özelliklerine dayanan kullanıcı tanımlı özellikleri (10, 11, 12) içerir. Pozisyon özelliklerinin numaralandırılmasında olduğu gibi, numaralandırmadaki tüm özellikleri bir kerede elde etmek için kullanılabilecek bir tanımlayıcı vardır.

Bunun ardından Uzman Danışmanın harici parametreleri gelir:

//--- External parameters of the Expert Advisor
input int            NumberOfBars=2;     // Number of Bullish/Bearish bars for a Buy/Sell
input double         Lot         =0.1;   // Lot
input double         StopLoss    =50;    // Stop Loss
input double         TakeProfit  =100;   // Take Profit
input double         TrailingStop=10;    // Trailing Stop
input bool           Reverse     =true;  // Position reverse

Harici parametrelere daha yakından bakalım:

  • NumberOfBars - bu parametre bir pozisyon açılması için tek yöndeki çubukların sayısını belirler;
  • Lot - pozisyon hacmi;
  • TakeProfit - Puan cinsinden Kâr Al seviyesi. Sıfır değeri, hiçbir Kâr Al ayarlaması yapılmasının gerekmediğini belirtir.
  • StopLoss - Puan cinsinden Zarar Durdur seviyesi. Sıfır değeri, hiçbir Zarar Durdur ayarlaması yapılmasının gerekmediğini belirtir.
  • TrailingStop - Puan cinsinden İz-süren Stop seviyesi. ALIŞ pozisyonu için hesaplama, çubuğun minimum değerini temel alır (minimum eksi StopLoss parametresinden alınan puan sayısı). SATIŞ pozisyonu için hesaplama, çubuğun maksimum değerini temel alır (maksimum atı StopLoss parametresinden alınan puan sayısı). Sıfır değeri, İz-süren Stop öğesinin kapalı olduğunu gösterir.
  • Ters Çevirme pozisyonun ters çevrilmesini etkinleştirir/devre dışı bırakır.

Yalnızca NumberOfBars parametresi daha fazla açıklama gerektirir. Bu parametre değerini örneğin 5'ten fazla olarak ayarlamanın bir anlamı yoktur, çünkü bu oldukça nadirdir ve böyle bir hareketten sonra pozisyon açmak için zaten geç kalınmıştır. Bu nedenle, bu parametrenin değerini ayarlamamıza yardımcı olacak bir değişkene ihtiyacımız olacaktır:

//--- To check the value of the NumberOfBars external parameter
int                  AllowedNumberOfBars=0;

Bu parametre ayrıca fiyat dizilerinde saklanacak çubuk verilerinin miktarını da belirleyecektir. Bu, özel fonksiyonları değiştirmeye başladığımızda tartışılacaktır.

Pozisyon özelliklerinde olduğu gibi, herhangi bir fonksiyondan erişim sağlamak için sembol özellikleri için değişkenleri global kapsamda tanımlarız:

//--- Symbol properties
int                  sym_digits=0;           // Number of decimal places
int                  sym_spread=0;           // Spread in points
int                  sym_stops_level=0;      // Stops level
double               sym_point=0.0;          // Point value
double               sym_ask=0.0;            // Ask price
double               sym_bid=0.0;            // Bid price
double               sym_volume_min=0.0;     // Minimum volume for a deal
double               sym_volume_max=0.0;     // Maximum volume for a deal
double               sym_volume_limit=0.0;   // Maximum permissible volume for a position and orders in one direction
double               sym_volume_step=0.0;    // Minimum volume change step for a deal
double               sym_offset=0.0;         // Offset from the maximum possible price for a transaction
double               sym_up_level=0.0;       // Upper Stop level price
double               sym_down_level=0.0;     // Lower Stop level price

İz-süren Stop değeri çubuğun yüksek ve düşük değerlerine göre hesaplanacağından, bu tür çubuk verileri için dizilere ihtiyacımız olacaktır:

//--- Price data arrays
double               close_price[]; // Close (closing prices of the bar)
double               open_price[];  // Open (opening prices of the bar)
double               high_price[];  // High (bar's highs)
double               low_price[];   // Low (bar's lows)

Şimdi fonksiyonları değiştirmeye ve oluşturmaya devam edelim. Çubukların açılış ve kapanış fiyatlarını fiyat dizilerine kopyalayan GetBarsData fonksiyonumuz zaten var. Şimdi, yüksek ve düşük değerlere ihtiyacımız var. Ayrıca NumberOfBars parametresinden elde edilen değerin ayarlanması gerekiyor. Değişiklikten sonra fonksiyon şu şekilde görünür:

//+------------------------------------------------------------------+
//| Getting bar values                                               |
//+------------------------------------------------------------------+
void GetBarsData()
  {
//--- Adjust the number of bars for the position opening condition
   if(NumberOfBars<=1)
      AllowedNumberOfBars=2;              // At least two bars are required
   if(NumberOfBars>=5)
      AllowedNumberOfBars=5;              // but no more than 5
   else
      AllowedNumberOfBars=NumberOfBars+1; // and always more by one
//--- Reverse the indexing order (... 3 2 1 0)
   ArraySetAsSeries(close_price,true);
   ArraySetAsSeries(open_price,true);
   ArraySetAsSeries(high_price,true);
   ArraySetAsSeries(low_price,true);
//--- Get the closing price of the bar
//    If the number of the obtained values is less than requested, print the relevant message
   if(CopyClose(_Symbol,Period(),0,AllowedNumberOfBars,close_price)<AllowedNumberOfBars)
     {
      Print("Failed to copy the values ("
            +_Symbol+", "+TimeframeToString(Period())+") to the Close price array! "
            "Error "+IntegerToString(GetLastError())+": "+ErrorDescription(GetLastError()));
     }
//--- Get the opening price of the bar
//    If the number of the obtained values is less than requested, print the relevant message
   if(CopyOpen(_Symbol,Period(),0,AllowedNumberOfBars,open_price)<AllowedNumberOfBars)
     {
      Print("Failed to copy the values ("
            +_Symbol+", "+TimeframeToString(Period())+") to the Open price array! "
            "Error "+IntegerToString(GetLastError())+": "+ErrorDescription(GetLastError()));
     }
//--- Get the bar's high
//    If the number of the obtained values is less than requested, print the relevant message
   if(CopyHigh(_Symbol,Period(),0,AllowedNumberOfBars,high_price)<AllowedNumberOfBars)
     {
      Print("Failed to copy the values ("
            +_Symbol+", "+TimeframeToString(Period())+") to the High price array! "
            "Error "+IntegerToString(GetLastError())+": "+ErrorDescription(GetLastError()));
     }
//--- Get the bar's low
//    If the number of the obtained values is less than requested, print the relevant message
   if(CopyLow(_Symbol,Period(),0,AllowedNumberOfBars,low_price)<AllowedNumberOfBars)
     {
      Print("Failed to copy the values ("
            +_Symbol+", "+TimeframeToString(Period())+") to the Low price array! "
            "Error "+IntegerToString(GetLastError())+": "+ErrorDescription(GetLastError()));
     }
  }

En az iki çubuk ve her zaman bir daha fazlasını gerektiren koşullar vardır, çünkü yalnızca indis [1] ile başlayan tamamlanmış çubuklarla ilerleyeceğiz. Aslında, bu durumda ayarlamalar gereksiz görülebilir, çünkü çubuk verileri CopyOpen, CopyClose, CopyHigh ve CopyLow fonksiyonlarının üçüncü parametresinde belirtilen dizinden başlayarak kopyalanabilir. 5 çubuk sınırı da kendi takdirinize göre değiştirilebilir (yukarı/aşağı).

GetTradingSignal fonksiyonu, koşul NumberOfBars parametresinde belirtilen çubuk sayısına göre farklı şekilde oluşturulacağından biraz daha karmaşık hale gelmiştir. Ayrıca, artık döndürülen değerin daha doğru bir türünü kullanıyoruz: emir türü:

//+------------------------------------------------------------------+
//| Determining trading signals                                      |
//+------------------------------------------------------------------+
ENUM_ORDER_TYPE GetTradingSignal()
  {
//--- A Buy signal (ORDER_TYPE_BUY) :
   if(AllowedNumberOfBars==2 && 
      close_price[1]>open_price[1])
      return(ORDER_TYPE_BUY);
   if(AllowedNumberOfBars==3 && 
      close_price[1]>open_price[1] && 
      close_price[2]>open_price[2])
      return(ORDER_TYPE_BUY);
   if(AllowedNumberOfBars==4 && 
      close_price[1]>open_price[1] && 
      close_price[2]>open_price[2] && 
      close_price[3]>open_price[3])
      return(ORDER_TYPE_BUY);
   if(AllowedNumberOfBars==5 && 
      close_price[1]>open_price[1] && 
      close_price[2]>open_price[2] && 
      close_price[3]>open_price[3] && 
      close_price[4]>open_price[4])
      return(ORDER_TYPE_BUY);
   if(AllowedNumberOfBars>=6 && 
      close_price[1]>open_price[1] && 
      close_price[2]>open_price[2] && 
      close_price[3]>open_price[3] && 
      close_price[4]>open_price[4] && 
      close_price[5]>open_price[5])
      return(ORDER_TYPE_BUY);
//--- A Sell signal (ORDER_TYPE_SELL) :
   if(AllowedNumberOfBars==2 && 
      close_price[1]<open_price[1])
      return(ORDER_TYPE_SELL);
   if(AllowedNumberOfBars==3 && 
      close_price[1]<open_price[1] && 
      close_price[2]<open_price[2])
      return(ORDER_TYPE_SELL);
   if(AllowedNumberOfBars==4 && 
      close_price[1]<open_price[1] && 
      close_price[2]<open_price[2] && 
      close_price[3]<open_price[3])
      return(ORDER_TYPE_SELL);
   if(AllowedNumberOfBars==5 && 
      close_price[1]<open_price[1] && 
      close_price[2]<open_price[2] && 
      close_price[3]<open_price[3] && 
      close_price[4]<open_price[4])
      return(ORDER_TYPE_SELL);
   if(AllowedNumberOfBars>=6 && 
      close_price[1]<open_price[1] && 
      close_price[2]<open_price[2] && 
      close_price[3]<open_price[3] && 
      close_price[4]<open_price[4] && 
      close_price[5]<open_price[5])
      return(ORDER_TYPE_SELL);
//--- No signal (WRONG_VALUE):
   return(WRONG_VALUE);
  }

Şimdi GetPositionProperties fonksiyonunu değiştirelim. Önceki makalelerde bu, tüm özellikleri tek seferde elde etmemizi sağlamıştır. Ancak bazen sadece bir özelliği elde etmemiz gerekebilir. Bunun için, dilin sunduğu standart fonksiyonları elbette kullanabilirsiniz, ancak bu bizim istediğimiz kadar kullanışlı olmaz. Aşağıda, değiştirilen GetPositionProperties fonksiyonunun kodu yer almaktadır. Artık ENUM_POSITION_PROPERTIES numaralandırmasından belirli bir tanımlayıcıyı aktarırken, belirli bir tek pozisyon özelliğini veya tüm özellikleri tek seferde elde edebilirsiniz.

//+------------------------------------------------------------------+
//| Getting position properties                                      |
//+------------------------------------------------------------------+
void GetPositionProperties(ENUM_POSITION_PROPERTIES position_property)
  {
//--- Check if there is an open position
   pos_open=PositionSelect(_Symbol);
//--- If an open position exists, get its properties
   if(pos_open)
     {
      switch(position_property)
        {
         case P_SYMBOL        : pos_symbol=PositionGetString(POSITION_SYMBOL);                  break;
         case P_MAGIC         : pos_magic=PositionGetInteger(POSITION_MAGIC);                   break;
         case P_COMMENT       : pos_comment=PositionGetString(POSITION_COMMENT);                break;
         case P_SWAP          : pos_swap=PositionGetDouble(POSITION_SWAP);                      break;
         case P_COMMISSION    : pos_commission=PositionGetDouble(POSITION_COMMISSION);          break;
         case P_PRICE_OPEN    : pos_price=PositionGetDouble(POSITION_PRICE_OPEN);               break;
         case P_PRICE_CURRENT : pos_cprice=PositionGetDouble(POSITION_PRICE_CURRENT);           break;
         case P_PROFIT        : pos_profit=PositionGetDouble(POSITION_PROFIT);                  break;
         case P_VOLUME        : pos_volume=PositionGetDouble(POSITION_VOLUME);                  break;
         case P_SL            : pos_sl=PositionGetDouble(POSITION_SL);                          break;
         case P_TP            : pos_tp=PositionGetDouble(POSITION_TP);                          break;
         case P_TIME          : pos_time=(datetime)PositionGetInteger(POSITION_TIME);           break;
         case P_ID            : pos_id=PositionGetInteger(POSITION_IDENTIFIER);                 break;
         case P_TYPE          : pos_type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); break;
         case P_ALL           :
            pos_symbol=PositionGetString(POSITION_SYMBOL);
            pos_magic=PositionGetInteger(POSITION_MAGIC);
            pos_comment=PositionGetString(POSITION_COMMENT);
            pos_swap=PositionGetDouble(POSITION_SWAP);
            pos_commission=PositionGetDouble(POSITION_COMMISSION);
            pos_price=PositionGetDouble(POSITION_PRICE_OPEN);
            pos_cprice=PositionGetDouble(POSITION_PRICE_CURRENT);
            pos_profit=PositionGetDouble(POSITION_PROFIT);
            pos_volume=PositionGetDouble(POSITION_VOLUME);
            pos_sl=PositionGetDouble(POSITION_SL);
            pos_tp=PositionGetDouble(POSITION_TP);
            pos_time=(datetime)PositionGetInteger(POSITION_TIME);
            pos_id=PositionGetInteger(POSITION_IDENTIFIER);
            pos_type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);                     break;
         default: Print("The passed position property is not listed in the enumeration!");               return;
        }
     }
//--- If there is no open position, zero out variables for position properties
   else
      ZeroPositionProperties();
  }

Benzer şekilde, sembol özelliklerini elde etmek için GetSymbolProperties fonksiyonunu uygularız:

//+------------------------------------------------------------------+
//| Getting symbol properties                                        |
//+------------------------------------------------------------------+
void GetSymbolProperties(ENUM_SYMBOL_PROPERTIES symbol_property)
  {
   int lot_offset=1; // Number of points for the offset from the Stops level
//---
   switch(symbol_property)
     {
      case S_DIGITS        : sym_digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);                   break;
      case S_SPREAD        : sym_spread=(int)SymbolInfoInteger(_Symbol,SYMBOL_SPREAD);                   break;
      case S_STOPSLEVEL    : sym_stops_level=(int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL);   break;
      case S_POINT         : sym_point=SymbolInfoDouble(_Symbol,SYMBOL_POINT);                           break;
      //---
      case S_ASK           :
         sym_digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
         sym_ask=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),sym_digits);                       break;
      case S_BID           :
         sym_digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
         sym_bid=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),sym_digits);                       break;
         //---
      case S_VOLUME_MIN    : sym_volume_min=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);                 break;
      case S_VOLUME_MAX    : sym_volume_max=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX);                 break;
      case S_VOLUME_LIMIT  : sym_volume_limit=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_LIMIT);             break;
      case S_VOLUME_STEP   : sym_volume_step=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);               break;
      //---
      case S_FILTER        :
         sym_digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
         sym_point=SymbolInfoDouble(_Symbol,SYMBOL_POINT);
         sym_offset=NormalizeDouble(CorrectValueBySymbolDigits(lot_offset*sym_point),sym_digits);        break;
         //---
      case S_UP_LEVEL      :
         sym_digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
         sym_stops_level=(int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL);
         sym_point=SymbolInfoDouble(_Symbol,SYMBOL_POINT);
         sym_ask=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),sym_digits);
         sym_up_level=NormalizeDouble(sym_ask+sym_stops_level*sym_point,sym_digits);                     break;
         //---
      case S_DOWN_LEVEL    :
         sym_digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
         sym_stops_level=(int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL);
         sym_point=SymbolInfoDouble(_Symbol,SYMBOL_POINT);
         sym_bid=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),sym_digits);
         sym_down_level=NormalizeDouble(sym_bid-sym_stops_level*sym_point,sym_digits);                   break;
         //---
      case S_ALL           :
         sym_digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
         sym_spread=(int)SymbolInfoInteger(_Symbol,SYMBOL_SPREAD);
         sym_stops_level=(int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL);
         sym_point=SymbolInfoDouble(_Symbol,SYMBOL_POINT);
         sym_ask=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),sym_digits);
         sym_bid=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),sym_digits);
         sym_volume_min=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
         sym_volume_max=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX);
         sym_volume_limit=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_LIMIT);
         sym_volume_step=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);
         sym_offset=NormalizeDouble(CorrectValueBySymbolDigits(lot_offset*sym_point),sym_digits);
         sym_up_level=NormalizeDouble(sym_ask+sym_stops_level*sym_point,sym_digits);
         sym_down_level=NormalizeDouble(sym_bid-sym_stops_level*sym_point,sym_digits);                   break;
         //---
      default: Print("The passed symbol property is not listed in the enumeration!"); return;
     }
  }

Lütfen bazı sembol özelliklerinin önce diğer özellikleri almanızı gerektirebileceğini unutmayın.

Yeni bir fonksiyonumuz var: CorrectValueBySymbolDigits. Bu, fiyattaki ondalık basamak sayısına göre ilgili değeri döndürür. Fonksiyona bir tamsayı veya gerçek bir sayı aktarabilir. Aktarılan verilerin türü, kullanılacak fonksiyonun sürümünü belirler. Bu özelliğe fonksiyon aşırı yüklemesi adı verilir.

//+------------------------------------------------------------------+
//| Adjusting the value based on the number of digits in the price (int)|
//+------------------------------------------------------------------+
int CorrectValueBySymbolDigits(int value)
  {
   return (sym_digits==3 || sym_digits==5) ? value*=10 : value;
  }
//+------------------------------------------------------------------+
//| Adjusting the value based on the number of digits in the price (double)|
//+------------------------------------------------------------------+
double CorrectValueBySymbolDigits(double value)
  {
   return (sym_digits==3 || sym_digits==5) ? value*=10 : value;
  }

Uzman Danışmanımızın, açılış pozisyonunun hacmini (Lot) belirtmek için bir harici parametresi olacaktır. Sembol spesifikasyonuna göre lotu ayarlatacak bir fonksiyon oluşturalım: CalculateLot:

//+------------------------------------------------------------------+
//| Calculating position lot                                         |
//+------------------------------------------------------------------+
double CalculateLot(double lot)
  {
//--- To adjust as per the step
   double corrected_lot=0.0;
//---
   GetSymbolProperties(S_VOLUME_MIN);  // Get the minimum possible lot
   GetSymbolProperties(S_VOLUME_MAX);  // Get the maximum possible lot
   GetSymbolProperties(S_VOLUME_STEP); // Get the lot increase/decrease step
//--- Adjust as per the lot step
   corrected_lot=MathRound(lot/sym_volume_step)*sym_volume_step;
//--- If less than the minimum, return the minimum
   if(corrected_lot<sym_volume_min)
      return(NormalizeDouble(sym_volume_min,2));
//--- If greater than the maximum, return the maximum
   if(corrected_lot>sym_volume_max)
      return(NormalizeDouble(sym_volume_max,2));
//---
   return(NormalizeDouble(corrected_lot,2));
  }

Şimdi doğrudan makalenin başlığıyla ilgili fonksiyonlara geçelim. Bunlar, oldukça basit ve anlaşılırdır ve koddaki yorumları kullanarak hiç zorluk çekmeden amaçlarını anlayabilirsiniz.

CalculateTakeProfit fonksiyonu Kâr Al değerini hesaplamak için kullanılmaktadır:

//+------------------------------------------------------------------+
//| Calculating the Take Profit value                                |
//+------------------------------------------------------------------+
double CalculateTakeProfit(ENUM_ORDER_TYPE order_type)
  {
//--- If Take Profit is required
   if(TakeProfit>0)
     {
      //--- For the calculated Take Profit value
      double tp=0.0;
      //--- If you need to calculate the value for a SELL position
      if(order_type==ORDER_TYPE_SELL)
        {
         //--- Calculate the level
         tp=NormalizeDouble(sym_bid-CorrectValueBySymbolDigits(TakeProfit*sym_point),sym_digits);
         //--- Return the calculated value if it is lower than the lower limit of the Stops level
         //    If the value is higher or equal, return the adjusted value
         return(tp<sym_down_level ? tp : sym_down_level-sym_offset);
        }
      //--- If you need to calculate the value for a BUY position
      if(order_type==ORDER_TYPE_BUY)
        {
         //--- Calculate the level
         tp=NormalizeDouble(sym_ask+CorrectValueBySymbolDigits(TakeProfit*sym_point),sym_digits);
         //--- Return the calculated value if it is higher that the upper limit of the Stops level
         //    If the value is lower or equal, return the adjusted value
         return(tp>sym_up_level ? tp : sym_up_level+sym_offset);
        }
     }
//---
   return(0.0);
  }

CalculateStopLoss fonksiyonu Zarar Durdur değerini hesaplamak için kullanılmaktadır:

//+------------------------------------------------------------------+
//| Calculating the Stop Loss value                                  |
//+------------------------------------------------------------------+
double CalculateStopLoss(ENUM_ORDER_TYPE order_type)
  {
//--- If Stop Loss is required
   if(StopLoss>0)
     {
      //--- For the calculated Stop Loss value
      double sl=0.0;
      //--- If you need to calculate the value for a BUY position
      if(order_type==ORDER_TYPE_BUY)
        {
         // Calculate the level
         sl=NormalizeDouble(sym_ask-CorrectValueBySymbolDigits(StopLoss*sym_point),sym_digits);
         //--- Return the calculated value if it is lower that the lower limit of the Stops level
         //    If the value is higher or equal, return the adjusted value
         return(sl<sym_down_level ? sl : sym_down_level-sym_offset);
        }
      //--- If you need to calculate the value for a SELL position
      if(order_type==ORDER_TYPE_SELL)
        {
         //--- Calculate the level
         sl=NormalizeDouble(sym_bid+CorrectValueBySymbolDigits(StopLoss*sym_point),sym_digits);
         //--- Return the calculated value if it is higher than the upper limit of the Stops level
         //    If the value is lower or equal, return the adjusted value
         return(sl>sym_up_level ? sl : sym_up_level+sym_offset);
        }
     }
//---
   return(0.0);
  }

CalculateTrailingStop fonksiyonu Takip Durdur değerini hesaplamak için kullanılmaktadır:

//+------------------------------------------------------------------+
//| Calculating the Trailing Stop value                              |
//+------------------------------------------------------------------+
double CalculateTrailingStop(ENUM_POSITION_TYPE position_type)
  {
//--- Variables for calculations
   double            level       =0.0;
   double            buy_point   =low_price[1];    // The Low value for a Buy
   double            sell_point  =high_price[1];   // The High value for a Sell
//--- Calculate the level for a BUY position
   if(position_type==POSITION_TYPE_BUY)
     {
      //--- Bar's low minus the specified number of points
      level=NormalizeDouble(buy_point-CorrectValueBySymbolDigits(StopLoss*sym_point),sym_digits);
      //--- If the calculated level is lower than the lower limit of the Stops level, 
      //    the calculation is complete, return the current value of the level
      if(level<sym_down_level)
         return(level);
      //--- If it is not lower, try to calculate based on the bid price
      else
        {
         level=NormalizeDouble(sym_bid-CorrectValueBySymbolDigits(StopLoss*sym_point),sym_digits);
         //--- If the calculated level is lower than the limit, return the current value of the level
         //    otherwise set the nearest possible value
         return(level<sym_down_level ? level : sym_down_level-sym_offset);
        }
     }
//--- Calculate the level for a SELL position
   if(position_type==POSITION_TYPE_SELL)
     {
      // Bar's high plus the specified number of points
      level=NormalizeDouble(sell_point+CorrectValueBySymbolDigits(StopLoss*sym_point),sym_digits);
      //--- If the calculated level is higher than the upper limit of the Stops level, 
      //    the calculation is complete, return the current value of the level
      if(level>sym_up_level)
         return(level);
      //--- If it is not higher, try to calculate based on the ask price
      else
        {
         level=NormalizeDouble(sym_ask+CorrectValueBySymbolDigits(StopLoss*sym_point),sym_digits);
         //--- If the calculated level is higher than the limit, return the current value of the level
         //    Otherwise set the nearest possible value
         return(level>sym_up_level ? level : sym_up_level+sym_offset);
        }
     }
//---
   return(0.0);
  }

Artık alım satım işlemleri için doğru değerleri döndüren gerekli tüm fonksiyonlarımız var. İz-süren Stop değerini değiştirmek için bir koşulu kontrol edecek ve belirtilen koşul karşılanırsa bunu değiştirecek bir fonksiyon oluşturalım: ModifyTrailingStop. Aşağıda, detaylı yorumlar ile fonksiyon kodu yer almaktadır:

Lütfen yukarıda oluşturulan/değiştirilen tüm fonksiyonların kullanımına dikkat edin. Anahtar (Switch) Anahtar, geçerli pozisyonun türüne göre ilgili koşulu belirler ve ardından koşul sonucu durum (condition) değişkeninde saklanır. Bir pozisyonu değiştirmek için Standart Kitaplıktan CTrade sınıfından PositionModify yöntemini kullanırız.

//+------------------------------------------------------------------+
//| Modifying the Trailing Stop level                                |
//+------------------------------------------------------------------+
void ModifyTrailingStop()
  {
//--- If the Trailing Stop and Stop Loss are set
   if(TrailingStop>0 && StopLoss>0)
     {
      double         new_sl=0.0;       // For calculating the new Stop Loss level
      bool           condition=false;  // For checking the modification condition
      //--- Get the flag of presence/absence of the position
      pos_open=PositionSelect(_Symbol);
      //--- If the position exists
      if(pos_open)
        {
         //--- Get the symbol properties
         GetSymbolProperties(S_ALL);
         //--- Get the position properties
         GetPositionProperties(P_ALL);
         //--- Get the Stop Loss level
         new_sl=CalculateTrailingStop(pos_type);
         //--- Depending on the position type, check the relevant condition for the Trailing Stop modification
         switch(pos_type)
           {
            case POSITION_TYPE_BUY  :
               //--- If the new Stop Loss value is higher
               //    than the current value plus the set step
               condition=new_sl>pos_sl+CorrectValueBySymbolDigits(TrailingStop*sym_point);
               break;
            case POSITION_TYPE_SELL :
               //--- If the new Stop Loss value is lower
               //    than the current value minus the set step
               condition=new_sl<pos_sl-CorrectValueBySymbolDigits(TrailingStop*sym_point);
               break;
           }
         //--- If there is a Stop Loss, compare the values before modification
         if(pos_sl>0)
           {
            //--- If the condition for the order modification is met, i.e. the new value is lower/higher 
            //    than the current one, modify the Trailing Stop of the position
            if(condition)
              {
               if(!trade.PositionModify(_Symbol,new_sl,pos_tp))
                  Print("Error modifying the position: ",GetLastError()," - ",ErrorDescription(GetLastError()));
              }
           }
         //--- If there is no Stop Loss, simply set it
         if(pos_sl==0)
           {
            if(!trade.PositionModify(_Symbol,new_sl,pos_tp))
               Print("Error modifying the position: ",GetLastError()," - ",ErrorDescription(GetLastError()));
           }
        }
     }
  }

Şimdi TradingBlock fonksiyonunu yukarıdaki tüm değişikliklere göre ayarlayalım. ModifyTrailingStop fonksiyonunda olduğu gibi, bir alım satım emri için tüm değişken değerleri anahtar anahtarı kullanılarak belirlenir. Kod miktarını önemli ölçüde azaltır ve iki pozisyon türü için birer dal yerine sadece bir dal kaldığı için diğer modifikasyonları basitleştirir.

//+------------------------------------------------------------------+
//| Trading block                                                    |
//+------------------------------------------------------------------+
void TradingBlock()
  {
   ENUM_ORDER_TYPE      signal=WRONG_VALUE;                 // Variable for getting a signal
   string               comment="hello :)";                 // Position comment
   double               tp=0.0;                             // Take Profit
   double               sl=0.0;                             // Stop Loss
   double               lot=0.0;                            // Volume for position calculation in case of reverse position
   double               position_open_price=0.0;            // Position opening price
   ENUM_ORDER_TYPE      order_type=WRONG_VALUE;             // Order type for opening a position
   ENUM_POSITION_TYPE   opposite_position_type=WRONG_VALUE; // Opposite position type
//--- Get a signal
   signal=GetTradingSignal();
//--- If there is no signal, exit
   if(signal==WRONG_VALUE)
      return;
//--- Find out if there is a position
   pos_open=PositionSelect(_Symbol);
//--- Get all symbol properties
   GetSymbolProperties(S_ALL);
//--- Determine values for trade variables
   switch(signal)
     {
      //--- Assign values to variables for a BUY
      case ORDER_TYPE_BUY  :
         position_open_price=sym_ask;
         order_type=ORDER_TYPE_BUY;
         opposite_position_type=POSITION_TYPE_SELL;
         break;
         //--- Assign values to variables for a SELL
      case ORDER_TYPE_SELL :
         position_open_price=sym_bid;
         order_type=ORDER_TYPE_SELL;
         opposite_position_type=POSITION_TYPE_BUY;
         break;
     }
//--- Calculate the Take Profit and Stop Loss levels
   sl=CalculateStopLoss(order_type);
   tp=CalculateTakeProfit(order_type);
//--- If there is no position
   if(!pos_open)
     {
      //--- Adjust the volume
      lot=CalculateLot(Lot);
      //--- Open a position
      //    If the position failed to open, print the relevant message
      if(!trade.PositionOpen(_Symbol,order_type,lot,position_open_price,sl,tp,comment))
        {
         Print("Error opening the position: ",GetLastError()," - ",ErrorDescription(GetLastError()));
        }
     }
//--- If there is a position
   else
     {
      //--- Get the position type
      GetPositionProperties(P_TYPE);
      //--- If the position is opposite to the signal and the position reverse is enabled
      if(pos_type==opposite_position_type && Reverse)
        {
         //--- Get the position volume
         GetPositionProperties(P_VOLUME);
         //--- Adjust the volume
         lot=pos_volume+CalculateLot(Lot);
         //--- Open the position. If the position failed to open, print the relevant message
         if(!trade.PositionOpen(_Symbol,order_type,lot,position_open_price,sl,tp,comment))
           {
            Print("Error opening the position: ",GetLastError()," - ",ErrorDescription(GetLastError()));
           }
        }
     }
//---
   return;
  }

SetInfoPanel fonksiyonunda da önemli bir düzeltme daha yapmamız gerekiyor, ama ilk olarak programın nasıl/nerede kullanıldığını gösteren birkaç yardımcı fonksiyon hazırlayalım:

//+------------------------------------------------------------------+
//| Returning the testing flag                                       |
//+------------------------------------------------------------------+
bool IsTester()
  {
   return(MQL5InfoInteger(MQL5_TESTER));
  }
//+------------------------------------------------------------------+
//| Returning the optimization flag                                  |
//+------------------------------------------------------------------+
bool IsOptimization()
  {
   return(MQL5InfoInteger(MQL5_OPTIMIZATION));
  }
//+------------------------------------------------------------------+
//| Returning the visual testing mode flag                           |
//+------------------------------------------------------------------+
bool IsVisualMode()
  {
   return(MQL5InfoInteger(MQL5_VISUAL_MODE));
  }
//+------------------------------------------------------------------+
//| Returning the flag for real time mode outside the Strategy Tester|
//| if all conditions are met                                        |
//+------------------------------------------------------------------+
bool IsRealtime()
  {
   if(!IsTester() && !IsOptimization() && !IsVisualMode())
      return(true);
   else
      return(false); 
  }

SetInfoPanel fonksiyonuna eklememiz gereken tek şey, programa bilgi panelinin sadece görselleştirme modunda ve gerçek zamanlı modlarda görüntülenmesi gerektiğini belirten bir koşuldur. Bu göz ardı edilirse, test süresi 4-5 kat daha uzun olacaktır. Bu, özellikle parametreleri optimize ederken önemlidir.

//+------------------------------------------------------------------+
//| Setting the info panel                                           |
//|------------------------------------------------------------------+
void SetInfoPanel()
  {
//--- Visualization or real time modes
   if(IsVisualMode() || IsRealtime())
     {
     // The remaining code of the SetInfoPanel() function
     // ...
     }
  }

Şimdi, parametre optimizasyonuna ve Uzman Danışman testine devam edebilmek için ana program fonksiyonlarında bazı değişiklikler yapmamız gerekiyor.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Initialize the new bar
   CheckNewBar();
//--- Get the properties and set the panel
   GetPositionProperties(P_ALL);
//--- Set the info panel
   SetInfoPanel();
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- If the bar is not new, exit
   if(!CheckNewBar())
      return;
//--- If there is a new bar
   else
     {
      GetBarsData();          // Get bar data
      TradingBlock();         // Check the conditions and trade
      ModifyTrailingStop();   // Modify the Trailing Stop level
     }
//--- Get the properties and update the values on the panel
   GetPositionProperties(P_ALL);
//--- Update the info panel
   SetInfoPanel();
  }
//+------------------------------------------------------------------+
//| Trade event                                                      |
//+------------------------------------------------------------------+
void OnTrade()
  {
//--- Get position properties and update the values on the panel
   GetPositionProperties(P_ALL);
//--- Update the info panel
   SetInfoPanel();
  }


Parametreleri Optimize Etme ve Uzman Danışmanı Test Etme

Şimdi parametreleri optimize edelim. Aşağıda gösterilen Strateji Test Cihazı ayarlarını yapacağız:

Şekil 1. Parametre optimizasyonu için Strateji Test Cihazı ayarları

Şekil 1. Parametre optimizasyonu için Uzman Danışman ayarları

Uzman Danışman parametrelerine çok çeşitli değerler verilecektir:

Şekil 2. Parametre optimizasyonu için Uzman Danışman ayarları

Şekil 2. Parametre optimizasyonu için Uzman Danışman ayarları.

Optimizasyon, çift çekirdekli bir işlemcide (Intel Core2 Duo P7350 @ 2.00GHz) yaklaşık 7 dakika sürmüştür. Maksimum kurtarma faktörü test sonuçları aşağıdaki gibidir:

Şekil 3. Maksimum kurtarma faktörü test sonuçları.

Şekil 3. Maksimum kurtarma faktörü test sonuçları


Sonuç

Neredeyse hepsi bu. Çalışın, test edin, optimize edin, deneyin ve işte mükemmel sonuç! Makalede yer alan Uzman Danışmanın kaynak kodu, daha fazla çalışma için aşağıdaki bağlantı kullanılarak indirilebilir.

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

Ekli dosyalar |
MQL5 Tarif Defteri: Pozisyon Özelliklerini Elde Etmek için İşlemler Geçmişi ve Fonksiyon Kitaplığı MQL5 Tarif Defteri: Pozisyon Özelliklerini Elde Etmek için İşlemler Geçmişi ve Fonksiyon Kitaplığı
Pozisyon özellikleri ile ilgili önceki makalelerde verilen bilgileri kısaca özetlemenin zamanı geldi. Bu makalemizde, işlemler geçmişine erişimin ardından edinilebilecek özellikleri elde etmek için birkaç ek fonksiyon oluşturacağız. Ayrıca pozisyon ve sembol özelliklerine daha rahat erişmemizi sağlayacak veri yapılarını da öğreneceğiz.
MQL5 Tarif Defteri Pozisyon Parametrelerini MetaTrader 5 Strateji Test Cihazında Analiz Etme MQL5 Tarif Defteri Pozisyon Parametrelerini MetaTrader 5 Strateji Test Cihazında Analiz Etme
"MQL5 Tarif Defteri: Özel Bilgi Panelindeki Pozisyon Özellikleri" adlı önceki makalesinden Uzman Danışmanın değiştirilmiş bir versiyonunu sunacağız. Ele alacağımız sorunlardan bazıları, çubuklardan veri alma, mevcut sembol üzerindeki yeni çubuk olaylarını kontrol etme, Standart Kitaplığın bir alım satım sınıfını bir dosyaya ekleme, alım satım sinyallerini aramak için bir fonksiyon ve alım satım işlemlerini yürütmek için bir fonksiyon oluşturmanın yanı sıra OnTrade() fonksiyonundaki alım satım olaylarını belirlemeyi içerir.
MQL5 Tarif Defteri: Uzman Danışmanlarda Alım Satım Koşullarını Belirlemek için Göstergeleri Kullanma MQL5 Tarif Defteri: Uzman Danışmanlarda Alım Satım Koşullarını Belirlemek için Göstergeleri Kullanma
Bu makalede, MQL5 Tarif Defteri serisinin önceki makalelerinde üzerinde çalıştığımız Uzman Danışmanı değiştirmeye devam edeceğiz. Bu sefer Uzman Danışman, değerleri pozisyon açma koşullarını kontrol etmek için kullanılacak göstergeler ile güçlendirilecektir. Renk katmak için, üç alım satım göstergesinden birini seçebilmek için harici parametrelerde bir açılır liste oluşturacağız.
MQL5 Tarif Defteri Özel Bilgi Panelindeki Pozisyon Özellikleri MQL5 Tarif Defteri Özel Bilgi Panelindeki Pozisyon Özellikleri
Bu defa, mevcut sembol üzerindeki pozisyon özelliklerini elde ederek manuel alım satım sırasında bunları özel bilgi panelinde gösterecek basit bir Uzman Danışman oluşturacağız. Bilgi paneli, grafik nesneler kullanılarak oluşturulacak ve her tikte görüntülenen bilgiler yenilenecektir. Bu, aşağıdaki serinin önceki makalesinde açıklanan betiği manuel olarak çalıştırmak zorunda kalmaktan çok daha uygun olacaktır: "MQL5 Tarif Defteri: Pozisyon Özelliklerini Elde Etme".