English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Italiano
MQL5 Tarif Defteri Uzman Danışmanın Belirlenen Kriterlere Göre Optimizasyon Sonuçlarını Kaydetme

MQL5 Tarif Defteri Uzman Danışmanın Belirlenen Kriterlere Göre Optimizasyon Sonuçlarını Kaydetme

MetaTrader 5Sınayıcı | 13 Ocak 2022, 16:47
81 0
Anatoli Kazharski
Anatoli Kazharski

Giriş

MQL5 programlamasına dair makaleler serisine devam ediyoruz. Bu sefer, Uzman Danışman parametre optimizasyonu sırasında her bir optimizasyon geçişinin sonucunun nasıl elde edileceğini göreceğiz. Uygulama, harici parametrelerde belirtilen belirli bir koşulun sağlanması durumunda ilgili geçiş değerlerinin bir dosyaya yazılmasını sağlayacak şekilde yapılacaktır. Test değerlerine ek olarak, bu sonuçlara neden olan parametreleri de kaydedeceğiz.

 

Geliştirme

Bu fikri uygulamak için, aşağıdaki makalede açıklanan basit bir alım satım algoritmasına sahip hazır Uzman Danışmanı kullanacağız ve sadece bunu gerekli fonksiyonlara ekleyeceğiz: "MQL5 Tarif Defteri: Alım Satım Seviyelerini Ayarlarken/Değiştirirken Hatalardan Nasıl Kaçınılır?" Kaynak kodu, serinin en son makalelerinde kullanılan yaklaşım kullanılarak hazırlanmıştır. Böylece tüm fonksiyonlar farklı dosyalar halinde düzenlenir ve ana proje dosyasına eklenir. Dosyaların projeye nasıl eklenebileceğini şu makalede görebilirsiniz: "MQL5 Tarif Defteri: Uzman Danışmanlarda Alım Satım Koşullarını Belirlemek için Göstergeleri Kullanma" önceki makalesinden programı değiştireceğiz.

Optimizasyon sırasında verilere erişmek için özel MQL5 fonksiyonlarını kullanabilirsiniz: OnTesterInit(), OnTester(), OnTesterPass() ve OnTesterDeinit(). Her birine hızlıca bir göz atalım:

  • OnTesterInit() - bu fonksiyon optimizasyon başlangıcını belirlemek için kullanılır.
  • OnTester() - bu fonksiyon, her optimizasyon geçişinden sonra sözde çerçevelerin eklenmesinden sorumludur. Çerçevelerin tanımı aşağıda ayrıca verilecektir.
  • OnTesterPass() - bu fonksiyon her optimizasyon geçişinden sonra çerçeveleri alır.
  • OnTesterDeinit() - bu fonksiyon, Uzman Danışman parametre optimizasyonunun sonu olayını oluşturur.

Şimdi bir çerçeve tanımlamalıyız. Çerçeve, tek bir optimizasyon geçişinin bir tür veri yapısıdır. Optimizasyon sırasında çerçeveler, MetaTrader 5/MQL5/Files/Tester klasöründe oluşturulan *.mqd arşivine kaydedilir. Bu arşivin verilerine (çerçevelerine) hem optimizasyon sırasında "anında" hem de tamamlandıktan sonra erişilebilir. Örneğin, "MetaTrader 5 Test Cihazında Bir Stratejiyi Görselleştirme" makalesi, optimizasyon sürecini "anında" nasıl görselleştirebileceğimizi ve ardından optimizasyon sonrası sonuçları nasıl görüntüleyebileceğimizi gösterir.

Bu makalede çerçeveler ile çalışmak için aşağıdaki fonksiyonları kullanacağız:

  • FrameAdd() - bir dosyadan veya diziden veri ekler.
  • FrameNext() - tek bir sayısal değer veya tüm çerçeve verisini elde etmek için bir çağrıdır.
  • FrameInputs() - belirtilen geçiş numarasına sahip belirli bir çerçevenin oluşturulmasına göre giriş parametrelerini alır.

Yukarıda listelenen fonksiyonlar hakkında daha fazla bilgi MQL5 Referansı içinde bulunabilir. Her zamanki gibi, harici parametreler ile başlıyoruz. Aşağıda, halihazırda mevcut olanlara hangi parametrelerin eklenmesi gerektiğini görebilirsiniz:

//--- External parameters of the Expert Advisor
input  int              NumberOfBars           = 2;              // Number of one-direction bars
sinput double           Lot                    = 0.1;            // Lot
input  double           TakeProfit             = 100;            // Take Profit
input  double           StopLoss               = 50;             // Stop Loss
input  double           TrailingStop           = 10;             // Trailing Stop
input  bool             Reverse                = true;           // Position reversal
sinput string           delimeter=""; // --------------------------------
sinput bool             LogOptimizationReport  = true;           // Writing results to a file
sinput CRITERION_RULE   CriterionSelectionRule = RULE_AND;       // Condition for writing
sinput ENUM_STATS       Criterion_01           = C_NO_CRITERION; // 01 - Criterion name
sinput double           CriterionValue_01      = 0;              // ---- Criterion value
sinput ENUM_STATS       Criterion_02           = C_NO_CRITERION; // 02 - Criterion name
sinput double           CriterionValue_02      = 0;              // ---- Criterion value
sinput ENUM_STATS       Criterion_03           = C_NO_CRITERION; // 03 - Criterion name
sinput double           CriterionValue_03      = 0;              // ---- Criterion value

LogOptimizationReport parametresi, optimizasyon sırasında sonuçların ve parametrelerin bir dosyaya yazılıp yazılmaması gerektiğini belirtmek için kullanılacaktır.

Bu örnekte, hangi sonuçların bir dosyaya yazılmak üzere seçileceğine bağlı olarak en fazla üç kriter belirleme imkanını uygulayacağız. Ayrıca, sonuçların, verilen tüm koşullar karşılandığında (VE) mı yoksa bunlardan en az biri karşılandığında (VEYA) mı yazılacağını belirleyebileceğiniz bir kural (CriterionSelectionRule parametresi) ekleyeceğiz. Bunun için Enums.mqh dosyasında bir numaralandırma oluşturuyoruz:

//--- Rules for checking criteria
enum CRITERION_RULE
  {
   RULE_AND = 0, // AND
   RULE_OR  = 1  // OR
  };

Ana test parametreleri kriter olarak kullanılacaktır. Burada başka bir numaralandırmaya ihtiyacımız var:

//--- Statistical parameters
enum ENUM_STATS
  {
   C_NO_CRITERION              = 0, // No criterion
   C_STAT_PROFIT               = 1, // Profit
   C_STAT_DEALS                = 2, // Total deals
   C_STAT_PROFIT_FACTOR        = 3, // Profit factor
   C_STAT_EXPECTED_PAYOFF      = 4, // Expected payoff
   C_STAT_EQUITY_DDREL_PERCENT = 5, // Max. equity drawdown, %
   C_STAT_RECOVERY_FACTOR      = 6, // Recovery factor
   C_STAT_SHARPE_RATIO         = 7  // Sharpe ratio
  };

Her parametre harici parametrede belirtilen değeri aşması açısından kontrol edilecektir, bunun tek istisnası seçimin minimum düşüşe göre yapılması gerektiğinden maksimum öz varlık düşüşüdür.

Ayrıca birkaç global değişken eklememiz gerekiyor (aşağıdaki koda bakın):

//--- Global variables
int    AllowedNumberOfBars=0;       // For checking the NumberOfBars external parameter value
string OptimizationResultsPath=""// Location of the folder for saving folders and files
int    UsedCriteriaCount=0;         // Number of used criteria
int    OptimizationFileHandle=-1;   // Handle of the file of optimization results

Ayrıca, aşağıdaki diziler gereklidir:

int    criteria[3];                    // Criteria for optimization report generation
double criteria_values[3];             // Values of criteria
double stat_values[STAT_VALUES_COUNT]; // Array for testing parameters

Uzman Danışmanın ana dosyasının, makalenin başında açıklanan Strateji Test Cihazı olaylarını işlemeye yönelik fonksiyonlar ile geliştirilmesi gerekir:

//+--------------------------------------------------------------------+
//| Optimization start                                                 |
//+--------------------------------------------------------------------+
void OnTesterInit()
  {
   Print(__FUNCTION__,"(): Start Optimization \n-----------");
  }
//+--------------------------------------------------------------------+
//| Test completion event handler                                      |
//+--------------------------------------------------------------------+
double OnTester()
  {
//--- If writing of optimization results is enabled
   if(LogOptimizationReport)
      //---
//---
   return(0.0);
  }
//+--------------------------------------------------------------------+
//| Next optimization pass                                             |
//+--------------------------------------------------------------------+
void OnTesterPass()
  {
//--- If writing of optimization results is enabled
   if(LogOptimizationReport)
      //---
  }
//+--------------------------------------------------------------------+
//| End of optimization                                                |
//+--------------------------------------------------------------------+
void OnTesterDeinit()
  {
   Print("-----------\n",__FUNCTION__,"(): End Optimization");
//--- If writing of optimization results is enabled
   if(LogOptimizationReport)
      //---
  }

Optimizasyona şimdi başlarsak, terminalde Uzman Danışmanın çalıştığı sembol ve zaman aralığını içeren grafik görünecektir. Yukarıdaki kodda kullanılan fonksiyonlardan gelen mesajlar, Strateji Test Cihazının günlüğü yerine terminalin günlüğüne yazdırılacaktır. Optimizasyonun en başında OnTesterInit() fonksiyonundan bir mesaj yazdırılacaktır. Ancak optimizasyon sırasında ve tamamlandıktan sonra günlükte herhangi bir mesaj göremezsiniz. Optimizasyondan sonra Strateji Test Cihazı tarafından açılan grafiği silerseniz, günlüğe OnTesterDeinit() fonksiyonundan bir mesaj yazdırılacaktır. Bu neden olur?

Sorun şu ki, doğru çalışmayı sağlamak için OnTester() fonksiyonunun, aşağıda gösterildiği gibi bir çerçeve eklemek için FrameAdd() fonksiyonunu kullanması gerekir.

//+--------------------------------------------------------------------+
//| Test completion event handler                                      |
//+--------------------------------------------------------------------+
double OnTester()
  {
//--- If writing of optimization results is enabled
   if(LogOptimizationReport)
     {
      //--- Create a frame
      FrameAdd("Statistics",1,0,stat_values);
     }
//---
   return(0.0);
  }

Şimdi, optimizasyon sırasında, her optimizasyon geçişinden sonra OnTesterPass() fonksiyonundan bir mesaj günlüğe yazdırılacak ve optimizasyonun tamamlanmasıyla ilgili mesaj, optimizasyonun sona ermesinden sonra OnTesterDeinit() fonksiyonu tarafından eklenecektir. Optimizasyon manuel olarak durdurulursa, optimizasyon tamamlandı mesajı da oluşturulur.

Şekil 1 - Günlüğe yazdırılan test ve optimizasyon fonksiyonlarından gelen mesajlar

Şekil 1 - Günlüğe yazdırılan test ve optimizasyon fonksiyonlarından gelen mesajlar

Artık her şey, klasörler ve dosyalar oluşturmaktan, belirtilen optimizasyon parametrelerini belirlemekten ve koşulları karşılayan sonuçları yazmaktan sorumlu fonksiyonlara geçmeye hazır.

FileFunctions.mqh adında bir dosya oluşturalım ve bunu projeye dahil edelim. Bu dosyanın en başında, her optimizasyon geçişini değerlerle doldurmak için referans olarak bir dizi elde edecek GetTestStatistics() fonksiyonunu yazıyoruz.

//+--------------------------------------------------------------------+
//| Filling the array with test results                                |
//+--------------------------------------------------------------------+
void GetTestStatistics(double &stat_array[])
  {
//--- Auxiliary variables for value adjustment
   double profit_factor=0,sharpe_ratio=0;
//---
   stat_array[0]=TesterStatistics(STAT_PROFIT);                // Net profit upon completion of testing
   stat_array[1]=TesterStatistics(STAT_DEALS);                 // Number of executed deals
//---
   profit_factor=TesterStatistics(STAT_PROFIT_FACTOR);         // Profit factor – the STAT_GROSS_PROFIT/STAT_GROSS_LOSS ratio 
   stat_array[2]=(profit_factor==DBL_MAX) ? 0 : profit_factor; // adjust if necessary
//---
   stat_array[3]=TesterStatistics(STAT_EXPECTED_PAYOFF);       // Expected payoff
   stat_array[4]=TesterStatistics(STAT_EQUITY_DDREL_PERCENT);  // Max. equity drawdown, %
   stat_array[5]=TesterStatistics(STAT_RECOVERY_FACTOR);       // Recovery factor – the STAT_PROFIT/STAT_BALANCE_DD ratio
//---
   sharpe_ratio=TesterStatistics(STAT_SHARPE_RATIO);           // Sharpe ratio - investment portfolio (asset) efficiency index
   stat_array[6]=(sharpe_ratio==DBL_MAX) ? 0 : sharpe_ratio;   // adjust if necessary
  }

Bir çerçeve eklemeden önce GetTestStatistics() fonksiyonu eklenmelidir:

//+--------------------------------------------------------------------+
//| Test completion event handler                                      |
//+--------------------------------------------------------------------+
double OnTester()
  {
//--- If writing of optimization results is enabled
   if(LogOptimizationReport)
     {
      //--- Fill the array with test values
      GetTestStatistics(stat_values);
      //--- Create a frame
      FrameAdd("Statistics",1,0,stat_values);
     }
//---
   return(0.0);
  }

Doldurulan dizi, son argüman olarak FrameAdd() fonksiyonuna aktarılır. Hatta gerekirse bir veri dosyasını da aktarabilirsiniz.

Artık OnTesterPass() fonksiyonunda elde edilen verileri kontrol edebiliriz. Nasıl çalıştığını görmek için şimdi terminal günlüğündeki her bir sonucun kârını göstereceğiz. Mevcut çerçeve değerlerini elde etmek için FrameNext() fonksiyonunu kullanın. Lütfen aşağıdaki örneğe bakın:

//+--------------------------------------------------------------------+
//| Next optimization pass                                             |
//+--------------------------------------------------------------------+
void OnTesterPass()
  {
//--- If writing of optimization results is enabled
   if(LogOptimizationReport)
     {
      string name ="";  // Public name/frame label
      ulong  pass =0;   // Number of the optimization pass at which the frame is added
      long   id   =0;   // Public id of the frame
      double val  =0.0; // Single numerical value of the frame
      //---
      FrameNext(pass,name,id,val,stat_values);
      //---
      Print(__FUNCTION__,"(): pass: "+IntegerToString(pass)+"; STAT_PROFIT: ",DoubleToString(stat_values[0],2));
     }
  }

FrameNext() fonksiyonunu kullanmazsanız, stat_values dizisindeki değerler sıfır olacaktır. Ancak, her şey doğru yapılırsa, aşağıdaki ekran görüntüsünde gösterilen sonucu elde ederiz:

Şekil 2 - Günlüğe yazdırılan OnTesterPass() fonksiyonundan mesajlar

Şekil 2 - Günlüğe yazdırılan OnTesterPass() fonksiyonundan mesajlar

Bu arada, optimizasyon harici parametreler değiştirilmeden çalıştırılırsa, sonuçlar OnTesterPass() ve OnTesterDeinit() fonksiyonları atlanarak önbellekten Strateji Test Cihazına yüklenecektir. Bir hata olduğunu düşünmemeniz için bunu göz önünde bulundurmalısınız.

Ayrıca, FileFunctions.mqh içinde bir CreateOptimizationReport() fonksiyonu oluşturuyoruz. Kilit aktivite bu fonksiyon içerisinde gerçekleştirilecektir. Fonksiyon kodu aşağıda verilmektedir:

//+--------------------------------------------------------------------+
//| Generating and writing report on optimization results              |
//+--------------------------------------------------------------------+
void CreateOptimizationReport()
  {
   static int passes_count=0;                // Pass counter
   int        parameters_count=0;            // Number of Expert Advisor parameters
   int        optimized_parameters_count=0;  // Counter of optimized parameters
   string     string_to_write="";            // String for writing
   bool       include_criteria_list=false;   // For determining the start of the list of parameters/criteria
   int        equality_sign_index=0;         // The '=' sign index in the string
   string     name            ="";           // Public name/frame label
   ulong      pass            =0;            // Number of the optimization pass at which the frame is added
   long       id              =0;            // Public id of the frame
   double     value           =0.0;          // Single numerical value of the frame
   string     parameters_list[];             // List of the Expert Advisor parameters of the "parameterN=valueN" form
   string     parameter_names[];             // Array of parameter names
   string     parameter_values[];            // Array of parameter values
//--- Increase the pass counter
   passes_count++;
//--- Place statistical values into the array
   FrameNext(pass,name,id,value,stat_values);
//--- Get the pass number, list of parameters, number of parameters
   FrameInputs(pass,parameters_list,parameters_count);
//--- Iterate over the list of parameters in a loop (starting from the upper one on the list)
//    The list starts with the parameters that are flagged for optimization
   for(int i=0; i<parameters_count; i++)
     {
      //--- Get the criteria for selection of results at the first pass
      if(passes_count==1)
        {
         string current_value="";      // Current parameter value
         static int c=0,v=0,trigger=0; // Counters and trigger
         //--- Set a flag if you reached the list of criteria
         if(StringFind(parameters_list[i],"CriterionSelectionRule",0)>=0)
           {
            include_criteria_list=true;
            continue;
           }
         //--- At the last parameter, count the used criteria,
         //    if the AND mode is selected
         if(CriterionSelectionRule==RULE_AND && i==parameters_count-1)
            CalculateUsedCriteria();
         //--- If you reached criteria in the parameter list
         if(include_criteria_list)
           {
            //--- Determine names of criteria
            if(trigger==0)
              {
               equality_sign_index=StringFind(parameters_list[i],"=",0)+1;          // Determine the '=' sign position in the string
               current_value =StringSubstr(parameters_list[i],equality_sign_index); // Get the parameter value
               //---
               criteria[c]=(int)StringToInteger(current_value);
               trigger=1; // Next parameter will be a value
               c++;
               continue;
              }
            //--- Determine values of criteria
            if(trigger==1)
              {
               equality_sign_index=StringFind(parameters_list[i],"=",0)+1;          // Determine the '=' sign position in the string
               current_value=StringSubstr(parameters_list[i],equality_sign_index);  // Get the parameter value
               //---           
               criteria_values[v]=StringToDouble(current_value);
               trigger=0; // Next parameter will be a criterion
               v++; 
               continue;
              }
           }
        }
      //--- If the parameter is enabled for optimization
      if(ParameterEnabledForOptimization(parameters_list[i]))
        {
         //--- Increase the counter of the optimized parameters
         optimized_parameters_count++;
         //--- Write the names of the optimized parameters to the array
         //    only at the first pass (for headers)
         if(passes_count==1)
           {
            //--- Increase the size of the array of parameter values
            ArrayResize(parameter_names,optimized_parameters_count);
            //--- Determine the '=' sign position         
            equality_sign_index=StringFind(parameters_list[i],"=",0);
            //--- Take the parameter name
            parameter_names[i]=StringSubstr(parameters_list[i],0,equality_sign_index);
           }
         //--- Increase the size of the array of parameter values
         ArrayResize(parameter_values,optimized_parameters_count);
         //--- Determine the '=' sign position
         equality_sign_index=StringFind(parameters_list[i],"=",0)+1;
         //--- Take the parameter value
         parameter_values[i]=StringSubstr(parameters_list[i],equality_sign_index);
        }
     }
//--- Generate a string of values to the optimized parameters
   for(int i=0; i<STAT_VALUES_COUNT; i++)
      StringAdd(string_to_write,DoubleToString(stat_values[i],2)+",");
//--- Add values of the optimized parameters to the string of values
   for(int i=0; i<optimized_parameters_count; i++)
     {
      //--- If it is the last value in the string, do not use the separator
      if(i==optimized_parameters_count-1)
        {
         StringAdd(string_to_write,parameter_values[i]);
         break;
        }
      //--- Otherwise use the separator
      else
         StringAdd(string_to_write,parameter_values[i]+",");
     }
//--- At the first pass, generate the optimization report file with headers
   if(passes_count==1)
      WriteOptimizationReport(parameter_names);
//--- Write data to the file of optimization results
   WriteOptimizationResults(string_to_write);
  }

Oldukça büyük bir fonksiyonumuz var. Gelin buna daha yakından bakalım. En başta, değişkenleri ve dizileri tanımladıktan hemen sonra, yukarıdaki örneklerde gösterildiği gibi FrameNext() fonksiyonunu kullanarak çerçeve verilerini elde ederiz. Ardından, FrameInputs() fonksiyonunu kullanarak parameters_count değişkenine aktarılan toplam parametre sayısı ile birlikte parameters_list[] dize dizisine parametre listesini elde ederiz.

FrameInputs() fonksiyonundan alınan parametre listesindeki optimize edilmiş parametreler (Strateji Test Cihazında işaretlenir), Uzman Danışmanın harici parametreler listesindeki sıralarına bakılmaksızın en başta bulunur.

Bunu, parametre listesi üzerinde yinelenen döngü izler. criteria[] kriter dizisi ve criteria_values[] kriter değerleri dizisi ilk geçişte doldurulur. Kullanılan kriterler, VE modunun etkinleştirilmesi ve geçerli parametrenin sonuncusu olması koşuluyla CalculateUsedCriteria() fonksiyonunda sayılır:

//+--------------------------------------------------------------------+
//| Counting the number of used criteria                               |
//+--------------------------------------------------------------------+
void CalculateUsedCriteria()
  {
   UsedCriteriaCount=0; // Zeroing out
//--- Iterate over the list of criteria in a loop
   for(int i=0; i<ArraySize(criteria); i++)
     {
      //--- count the used criteria
      if(criteria[i]!=C_NO_CRITERION)
         UsedCriteriaCount++;
     }
  }

Aynı döngüde, optimizasyon için herhangi bir parametrenin seçilip seçilmediğini de kontrol ederiz. Kontrol, her geçişte gerçekleştirilir ve geçerli harici parametrenin kontrol için aktarıldığı ParameterEnabledForOptimization() fonksiyonu kullanılarak yapılır. Fonksiyon true değerini döndürürse, parametre optimize edilecektir.

//+---------------------------------------------------------------------+
//| Checking whether the external parameter is enabled for optimization |
//+---------------------------------------------------------------------+
bool ParameterEnabledForOptimization(string parameter_string)
  {
   bool enable;
   long value,start,step,stop;
//--- Determine the '=' sign position in the string
   int equality_sign_index=StringFind(parameter_string,"=",0);
//--- Get the parameter values
   ParameterGetRange(StringSubstr(parameter_string,0,equality_sign_index),
                     enable,value,start,step,stop);
//--- Return the parameter status
   return(enable);
  }

Bu durumda, adlar parameter_names ve parameter_values parametre değerleri için diziler doldurulur. Optimize edilmiş parametre adları dizisi yalnızca ilk geçişte doldurulur.

Ardından, iki döngü kullanarak, bir dosyaya yazmak için test ve parametre değerleri dizisini oluştururuz. Bunu takiben, ilk geçişte WriteOptimizationReport() fonksiyonu kullanılarak yazma dosyası oluşturulur.

//+--------------------------------------------------------------------+
//| Generating the optimization report file                            |
//+--------------------------------------------------------------------+
void WriteOptimizationReport(string &parameter_names[])
  {
   int files_count     =1; // Counter of optimization files
//--- Generate a header to the optimized parameters
   string headers="#,PROFIT,TOTAL DEALS,PROFIT FACTOR,EXPECTED PAYOFF,EQUITY DD MAX REL%,RECOVERY FACTOR,SHARPE RATIO,";
//--- Add the optimized parameters to the header
   for(int i=0; i<ArraySize(parameter_names); i++)
     {
      if(i==ArraySize(parameter_names)-1)
         StringAdd(headers,parameter_names[i]);
      else
         StringAdd(headers,parameter_names[i]+",");
     }
//--- Get the location for the optimization file and the number of files for the index number
   OptimizationResultsPath=CreateOptimizationResultsFolder(files_count);
//--- If there is an error when getting the folder, exit
   if(OptimizationResultsPath=="")
     {
      Print("Empty path: ",OptimizationResultsPath);
      return;
     }
   else
     {
      OptimizationFileHandle=FileOpen(OptimizationResultsPath+"\optimization_results"+IntegerToString(files_count)+".csv",
                                      FILE_CSV|FILE_READ|FILE_WRITE|FILE_ANSI|FILE_COMMON,",");
      //---
      if(OptimizationFileHandle!=INVALID_HANDLE)
         FileWrite(OptimizationFileHandle,headers);
     }
  }

WriteOptimizationReport() fonksiyonunun amacı, başlıklar oluşturmak, gerekirse terminalin ortak klasöründe klasörler oluşturmak ve ayrıca yazmak için bir dosya oluşturmaktır. Diğer bir deyişle, önceki optimizasyonlarla ilişkili dosyalar kaldırılmaz ve fonksiyon her seferinde indis numarasıyla yeni bir dosya oluşturur. Başlıklar yeni oluşturulan bir dosyaya kaydedilir. Dosyanın kendisi optimizasyonun sonuna kadar açık kalır.

Yukarıdaki kod, dosyaları optimizasyon sonuçları ile kaydetmek için klasörlerin oluşturulduğu CreateOptimizationResultsFolder() fonksiyonuna sahip dizeyi içerir:

//+--------------------------------------------------------------------+
//| Creating folders for optimization results                          |
//+--------------------------------------------------------------------+
string CreateOptimizationResultsFolder(int &files_count)
  {
   long   search_handle       =INVALID_HANDLE;        // Search handle
   string returned_filename   ="";                    // Name of the found object (file/folder)
   string path                ="";                    // File/folder search location
   string search_filter       ="*";                   // Search filter (* - check all files/folders)
   string root_folder         ="OPTIMIZATION_DATA\\"; // Root folder
   string expert_folder       =EXPERT_NAME+"\\";      // Folder of the Expert Advisor
   bool   root_folder_exists  =false;                 // Flag of existence of the root folder
   bool   expert_folder_exists=false;                 // Flag of existence of the Expert Advisor folder
//--- Search for the OPTIMIZATION_DATA root folder in the common folder of the terminal
   path=search_filter;
//--- Set the search handle in the common folder of all client terminals \Files
   search_handle=FileFindFirst(path,returned_filename,FILE_COMMON);
//--- Print the location of the common folder of the terminal to the journal
   Print("TERMINAL_COMMONDATA_PATH: ",COMMONDATA_PATH);
//--- If the first folder is the root folder, flag it
   if(returned_filename==root_folder)
     {
      root_folder_exists=true;
      Print("The "+root_folder+" root folder exists.");
     }
//--- If the search handle has been obtained
   if(search_handle!=INVALID_HANDLE)
     {
      //--- If the first folder is not the root folder
      if(!root_folder_exists)
        {
         //--- Iterate over all files to find the root folder
         while(FileFindNext(search_handle,returned_filename))
           {
            //--- If it is found, flag it
            if(returned_filename==root_folder)
              {
               root_folder_exists=true;
               Print("The "+root_folder+" root folder exists.");
               break;
              }
           }
        }
      //--- Close the root folder search handle
      FileFindClose(search_handle);
     }
   else
     {
      Print("Error when getting the search handle "
            "or the "+COMMONDATA_PATH+" folder is empty: ",ErrorDescription(GetLastError()));
     }
//--- Search for the Expert Advisor folder in the OPTIMIZATION_DATA folder
   path=root_folder+search_filter;
//--- Set the search handle in the ..\Files\OPTIMIZATION_DATA\ folder
   search_handle=FileFindFirst(path,returned_filename,FILE_COMMON);
//--- If the first folder is the folder of the Expert Advisor
   if(returned_filename==expert_folder)
     {
      expert_folder_exists=true; // Remember this
      Print("The "+expert_folder+" Expert Advisor folder exists.");
     }
//--- If the search handle has been obtained
   if(search_handle!=INVALID_HANDLE)
     {
      //--- If the first folder is not the folder of the Expert Advisor
      if(!expert_folder_exists)
        {
         //--- Iterate over all files in the DATA_OPTIMIZATION folder to find the folder of the Expert Advisor
         while(FileFindNext(search_handle,returned_filename))
           {
            //--- If it is found, flag it
            if(returned_filename==expert_folder)
              {
               expert_folder_exists=true;
               Print("The "+expert_folder+" Expert Advisor folder exists.");
               break;
              }
           }
        }
      //--- Close the root folder search handle
      FileFindClose(search_handle);
     }
   else
      Print("Error when getting the search handle or the "+path+" folder is empty.");
//--- Generate the path to count the files
   path=root_folder+expert_folder+search_filter;
//--- Set the search handle in the ..\Files\OPTIMIZATION_DATA\ folder of optimization results
   search_handle=FileFindFirst(path,returned_filename,FILE_COMMON);
//--- If the folder is not empty, start the count
   if(StringFind(returned_filename,"optimization_results",0)>=0)
      files_count++;
//--- If the search handle has been obtained
   if(search_handle!=INVALID_HANDLE)
     {
      //--- Count all files in the Expert Advisor folder
      while(FileFindNext(search_handle,returned_filename))
         files_count++;
      //---
      Print("Total files: ",files_count);
      //--- Close the Expert Advisor folder search handle
      FileFindClose(search_handle);
     }
   else
      Print("Error when getting the search handle or the "+path+" folder is empty");
//--- Create the necessary folders based on the check results
//    If there is no OPTIMIZATION_DATA root folder
   if(!root_folder_exists)
     {
      if(FolderCreate("OPTIMIZATION_DATA",FILE_COMMON))
        {
         root_folder_exists=true;
         Print("The root folder ..\Files\OPTIMIZATION_DATA\\ has been created");
        }
      else
        {
         Print("Error when creating the OPTIMIZATION_DATA root folder: ",
               ErrorDescription(GetLastError()));
         return("");
        }
     }
//--- If there is no Expert Advisor folder
   if(!expert_folder_exists)
     {
      if(FolderCreate(root_folder+EXPERT_NAME,FILE_COMMON))
        {
         expert_folder_exists=true;
         Print("The Expert Advisor folder ..\Files\OPTIMIZATION_DATA\\ has been created"+expert_folder);
        }
      else
        {
         Print("Error when creating the Expert Advisor folder ..\Files\\"+expert_folder+"\: ",
               ErrorDescription(GetLastError()));
         return("");
        }
     }
//--- If the necessary folders exist
   if(root_folder_exists && expert_folder_exists)
     {
      //--- Return the location for creating the file of optimization results
      return(root_folder+EXPERT_NAME);
     }
//---
   return("");
  }

Yukarıdaki kod ayrıntılı yorumlarla birlikte verilmiştir, bu nedenle anlamakta herhangi bir zorlukla karşılaşmamalısınız. Sadece kilit noktaları özetleyelim.

İlk olarak, optimizasyon sonuçlarını içeren OPTIMIZATION_DATA kök klasörünü kontrol ediyoruz. Klasör varsa, bu root_folder_exists değişkeninde işaretlenir. Ardından, arama işleyici Uzman Danışman klasörünü kontrol ettiğimiz OPTIMIZATION_DATA klasöründe ayarlanır.

Uzman Danışman klasörünün içerdiği dosyaları da sayarız. Son olarak, kontrol sonuçlarına göre, gerektiğinde (klasörler bulunamadıysa), gerekli klasörler oluşturulur ve indis numarası ile yeni dosyanın konumu döndürülür. Bir hata oluşursa, boş bir dize döndürülür.

Şimdi sadece dosyaya veri yazma koşullarını kontrol ettiğimiz ve koşul karşılanıyorsa veriyi yazdığımız WriteOptimizationResults() fonksiyonunu dikkate almamız gerekiyor. Bu fonksiyonun kodu aşağıda verilmektedir:

//+--------------------------------------------------------------------+
//| Writing the results of the optimization by criteria                |
//+--------------------------------------------------------------------+
void WriteOptimizationResults(string string_to_write)
  {
   bool condition=false; // To check the condition
//--- If at least one criterion is satisfied
   if(CriterionSelectionRule==RULE_OR)
      condition=AccessCriterionOR();
//--- If all criteria are satisfied
   if(CriterionSelectionRule==RULE_AND)
      condition=AccessCriterionAND();
//--- If the conditions for criteria are satisfied
   if(condition)
     {
      //--- If the file of optimization results is opened
      if(OptimizationFileHandle!=INVALID_HANDLE)
        {
         int strings_count=0; // String counter
         //--- Get the number of strings in the file and move the pointer to the end
         strings_count=GetStringsCount();
         //--- Write the string with criteria
         FileWrite(OptimizationFileHandle,IntegerToString(strings_count),string_to_write);
        }
      else
         Print("Invalid optimization file handle!");
     }
  }

Kodda vurgulanan fonksiyonları içeren dizelere bir göz atalım. Kullanılan fonksiyonun seçimi, kriterleri kontrol etmek için seçilen kurala bağlıdır. Belirtilen tüm kriterlerin karşılanması gerekiyorsa, AccessCriterionAND() fonksiyonunu kullanırız:

//+--------------------------------------------------------------------+
//| Checking multiple conditions for writing to the file               |
//+--------------------------------------------------------------------+
bool AccessCriterionAND()
  {
   int count=0; // Criterion counter
//--- Iterate over the array of criteria in a loop and see
//    if all the conditions for writing parameters to the file are met
   for(int i=0; i<ArraySize(criteria); i++)
     {
      //--- Move to the next iteration, if the criterion is not determined
      if(criteria[i]==C_NO_CRITERION)
         continue;
      //--- PROFIT
      if(criteria[i]==C_STAT_PROFIT)
        {
         if(stat_values[0]>criteria_values[i])
           {
            count++;
            if(count==UsedCriteriaCount)
               return(true);
           }
        }
      //--- TOTAL DEALS
      if(criteria[i]==C_STAT_DEALS)
        {
         if(stat_values[1]>criteria_values[i])
           {
            count++;
            if(count==UsedCriteriaCount)
               return(true);
           }
        }
      //--- PROFIT FACTOR
      if(criteria[i]==C_STAT_PROFIT_FACTOR)
        {
         if(stat_values[2]>criteria_values[i])
           {
            count++;
            if(count==UsedCriteriaCount)
               return(true);
           }
        }
      //--- EXPECTED PAYOFF
      if(criteria[i]==C_STAT_EXPECTED_PAYOFF)
        {
         if(stat_values[3]>criteria_values[i])
           {
            count++;
            if(count==UsedCriteriaCount)
               return(true);
           }
        }
      //--- EQUITY DD REL PERC
      if(criteria[i]==C_STAT_EQUITY_DDREL_PERCENT)
        {
         if(stat_values[4]<criteria_values[i])
           {
            count++;
            if(count==UsedCriteriaCount)
               return(true);
           }
        }
      //--- RECOVERY FACTOR
      if(criteria[i]==C_STAT_RECOVERY_FACTOR)
        {
         if(stat_values[5]>criteria_values[i])
           {
            count++;
            if(count==UsedCriteriaCount)
               return(true);
           }
        }
      //--- SHARPE RATIO
      if(criteria[i]==C_STAT_SHARPE_RATIO)
        {
         if(stat_values[6]>criteria_values[i])
           {
            count++;
            if(count==UsedCriteriaCount)
               return(true);
           }
        }
     }
//--- Conditions for writing are not met
   return(false);
  }

Belirtilen kriterlerden en az birine ihtiyacınız varsa, AccessCriterionOR() fonksiyonunu kullanın:

//+--------------------------------------------------------------------+
//| Checking for meeting one of the conditions for writing to the file |
//+--------------------------------------------------------------------+
bool AccessCriterionOR()
  {
//--- Iterate over the array of criteria in a loop and see 
//    if all the conditions for writing parameters to the file are met
   for(int i=0; i<ArraySize(criteria); i++)
     {
     //---
      if(criteria[i]==C_NO_CRITERION)
         continue;
      //--- PROFIT
      if(criteria[i]==C_STAT_PROFIT)
        {
         if(stat_values[0]>criteria_values[i])
            return(true);
        }
      //--- TOTAL DEALS
      if(criteria[i]==C_STAT_DEALS)
        {
         if(stat_values[1]>criteria_values[i])
            return(true);
        }
      //--- PROFIT FACTOR
      if(criteria[i]==C_STAT_PROFIT_FACTOR)
        {
         if(stat_values[2]>criteria_values[i])
            return(true);
        }
      //--- EXPECTED PAYOFF
      if(criteria[i]==C_STAT_EXPECTED_PAYOFF)
        {
         if(stat_values[3]>criteria_values[i])
            return(true);
        }
      //--- EQUITY DD REL PERC
      if(criteria[i]==C_STAT_EQUITY_DDREL_PERCENT)
        {
         if(stat_values[4]<criteria_values[i])
            return(true);
        }
      //--- RECOVERY FACTOR
      if(criteria[i]==C_STAT_RECOVERY_FACTOR)
        {
         if(stat_values[5]>criteria_values[i])
            return(true);
        }
      //--- SHARPE RATIO
      if(criteria[i]==C_STAT_SHARPE_RATIO)
        {
         if(stat_values[6]>criteria_values[i])
            return(true);
        }
     }
//--- Conditions for writing are not met
   return(false);
  }

GetStringsCount() fonksiyonu, işaretçiyi dosyanın sonuna taşır ve dosyadaki dizelerin sayısını döndürür:

//+--------------------------------------------------------------------+
//| Counting the number of strings in the file                         |
//+--------------------------------------------------------------------+
int GetStringsCount()
  {
   int   strings_count =0;  // String counter
   ulong offset        =0;  // Offset for determining the position of the file pointer
//--- Move the file pointer to the beginning
   FileSeek(OptimizationFileHandle,0,SEEK_SET);
//--- Read until the current position of the file pointer reaches the end of the file
   while(!FileIsEnding(OptimizationFileHandle) || !IsStopped())
     {
      //--- Read the whole string
      while(!FileIsLineEnding(OptimizationFileHandle) || !IsStopped())
        {
         //--- Read the string
         FileReadString(OptimizationFileHandle);
         //--- Get the position of the pointer
         offset=FileTell(OptimizationFileHandle);
         //--- If it's the end of the string
         if(FileIsLineEnding(OptimizationFileHandle))
           {
            //--- Move to the next string 
            //    if it's not the end of the file, increase the pointer counter
            if(!FileIsEnding(OptimizationFileHandle))
               offset++;
            //--- Move the pointer
            FileSeek(OptimizationFileHandle,offset,SEEK_SET);
            //--- Increase the string counter
            strings_count++;
            break;
           }
        }
      //--- If it's the end of the file, exit the loop
      if(FileIsEnding(OptimizationFileHandle))
         break;
     }
//--- Move the pointer to the end of the file for writing
   FileSeek(OptimizationFileHandle,0,SEEK_END);
//--- Return the number of strings
   return(strings_count);
  }

Her şey ayarlandı ve şimdi hazır. Şimdi CreateOptimizationReport() fonksiyonunu OnTesterPass() fonksiyon gövdesine eklememiz ve OnTesterDeinit() fonksiyonundaki optimizasyon dosyası işleyicisini kapatmamız gerekiyor.

Şimdi Uzman Danışmanı test edelim. Parametreleri, dağıtılmış bilgi işlemli MQL5 Cloud Network kullanılarak optimize edilecektir. Strateji Test Cihazı aşağıdaki ekran görüntüsünde gösterildiği gibi ayarlanmalıdır:

Şekil 3. Strateji Test Cihazı ayarları

Şekil 3 - Strateji Test Cihazı ayarları

Uzman Danışmanın tüm parametrelerini optimize edeceğiz ve kriterlerin parametrelerini, yalnızca Kâr Faktörü değerinin 1'den büyük ve Kurtarma Faktörü değerinin 2'den büyük olduğu sonuçların dosyaya yazılacağı şekilde ayarlayacağız (aşağıdaki ekran görüntüsüne bakın):

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

Şekil 4 - Parametre optimizasyonu için Uzman Danışman ayarları

Dağıtılmış bilgi işlemin MQL5 Bulut Ağı yalnızca ~5 dakika içinde 101.000 geçişi işlemiştir! Ağ kaynaklarını kullanmamış olsaydım, optimizasyonun tamamlanması birkaç gün sürecekti. Bu, zamanın değerini bilen herkes için büyük bir fırsat.

Ortaya çıkan dosya artık Excel'de açılabilir. Dosyaya yazılmak üzere 101.000 geçiş arasından 719 sonuç seçilmiştir. Aşağıdaki ekran görüntüsünde, sonuçların seçildiği parametrelere sahip sütunları vurguladım:

Şekil 5 - Excel'de optimizasyon sonuçları

Şekil 5 - Excel'de optimizasyon sonuçları

 

Sonuç

Bu makalenin altına bir çizgi çekmenin zamanı geldi. Optimizasyon sonuçlarının analizi konusu aslında tamamen bitmiş olmaktan çok uzaktır ve gelecek makalelerde kesinlikle buna geri döneceğiz. Uzman Danışmanın dosyalarını içeren indirilebilir arşiv değerlendirmeniz için makaleye eklenmiştir.

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

Ekli dosyalar |
MQL5 Tarif Defteri MetaTrader 5 Alım Satım Olayları için Sesli Bildirimler MQL5 Tarif Defteri MetaTrader 5 Alım Satım Olayları için Sesli Bildirimler
Bu makalede, Uzman Danışmanın dosyasına ses dosyaları eklenmesi ve böylece alım satım olaylarına sesli bildirimler eklenmesi gibi konuları ele alacağız. Dosyaların eklenmesi, ses dosyalarının Uzman Danışman içine yerleştirileceği anlamına gelir. Bu nedenle, Uzman Danışmanın derlenmiş sürümünü (*.ex5) başka bir kullanıcıya sağlarken, ses dosyalarını da sağlamanız ve nereye kaydedilmeleri gerektiğini açıklamanız gerekmez.
MQL5 Standart Kitaplığını Genişletme ve Kodu Yeniden Kullanma MQL5 Standart Kitaplığını Genişletme ve Kodu Yeniden Kullanma
MQL5 Standart Kitaplığı, bir geliştirici olarak hayatınızı kolaylaştırır. Ancak, dünyadaki bütün geliştiricilerin ihtiyaçlarını karşılamaz, bu yüzden daha özel bir şeye ihtiyacınız olduğunu düşünüyorsanız, bir adım daha ileri gidebilir ve bunu genişletebilirsiniz. Bu makale, size MetaQuotes'un Zig-Zag teknik göstergesini Standart Kitaplığa entegre etme konusunu anlatır. Amacımıza ulaşmak için MetaQuotes'un tasarım felsefesinden ilham aldık.
MQL5 Tarif Defteri Birkaç Zaman Aralığını Tek bir Pencerede Görüntüleme MQL5 Tarif Defteri Birkaç Zaman Aralığını Tek bir Pencerede Görüntüleme
Analiz için MetaTrader 5'te kullanılabilir 21 zaman aralığı vardır. Mevcut grafiğe yerleştirebileceğiniz özel grafik nesnelerinden yararlanabilir ve sembolü, zaman aralığını ve diğer bazı özellikleri tam buradan ayarlayabilirsiniz. Bu makale, bu grafik nesnelerine dair ayrıntılı bilgi sağlayacaktır: bir alt pencerede aynı anda birçok grafik nesnesi ayarlamamıza olanak sağlayacak kontrollere (düğmelere) sahip bir gösterge oluşturacağız. Ayrıca, grafik nesneleri alt pencereye tam olarak sığar ve ana grafik veya terminal penceresi yeniden boyutlandırıldığında otomatik olarak ayarlanır.
Teknik Göstergeler ve Sayısal Filtreler Teknik Göstergeler ve Sayısal Filtreler
Bu makalede, teknik göstergeler sayısal filtreler olarak ele alınmaktadır. Sayısal filtrelerin çalışma ilkeleri ve temel özellikleri açıklanmaktadır. Ayrıca, MetaTrader 5 terminalinde filtre çekirdeği almanın bazı pratik yolları ve "Bir Spektrum Analiz Cihazı Oluşturma" makalesinde önerilen hazır bir spektrum analiz cihazı ile entegrasyon ele alınmaktadır. Tipik sayısal filtrelerin darbe ve spektrum özellikleri örnek olarak kullanılmaktadır.