【MQL4関数】OnTester関数とは?バックテスト終了時に実行されるイベント関数

【中級編】MQLプログラムの読み方・書き方
朝日奈りさ
朝日奈りさ

OnTester関数は、バックテスト終了時に実行されるイベント関数です。バックテスト結果を表示できるだけでなく、バックテストの最適化もできるようになります。

OnTester関数とは

「バックテストの結果を詳しく表示したい!」

という方は、OnTester関数を使ってみましょう!

OnTester関数は、イベント関数の一つで、バックテスト終了時に実行される関数です。

そのため、バックテストの結果を表示することに使われることが多いです。

つまり、OnTester関数内で計算させることにより、普通のバックテストの結果では得られない結果を表示させることができるようになります。

例えば、シャープレシオ(リスクに対するリターンの大きさ)は普通のバックテストでは得られませんが、OnTester関数内でシャープレシオを計算させ、表示させることも可能です。

また、自動売買(エキスパートアドバイザ)を新規作成すると、イベント関数を追加するウィンドウが出てきます。

イベント関数を追加するウィンドウで、OnTester関数を選択することで、自動的にプログラムに追加することができるので、利用すると良いでしょう。

また、OnTester関数以外にも、主に以下の3つの関数が、自動的にプログラミングされているので、確認しておくと良いでしょう。

今回は、OnTester関数に注目して解説していきます。

OnTester関数を追加し、自動売買を新規作成したときのプログラム

自動売買を新規作成すると、以下のようなプログラムが自動作成されます。

//+------------------------------------------------------------------+
//|                                           SampleMQL_OnTester.mq4 |
//|                                     Copyright 2022, Asahina Risa |
//|                                      https://mql-programing.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, Asahina Risa"
#property link      "https://mql-programing.com/"
#property version   "1.00"
#property strict
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Tester function                                                  |
//+------------------------------------------------------------------+
double OnTester()
  {
//---
   double ret=0.0;
//---

//---
   return(ret);
  }
//+------------------------------------------------------------------+

このように、すでにOnInit関数・OnDeinit関数・OnTick関数・OnTester関数が記述されています。

OnTester関数は、39行目にあります。

OnTester関数の特徴

OnTester関数はイベント関数なので、イベントが起こったときに実行されます。

そのイベントとは、「バックテストが終了したとき」です。

つまり、バックテストが終わったときに、一度だけ自動的に実行される関数です。

そのため、バックテストの結果に別の計算を加えたり、その結果を表示させたりすることができます。

例えば、シャープレシオを計算したり、シャープレシオを計算したり、普通のバックテストでは得られない結果を計算させて表示することと相性が良いです。

また、大きな特徴として、バックテストの最適化方法をカスタムできるという優れた特徴があります。

例えば、OnTester関数の戻り値をシャープレシオにすることにより、この戻り値を最大化するようにバックテストを行うことができるようになります。

つまり、普段は残高最大化で行っているバックテストを、シャープレシオ最大化でバックテストできるということです。

最適化方法を自由に変えることができるので、得られる結果は大きく変わるでしょう。

OnTester関数による最適化をしたい場合は、以下のように「Custom」を選択します。

OnTester関数は、自動売買(エキスパートアドバイザ)で利用できます。

インジケータやスクリプトでは実行できない関数です。

OnTester関数の書き方

OnTester関数の使い方は、基本的な関数と同じです。

関数をマスターしていない方はこちらの記事が参考になります。

基本的な書き方

OnTester関数はとてもシンプルな関数です。

引数はありません。

戻り値はdouble型です。

OnTester関数の基本的な書き方は以下の通りです。

double OnTester()
  {
   double ret=0.0;
   return(ret);
  }

戻り値はdouble型なので、double型の値をreturnしましょう。

戻り値

double型

引数

なし

OnTester関数のサンプルプログラム

サンプルプログラムは、こちらになります。

このプログラムは、偶数時間ごとに買いエントリーし、奇数時間ごとにエグジットします。

その結果のシャープレシオを表示するプログラムです。

//+------------------------------------------------------------------+
//|                                           SampleMQL_OnTester.mq4 |
//|                                     Copyright 2022, Asahina Risa |
//|                                      https://mql-programing.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, Asahina Risa"
#property link      "https://mql-programing.com/"
#property version   "1.00"
#property strict

input double LOT = 0.01;
input int MAGIC_NUMBER = 1111;
input int SLIPPAGE = 30;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   if(IsTesting() || IsOptimization())
      GlobalVariableSet("InitialBalance", AccountBalance());

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(IsTesting() || IsOptimization())
      GlobalVariableDel("InitialBalance");

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
//時間が2で割り切れるとき
   if(TimeHour(Time[0]) % 2 == 0)
     {
      if(OrdersTotal() < 1)
        {
         //買いエントリー
         bool send = OrderSend(_Symbol, OP_BUY, LOT, Ask, SLIPPAGE, 0, 0, "", MAGIC_NUMBER, 0, clrRed);
        }
     }
   else
     {
      //ポジションを選択
      if(OrderSelect(0, SELECT_BY_POS, MODE_TRADES))
         if(OrderSymbol() == _Symbol)
            if(OrderMagicNumber() == MAGIC_NUMBER)
              {
               //エグジット
               bool close = OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), SLIPPAGE);
              }

     }

  }
//-----------------------------------------------------------+
//| Tester function                                                  |
//+------------------------------------------------------------------+
double OnTester()
  {

   double sharpeRatio = SharpeRatio(GlobalVariableGet("InitialBalance"));
   Print("SharpeRatio = " + (string)sharpeRatio);

//---
   return(sharpeRatio);
  }
//+------------------------------------------------------------------+

//シャープレシオを計算する関数

double SharpeRatio(double Balance){   
   int i;
   int CalcMonth = 0;
   int TradeMonths = 0;
   double MonthlyProfit[];

   for(i = 0; i < OrdersHistoryTotal(); i++){
      if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY) == false) break;

      int CloseMonth = TimeMonth(OrderCloseTime());
      if(CalcMonth != CloseMonth){
         CalcMonth = CloseMonth;
         TradeMonths++;
         ArrayResize(MonthlyProfit, TradeMonths);
      }
      MonthlyProfit[TradeMonths - 1] += OrderProfit();
   }

   double MonthlyEarningRate[];   
   ArrayResize(MonthlyEarningRate, ArraySize(MonthlyProfit));
   double SumMER = 0;

   for(i = 0; i < ArraySize(MonthlyProfit); i++){
      MonthlyEarningRate[i] = MonthlyProfit[i] / Balance;
      SumMER += MonthlyEarningRate[i];
      Balance += MonthlyProfit[i];
   }

   double MER_Average = SumMER / TradeMonths;
   double MER_SD = iStdDevOnArray(MonthlyEarningRate, 0, TradeMonths, 0, 0, 0);
   double SR = 1;
   if(MER_SD != 0) SR = MER_Average / MER_SD; // ゼロ割を回避

   return SR;
}

 

 

まとめ

OnTester関数は、「バックテストが終了したとき」に実行されるイベント関数です。

そのため、バックテストの結果に対して、計算を加えて表示するために使われることが多いです。

また、OnTester関数の戻り値を最大化するように、バックテストすることが可能になります。

残高最大化のバックテストと、OnTester関数の戻り値の最大化のバックテストを使い分けることで、最適化を高速に行うことができます。