EA(エキスパートアドバイザー)のテストと最適化

Michael | 19 10月, 2015

概論

この記事は、MetaTrader 4取引プラットフォームのテスターでの、EAのテストと最適化のプロセスについて書いています。こういった類のデータの必要性と需要を過小評価することはできません。多くのフォーラムの新規ユーザには、EAを使う時に何をどうするべきか想像することが難しいものです。

ほぼ毎日(誇張なしで)フォーラムでは、新規ユーザの質問が上がってきます。どのようにターミナルにEAを設定するのか、どのようにEAを起動させるのか、最適化とは何か、MetaTrader 4のテスターでどう実行するのか、フォワードテストとは何か等々。

この記事では簡単に、そして分かり易くこれらの質問に答え、具体例を出す中で、もう少しプロフェッショナルにこの興味深い仕事にアプローチすることができる可能性を与えます。今後の為のより詳細なテストと最適化プロセスの記述は、プレゼンテーションとして、記事に付随するリンクやMQL4-communityのサイトページで得ることができます。


EA(エキスパートアドバイザー)のテストと最適化

EAを使う時の動作を一番始めから順を追って見ていきましょう。例として私達によって簡単に変更されたEA-Moving Average(移動平均)を使います。取引プラットフォームのMetaTrader 4の初期バージョンとの違いは、私達のバージョンでは移動平均線の1つと交じり合った時にオープンポジションをとり、逆方向に移動平均線が他の期間に交じりあった時にクローズポジションをとります。私達のバージョンには同様に、市場取引注文Market Executionの実行条件下でポジションを開く機能を加えます。こうしたプログラムの変更は最近特に要求されているようですから。これがEAのコードです。

//+------------------------------------------------------------------+
//|                                      Moving Average_Мodified.mq4 |
//|                      Copyright © 2013, MetaQuotes Software Corp. |
//|                                      Modified by BARS            |
//+------------------------------------------------------------------+
#define MAGICMA  20050610
//-----------------------------------------
extern int     StopLoss           = 500;
extern int     TakeProfit         = 500;
extern double  Lots               = 0.1;
extern double  MaximumRisk        = 0.02;
extern double  DecreaseFactor     = 3;
extern int     MovingPeriod_Open  = 12;
extern int     MovingPeriod_Close = 21;
extern int     MovingShift        = 1;
extern color   BuyColor           = clrCornflowerBlue;
extern color   SellColor          = clrSalmon;
//---
double SL=0,TP=0;
//-- 連結しているモジュール --
#include <stderror.mqh>
#include <stdlib.mqh>
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void start()
  {
//--- もしチャート上に100以上のバーがあり、取引の流れが自由なら
   if(Bars<100 || IsTradeAllowed()==false)
      return;
//--- もし予想したロットサイズが現在のデポジットと一致するなら
   if(CalculateCurrentOrders(Symbol())==0)
      CheckForOpen();   // 動作を始めよう
   else
      CheckForClose();  // 好ましくない時にはポジションを閉じましょう
  }
//+------------------------------------------------------------------+
//| オープンポジションの有無を確かめています                                 |
//+------------------------------------------------------------------+
int CalculateCurrentOrders(string symbol)
  {
   int buys=0,sells=0;
   for(int i=0; i<OrdersTotal(); i++)
     {
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false)
         break;
      if(OrderSymbol()==Symbol() && OrderMagicNumber()==MAGICMA)
        {
         if(OrderType()==OP_BUY) buys++;
         if(OrderType()==OP_SELL) sells++;
        }
     }
//---- return orders volume
   if(buys>0)
      return(buys);
   else
      return(-sells);
  }
//+------------------------------------------------------------------+
//| 最適なロットサイズを計算しています                                       |
//+------------------------------------------------------------------+
double LotsOptimized()
  {
   double lot=Lots;
   int    orders=HistoryTotal(); // history orders total
   int    losses=0;              // number of loss orders without a break
//---- select lot size
   lot=NormalizeDouble(AccountFreeMargin()*MaximumRisk/1000.0,1);
//---- calcuulate number of loss orders without a break
   if(DecreaseFactor>0)
     {
      for(int i=orders-1;i>=0;i--)
        {
         if(OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)==false)
           {
            Print("Error in history!");
            break;
           }
         if(OrderSymbol()!=Symbol() || OrderType()>OP_SELL)
            continue;
         //----
         if(OrderProfit()>0)
            break;
         if(OrderProfit()<0)
            losses++;
        }
      if(losses>1)
         lot=NormalizeDouble(lot-lot*losses/DecreaseFactor,1);
     }
//---- return lot size
   if(lot<0.1)
      lot=0.1;
   return(lot);
  }
//+------------------------------------------------------------------+
//| オープンポジション機能                                                |
//+------------------------------------------------------------------+
void CheckForOpen()
  {
   double ma;
   int    res;
//---- go trading only for first tiks of new bar
   if(Volume[0]>1)
      return;
//---- get Moving Average 
   ma=iMA(NULL,0,MovingPeriod_Open,MovingShift,MODE_SMA,PRICE_CLOSE,0);
//---- sell conditions
   if(Open[1]>ma && Close[1]<ma)
     {
      if(StopLoss>0)
         SL=Bid+Point*StopLoss;
      if(TakeProfit>0)
         TP=Bid-Point*TakeProfit;
      res=WHCOrderSend(Symbol(),OP_SELL,LotsOptimized(),Bid,3,SL,TP,"Moving Average",MAGICMA,0,SellColor);
      if(res<0)
        {
         Print("SELL #オーダーのオープンのエラー ",GetLastError());
         Sleep(10000);
         return;
        }
     }
//---- buy conditions
   if(Open[1]<ma && Close[1]>ma)
     {
      SL=0;TP=0;
      if(StopLoss>0)
         SL=Ask-Point*StopLoss;
      if(TakeProfit>0)
         TP=Ask+Point*TakeProfit;
      res=WHCOrderSend(Symbol(),OP_BUY,LotsOptimized(),Ask,3,SL,TP,"Moving Average",MAGICMA,0,BuyColor);
      if(res<0)
        {
         Print("BUY #オーダーのオープンのエラー",GetLastError());
         Sleep(10000);
         return;
        }
     }
//----
  }
//+------------------------------------------------------------------+
//| クローズポジション機能                                                |
//+------------------------------------------------------------------+
void CheckForClose()
  {
   double ma;
//---- go trading only for first tiks of new bar
//(新しいバーの始めのティックから動作を始めよう)
   if(Volume[0]>1) return;
//---- get Moving Average 
   ma=iMA(NULL,0,MovingPeriod_Close,MovingShift,MODE_SMA,PRICE_CLOSE,0);
//----
   for(int i=0;i<OrdersTotal();i++)
     {
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) break;
      if(OrderMagicNumber()!=MAGICMA || OrderSymbol()!=Symbol()) continue;
      //---- check order type 
      if(OrderType()==OP_BUY)
        {
         if(Open[1]>ma && Close[1]<ma)
            OrderClose(OrderTicket(),OrderLots(),Bid,3,BuyColor);     break;
        }
      if(OrderType()==OP_SELL)
        {
         if(Open[1]<ma && Close[1]>ma)
            OrderClose(OrderTicket(),OrderLots(),Ask,3,SellColor);     break;
        }
     }
  }
//+-------------------------------------------------------------------+
//| 取引注文実行の市場条件下でポジションを開く                                 |
//+-------------------------------------------------------------------+
int WHCOrderSend(string    symbol,
                 int       cmd,
                 double    volume,
                 double    price,
                 int       slippage,
                 double    stoploss,
                 double    takeprofit,
                 string    comment,
                 int       magic,
                 datetime  expiration,
                 color     arrow_color)
  {
   int ticket=OrderSend(symbol,cmd,volume,price,slippage,0,0,comment,magic,expiration,arrow_color);
   int check=-1;
   if(ticket>0 && (stoploss!=0 || takeprofit!=0))
     {
      if(!OrderModify(ticket,price,stoploss,takeprofit,expiration,arrow_color))
        {
         check=GetLastError();
         if(check!=ERR_NO_MQLERROR)
            Print("OrderModify error: ",ErrorDescription(check));
        }
     }
   else
     {
      check=GetLastError();
      if(check!=ERR_NO_ERROR)
         Print("OrderSend error: ",ErrorDescription(check));
     }
   return(ticket);
  }
//+------------------------------------------------------------------+

添付されているEAのファイル(Moving Average_Мodified.mq4.)をダウンロードしましょう。EAのファイルは、MetaTrader 4取引ターミナルの\MQL4\experts\ファイルに置く必要があります。例えば、もしあなたの取引ターミナルがC:\Program Files\MetaTrader 4\ファイルに配置されているとしたら、あなたはEAをC:\Program Files\MetaTrader 4\MQL4\experts\ファイルに配置しなければなりません。その後でターミナルを起動(再起動)させます。

左から、ウィンドウにはナビゲーター→EAで、Moving Average_Мodifiedという名前の私達のEAのファイルが出現します。

MetaTrader 4取引ターミナルのメニュー(Ctrl+R) からストラテジーテスターを起動します。以降、一連の動作はこのようになります(図を参照)

このモードについて特に言っておかなければいけないことがあります。このモードを使用する際は、シグナルがオープンまたはクローズポジションになるのは、現行また新規のバーが始まった時だけになります。すなわち、市場へのこのような入力モードが、私達のEAの動作のアルゴリズムでは考慮されているのです!より詳しくシュミレーションの方法について知りたい方は、『Strategy Tester: シュミレーションモード』をご参照ください。

テストを始めましょう。今のうちはEAの性能内のパラメータを初期設定のまま使用します。テスター画面の右下の角にある『スタート』ボタンを押し、下側の緑のラインが端まで到達したら、テストの結果を見ることができます。この為にはテスター画面の『結果』タブを開く必要があります。もしくは、テスター実行で得たバランスチャート、『チャート』ウィンドウを開きます。(上の図を参照)

ああ、ここでは私達には重苦しい図が見えますね。私達の初期証拠金は確実にゼロに向かって進んでいます・・・。このテストを実行した統計結果の総括は、『結果』ウィンドウで見ることができます。また新規ユーザの方には『EAテストの結果にある数字は何を意味しているのか』という記事を見ていただけたら有益になると思います。

結論:エキスパートの初期設定で設定されたパラメータでは、私達のEAは指定されたツールとタイムフレーム内での収益を出す動作には向いていません。EAがもっと利益を生み出せる他のパラメータを選出する必要があります。

最適化の下では、もっと良い結果を得ることを可能にする、取引システムによって選択されたこれらのパラメータの存在を理解することができる。』とも言われています。(記事『最適化のワナにはまらないためにはどうするべきか?』より)

最適化モードでは、EAはエキスパートの性能の中で私達が指定した計画にしたがって外見を変えながら、自動的に何度も追跡をします。MT4のテスターは一度にいくつかのパラメータを最適化することができます。私達は最適化のパラメータを指示する必要があります。この為には、テスターの中の『エキスパート設定』タブ(右上)をクリックし開きます。

私たちが最適化するパラメータの左側の部分にチェックを入れます。そしてこれらのパラメータの『スタート』、『ステップ』、そして『ストップ』の値を設定します。図から分るように、パラメータが最適化(収集)します。

合理的な考えに基づいて、最適化の為の始めと終わりの値を選択します。EURUSDの通貨ペアとタイムフレームH1に対しては、上の図にある数値を指示するのが理に適っていると思われます。この場合のMetaTrader 4の相場は5桁の数字であることに注視してください。

『始値』モードでは最適化はとても速いです。その為、私達は私達が選んだ4つのパラメータを同時に最適化することができます。OKをクリックし『エキスパート設定』ウィンドウを閉じて、テスターの右側の『最適化』欄にチェックを入れます。その後、テスターの右下にある『スタート』ボタンを押しましょう。最適化が始まりました。

テスターウィンドウの『最適化結果』または『最適化グラフ』で、最適化のプロセスの可視化を管理することができます。もし最適化グラフの代わりに緑色の領域が見えたら、より見やすくするために、マウスを右クリックして現れたウィンドウから『2Dサーフェス』のチェックを外すことをお勧めします。

最適化の終了後(下部の緑の線が最後までいったら)テスターウィンドウの『最適化結果』を開き、その上部の境界線を上に持ち上げましょう。その後で、結果が降順に表示されるように2つ目の列の『プロフィット』をクリックします。このような図が見られます。

図から分かるように、EAのいくつかのパラメータの組み合わせでは最大で4658$の利益に達しました。しかしながら、私達のEAにこれらのパラメータを搭載しようとするのは早計と言えます。このような利益に対しドローダウンが大きすぎます!だって始めの為には、提起されたバリエーションの中から、最大の利益と妥当なドローダウンの最適な組み合わせが理想的なのですから。その為、私達は2715$の利益と最少ドローダウン19.83%の2406パスを取ります。

この列をマウスで右クリックします。出てきたウィンドウの『パラメータの入力設定』をクリックします。この際、選択したパラメータは自動的にEAにインストールされます。テスターの右下にある『スタート』ボタンを押します。テスト(パス)終了後、『結果』ウィンドウを開け、テストの結果を見てみましょう。

この結果はどれくらい安定しているのか?EAはリアルタイムでも、テストでパスをしたように動作するのだろうか?これらの疑問に対する答えは、フォワードテストと呼ばれるものでいくらか出すことができます。

私達はテストと最適化の期間を2008年8月1日から2009年5月1日までに指定したことを思い出してください。私達は意図的にEAの最適化を2008年8月から今日まで(2009年6月8日)にしませんでした。私達は私達が指定したインターバルをEAに教え込みました。ここで、そこから、またEAに『厳しく質問する』―試験を行う時間がきました。

つまり、これらのパラメータをもつEAを、最適化の期間外―2009年5月2日から6月8日の間でテストします。まさにこのようなパスを『フォワードテスト』と言います。前回との違いは、バックテストということです。

フォワードテストの結果によって、私達はより確信的に、また客観的にリアルタイムでの私達のEAの動作予想を判断することができます。これ以上あなた達をずるく待たせることはしないで、上記のフォワードテストを行うことにしましょう!そのためにテストウィンドウで日付を、2009年5月2日から『今日』(6月8日)までに指定します。そして『スタート』ボタンを押しましょう!これが結果です。

驚くべき結果です!一番初めからこうなることはそんなにないし、私達自身予想していませんでした。フォワードテストは悪くない利益を出しました!しかしながらドローダウンもあります。理想では最大のドローダウンを出した損失のある取引の部分をテスターのビジュアルモードのチャート上で追跡し、このドローダウンの理由を明らかにし、同様にその克服法とやり方を検討する必要があります。この記事の読者のみなさんは書かれているプロセス(MetaTrader 4のアルパリ)を繰り返し、全ての得られた結果の十分な公平性を確認するかもしれません。最適化のより利益があり、よりドローダウンのある同様のフォワードテスト(3061と771.95)は遥かに悪い結果を出したことを付け加えておきます。

とはいえ、誘惑されるのはまだ早いですよ。より客観的なEAの評価の為には、いくつかのこれらのフォワードテストを履歴で行う必要があります。こういったプロセスの動きの方式と結果の総評価は、人気取引システムと取引ロボットの最適化の錬金術をベースにしたEAというシリーズの記事にとても良く、明快に書かれています。

私達はここではユーザにEAを使用するための最初の基本的な動作を紹介することが目的です。フォワードテストのパスで私達が得た、小さくないドローダウンに、戻ることにします。チャートから、ドローダウンは2009年5月18日以降の取引番号18-20に発生したことがわかります。テスターのビジュアルモードで状況を追跡してみよう。その為にテスターの『ビジュアルモード』欄にチェックを入れ、テスターの動作モードは、明瞭にする為に『モデル』欄を『全ティック』モードにします。ビジュアル化の動きは私達は時間経過の速度を管理することができます。(つまり、相場の入りの速度)それから日付を2009年5月18日からに指定し、『スタート』ボタンを押しましょう。

この損失区間の履歴ではこのような状況がわかりました。

3つ連続のSELLの損失取引は、私達にこの重要なドローダウンを与えました。その上、取引はトレンドに反して行われ、私達のEAの主な欠点―単純すぎる動作のアルゴリズムを明らかにしました。せめて最も単純なトレンドフィルターを加えるか、ロングとショートの取引入力メカニズムを自主動作、手動に組み分ける必要があります。例えば、規格外の自動取引という記事の中で提起されているように、あるいは他の適する方法を使うことです。しかしながら、これらの問題は私達のテーマの限界を越えてしまいます。


おわりに

テスターでのEAの最適化とテストの最も簡単な動作の例を書きました。今後の理解とこういった実験での経験を獲得する為に、『いかに自分の最適化の基準を実行するか』『サンプル外のテストと最適化』という記事を参照することができます。

おわりにでは、初期ユーザがEAのテストをする際に、最も頻繁に起こる問題をいくつか見てみよう。

1. なぜ様々なディーリングセンターで同じ名前のテストの結果が異なるのか?

異なるディーリングセンターでの、異なるテストの結果は様々な相場によって説明されます。それぞれのブローカーは自分の相場の供給者をもっています。ここから価格差が起こり、そして結果として、異なるテストに反映されます。

2. なぜ同じまたは別のディーリングセンターの同じ名前のテストで異なる結果があるのか。

同じまたは異なるディーリングセンターでの異なる結果は、いくつかの最もありふれた理由を持っています。不安定なスプレッドは、結果に特に小さいタイムフレームと全ティックでのテストの際に十分大きい影響を与えます。MetaTrader 4のターミナルのテスターは、最後のスプレッドの値を記憶します。次のパスの時に、スプレッドは変わる可能性があり、同様にテストの結果も変わります。

3. なぜティックとオープン価格ごとのテストの際に異なる結果があるのか?

全ては、ティックごとにEAが動作する場合、EAはそれぞれのティックとそれらを分析するデータを受け取ることにあります。オープン価格ごとにEAがデータや、新しいバーの出現した時だけシグナルを受け取ります。全ての結論を一緒に・・・。結論:EAがどのように動作するか明らかにし、必要なモードでテスターが起動される必要があります。

4. なぜEAでポジションを開けないのか?

まず初めにストラテジーテスターのログを開く必要があります。そこで考えられるエラーのコードが表示されます。エラー番号の解読はエラーコードにて見ることができます。

私達はMetaTrader 4取引プラットフォームの初期ユーザがこの記事で自分の為に多くの疑問に対する答えを見つけることができたことを期待しよう。本当の理解というのは経験によって得られもので、独自に記事に書かれた動作を実行して(繰り返して)、初心者は小さいこういった経験が得られるだろう。同様にこれからの実験の為に必要な技術を得られることでしょう!

テキストにて引用したリンクのデータ、同様にどうやってEAを起動するか?というページからデータをこの記事の準備をする際に利用しました。