実トレーディングにおける売買ロボットの自動最適化

Igor Malcev | 17 2月, 2016


はじめに

履歴に対してインプットが調整されている Expert Advisor は、初回(かなり短く)の利益のためにトレードを行うと想定されています。この考えかたは自動売買トレーディング チャンピオンシップ 2006を見た後で間接的に確認されたようです。「チャンピオンシップ」が開始されたとき、その後、何件かは競争力がないと判明したときに比べ、収益性あるがもっと多くありました。ゴールに 到達しなかった Expert Advisors のほとんどは履歴に対して調整されていたと想定したのはこの理由によります。

実務でこの仮定を確認する考えは、本ウェブサイトのロシア語フォーラム、理想的な自動トレーディングシステムのセクションで生まれました。主旨は日次で EA の最適化を自動でスタートし、その後取得した最適化結果を分析し、EA の変数にそれを記録する、というものです。

この考えを実現するため、MetaTrader 4 クライアントターミナルから既成のExpert Advisor と MACD サンプを採り、そこに自分達の自動最適化関数を挿入することにしました。その少しあと、自動オプティマイザのコードは準備ができ、同じフォーラムの 自動オプティマイザセクションにアップロードされました。それから少しのち、自動オプティマイザ部門でこの考えの最初の立証が見られたのです。それからオプティマイザはより良いユーザビリティのために mqh-library に姿を変えたのです。


自動オプティマイザのインストール

以下がこのタスクを行うために必要な事柄です。

  • 事前にインストールされインターネットに接続された MetaTrader 4 クライアントターミナルのフォルダに MACD Sample_1.mq4 をコピーします。
  • そして、MetaTrader 4 クライアントターミナルとともにそのフォルダーを新規ロケーションにコピーします。

ユーザビリティをよりよくするために、ここから元のターミナルを『ターミナル』、一方コピーを『ターミナルーテスター』と名付けます。H1 タイムフレームで EURUSD についてまずターミナルからの Expert Advisor でチェックテストを行いますが、MACD Sample_1.mq4. はやや変更したものとします。

ターミナルテスターのセットアップ

ターミナルテスターに MACD Sample_1.mq4 をコンパイルすることをお忘れなく。まず、クライアントターミナルを起動します。それからストラテジーテスタを起動し、以下のスクリーンショットにあるようにセットアップします。


最適化は3日間行われます。これは自動最適化を確認するのにかなり十分です。最適化日を次の式に従って『日から』で選択します。すなわち現在日付-3日です。最適化中は、選択されたシンボル(ここのでは EURUSD)に対して必要な履歴がダウンロードされます。


初めて最適化を行う方は MetaTrader 4 クライアントターミナルのヘルプメニュー:<ヘルプ - ヘルプトピック F1 - 自動トレーディング - エキスパート最適化 - 設定> で必要な手順説明を参照することができます。また記事 Testing of Expert Advisors in the MetaTrader 4 Client Terminal: An Outward Glance で読むことができます。


以下のスクリーンショットにある最適化する変数を確認します。



自動最適化は限られた4つの変数を対象としますが、われわれの目的達成と時間の節約には3つで十分です。変数が選択されたら、MACD Sample_1.set という名前の設定ファイルに最適化設定を格納します。このファイルはターミナルテスターの 'tester' フォルダに保存する必要があります。それからプレ最適化を起動し、開始時刻を記憶します。これは現在使用のパラメータによる自動最適化に必要な時間を計算するのに必要となります。この最適化終了後、必要な待ち時間を計算します。その後、このターミナルを閉じます。なぜかというとそれをプログラムで起動することはできないからです。

ターミナル内の Expert Advisor セットアップ

これには、MetaEditor でテスト用 Expert Advisor MACD Sample_1.mq4 を開き、以下を行います。

- 自動最適化の開始時刻を設定します。たとえば、毎日 00:01。
        datetime SetHour    = 0;  // Optimization starting hour; 
        datetime SetMinute  = 1;  // Optimization starting minute.

- 最適化日数を設定します(プレ最適化で設定したのと同じにします)

     int TestDay = 3;

- 最適化終了待ち時間を分で設定します。前に計算したもので、たとえば4分です。

        int TimeOut = 4;

- Expert Advisor 名を入力します。

        string NameMTS = "MACD Sample_1";  // EA's name

- 設定のある設定ファイル名を入力します。

        // Set-file name with the settings
        string NameFileSet = "MACD Sample_1.set"; 

- インストール済みターミナルテスターを持つフォルダーへのパスをs入力すます。たとえば以下です。

        // Path to the tester        
        string PuthTester = "D:\Program Files\Forex Best Trade Station";

- フィルターの優先順位を設定します。

        // Sorting by Maximal profit
        int Gross_Profit = 1;                      
        // Sorting by Maximal profit factor        
        int Profit_Factor = 2;     
        // Sorting by Maximal expected payoff
        int Expected_Payoff = 3;

- 最適化変数名を書き込みます。

        string Per1 = "FastEMA";
        string Per2 = "SlowEMA";
        string Per3 = "SignalSMA";
        string Per4 = "";

- インクルードフォルダにアタッチしたファイル auto_optimization.mqh をコピーします。
- Expert Advisor にライブラリファイルをインクルードします。

//--- Including the auto optimizer's library
#include <auto_optimization.mqh>

- あとはご自身の Expert Advisor の start() 関数の冒頭に以下のコードをコピーするだけです。MACD Sample_1.mq4 にはすでに入っています。


// Not to be launched at testing and optimizing   
  if(!IsTesting() && !IsOptimization())
    {
      // Compare the current hour with that preset for launching
      if(TimeHour(TimeLocal()) == SetHour)
        {
          // Protection against restarting
          if(!StartTest)
            {
              // Compare the minute range to the minute
              // preset for launching
              if(TimeMinute(TimeLocal()) > SetMinute - 1)
                {
                  // the range is necessary, in case that for some reasons 
                  // no new tick is available for a long time
                  if(TimeMinute(TimeLocal()) < SetMinute + 1)
                    {
                      // Flag of tester launching
                      StartTest    = true;
                      TimeStart    = TimeLocal();
                      Tester(TestDay, NameMTS, NameFileSet, PuthTester,
                             TimeOut, Gross_Profit, Profit_Factor,
                             Expected_Payoff, Per1, Per2, Per3, Per4);
                    
                    }
                }
            }
        }
    }
    

   FastEMA      = GlobalVariableGet(Per1);
   SlowEMA      = GlobalVariableGet(Per2);
   SignalSMA    = GlobalVariableGet(Per3);
   TrailingStop = GlobalVariableGet(Per4);  
// If the tester launching is flagged  
  if(StartTest)
    {
      // If more time has elapsed the launching than it was set 
      // to be the test waiting time
      if(TimeLocal() - TimeStart > TimeOut*60)
        {
          // Zeroize the flag
          StartTest = false;
        }
    }

以上です。自動オプティマイザが再度コンパイルされたら、起動することができますが、プレ最適化が行われたのと同一シンボルおよびタイムフレームでしか使用できません。われわれの場合、H1で EURUSD についてです。自動オプティマイザを確認するには、int init () function関数に以下で提供されているコードを挿入します。そうすると、Expert Advisor の起動時に自動オプティマイザが起動します。

Tester(TestDay,NameMTS,NameFileSet,PuthTester,TimeOut, Gross_Profit,Profit_Factor, 
       Expected_Payoff, Per1,Per2,Per3,Per4);


自動オプティマイザの機能

自動オプティマイザはターミナルのチャートにアタッチされた Expert Advisor のパラメータを最適化するためにターミナルテスターを用いることで動作します。このためにプログラムは、ターミナルテスターに対して最適化パラメータ(optimise.ini)を持つプログラムを送信し、最適化モードでターミナルテスターを起動します。そののち、それは取得した "FileReport........htm" の結果をターミナルにコピーし、取得結果から最良値をフィルターにかけます。



自動オプティマイザ機能の詳細

現時点では、たとえば 00.01 で自動オプティマイザを起動する必要があります。変数には値を書き込みます。

 // Path to the terminal 
string PuthTerminal = TerminalPath() + "\experts\files";
// Name of the ini file for the tester
string FileOptim    = "optimise.ini";
string FileOptim1   = "\optimise.ini";                                  
// Calculation of the starting date
datetime DayStart   = TimeLocal()-86400*TestDay;
// Optimization starting date
string DateStart    = TimeToStr(DayStart,TIME_DATE);
// Optimization ending date
string DateStop     = TimeToStr(TimeLocal(),TIME_DATE);
// Tester report file name
string FileReport   = "FileReport_" + Symbol() + "_" + DateStop + ".htm";
string FileReport1  = "\FileReport_" + Symbol() + "_" + DateStop + ".htm";
// Limitation for the minimum amount of trades per day
double MinTr        = TestDay - 2;
// Limitation for maximal amount of trades per day
double MaxTr        = (60 / Period()*TestDay) + 2;
// The amount of attempts to copy the report file
int    KvoPptk      = 10;
// The amount of lines for sorting
int    StepRes      = 12;

そして文字列配列に ini ファイルのパラメータが書き込まれます。

// Prepare the ini file for optimization
ArrayOpttim[0] = ";optimise strategy tester";             
// Enable/Disable Expert Advisors
ArrayOpttim[1] = "ExpertsEnable = false";
// Name of the EA file
ArrayOpttim[2] = "TestExpert=" + NameMTS;
// Name of the file containing parameters
ArrayOpttim[3] = "TestExpertParameters=" + NameFileSet;
// Symbol
ArrayOpttim[4] = "TestSymbol=" + Symbol();
// Timeframe
ArrayOpttim[5] = "TestPeriod=" + Period();
// Modeling mode
ArrayOpttim[6] = "TestModel=" + 0;
// Recalculate
ArrayOpttim[7] = "TestRecalculate=false";
// Optimization
ArrayOpttim[8] = "TestOptimization=true";
// Use date
ArrayOpttim[9] = "TestDateEnable=true";
// From
ArrayOpttim[10] = "TestFromDate=" + DateStart;
// To
ArrayOpttim[11] = "TestToDate=" + DateStop;
// Report file name
ArrayOpttim[12] = "TestReport=" + FileReport;
// Rewrite the report file
ArrayOpttim[13] = "TestReplaceReport=true";
// Shut down the terminal upon completion
ArrayOpttim[14] = "TestShutdownTerminal=true";

最適化パラメータは配列から ini ファイルに記録されます。ini ファイルの作成方法も MetaTrader 4 クライアントターミナルヘルプで確認可能です。<ヘルプ - ヘルプ トピック F1 - ツール - 起動時コンフィギュレーション>を参照ください。

// Write data into the ini file                
// Find out about the array size
OptimArraySize = ArraySize(ArrayOpttim);
// Open a file to write
opttim = FileOpen(FileOptim, FILE_CSV|FILE_WRITE, 0x7F);
if(opttim > 0)
  {
    for(int i = 0; i < OptimArraySize; i++)
      {
        // from the array into the variable
        ini = ArrayOpttim[i];                                     
        // from the variable into the file
        FileWrite(opttim, ini);
      } 
    // close the file
    FileClose(opttim);
  }
else
  {
    Print("Failed writing data into the ini file. Error No ", 
          GetLastError());
    return(0);
  }

ini ファイルにパラメータが記録されると、標準ウィンドウズデリバリにインクルードされた shell32.dll が接続され、ShellExecuteA 関数が起動します。

#import  "shell32.dll"               //Connect a dll (provided with Windows)       
  int ShellExecuteA(int hwnd,string Operation,string 
                    File,string Parameters,string Directory,int ShowCmd); 
#import

パラメータを持つファイルがターミナルテスターフォルダに送信されます。

// copy the ini file into the tester folder 
copyini = ShellExecuteA(0,"Open","xcopy", "\"" + PuthTerminal + 
                        FileOptim1 + "\" \"" + PuthTester + "\" /y", 
                        "", 3);
// wait until the file is copied
Sleep(1200);                                                    
if(copyini < 0)
  {
    Print("Failed copying ini file");
    return(0);
  }

そしてテスターが起動し、定義済み関数の最適化を始めます。Expert Advisor は最適化中停止状態となります。

// Start Tester 
start = ShellExecuteA(0, "Open", "terminal.exe", FileOptim,
                      PuthTester, 3);
if(start < 0)
  {
    Print("Failed starting Tester");
    return(0);
  }
Comment("Wait until optimization is complete");
// wait until optimization is complete
Sleep(60000*TimeOut);

最適化が完了すると、テスターは自動でレポートファイルに結果を記録します。このファイルはターミナルを持つフォルダにコピーされます。

 for(Pptk = 0; Pptk < KvoPptk; Pptk++)
  {                    
    //Start a cycle attempting to compy the resport file
    Comment("Attempt # " + Pptk + " to copy the report file");
    ShellExecuteA(0, "Open", "xcopy", "\"" + PuthTester + FileReport1 + 
                  "\" \"" + PuthTerminal + "\" /y", "", 3);
    // wait until the file is copied
    Sleep(1200);
    // Try to open the report file
    file = FileOpen(FileReport, FILE_READ, 0x7F);
    if(file < 0)
      {
        // if it fails to open, wait some more and try again
        Sleep(60000);
      }                    
    else 
        break;             
  }
if(file < 0)
  {
    Print("Failed copying the report file");
    return(0);
  }

そうするとのちの処理に備え、レポートファイルからのデータが文字列配列に入れられます。

// Read from file into the array
// Cycle, until the file ends
while(FileIsEnding(file) == false)
  {                 
    // Read a string from the report file
    FileLine = FileReadString(file);
    // Find the necessary string and set the reference point there
    index = StringFind(FileLine, "title", 20);
    if(index > 0)
      {
        // Increase the array in size
        ArrayResize(ArrayStrg, NumStr + 1);
        // Record the strings from the file in the array
        ArrayStrg[NumStr] = FileLine;
        NumStr++;
      }
  }
// Close the file
FileClose(file);
// Delete the file in order not to produce too many copies
FileDelete(FileReport);
// Set the array size by the amount of data read from the file
ArrayResize(ArrayData, NumStr); strings
    
.

そして配列内で必要な値が選択されます。

for(text = 0; text < NumStr; text++)
     {
      select = ArrayStrg[text]; 
    //-------------------------------------------------------------------------
    //   Reporting text processing (These are apples and oranges)              | 
    //-------------------------------------------------------------------------
    // Position Pass 
    ClStep=StringFind(select, "; \">",20)+4;
    // Find the end of position
    ClStepRazm = StringFind(select, "td>", ClStep);
    // Read the value
    CycleStep = StringSubstr(select, ClStep, ClStepRazm - ClStep);
    // Position Profit 
    // Find the beginning of the position
    GrProf = StringFind(select, "", ClStepRazm);
    // Find the end of position
    GrProfRazm = StringFind(select, "td>", GrProf);
    // Read value
    GrossProfit = StringSubstr(select, GrProf+4, GrProfRazm - (GrProf + 4));
    // Position Total Trades
    // Find the beginning of position
    TotTrad = StringFind(select, "", GrProfRazm);
    // Find the end of position
    TotTradRazm = StringFind(select, "td>", TotTrad);
    // Read the value
    TotalTrades = StringSubstr(select, TotTrad+4, TotTradRazm - 
                               (TotTrad + 4));
    // Position Profitability
    // Find the beginning of position
    ProfFact = StringFind(select, "", TotTradRazm);
    // Find the end of position
    ProfFactRazm = StringFind(select, "td>", ProfFact);
    // Read the value
    ProfitFactor = StringSubstr(select, ProfFact + 4, ProfFactRazm - 
                                (ProfFact + 4));
    // Position Expected Payoff 
    // Find the beginning of position
    ExpPay = StringFind(select, "", ProfFactRazm);
    // Find the dn of position
    ExpPayRazm=StringFind(select, "td>", ExpPay);
    // Read the value
    ExpectedPayoff = StringSubstr(select, ExpPay+4, ExpPayRazm - 
                                  (ExpPay + 4));
    // Variables' positions starting with the second one
    // Find the beginning of position
    P1 = StringFind(select, Per1, 20);
    // Find the end of position
    P1k = StringFind(select, ";", P1);
    // Read the Variable
    Perem1 = StringSubstr(select, P1 + StringLen(Per1) + 1, P1k - 
                          (P1 + 1 + StringLen(Per1)));
    // Find the beginning of position
    P2 = StringFind(select, Per2, 20);
    // Find the end of position
    P2k = StringFind(select, ";", P2); 
    // Read the Variable
    Perem2 = StringSubstr(select, P2 + StringLen(Per2) + 1, P2k - 
                          (P2 + 1 + StringLen(Per2)));
    // Find the beginning of position
    P3 = StringFind(select, Per3, 20);
    // Find the end of position
    P3k = StringFind(select, ";", P3);
    // Read the Variable
    Perem3 = StringSubstr(select, P3 + StringLen(Per3) + 1, P3k - 
                          (P3 + 1 + StringLen(Per3)));
    // Find the beginning of position
    P4 = StringFind(select, Per4, 20);
    // Find the end of position
    P4k = StringFind(select, ";", P4);
    // Read the Variable 
    Perem4 = StringSubstr(select, P4 + StringLen(Per4) + 1, P4k - 
                          (P4 + 1 + StringLen(Per4)));
    Comment("The obtained results are being analyzed");


その後、数字形式に変換される前に、取得された結果はトレードの最低額および最高額でフィルターにかけられます。 Profit_Factor 値ゼロは正しく格納し、その後振るい分けをするために1000と置き換えられます。

// Transform into number format
TotalTradesTransit = StrToDouble(TotalTrades);
GrossProfitTransit = StrToDouble(GrossProfit);
ExpectedPayoffTran = StrToDouble(ExpectedPayoff);
nodubl = true;
if(MinTr < TotalTradesTransit && MaxTr > TotalTradesTransit)
  {                    
    // Filter by the amount of trades
    PrFactDouble = StrToDouble(ProfitFactor);
    // Replace 0 in the Profit_Factor for proper analysis
    if(PrFactDouble == 0)
      {
        PrFactDouble = 1000;
      }

そして値は重複がないか確認され、フィルターにかけられます。

// Filter data having identical values
for(Dubl = 0; Dubl <= ResizeArayNew; Dubl++)
  {                    
    // Start the loop searching for identical values
    if(GrossProfitTransit == ArrayData[Dubl][1])
      {          
        // check whether the results for maximal profit coincide
        if(TotalTradesTransit == ArrayData[Dubl][2])
          {       
            // check whether the results for the amount of trades coincide
            if(PrFactDouble == ArrayData[Dubl][3])
              {          
                // check whether the results for Profit Factor coincide
                if(ExpectedPayoffTran == ArrayData[Dubl][4])
                  { 
                    // check whether the results for expected payoff coincide
                    nodubl=false;                              
                    // If everything coincides, flag it as coincided
                  }
              }
          }
      }
  }

ソート用に準備された値が配列に書き込まれます。


// Write the filtered data in the array
if(nodubl)
  {
    ArrayData[text][1] = GrossProfitTransit;                                
    ArrayData[text][2] = TotalTradesTransit;
    ArrayData[text][3] = PrFactDouble;
    ArrayData[text][4] = ExpectedPayoffTran;
    ArrayData[text][5] = StrToDouble(Perem1);
    ArrayData[text][6] = StrToDouble(Perem2);
    ArrayData[text][7] = StrToDouble(Perem3);
    ArrayData[text][8] = StrToDouble(Perem4);
    ResizeArayNew++; 
  }

データは事前に設定した優先順位で分析開始されます。分析は以下のように行われます。

  • ループが起動し、最初のパスで第一パラメータによって値がソートされます。たとえば、最高利益額などです。複数の最良値が選択され(デフォルトでは12)、その他は削除されます。
  • 第二のパスでは、値は2番目のパラメータによってソートされます。たとえば、プロフィットファクターです。最初のソートの半分にあたる最良値がいくつか選択され、残りは削除されます。
  • 第三のパスでは、3番目のパラメータについて最終ソートが行われます。たとえば予想ペイオフです。2番目のソート後の半分の値が残り、あとは削除されます。
// Analyzer
// Analyzing principle is the sequential checking of maximal 
// values according to the predefined filtering priority   
ArrayResize(ArrayTrans, ResizeArayNew - 1);
for(int PrioStep = 1; PrioStep < 4; PrioStep++)
  {
    for(PrCycle = 0; PrCycle < ResizeArayNew; PrCycle++)
      {
        Sort     = ArrayData[PrCycle][0];
        Prior1   = ArrayData[PrCycle][1];
        transit  = ArrayData[PrCycle][2];
        Prior2   = ArrayData[PrCycle][3];
        Prior3   = ArrayData[PrCycle][4];
        transit1 = ArrayData[PrCycle][5];
        transit2 = ArrayData[PrCycle][6];
        transit3 = ArrayData[PrCycle][7];
        transit4 = ArrayData[PrCycle][8];
           
        if(PrioStep == 1)
          {
            //Prepare for the 1st sorting
            if(Gross_Profit ==1)
              {
                SortTrans = Prior1;
              }
            if(Profit_Factor == 1)
              {
                SortTrans = Prior2;
              }
            if(Expected_Payoff == 1)
              {
                SortTrans = Prior3;
              }
          }
        if(PrioStep == 2)
          {
            // Restore
            if(Gross_Profit ==1)
              {
                Prior1 = Sort;
              }
            if(Profit_Factor == 1)
              {
                Prior2 = Sort;
              }
            if(Expected_Payoff == 1)
              {
                Prior3 = Sort;
              }
            //Prepare for the 2nd sorting
            if(Gross_Profit == 2)
              {
                SortTrans = Prior1;
              }
            if(Profit_Factor == 2)
              {
                SortTrans = Prior2;
              }
            if(Expected_Payoff == 2)
              {
                SortTrans = Prior3;
              }
          }
        if(PrioStep == 3)
          {
            // Restore
            if(Gross_Profit == 2)
              {
                Prior1 = Sort;
              }
            if(Profit_Factor == 2)
              {
                Prior2 = Sort;
              }
            if(Expected_Payoff == 2)
              {
                Prior3 = Sort;
              }
            //Prepare for the 3rd sorting
            if(Gross_Profit ==3)
              {
                SortTrans = Prior1;
              }
            if(Profit_Factor == 3)
              {
                SortTrans = Prior2;
              }
            if(Expected_Payoff == 3)
              {
                SortTrans = Prior3;
              }
          }
        ArrayTrans[PrCycle][0] = SortTrans;
        ArrayTrans[PrCycle][1] = Prior1;
        ArrayTrans[PrCycle][2] = transit;
        ArrayTrans[PrCycle][3] = Prior2;
        ArrayTrans[PrCycle][4] = Prior3;
        ArrayTrans[PrCycle][5] = transit1;
        ArrayTrans[PrCycle][6] = transit2;
        ArrayTrans[PrCycle][7] = transit3;
        ArrayTrans[PrCycle][8] = transit4;
      }
    ArraySort(ArrayTrans,StepRes, 0, MODE_DESCEND); // Sort the array
    ArrayResize(ArrayTrans, StepRes);               // Cut off the unnecessary things
    for(int CopyAr = 0; CopyAr < StepRes; CopyAr++)
      {
        ArrayData[CopyAr][0] = ArrayTrans[CopyAr][0];
        ArrayData[CopyAr][1] = ArrayTrans[CopyAr][1];
        ArrayData[CopyAr][2] = ArrayTrans[CopyAr][2];
        ArrayData[CopyAr][3] = ArrayTrans[CopyAr][3];
        ArrayData[CopyAr][4] = ArrayTrans[CopyAr][4];
        // Per1    Variable 1
        ArrayData[CopyAr][5] = ArrayTrans[CopyAr][5];
        // Per2    Variable 2
        ArrayData[CopyAr][6] = ArrayTrans[CopyAr][6];
        // Per3    Variable 3
        ArrayData[CopyAr][7] = ArrayTrans[CopyAr][7];
        // Per4    Variable 4
        ArrayData[CopyAr][8] = ArrayTrans[CopyAr][8];
      }
   StepRes = StepRes / 2;
  }

この方法でフィルターにかけられた値がグローバル変数に書き込まれます。グローバル変数の値は EA に代入されます。

 // Write the obtained results in variables
   double Peremen1 = ArrayTrans[0][5];
   double Peremen2 = ArrayTrans[0][6];
   double Peremen3 = ArrayTrans[0][7];
   double Peremen4 = ArrayTrans[0][8];
   // If the variable name is specified, write the result in 
   // global variables
   if(Per1 != "")
     {
       GlobalVariableSet(Per1, Peremen1);
     }
   if(Per2 != "")
     {
       GlobalVariableSet(Per2,Peremen2);
     }
   if(Per3 != "")
     {
       GlobalVariableSet(Per3,Peremen3);
     }
   if(Per4 != "")
     {
       GlobalVariableSet(Per4,Peremen4);
     }
   Comment(Per1, " ", Peremen1, "  | ", Per2, " ", Peremen2, "  | ", Per3,
           " ", Peremen3, "  | ", Per4, " ", Peremen4);
   Print(Per1, " ", Peremen1, "  | ", Per2, " ", Peremen2, "  | ", Per3,
         " ", Peremen3,"  | ",Per4," ",Peremen4);
  }  // Function ends. 以上です。自動最適化は完了です。


自動オプティマイザ処理結果

自動オプティマイザの処理結果は以下のスクリーンショットにあるように、チャートの左上に表示されるメッセージで見ることができます。



最適化完了参照時間


最適化後の取得値分析


変数の結果値

最適化結果がメッセージに表示されると、最適化が完了しデータが受け取られたことを意味します。

個別に自動オプティマイザの動作を推定するには、中央値を持ち動作プロセスで保存されたファイルをすべて調べます。テスターは "FileReport_EURUSD_2007.03.12.htm" という名前のファイルにデータを格納します。ここで、シンボルとデータが選択されたシンボルと現在の最適化日付に応じてファイル名に代入されます。このファイルはターミナルテスターフォルダにあります。レポートを持つこういったファイルは自動的に削除されます。よってパラメータ変化のチェックにそれらを利用することができます。



次のファイル FileTest1.csv はトレード金額によって値がフィルターにかけられ、コピーが削除されたあとに保存されます。ファイルは D:\Program Files\terminal_folder_ame\experts\files に保存されます。



それから各ふり分けステップ後に取得した値は FileTest2.csv に保存されます。ファイルは次のフォルダにも保存されます。:D:\Program Files\terminal_folder_name\experts\files



上記テーブルは取得値がどのようにフィルターにかけられるか示しています。フィルターにかける順序はデフォルトでは次のように設定されています。:1- Gross_Profit、2- Profit_Factor、3- Expected_Payoff.

自動オプティマイザのコードには詳しいコメントが入っており、必要に応じてもっとも適した変数パラメータを固定することができます。たとえば、直近日付以外の期間に対してを最適化したい、または最適化期間中のトレード額を増やす/減らす予定をしているなど。このためには auto_optimization.mqh 内で対応する値を直接変更するだけです。
 // Limitation of minimal amount of trades per day
double MinTr   = TestDay - 2; 
// Limitation on maximal amount of trades per day
double MaxTr   = (60 / Period()*TestDay) + 2;
// The amount of attempts to copy the report file
int    KvoPptk = 10;
// The amount of strings to be sorted
int    StepRes = 12;

おわりに

本稿は初心者に Expert Advisor 最適化のエレメントを教えることを目的としているのではありません。よって、の自動最適化を設定する前に通常の最適化を学習することを強くお薦めします。異なる時間に違った影響を Expert Advisor に与える基本的な変数を選択してから自動オプティマイザを使用する方がよいのです。すなわち、マーケットの変動に応じて他の変数よりも EA 処理に影響をあたえる変数のパラメータを固定するためにこの自動オプティマイザを使用するほうがよいのです。

そのうえ、あまり大きな最適化期間は設定しないようがよいものです。Expert Advisor が毎日6~12 時間最適化されるとします。そうするとある疑問が出ます。:それは何をトレードするのか?別の言い方をすれば、最適化はそれ自体としては必要ないのです。EA がトレードを行うとされるタイムフレームを考慮して、最適化の周期(最適化起動周期を意味します)を設定することをお薦めします。これはテスターターミナルの起動時、履歴データが大量に投入されることを考慮する必要があり、ブローカーは指定の時期に対する履歴データを必要な分所有しない可能性があることを意味します。本稿の冒頭で述べた仮定を検証するには、24時間の安定したインターネット接続が必要となるでしょう。


作成された自動最適化プログラムは、添付ファイル内:auto_optimization.mqh にあります-ライブラリ自体、MACD Sample_1.mq4 -MetaTrader 4 クライアントターミナル標準発送セットに入っているやや変更された Expert Advisor。