
MQL5 クックブック:パラメータ数無制限での複数通貨対応 EXPERT 作成
はじめに
以前の記事 "MQL5 Cookbook: Multi-Currency Expert Advisor - Simple, Neat and Quick Approach"で考察されている複数通貨対応 Expert Advisor は使用されるシンボルと戦略パラメータ数がちいさい場合はひじょうに便利です。ただし MQL5では Expert Advisor の入力パラメータ数に制約があります。1024を越えてはいけないのです。
多くの場合この数は十分であってもそのように大きなパラメータリストを使うことはひじょうに不便です。既定のシンボルに対するパラメータに変更や最適化が必要となるたびに長いパラメータリストでその指定のシンボルに対するパラメータを探す必要があります。
本稿ではトレーディングシステムの最適化に対して一組のパラメータを使うパターンを作成していきます。同時にパラメータ数は無制限に認めます。シンボルリストは標準的なテキストファイル (*.txt) 内に作成します。各シンボルに対する入力パラメータもファイルに格納されます。
Expert Advisor は通常のオペレーションモードではシンボル一つについて動作しますが、「ストラテジーテスタ」では選択した多様なシンボルについて(各シンボル個別に)検証を行うことが可能であることをお伝えする必要があります。
既製のシンボルセットを保存することができることを考慮すると、実際シンボルリストを銘柄リストウィンドウで直接作成する方が都合がよいことでしょう。Expert Advisor が「ストラテジーテスタ」から直接「銘柄リスト」ウィンドウのシンボルリストを取得できるようにすることも可能かもしれません。ただ残念ながら現時点では「ストラテジーテスタ」から「銘柄リスト」にアクセスすることはできないため、事前にマニュアルでまたはスクリプトを持ち手シンボルリストを作成する必要があります。
Expert Advisor の作成
前稿 "MQL5 Cookbook: Multi-Currency Expert Advisor - Simple, Neat and Quick Approach" で取り上げている複数通過対応 Expert Advisor はテンプレートとして取り入れられます。まず入力パラメータを決めます。上述のようにパラメータは1組のみ残します。以下はExpert Advisorの入力パラメータリストです。
//--- Input parameters of the Expert Advisor sinput long MagicNumber =777; // Magic number sinput int Deviation =10; // Slippage sinput string delimeter_00=""; // -------------------------------- sinput int SymbolNumber =1; // Number of the tested symbol sinput bool RewriteParameters =false; // Rewriting parameters sinput ENUM_INPUTS_READING_MODE ParametersReadingMode=FILE; // Parameter reading mode sinput string delimeter_01=""; // -------------------------------- input int IndicatorPeriod =5; // Indicator period input double TakeProfit =100; // Take Profit input double StopLoss =50; // Stop Loss input double TrailingStop =10; // Trailing Stop input bool Reverse =true; // Position reversal input double Lot =0.1; // Lot input double VolumeIncrease =0.1; // Position volume increase input double VolumeIncreaseStep =10; // Volume increase step
入力モディファイアを持つパラメータのみファイルに書き込まれます。後に、これまで出くわしたことのない新しいパラメータ3つに関して拡張する必要があります。
- SymbolNumber -このパラメータはシンボルリストを持つファイルからシンボル番号を指示します。0に設定されているとリストからの全シンボルが検証されます。リスト以上におよぶと検証は行われません。
- RewriteParameters -このパラメータ値が真に設定されていれば、指定されたシンボルのパラメータ(SymbolNumber内の番号)を持つファイルが現在の入力パラメータ値を用いて再度書き込まれます。またこのパラメータが偽に設定されていればファイルからパラメータが読みだされます。
- ParametersReadingMode -このパラメータは入力パラメータに関して読み出しモードを指示します。パラメータタイプはオプションを2つ提供する ENUM_INPUTS_READING_MODE カスタムリストです。そのオプションはファイルからの読み出しと現在パラメータの使用です。
以下がENUM_INPUTS_READING_MODE 列挙コードです。
//+------------------------------------------------------------------+ //| Input parameter reading modes | //+------------------------------------------------------------------+ enum ENUM_INPUTS_READING_MODE { FILE = 0, // File INPUT_PARAMETERS = 1 // Input parameters };
シンボル番号は NUMBER_OF_SYMBOLS 定数を用いて事前に決められています。ここでこの値は異なるモードに依存するため、それをグローバル変数 SYMBOLS_COUNT に変更します。
//--- Number of traded symbols. It is calculated and depends on the testing mode and the number of symbols in the file int SYMBOLS_COUNT=0に依存します。
Expert Advisorコードの冒頭では別の定数TESTED_PARAMETERS_COUNTを宣言します。それは配列サイズと入力パラメータを反復する場合はループ反復回数を決めます。
//--- Number of tested/optimized parameters #define TESTED_PARAMETERS_COUNT 8
シンボルリストを持つファイルは、各シンボルにたいするパラメータを持つファイルフォルダ同様、端末の共通フォルダーに入れる必要があります。それは Expert Advisor の通常処理モードおよび検証中のどちらでもプログラムレベルにアクセス可能となるためです。
ここでのちに作業する配列の準備をします。変更をしようとしている前稿から流用している複数通貨対応 Expert Advisor の配列はすべて静的です。すなわちエレメントのサイズが事前に設定されているのです。ところがここではエレメントサイズはファイルで使用されるシンボル番号と入力パラメータの読み出しモードに依存するため配列をすべて動的に変更する必要があります。またファイル作業に必要な新しい配列(黄色で強調表示しています)を追加しようと思います。
//--- Arrays of input parameters string InputSymbols[]; // Symbol names //--- int InputIndicatorPeriod[]; // Indicator periods double InputTakeProfit[]; // Take Profit values double InputStopLoss[]; // Stop Loss values double InputTrailingStop[]; // Trailing Stop values bool InputReverse[]; // Values of position reversal flags double InputLot[]; // Lot values double InputVolumeIncrease[]; // Position volume increases double InputVolumeIncreaseStep[]; // Volume increase steps //--- Array of handles for indicator agents int spy_indicator_handles[]; //--- Array of signal indicator handles int signal_indicator_handles[]; //--- Data arrays for checking trading conditions struct PriceData { double value[]; }; PriceData open[]; // Opening price of the bar PriceData high[]; // High price of the bar PriceData low[]; // Low price of the bar PriceData close[]; // Closing price of the bar PriceData indicator[]; // Array of indicator values //--- Arrays for getting the opening time of the current bar struct Datetime { datetime time[]; }; Datetime lastbar_time[]; //--- Array for checking the new bar for each symbol datetime new_bar[]; //--- Array of input parameter names for writing to the file string input_parameters[TESTED_PARAMETERS_COUNT]= { "IndicatorPeriod", // Indicator period "TakeProfit", // Take Profit "StopLoss", // Stop Loss "TrailingStop", // Trailing Stop "Reverse", // Position reversal "Lot", // Lot "VolumeIncrease", // Position volume increase "VolumeIncreaseStep" // Volume increase step }; //--- Array for untested symbols string temporary_symbols[]; //--- Array for storing input parameters from the file of the symbol selected for testing or trading double tested_parameters_from_file[]; //--- Array of input parameter values for writing to the file double tested_parameters_values[TESTED_PARAMETERS_COUNT];
それから配列サイズを決め、値に対して起動します。
上記で作成された入力パラメータ値の配列は、 InitializeArrays.mqhファイルに作成する予定のInitializeTestedParametersValues()関数内で初期化します。その配列はファイルに入力パラメータ値を書き込むのに使用されます。
//+------------------------------------------------------------------+ //| Initializing the array of tested input parameters | //+------------------------------------------------------------------+ void InitializeTestedParametersValues() { tested_parameters_values[0]=IndicatorPeriod; tested_parameters_values[1]=TakeProfit; tested_parameters_values[2]=StopLoss; tested_parameters_values[3]=TrailingStop; tested_parameters_values[4]=Reverse; tested_parameters_values[5]=Lot; tested_parameters_values[6]=VolumeIncrease; tested_parameters_values[7]=VolumeIncreaseStep; }
ではファイル処理について考察しましょう。まずもう一つ別の関数ライブラリ、FileFunctions.mqhを Expert Advisor のUnlimitedParametersEA\Include フォルダに作成します。このライブラリはファイルからデータを読んだりファイルにデータを書き込む関数を作成する際に利用します。それを Expert Advisor のメインファイルおよびプロジェクトのその他ファイルにインクルードします。
//---Include custom libraries #include "Include\Enums.mqh" #include "Include\InitializeArrays.mqh" #include "Include\Errors.mqh" #include "Include\FileFunctions.mqh" #include "Include\TradeSignals.mqh" #include "Include\TradeFunctions.mqh" #include "Include\ToString.mqh" #include "Include\Auxiliary.mqh"
テクストファイル:ReadSymbolsFromFile()からシンボルリストを読み出す関数を作成することからスタートします。このファイル( TestedSymbols.txtと名前を付けましょう)はMetaTrader 5 クライアントターミナルの共通フォルダのサブフォルダ \Files に入れます。私の場合それはC:\ProgramData\MetaQuotes\Terminal\Common ですが、標準定数 TERMINAL_COMMONDATA_PATH を用いてパスを注意して確認します。
TerminalInfoString(TERMINAL_COMMONDATA_PATH)
機能性を確認するには、作成したテキストファイルにシンボルを数個追加します。その際、各シンボルは改行 ("\r\n")で分離します。
図1 ターミナルの共通フォルダのファイル内にあるシンボルリスト
それから ReadSymbolsFromFile() 関数コードを見ていきます。
//+------------------------------------------------------------------+ //| Returning the number of strings (symbols) in the file and | //| filling the temporary array of symbols temporary_symbols[] | //+------------------------------------------------------------------+ //--- When preparing the file, symbols in the list should be separated with a line break int ReadSymbolsFromFile(string file_name) { int strings_count=0; // String counter //--- Open the file for reading from the common folder of the terminal int file_handle=FileOpen(file_name,FILE_READ|FILE_ANSI|FILE_COMMON); //--- If the file handle has been obtained if(file_handle!=INVALID_HANDLE) { ulong offset =0; // Offset for determining the position of the file pointer string text =""; // The read string will be written to this variable //--- Read until the current position of the file pointer reaches the end of the file or until the program is deleted while(!FileIsEnding(file_handle) || !IsStopped()) { //--- Read until the end of the string or until the program is deleted while(!FileIsLineEnding(file_handle) || !IsStopped()) { //--- Read the whole string text=FileReadString(file_handle); //--- Get the position of the pointer offset=FileTell(file_handle); //--- Go to the next string if this is not the end of the file // For this purpose, increase the offset of the file pointer if(!FileIsEnding(file_handle)) offset++; //--- Move it to the next string FileSeek(file_handle,offset,SEEK_SET); //--- If the string is not empty if(text!="") { //--- Increase the string counter strings_count++; //--- Increase the size of the array of strings, ArrayResize(temporary_symbols,strings_count); //--- Write the read string to the current index temporary_symbols[strings_count-1]=text; } //--- Exit the nested loop break; } //--- If this is the end of the file, terminate the main loop if(FileIsEnding(file_handle)) break; } //--- Close the file FileClose(file_handle); } //--- Return the number of strings in the file return(strings_count); }
ここでテキストファイルが一行ずつ読まれます。読み出された各ファイナンシャルインスツルメントの名前は前に作成済みの一時的な temporary_symbols[] 配列に書き込まれます(トレードサーバー上のシンボルの実際のアクセスしやすさ は後に確認します)。また関数の末尾では読み出された文字列数が返されます。すなわちわれわれの Expert Advisor が検証される対処うのシンボル数です。
前に宣言されたシンボル名の InputSymbols[] 配列に情報を書き込むためのInitializeInputSymbols()関数を作成するInitializeArrays.mqh ファイルの話に戻ります。初心者の方はおそらく複雑だと思われるでしょう。そこでコードには詳しいコメントを施しました。
//+------------------------------------------------------------------+ //| Filling the InputSymbol[] array of symbols | //+------------------------------------------------------------------+ void InitializeInputSymbols() { int strings_count=0; // Number of strings in the symbol file string checked_symbol=""; // To check the accessibility of the symbol on the trade server //--- Get the number of symbols from the "TestedSymbols.txt" file strings_count=ReadSymbolsFromFile("TestedSymbols.txt"); //--- In optimization mode or in one of the two modes (testing or visualization), provided that the symbol NUMBER IS SPECIFIED if(IsOptimization() || ((IsTester() || IsVisualMode()) && SymbolNumber>0)) { //--- Determine the symbol to be involved in parameter optimization for(int s=0; s<strings_count; s++) { //--- If the number specified in the parameters and the current loop index match if(s==SymbolNumber-1) { //--- Check whether the symbol is on the trade server if((checked_symbol=GetSymbolByName(temporary_symbols[s]))!="") { //--- Set the number of symbols SYMBOLS_COUNT=1; //--- Set the size of the array of symbols ArrayResize(InputSymbols,SYMBOLS_COUNT); //--- Write the symbol name InputSymbols[0]=checked_symbol; } //--- Exit return; } } } //--- In testing or visualization mode, if you need to test ALL symbols from the list in the file if((IsTester() || IsVisualMode()) && SymbolNumber==0) { //--- Parameter reading mode: from the file if(ParametersReadingMode==FILE) { //--- Iterate over all symbols in the file for(int s=0; s<strings_count; s++) { //--- Check if the symbol is on the trade server if((checked_symbol=GetSymbolByName(temporary_symbols[s]))!="") { //--- Increase the symbol counter SYMBOLS_COUNT++; //--- Set the size of the array of symbols ArrayResize(InputSymbols,SYMBOLS_COUNT); //--- Write the symbol name InputSymbols[SYMBOLS_COUNT-1]=checked_symbol; } } //--- Exit return; } //--- Parameter reading mode: from input parameters of the Expert Advisor if(ParametersReadingMode==INPUT_PARAMETERS) { //--- Set the number of symbols SYMBOLS_COUNT=1; //--- Set the size of the array of symbols ArrayResize(InputSymbols,SYMBOLS_COUNT); //--- Write the current symbol name InputSymbols[0]=Symbol(); //--- Exit return; } } //--- In normal operation mode of the Expert Advisor, use the current chart symbol if(IsRealtime()) { //--- Set the number of symbols SYMBOLS_COUNT=1; //--- Set the size of the array of symbols ArrayResize(InputSymbols,SYMBOLS_COUNT); //--- Write the symbol name InputSymbols[0]=Symbol(); } //--- }
使用されるシンボル数によって入力パラメータの配列サイズが決まると、入力パラメータのその他配列すべてについてサイズを設定します。それを別の関数:ResizeInputParametersArrays()として実装します。
//+------------------------------------------------------------------+ //| Setting the new size for arrays of input parameters | //+------------------------------------------------------------------+ void ResizeInputParametersArrays() { ArrayResize(InputIndicatorPeriod,SYMBOLS_COUNT); ArrayResize(InputTakeProfit,SYMBOLS_COUNT); ArrayResize(InputStopLoss,SYMBOLS_COUNT); ArrayResize(InputTrailingStop,SYMBOLS_COUNT); ArrayResize(InputReverse,SYMBOLS_COUNT); ArrayResize(InputLot,SYMBOLS_COUNT); ArrayResize(InputVolumeIncrease,SYMBOLS_COUNT); ArrayResize(InputVolumeIncreaseStep,SYMBOLS_COUNT); }
ここで各シンボルについて入力パラメータ値を読むことを可能にする機能を作成する必要があります。同時に選択される入力モードおよび Expert Advisor の設定に応じてこれらパラメータ値を別個のファイルに書き込む機能も作成します。これはファイルからシンボルリストを読み出すのと似た方法で行います。複雑なタスクなので複数の手順に分けましょう。
まず、ファイルから配列に入力パラメータ読み出しを学習します。配列はダブルタイプの値を持ちます。のちにそれらのいくつか(インディケータ期間とポジション逆フラグ)をそれぞれ int タイプとブールタイプに変換します。オープンファイルのハンドルとファイルからパラメータ値が格納される配列が ReadInputParametersValuesFromFile() 関数に渡されます。
//+----------------------------------------------------------------------+ //| Reading parameters from the file and storing them in the passed array| //| Text file format: key=value //+----------------------------------------------------------------------+ bool ReadInputParametersValuesFromFile(int handle,double &array[]) { int delimiter_position =0; // Number of the symbol position "=" in the string int strings_count =0; // String counter string read_string =""; // The read string ulong offset =0; // Position of the pointer (offset in bytes) //--- Move the file pointer to the beginning of the file FileSeek(handle,0,SEEK_SET); //--- Read until the current position of the file pointer reaches the end of the file while(!FileIsEnding(handle)) { //--- If the user deleted the program if(IsStopped()) return(false); //--- Read until the end of the string while(!FileIsLineEnding(handle)) { //--- If the user deleted the program if(IsStopped()) return(false); //--- Read the string read_string=FileReadString(handle); //--- Get the index of the separator ("=") in the read string delimiter_position=StringFind(read_string,"=",0); //--- Get everything that follows the separator ("=") until the end of the string read_string=StringSubstr(read_string,delimiter_position+1); //--- Place the obtained value converted to the double type in the output array array[strings_count]=StringToDouble(read_string); //--- Get the current position of the file pointer offset=FileTell(handle); //--- If it's the end of the string if(FileIsLineEnding(handle)) { //--- Go to the next string if it's not the end of the file if(!FileIsEnding(handle)) //--- Increase the offset of the file pointer by 1 to go to the next string offset++; //--- Move the file pointer relative to the beginning of the file FileSeek(handle,offset,SEEK_SET); //--- Increase the string counter strings_count++; //--- Exit the nested loop for reading the string break; } } //--- If it's the end of the file, exit the main loop if(FileIsEnding(handle)) break; } //--- Return the fact of successful completion return(true); }
この関数にはどのファイルハンドルと配列を渡すのでしょうか?それは "TestedSymbols.txt" ファイルから読み出してからどのシンボルを処理するかによります。シンボルはそれぞれ入力パラメータ値を持つ特定のテキストファイルに対応します。考察するシナリオが2とおりあります。
- ファイルが存在し、上述の関数 ReadInputParametersValuesFromFile() を用いてファイルから入力パラメータ値を読み出す、というもの。
- ファイルが存在しない、または既存の入力パラメータ値を書き直す必要がある場合。
入力パラメータ値を持つテキストファイルのフォーマット(.ini ファイルですが、必要に応じて考察する別の拡張ファイルを選択することも可能です)はシンプルです。
input_parameter_name1=value input_parameter_name2=value .... input_parameter_nameN=value
このロジックを単独の関数:ReadWriteInputParameters()内で融合します。以下がそのコードです。
//+------------------------------------------------------------------+ //| Reading/writing input parameters from/to a file for a symbol | //+------------------------------------------------------------------+ void ReadWriteInputParameters(int symbol_number,string path) { string file_name=path+InputSymbols[symbol_number]+".ini"; // File name //--- Print("Find the file '"+file_name+"' ..."); //--- Open the file with input parameters of the symbol int file_handle_read=FileOpen(file_name,FILE_READ|FILE_ANSI|FILE_COMMON); //--- Scenario #1: the file exists and parameter values do not need to be rewritten if(file_handle_read!=INVALID_HANDLE && !RewriteParameters) { Print("The file '"+InputSymbols[symbol_number]+".ini' exists, reading..."); //--- Set the array size ArrayResize(tested_parameters_from_file,TESTED_PARAMETERS_COUNT); //--- Fill the array with file values ReadInputParametersValuesFromFile(file_handle_read,tested_parameters_from_file); //--- If the array size is correct if(ArraySize(tested_parameters_from_file)==TESTED_PARAMETERS_COUNT) { //--- Write parameter values to arrays InputIndicatorPeriod[symbol_number] =(int)tested_parameters_from_file[0]; Print("InputIndicatorPeriod[symbol_number] = "+(string)InputIndicatorPeriod[symbol_number]); InputTakeProfit[symbol_number] =tested_parameters_from_file[1]; InputStopLoss[symbol_number] =tested_parameters_from_file[2]; InputTrailingStop[symbol_number] =tested_parameters_from_file[3]; InputReverse[symbol_number] =(bool)tested_parameters_from_file[4]; InputLot[symbol_number] =tested_parameters_from_file[5]; InputVolumeIncrease[symbol_number] =tested_parameters_from_file[6]; InputVolumeIncreaseStep[symbol_number] =tested_parameters_from_file[7]; } //--- Close the file and exit FileClose(file_handle_read); return; } //--- Scenario #2: If the file does not exist or the parameters need to be rewritten if(file_handle_read==INVALID_HANDLE || RewriteParameters) { //--- Close the handle of the file for reading FileClose(file_handle_read); //--- Get the handle of the file for writing int file_handle_write=FileOpen(file_name,FILE_WRITE|FILE_CSV|FILE_ANSI|FILE_COMMON,""); //--- If the handle has been obtained if(file_handle_write!=INVALID_HANDLE) { string delimiter="="; // Separator //--- Write the parameters for(int i=0; i<TESTED_PARAMETERS_COUNT; i++) { FileWrite(file_handle_write,input_parameters_names[i],delimiter,tested_parameters_values[i]); Print(input_parameters_names[i],delimiter,tested_parameters_values[i]); } //--- Write parameter values to arrays InputIndicatorPeriod[symbol_number] =(int)tested_parameters_values[0]; InputTakeProfit[symbol_number] =tested_parameters_values[1]; InputStopLoss[symbol_number] =tested_parameters_values[2]; InputTrailingStop[symbol_number] =tested_parameters_values[3]; InputReverse[symbol_number] =(bool)tested_parameters_values[4]; InputLot[symbol_number] =tested_parameters_values[5]; InputVolumeIncrease[symbol_number] =tested_parameters_values[6]; InputVolumeIncreaseStep[symbol_number] =tested_parameters_values[7]; //--- Depending on the indication, print the relevant message if(RewriteParameters) Print("The file '"+InputSymbols[symbol_number]+".ini' with parameters of the '"+EXPERT_NAME+".ex5 Expert Advisor has been rewritten'"); else Print("The file '"+InputSymbols[symbol_number]+".ini' with parameters of the '"+EXPERT_NAME+".ex5 Expert Advisor has been created'"); } //--- Close the handle of the file for writing FileClose(file_handle_write); } }
最後のファイル関数: CreateInputParametersFolder() クライアントターミナルの共通フォルダ内の Expert Advisor の名前でフォルダを作成します。これは入力パラメータ値を持つテキストファイル(われわれの場合 .ini ファイルです)が読み出し/書き込みが行われるフォルダです。前の関数とちょうど同じようにフォルダが存在するか確認します。フォルダがうまく作成されるかすでに存在していたらこの関数はパスを返します。またエラーの場合は空の文字列を返します。
//+----------------------------------------------------------------------------------+ //| Creating a folder for files of input parameters in case the folder does not exist| //| and returns the path in case of success | //+----------------------------------------------------------------------------------+ string CreateInputParametersFolder() { long search_handle =INVALID_HANDLE; // Folder/file search handle string EA_root_folder =EXPERT_NAME+"\\"; // Root folder of the Expert Advisor string returned_filename =""; // Name of the found object (file/folder) string search_path =""; // Search path string folder_filter ="*"; // Search filter (* - check all files/folders) bool is_root_folder =false; // Flag of existence/absence of the root folder of the Expert Advisor //--- Find the root folder of the Expert Advisor search_path=folder_filter; //--- Set the search handle in the common folder of the terminal search_handle=FileFindFirst(search_path,returned_filename,FILE_COMMON); //--- If the first folder is the root folder, flag it if(returned_filename==EA_root_folder) is_root_folder=true; //--- If the search handle has been obtained if(search_handle!=INVALID_HANDLE) { //--- If the first folder is not the root folder if(!is_root_folder) { //--- Iterate over all files to find the root folder while(FileFindNext(search_handle,returned_filename)) { //--- Process terminated by the user if(IsStopped()) return(""); //--- If it is found, flag it if(returned_filename==EA_root_folder) { is_root_folder=true; break; } } } //--- Close the root folder search handle FileFindClose(search_handle); //search_handle=INVALID_HANDLE; } //--- Otherwise print an error message else Print("Error when getting the search handle or " "the folder '"+TerminalInfoString(TERMINAL_COMMONDATA_PATH)+"' is empty: ",ErrorDescription(GetLastError())); //--- Based on the check results, create the necessary folder search_path=EXPERT_NAME+"\\"; //--- If the root folder of the Expert Advisor does not exist if(!is_root_folder) { //--- Create it. if(FolderCreate(EXPERT_NAME,FILE_COMMON)) { //--- If the folder has been created, flag it is_root_folder=true; Print("The root folder of the '..\\"+EXPERT_NAME+"\\ Expert Advisor has been created'"); } else { Print("Error when creating " "the root folder of the Expert Advisor: ",ErrorDescription(GetLastError())); return(""); } } //--- If the required folder exists if(is_root_folder) //--- Return the path to create a file for writing parameters of the Expert Advisor return(search_path+"\\"); //--- In case of errors, return an empty string return(""); }
それでは上記の関数呼び出しを一緒に単独の関数: InitializeInputParametersArrays()に入れましょう。この関数は Expert Advisorと連携するとき、入力パラメータ初期化オプションを4つ処理します。
- 現在入力パラメータ値を用いた標準処理モード(または選択されたシンボルに対するパラメータ初期化)
- 検証または最適化時のパラメータのファイルへの再書き込み
- 選択シンボルの検証
- ファイルからのリスト上前シンボルの検証
処理すべてについての詳細説明はコードのコメントにあります。
//+-------------------------------------------------------------------+ //| Initializing arrays of input parameters depending on the mode | //+-------------------------------------------------------------------+ void InitializeInputParametersArrays() { string path=""; // To determine the folder that contains files with input parameters //--- Mode #1 : // - standard operation mode of the Expert Advisor OR // - optimization mode OR // - reading from input parameters of the Expert Advisor without rewriting the file if(IsRealtime() || IsOptimization() || (ParametersReadingMode==INPUT_PARAMETERS && !RewriteParameters)) { //--- Initialize parameter arrays to current values InitializeWithCurrentValues(); return; } //--- Mode #2 : // - rewriting parameters in the file for the specified symbol if(RewriteParameters) { //--- Initialize parameter arrays to current values InitializeWithCurrentValues(); //--- If the folder of the Expert Advisor exists or in case no errors occurred when it was being created if((path=CreateInputParametersFolder())!="") //--- Write/read the file of symbol parameters ReadWriteInputParameters(0,path); //--- return; } //--- Mode #3 : // - testing (it may be in visualization mode, without optimization) the Expert Advisor on a SELECTED symbol if((IsTester() || IsVisualMode()) && !IsOptimization() && SymbolNumber>0) { //--- If the folder of the Expert Advisor exists or in case no errors occurred when it was being created if((path=CreateInputParametersFolder())!="") { //--- Iterate over all symbols (in this case, the number of symbols = 1) for(int s=0; s<SYMBOLS_COUNT; s++) //--- Write or read the file of symbol parameters ReadWriteInputParameters(s,path); } return; } //--- Mode #4 : // - testing (it may be in visualization mode, without optimization) the Expert Advisor on ALL symbols if((IsTester() || IsVisualMode()) && !IsOptimization() && SymbolNumber==0) { //--- If the folder of the Expert Advisor exists and // no errors occurred when it was being created if((path=CreateInputParametersFolder())!="") { //--- Iterate over all symbols for(int s=0; s<SYMBOLS_COUNT; s++) //--- Write or read the file of symbol parameters ReadWriteInputParameters(s,path); } return; } }
モード #1 および #2 では InitializeWithCurrentValues() 関数を使用します。これはゼロ(sole)インデックスを現在入力パラメータ値に対して初期化します。すなわちこの関数はシンボルが1個だけ必要な場合に使われます。
//+------------------------------------------------------------------+ //| Initializing arrays of input parameters to current values | //+------------------------------------------------------------------+ void InitializeWithCurrentValues() { InputIndicatorPeriod[0]=IndicatorPeriod; InputTakeProfit[0]=TakeProfit; InputStopLoss[0]=StopLoss; InputTrailingStop[0]=TrailingStop; InputReverse[0]=Reverse; InputLot[0]=Lot; InputVolumeIncrease[0]=VolumeIncrease; InputVolumeIncreaseStep[0]=VolumeIncreaseStep; }
ここではもっともシンプルですが、最重要なことがらを行う必要があります。それはエントリー点から上記関数を連続して呼ぶ機能、 OnInit() 関数の実装です。
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ void OnInit() { //--- Initialize the array of tested input parameters for writing to the file InitializeTestedParametersValues(); //--- Fill the array of symbol names InitializeInputSymbols(); //--- Set the size of arrays of input parameters ResizeInputParametersArrays(); //--- Initialize arrays of indicator handles InitializeIndicatorHandlesArrays(); //--- Initialize arrays of input parameters depending on the operation mode of the Expert Advisor InitializeInputParametersArrays(); //--- Get agent handles from the "EventsSpy.ex5" indicator GetSpyHandles(); //--- Get indicator handles GetIndicatorHandles(); //--- Initialize the new bar InitializeNewBarArray(); //--- Initialize price arrays and indicator buffers ResizeDataArrays(); }
とうわけでコードは完了です。本稿添付のファイルで記述した関数を詳しく知ることができます。そこでは難しいことは何もありません。これまでの作業結果何を得たか、それをどのように利用することができるのか見るためさらに先に進みましょう。
パラメータの最適化と Expert Advisorの検証
すでにお話しましたが、クライアントターミナルの共通フォルダ内にシンボルリストを持つ TestedSymbols.txt ファイルが必要です。検証例として3とおりのシンボルについてリストを作成します。AUDUSD、EURUSD、NZDUSDです。それぞれシンボルに対して個別に連続して入力パラメータの最適化を行います。「ストラテジーテスタ」は以下のように設定します。
図2 ストラテジーテスタ設定
Expert Advisorに影響はないので、いずれかのシンボル(われわれの場合はEURUSD)を『設定』タブに設定することができます。そしてe Expert Advisorを最適化するためのパラメータを選択します。
図3 Expert Advisorの入力パラメータ
上図は SymbolNumber パラメータ(検証されたシンボル数)が 1に設定されていることを示しています。これは最適化実行時 Expert Advisor が TestedSymbols.txt ファイルのリストにある最初のシンボルを使うことを意味します。われわれの場合それは AUDUSDです。
注意:この Expert Advisor の特殊性(シンボルリストがテキストファイルから読み出されるように設定されています)によりリモートエージェントによる最適化は行えません。
以下のこのシリーズの記事の一つでこの制約を回避してみます。
最適化完了後、検証を実行することができます。そして異なる最適化パスの結果を調査します。Expert Advisor にファイルからパラメータを読ませたいと思うなら、ParametersReadingMode パラメータ(パラメータ読み出しモード)のドロップダウンリストでFileを選択する必要があります。Expert Advisor の現在パラメータ(『設定』タブで設定します)を使用できるようにするには 入力パラメータ オプションを選択します。
入力パラメータ オプションは最適化結果を閲覧するために必ず必要です。最初に検証を実行する際、Expert Advisor はターミナルの共通フォルダ内に対応する名前でフォルダを1個作成します。作成されたフォルダは検証されるシンボルの現在パラメータを伴うファイルを持ちます。われわれの場合それは AUDUSD.iniです。このファイルの内容は下図で確認することができます。
図4 シンボルファイル内の入力パラメータリスト
必要なパラメータの組合せがみつかれば、RewriteParameters パラメータ(書き直すパラメータ)に真を設定します。 パラメータファイルは更新されます。それから再び 偽 を設定し、最適化パスのその他結果を確認します。パラメータ読み出しモード パラメータのオプション間で切り替えるだけでファイルに書き込まれる値を入力パラメータに設定される値と結果同士を便利に比較できます。
その後 EURUSDに対して最適化を行います。これはシンボルリストファイルの2番目のシンボルです。このために検証シンボル番号 パラメータを 2に設定する必要がります。最適化に続き、パラメータを決めそれをファイルに書き込んだ後、リストの3番目のシンボルに対して同じ処理を行います。
すべてのシンボルについてのパラメータがファイルに書き込まれると、シンボルそれぞれに対してシンボル番号を指定することで結果を個別に閲覧するか、 検証シンボル番号 を 0に設定することですべてのシンボルについて集積された結果を閲覧することができます。私は以下のようにすべてのシンボルに対する集積結果を入手しました。
図5 複数通貨対応 Expert Advisorの集積検証結果
おわりに
結論として複数通貨対応 Expert Advisorsに対してかなり便利なパターンを取得しました。ご希望であればそれはもっと発展されることができます。本稿にはみなさんが考察されるためのExpert Advisor ファイルを持つダウンロード可能なアーカイブが添付されています。ファイルを解凍したら、 UnlimitedParametersEA フォルダを <MetaTrader 5 terminal folder>\MQL5\Expertsに入れてください。EventsSpy.mq5 インディケータは <MetaTrader 5 terminal folder>\MQL5\Indicatorsに入れる必要があります。また、クライアントターミナルの共通フォルダ内に TestedSymbols.txt テクストファイルを作成することを忘れないでください。
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/650





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索