OnTester

この関数は、Testerイベントが発生してテスト後に適切なアクションを取るために、EAで呼び出されます。

double  OnTester(void);

戻り値

テスト結果を評価するためのカスタム条件最適化の値  

注意事項

OnTester()関数は、EAのテスト時にのみ使用でき、主に入力パラメータの遺伝的最適化のためのカスタム最大基準として使用される値の計算のために意図されています。

遺伝的最適化では一世代内の結果は降順にソートされます。これは、最高値の結果が最適化基準の観点から最良とみなされることを意味します。このようなソーティングの最悪値は最後に配置され、その後破棄されます。したがって、それらは次世代の形成に参加しません。

したがって、OnTester()関数を使用すると、独自のテスト結果レポートが作成され保存されるだけでなく、最適化プロセスを制御してトレーディング戦略の最良のパラメータを見つけることができます。

以下はカスタム条件最適化を計算するです。アイディアは、バランスグラフの線形回帰を計算することです。これはOptimizing a strategy using balance graph and comparing results with "Balance + max Sharpe Ratio" criterion(バランスグラフを使用した戦略の最適化と、結果の「バランス+最大シャープレシオ」基準との比較)稿で説明されています。

//+------------------------------------------------------------------+
//|                                              OnTester_Sample.mq5 |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2000-2024, MetaQuotes Ltd."
#property link     "https://www.mql5.com"
#property version   "1.00"
#property description "Sample EA with the OnTester() handler"
#property description "As a custom optimization criterion, "
#property description "the ratio of the balance graph linear regression"
#property description "divided by the deviation mean-square error is returned"
//-- 取引操作クラスをインクルードする
#include <Trade\Trade.mqh>
//--- EA入力パラメータ
input double Lots               = 0.1;     // ボリューム
input int   Slippage           = 10;     // 許容されるスリッページ
input int   MovingPeriod       = 80;     // 移動平均期間
input int   MovingShift        = 6;       // 移動平均シフト
//--- グローバル変数
int    IndicatorHandle=0; // 指標ハンドル
bool   IsHedging=false;   // アカウントのフラグ
CTrade trade;             // 取引操作の実行
//---
#define EA_MAGIC 18052018
//+------------------------------------------------------------------+
//| ポジションを開く条件を確認する                                            |
//+------------------------------------------------------------------+
void CheckForOpen(void)
 {
  MqlRates rt[2];
//--- 新しいバーの始めのみで取引する
  if(CopyRates(_Symbol,_Period,0,2,rt)!=2)
    {
    Print("CopyRates of ",_Symbol," failed, no history");
    return;
    }
//--- ティックボリューム
  if(rt[1].tick_volume>1)
    return;
//--- 移動平均値を受け取る
  double   ma[1];
  if(CopyBuffer(IndicatorHandle,0,1,1,ma)!=1)
    {
    Print("CopyBuffer from iMA failed, no data");
    return;
    }
//--- シグナルの存在を確認する
  ENUM_ORDER_TYPE signal=WRONG_VALUE;
//--- ローソク足は移動平均より高く開いたが、移動平均より低く閉じた
  if(rt[0].open>ma[0] && rt[0].close<ma[0])
     signal=ORDER_TYPE_BUY;   // 買いシグナル
  else // ローソク足は移動平均より低く開いたが、移動平均より高く閉じた
    {
    if(rt[0].open<ma[0] && rt[0].close>ma[0])
        signal=ORDER_TYPE_SELL;// 売りシグナル
    }
//--- 追加的確認
  if(signal!=WRONG_VALUE)
    {
    if(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) && Bars(_Symbol,_Period)>100)
       {
        double price=SymbolInfoDouble(_Symbol,signal==ORDER_TYPE_SELL ?SYMBOL_BID:SYMBOL_ASK);
        trade.PositionOpen(_Symbol,signal,Lots,price,0,0);
       }
    }
//---
 }
//+------------------------------------------------------------------+
//| ポジションを閉じる条件を確認する                                          |
//+------------------------------------------------------------------+
void CheckForClose(void)
 {
  MqlRates rt[2];
//--- 新しいバーの始めのみで取引する
  if(CopyRates(_Symbol,_Period,0,2,rt)!=2)
    {
    Print("CopyRates of ",_Symbol," failed, no history");
    return;
    }
  if(rt[1].tick_volume>1)
    return;
//--- 移動平均値を受け取る
  double   ma[1];
  if(CopyBuffer(IndicatorHandle,0,1,1,ma)!=1)
    {
    Print("CopyBuffer from iMA failed, no data");
    return;
    }
//--- ポジションはPositionSelect()を使用してすでに選択されている
  bool signal=false;
  long type=PositionGetInteger(POSITION_TYPE);
//--- ローソク足は移動平均より高く開いたが、移動平均より低く閉じた - ショートポジションを決済する
  if(type==(long)POSITION_TYPE_SELL && rt[0].open>ma[0] && rt[0].close<ma[0])
     signal=true;
//--- ローソク足は移動平均より低く開いたが、移動平均より高く閉じた - ロングポジションを決済する
  if(type==(long)POSITION_TYPE_BUY && rt[0].open<ma[0] && rt[0].close>ma[0])
     signal=true;
//--- 追加的確認
  if(signal)
    {
    if(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) && Bars(_Symbol,_Period)>100)
        trade.PositionClose(_Symbol,Slippage);
    }
//---
 }
//+-------------------------------------------------------------------+
//| 口座タイプ(ネッティングまたはヘッジ)を考慮してポジションを選択する                     |
//+-------------------------------------------------------------------+
bool SelectPosition()
 {
  bool res=false;
//--- ヘッジ口座のポジションを選択する
  if(IsHedging)
    {
    uint total=PositionsTotal();
    for(uint i=0; i<total; i++)
       {
        string position_symbol=PositionGetSymbol(i);
        if(_Symbol==position_symbol && EA_MAGIC==PositionGetInteger(POSITION_MAGIC))
          {
           res=true;
          break;
          }
       }
    }
//--- ネッティング口座のポジションを選択する
  else
    {
    if(!PositionSelect(_Symbol))
        return(false);
    else
        return(PositionGetInteger(POSITION_MAGIC)==EA_MAGIC); //---check Magic number
    }
//--- 実行結果
  return(res);
 }
//+------------------------------------------------------------------+
//| エキスパート初期化関数                                                 |
//+------------------------------------------------------------------+
int OnInit(void)
 {
//--- 取引タイプ(ネッティングまたはヘッジ)を設定する
  IsHedging=((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING);
//--- 正しいポジション制御のためにオブジェクトを初期化する
  trade.SetExpertMagicNumber(EA_MAGIC);
  trade.SetMarginMode();
  trade.SetTypeFillingBySymbol(Symbol());
  trade.SetDeviationInPoints(Slippage);
//--- 移動平均指標を作成する
  IndicatorHandle=iMA(_Symbol,_Period,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE);
  if(IndicatorHandle==INVALID_HANDLE)
    {
    printf("Error creating iMA indicator");
    return(INIT_FAILED);
    }
//--- ok
  return(INIT_SUCCEEDED);
 }
//+------------------------------------------------------------------+
//| エキスパートティック関数                                                 |
//+------------------------------------------------------------------+
void OnTick(void)
 {
//--- ポジションが既に開かれている場合は、決済条件を確認する
  if(SelectPosition())
     CheckForClose();
// ポジションを開く条件を確認する
  CheckForOpen();
//---
 }
//+------------------------------------------------------------------+
//| テスタ関数                                                          |
//+------------------------------------------------------------------+
double OnTester()
 {
//--- カスタム条件最適化の値(高いほど良い)
  double ret=0.0;
//--- 取引結果を配列に入れる
  double array[];
  double trades_volume;
  GetTradeResultsToArray(array,trades_volume);
  int trades=ArraySize(array);
//--- 10取引未満の場合、肯定的結果がないことをテストする
  if(trades<10)
    return (0);
//--- 取引あたりの平均結果
  double average_pl=0;
  for(int i=0;i<ArraySize(array);i++)
     average_pl+=array[i];
  average_pl/=trades;
//--- 単一テストモード用のメッセージを表示する
  if(MQLInfoInteger(MQL_TESTER) && !MQLInfoInteger(MQL_OPTIMIZATION))
    PrintFormat("%s: Trades=%d, Average profit=%.2f",__FUNCTION__,trades,average_pl);
//--- 利益グラフの線形回帰を計算する
  double a,b,std_error;
  double chart[];
  if(!CalculateLinearRegression(array,chart,a,b))
    return (0);
//--- 回帰直線からグラフの偏差の誤差を計算する
  if(!CalculateStdError(chart,a,b,std_error))
    return (0);
//--- 傾向偏差の標準偏差を計算する
  ret=(std_error == 0.0) ? a*trades : a*trades/std_error;
//--- カスタム条件最適化値を返す
  return(ret);
 }
//+------------------------------------------------------------------+
//| 取引の利益/損失の配列を得る                                           |
//+------------------------------------------------------------------+
bool GetTradeResultsToArray(double &pl_results[],double &volume)
 {
//--- 完全な取引履歴をリクエストする
  if(!HistorySelect(0,TimeCurrent()))
    return (false);
  uint total_deals=HistoryDealsTotal();
  volume=0;
//--- 証拠金を持つ配列の初期サイズを、履歴の取引数で設定する
  ArrayResize(pl_results,total_deals);
//--- 取引結果を修正する取引のカウンター - 利益または損失
  int counter=0;
  ulong ticket_history_deal=0;
//--- 全ての取引を見る
  for(uint i=0;i<total_deals;i++)
    {
    //--- 取引を選択する
    if((ticket_history_deal=HistoryDealGetTicket(i))>0)
       {
        ENUM_DEAL_ENTRY deal_entry  =(ENUM_DEAL_ENTRY)HistoryDealGetInteger(ticket_history_deal,DEAL_ENTRY);
        long           deal_type   =HistoryDealGetInteger(ticket_history_deal,DEAL_TYPE);
        double         deal_profit =HistoryDealGetDouble(ticket_history_deal,DEAL_PROFIT);
        double         deal_volume =HistoryDealGetDouble(ticket_history_deal,DEAL_VOLUME);
        //--- 興味があるのは取引操作のみである
        if((deal_type!=DEAL_TYPE_BUY) && (deal_type!=DEAL_TYPE_SELL))
          continue;
        //--- 損益を固定する取引のみ
        if(deal_entry!=DEAL_ENTRY_IN)
          {
          //--- 取引結果を配列に書き込み、取引のカウンターを増やす
           pl_results[counter]=deal_profit;
           volume+=deal_volume;
           counter++;
          }
       }
    }
//--- 配列の最終サイズを設定する
  ArrayResize(pl_results,counter);
  return (true);
 }
//+------------------------------------------------------------------+
//| 線形回帰を計算する y=a*x+b                                           |
//+------------------------------------------------------------------+
bool CalculateLinearRegression(double  &change[],double &chartline[],
                              double  &a_coef,double  &b_coef)
 {
//--- データが十分か確認する
  if(ArraySize(change)<3)
    return (false);
//--- 蓄積されたチャート配列を作成する
  int N=ArraySize(change);
  ArrayResize(chartline,N);
  chartline[0]=change[0];
  for(int i=1;i<N;i++)
     chartline[i]=chartline[i-1]+change[i];
//--- 線形回帰を計算する
  double x=0,y=0,x2=0,xy=0;
  for(int i=0;i<N;i++)
    {
     x=x+i;
     y=y+chartline[i];
     xy=xy+i*chartline[i];
     x2=x2+i*i;
    }
  a_coef=(N*xy-x*y)/(N*x2-x*x);
  b_coef=(y-a_coef*x)/N;
//---
  return (true);
 }
//+------------------------------------------------------------------+
//|  指定されたaとbの平均二乗偏差誤差を計算する                               |
//+------------------------------------------------------------------+
bool  CalculateStdError(double  &data[],double  a_coef,double  b_coef,double &std_err)
 {
//--- 誤差の平方和
  double error=0;
  int N=ArraySize(data);
  if(N<=2)
    return (false);
  for(int i=0;i<N;i++)
     error+=MathPow(a_coef*i+b_coef-data[i],2);
  std_err=MathSqrt(error/(N-2));
//---
  return (true);
 }

参照

Testing trading strategiesTesterHideIndicatorsWorking with optimization resultsTesterStatisticsOnTesterInitOnTesterDeinitOnTesterPassMQL_TESTERMQL_OPTIMIZATIONFileOpenFileWriteFileLoadFileSave