English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
MQL5 Cookbook: 매개변수 수에 제한이 없는 다중 통화 Expert Advisor 개발

MQL5 Cookbook: 매개변수 수에 제한이 없는 다중 통화 Expert Advisor 개발

MetaTrader 5 | 2 9월 2021, 17:11
75 0
Anatoli Kazharski
Anatoli Kazharski

소개

이전 글 "MQL5 Cookbook: Multi-Currency Expert Advisor - Simple, Neat and Quick Approach"에서 고려한 다중 통화 Expert Advisor는 사용되는 기호의 수와 거래 전략 매개변수가 작다면 매우 유용할 수 있습니다. 그러나 MQL5에서 Expert Advisor의 입력 매개변수 수에는 제한이 있습니다. 1024개를 넘지 않아야 합니다.

그리고 이 숫자로 충분할 경우가 많지만 이렇게 방대한 매개변수 목록을 사용하는 것은 매우 불편합니다. 주어진 기호에 대한 매개변수를 변경하거나 최적화해야 할 때마다 긴 매개변수 목록에서 특정 기호에 대한 매개변수를 검색해야 합니다.

클라이언트 터미널 도움말의 입력 섹션에서 추가 제한 사항을 숙지할 수 있습니다.

이 글에서는 무제한의 매개변수를 허용하면서 거래 시스템 최적화를 위해 단일 매개변수 세트를 사용하는 패턴을 만들 것입니다. 기호 목록은 표준 텍스트 파일(*.txt)로 생성됩니다. 각 기호에 대한 입력 매개변수도 파일에 저장됩니다.

Expert Advisor는 정상 작동 모드에서 하나의 기호에 대해 작동하지만 선택한 다양한 기호(각 기호에 대해 개별적으로)에 대해 Strategy Tester에서 테스트할 수 있음을 여기에서 언급해야 합니다.

기성품 기호 집합을 저장할 수 있다는 점을 고려하면 실제로 시장 조사 창에서 기호 목록을 직접 생성하는 것이 훨씬 더 편리할 것입니다. Strategy Tester에서 직접 Market Watch 창의 기호 목록을 가져오도록 Expert Advisor를 만들 수도 있습니다. 그러나 안타깝게도 현재 Strategy Tester에서 Market Watch 창에 액세스할 수 없으므로 사전에 수동으로 또는 스크립트를 사용하여 기호 목록을 만들어야 합니다.


Expert Advisor 개발

이전 글 "MQL5 Cookbook: Multi-Currency Expert Advisor - Simple, Neat and Quick Approach"에서 다룬 다중 통화 Expert Advisor가 템플릿으로 사용됩니다. 먼저 입력 매개변수를 결정합시다. 위에서 언급했듯이 한 세트의 매개변수만 남길 것입니다. 다음은 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

입력 modifier가 있는 매개변수만 파일에 기록됩니다. 또한, 우리는 이전에 만나지 못한 세 가지 새로운 매개변수를 확장해야 합니다.

  • SymbolNumber - 이 매개변수는 기호 목록이 포함된 파일의 기호 번호를 나타냅니다. 0으로 설정하면 목록의 모든 기호가 테스트됩니다. 목록을 초과하면 테스트가 수행되지 않습니다.
  • RewriteParameters - 이 매개변수 값이 true로 설정되면 지정된 기호(SymbolNumber 매개변수의 숫자)의 매개변수가 있는 파일이 현재 입력 매개변수 값을 사용하여 다시 작성됩니다. 또는 false로 설정하면 파일에서 매개변수를 읽습니다.
  • ParametersReadingMode - 이 매개변수는 입력 매개변수에 대한 읽기 모드를 나타냅니다. 매개변수 유형은 파일에서 읽기와 현재 매개변수 사용이라는 두 가지 옵션을 제공하는 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;
  }

이제 파일 작업을 살펴보겠습니다. 먼저 Expert Advisor의 UnlimitedParametersEA\Include 폴더에 다른 함수 라이브러리 FileFunctions.mqh를 만듭니다. 이 라이브러리는 파일에 데이터를 읽고 쓰는 것과 관련된 함수를 만드는 데 사용됩니다. 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)
모든 사용자 지정 파일은 클라이언트 터미널 공통 폴더의 \Files 하위 폴더 또는 <Terminal Data Folder>\MQL5\Files\에 있어야 합니다. 파일 기능은 이 폴더에만 액세스할 수 있습니다.

기능을 확인하려면 생성된 텍스트 파일에 몇 개의 기호를 추가하고 각 기호를 라인 바꿈("\r\n")으로 구분합니다.

그림 1. 터미널의 공통 폴더에서 파일의 기호 목록입니다.

그림 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가 테스트될 기호의 수를 반환합니다.

이전에 볼 수 없었던 기능 및 식별자에 대한 자세한 정보는 항상 MQL5 참조를 참조하세요. 코드에 제공된 주석은 학습 과정을 단순화합니다.

InitializeArrays.mqh 파일로 돌아가서 앞에서 선언한 기호 이름의 InputSymbols[] 배열을 채우기 위한 InitializeInputSymbols() 함수를 생성할 것입니다. 초보자는 코드가 매우 복잡하다는 것을 알게 될 것이므로 코드에 대한 자세한 설명을 제공했습니다.

//+------------------------------------------------------------------+
//| 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 및 bool 유형으로 변환합니다. 열린 파일 핸들과 파일의 매개변수 값이 저장된 배열은 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" 파일에서 읽은 후 작업할 기호에 따라 다릅니다. 각 기호는 입력 매개변수 값이 포함된 특정 텍스트 파일에 해당합니다. 고려해야 할 두 가지 사례 시나리오가 있습니다.

  1. 파일이 존재하고 위에서 설명한 ReadInputParametersValuesFromFile() 함수를 사용하여 해당 파일에서 입력 매개변수 값을 읽습니다.
  2. 파일이 존재하지 않거나 기존 입력 매개변수 값을 다시 작성해야 합니다.

입력 매개변수 값을 포함하는 텍스트 파일(.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가지 입력 매개변수 초기화 옵션을 다룹니다.

  1. 현재 입력 매개변수 값을 사용하는 표준 작동 모드(또는 선택한 기호에 대한 매개변수 최적화)
  2. 테스트 또는 최적화시 파일의 매개변수 재작성
  3. 선택한 기호 테스트
  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() 함수를 사용합니다. 현재 입력 매개변수 값으로 0(단독) 인덱스를 초기화합니다. 즉, 이 함수는 하나의 기호만 필요한 경우에 사용됩니다.

//+------------------------------------------------------------------+
//| 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 파일이 있어야 합니다. 예를 들어/테스트 목적으로 AUDUSD, EURUSDNZDUSD의 세 가지 기호 목록을 만듭니다. 이제 각 기호에 대한 입력 매개변수를 개별적으로 연속적으로 최적화합니다. Strategy Tester는 아래와 같이 설정해야 합니다.

그림 2. 전략 테스터 설정.

그림 2. 전략 테스터 설정.

Expert Advisor에 영향을 미치지 않으므로 "설정" 탭에서 기호(이 경우 EURUSD)를 설정할 수 있습니다. 그런 다음 Expert Advisor 최적화를 위한 매개변수를 선택합니다.

그림 3. Expert Advisor의 입력 매개변수.

그림 3. Expert Advisor의 입력 매개변수.

위 그림은 SymbolNumber 매개변수(테스트된 기호의 번호)가 1로 설정되었음을 보여줍니다. 즉, 최적화를 실행할 때 Expert Advisor는 TestedSymbols.txt 파일의 목록에서 첫 번째 기호를 사용합니다. 우리의 경우 AUDUSD입니다.

참고: 이 Expert Advisor의 특성(기호 목록은 텍스트 파일에서 읽어서 설정됨)으로 인해 원격 에이전트로 최적화할 수 없습니다.

이 시리즈의 다음 글 중 하나에서 이 제한을 우회하려고 합니다.

최적화를 완료한 후 테스트를 실행하여 다양한 최적화 단계의 결과를 연구할 수 있습니다. Expert Advisor가 파일에서 매개변수를 읽도록 하려면 ParametersReadingMode 매개변수의 드롭다운 목록에서 파일을 선택해야 합니다(매개변수 읽기 모드). Expert Advisor가 파일에서 매개변수를 읽도록 하려면 드롭에서 파일을 선택해야 합니다. Expert Advisor의 현재 매개변수("설정" 탭에서 설정)를 사용하려면 다음을 선택해야 합니다. 입력 매개변수 옵션.

최적화 결과를 보려면 입력 매개변수 옵션이 반드시 필요합니다. 테스트를 처음 실행할 때 Expert Advisor는 터미널의 공통 폴더에 해당 이름으로 폴더를 생성합니다. 생성된 폴더에는 테스트된 기호의 현재 매개변수가 있는 파일이 포함됩니다. 이 경우 AUDUSD.ini입니다. 아래 그림에서 이 파일의 내용을 볼 수 있습니다.

그림 4. 기호 파일의 입력 매개변수 목록입니다.

그림 4. 기호 파일의 입력 매개변수 목록입니다.

필요한 매개변수 조합을 찾으면 RewriteParameters 매개변수(매개변수 다시 쓰기)에서 true를 설정하고 테스트를 다시 실행해야 합니다. 매개변수 파일이 업데이트됩니다. 나중에 false를 다시 설정하고 최적화 패스의 다른 결과를 확인할 수 있습니다. 또한 파라미터 읽기 모드 매개변수의 옵션을 간단히 전환하여 입력 매개변수에 설정된 값과 파일에 기록된 값으로 결과를 비교하는 것이 편리합니다.

그런 다음 기호 목록 파일에서 목록의 두 번째 기호인 EURUSD에 대한 최적화를 실행합니다. 이렇게 하려면 테스트된 기호의 수 매개변수의 값을 2와 동일하게 설정해야 합니다. 최적화를 수행하고 매개변수를 결정하고 파일에 기록한 후 목록의 세 번째 기호에 대해서도 동일한 작업을 수행해야 합니다.

모든 기호에 대한 매개변수가 파일에 기록되면 기호 번호를 지정하여 각 기호에 대한 결과를 개별적으로 보거나 테스트된 기호의 번호를 설정하여 모든 기호에 대한 누적 결과를 볼 수 있습니다 ~ 0. 모든 기호에 대해 다음과 같은 누적 결과를 얻었습니다.

그림 5. 다중 통화 Expert Advisor의 누적 결과입니다.

그림 5. 다중 통화 Expert Advisor의 누적 결과입니다.


결론

결과적으로 우리는 다중 통화 Expert Advisor를 위한 상당히 편리한 패턴을 갖게 되었습니다. 원하는 경우 추가로 개발할 수 있습니다. 글에 첨부된 파일은 귀하가 고려할 수 있도록 Expert Advisor의 파일과 함께 다운로드 가능한 아카이브입니다. 압축을 푼 후 <MetaTrader 5 터미널 폴더>\MQL5\Experts 아래에 UnlimitedParametersEA 폴더를 배치합니다. EventsSpy.mq5 지표는 <MetaTrader 5 터미널 폴더>\MQL5\Indicators에 있어야 합니다. 그 외에도 클라이언트 터미널의 공통 폴더에 TestedSymbols.txt 텍스트 파일을 생성하는 것을 잊지 마십시오.

MetaQuotes 소프트웨어 사를 통해 러시아어가 번역됨.
원본 기고글: https://www.mql5.com/ru/articles/650

파일 첨부됨 |
eventsspy.mq5 (7.61 KB)
MQL5 Cookbook: 파일에 거래 내역 쓰기 및 Excel의 각 기호에 대한 대차 대조표 생성 MQL5 Cookbook: 파일에 거래 내역 쓰기 및 Excel의 각 기호에 대한 대차 대조표 생성
다양한 포럼에서 커뮤니케이션할 때 Microsoft Excel 차트의 스크린샷으로 표시되는 테스트 결과의 예를 자주 사용했습니다. 그러한 차트를 만드는 방법을 설명하라는 요청을 여러 번 받았습니다. 마지막으로, 이제 이 글에서 모든 것을 설명할 시간이 있습니다.
MQL5 Cookbook: 다중 통화 Expert Advisor - 간단하고 깔끔하며 빠른 접근 MQL5 Cookbook: 다중 통화 Expert Advisor - 간단하고 깔끔하며 빠른 접근
이 글에서는 다중 통화 Expert Advisor에 적합한 간단한 접근 방식의 구현에 대해 설명합니다. 이는 동일한 조건에서 각 기호에 대해 다른 매개변수를 사용하여 테스트/거래를 위해 Expert Advisor를 설정할 수 있음을 의미합니다. 예를 들어, 필요한 경우 코드를 약간 변경하여 추가 기호를 추가할 수 있는 방식으로 두 개의 기호에 대한 패턴을 만들 것입니다.
MQL5 Cookbook: 과적합의 영향 줄이기 및 따옴표 부족 처리 MQL5 Cookbook: 과적합의 영향 줄이기 및 따옴표 부족 처리
어떤 거래 전략을 사용하든 항상 미래의 이익을 보장하기 위해 어떤 매개변수를 선택해야 하는지에 대한 질문이 있을 것입니다. 이 글에서는 동시에 여러 기호 매개변수를 최적화할 수 있는 Expert Advisor의 예를 제공합니다. 이 방법은 매개변수 과적합의 영향을 줄이고 단일 기호의 데이터가 연구에 충분하지 않은 상황을 처리하기 위한 것입니다.
MQL5 Cookbook: 트리플 스크린 전략에 기반한 거래 시스템을 위한 프레임워크 개발 MQL5 Cookbook: 트리플 스크린 전략에 기반한 거래 시스템을 위한 프레임워크 개발
이 글에서는 MQL5의 Triple Screen 전략을 기반으로 하는 거래 시스템의 프레임워크를 개발할 것입니다. Expert Advisor는 처음부터 개발되지 않습니다. 대신에 이미 우리의 목적에 실질적으로 부합하는 이전 글 "MQL5 Cookbook: 지표를 사용하여 Expert Advisors에서 거래 조건 설정"에서 프로그램을 수정하기만 하면 됩니다. 따라서 이 글에서는 기성 프로그램의 패턴을 쉽게 수정할 수 있는 방법도 보여줍니다.