MQL5 Tarif Defteri Uzman Danışmanın Belirlenen Kriterlere Göre Optimizasyon Sonuçlarını Kaydetme
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
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
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 ¶meter_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ı
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ı
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ı
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
- Ücretsiz ticaret uygulamaları
- İşlem kopyalama için 8.000'den fazla sinyal
- Finansal piyasaları keşfetmek için ekonomik haberler
Gizlilik ve Veri Koruma Politikasını ve MQL5.com Kullanım Şartlarını kabul edersiniz