FX自動売買基礎と応用

EA(自動売買)の精度を上げる方法とは?パラメータを最適化して検証


第11回で作成したEAでは、エントリーする曜日を制限することで、精度を上げることに成功しました。

>EA(自動売買)の精度を上げる方法|曜日ごとにバックテストを行い検証

この時セットされていた各種パラメータは、簡単な最適化を行ってセットした値であり、最適化の方法については詳しく紹介していませんでした。
そこで今回は、パラメータを最適化する流れについて改めて解説し、どれくらい精度が向上するのかを確認してみましょう。
第11回で作成したEAを使用します。

最適化の種類

最適化のモデルには「全ティック」「コントロールポイント」「始値のみ」の3種類があります。

最適化のモデルには「全ティック」「コントロールポイント」「始値のみ」の3種類

「全ティック」は、ローソク足の動きをティック単位でシミュレーションするモードです。
例えば、とあるローソク足が形成されるのに200回価格が更新されたとします。
この時、バックテストでも200回値の更新をシミュレーションします。

それに対して「始値のみ」は、バーの始値のみシミュレーションするモードです。
たとえ価格が200回更新されてできたローソク足であっても、始値の1回分の価格でしかシミュレーションしません。
つまり、200回値を更新したローソク足では、「全ティック」よりも「始値のみ」の方が、単純計算で200倍早くテストが終わります。

どのモデルを使うべき?

当然ですが、モデルが「全ティック」の場合が最も正確にバックテストを行い、「始値のみ」ではあまり正確な結果は出ません。
そのため最適化においても、モデルは「全ティック」を使用するべきです。
しかし、数年間のバックテストを数千、数万パターンテストすると、膨大な時間がかかってしまいます。

ではどうすれば良いのかというと、まず「始値のみ」で最適化を行い、最適化結果の中から良さそうなパラメータセットを抽出、再度「全ティック」でテストを行うのが有効です。
「始値のみ」の場合は、個々のテスト結果は正確ではありませんが、パラメータを変更した時の「どっちのパラメータの方が良い精度になるか」は比較できる傾向にあります。

「始値のみ」による最適化

それでは最適化を行っていきましょう。
以下の画像のように設定しました。

最適化の設定

イメージとしては、あまり精度に大きく影響しない、もしくは細かく刻むべきではないパラメータについては大きめに値を刻み、精度に大きく影響しそうなパラメータについては細かく刻んでいます。
遺伝的アルゴリズムによる最適化をONにし、数分で最適化は完了しました。
以下プロフィットファクターで降順にした最適化結果です。

遺伝的アルゴリズムによる最適化をONにして最適化は完了

「全ティック」によるバックテスト

それでは「始値のみ」で最適化を行った結果の中から、1つパラメータセットを抽出して「全ティック」でバックテストを行いましょう。
どのパラメータセットを使用するかは「どの指標(取引回数、PF、最大ドローダウンなど)を重視するか」によりますが、今回は無難にプロフィットファクターが高くなるようなパラメータセットを選択します。

最適化結果を見ると、上位はPFが10.2ですが、取引回数が5回しかありません。
取引回数が5回ではあてになりません。
同様に、取引回数が数十回しかない結果も除外します。
今回は上から14番目のPFが2.55、取引回数が568回のパラメータ設定を使用します。
以下、最適化前と最適化後のバックテスト結果です。

最適化前

最適化前

最適化後

最適化後

最適化前に比べて、かなりバックテストの精度が向上したことが分かります。
また、最適化の「始値のみ」の時に568回だった取引回数が、「全ティック」にすると1148回まで増えました。

モデルが「始値のみ」の場合は、「全ティック」に比べてティックごとのシミュレーションをしない分、エントリーチャンスが少なくなるので、このように「始値のみ」ではエントリー回数が少ない傾向にあります。
そのため「始値のみ」の結果だけで結論を出すことはやめましょう。

注意点

今回のバックテストは、スプレッドを固定で行っています。
日本時間早朝の相場が緩やかになる時間帯は、スプレッドが広がりやすいという特徴があります。
実運用を行うにはスプレッド制限をかけるなど、十分に十分注意しましょう。
また、マーチンゲールは自己資金を一瞬で失ってしまうリスクがあります。

プログラム全文(最適化後)


#property copyright "Copyright(C) 2023, OANDA"
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict

input int                   MAGICMA = 23498721;                         // マジックナンバー
input double                Lots =0.01;                                 // 1ロット十万通貨単位
input int                   Slippage = 4;                               // エントリー見送りスリッページ
input double                MaxSpread = 5;                              // エントリー見送りスプレッド
input double                TakeProfit = 5.0;                          // 利益確定幅(pips)
input double                LossCut = 20.0;                             // 損切確定幅(pips)
input int                   RSIPeriod=4;                                // 期間
input   ENUM_APPLIED_PRICE  RSIAppliedPrice = PRICE_CLOSE;              // 適用価格
input int                   UpLine = 90;                                // 上の線
input int                   DownLine = 20;                              // 下の線
input int                   TradeTime = 0;                              // トレードを行う時間
input int                   NanpinWidth = 7;                            //ナンピンの幅(pips)

//曜日設定
input int                   TradeDayOfWeek = 5;                         // トレード曜日(1:月曜、2火曜、3:水曜、4:木曜、5:金曜)
  
double dSpread;

int OnInit()
{
   return(INIT_SUCCEEDED);
}
void OnTick()
{
   dSpread = (Ask - Bid) / (Point * 10);
   
   if(CalculateCurrentOrders()==0 && dSpread < MaxSpread && TimeDayOfWeek(Time[0]) != TradeDayOfWeek) CheckForOpen(); 
   if(CalculateCurrentOrders()==1 && dSpread < MaxSpread) CheckForNanpin(); 
   CheckForClose();
}
void CheckForOpen()
{
   int res;
   double RSI = iRSI(Symbol(), 0, RSIPeriod, RSIAppliedPrice, 1);
   if(TradeTime == TimeHour(Time[1]))
   {
      if(RSI < DownLine)
      {
         res=OrderSend(Symbol(), OP_BUY, Lots, Ask, Slippage, 0, 0,"", MAGICMA, 0, Red);
      }
      if(RSI > UpLine)
      {
         res=OrderSend(Symbol(), OP_SELL, Lots, Bid, Slippage, 0, 0, "", MAGICMA, 0, Blue);
      }
   }
}
void CheckForNanpin()
{
   int res;
   double buyProfitPips = 0;
   double SellProfitPips = 0;
   
   for( int i=0; i<OrdersTotal(); i++ ){
      if( OrderSelect(i, SELECT_BY_POS) == true && OrderMagicNumber() == MAGICMA && OrderSymbol() == Symbol() ){
         if( OrderType() == OP_BUY ){
            buyProfitPips = (MarketInfo(Symbol(),MODE_BID) - OrderOpenPrice()) / (Point * 10); 
            break;
         }else if( OrderType() == OP_SELL ){
            SellProfitPips = (OrderOpenPrice() - MarketInfo(Symbol(),MODE_ASK)) / (Point * 10);
            break;
         }  
      }
   }
   
   if(SellProfitPips == 0 && buyProfitPips < NanpinWidth * -1)
   {
      res=OrderSend(Symbol(), OP_BUY, Lots, Ask, Slippage, 0, 0,"", MAGICMA, 0, Red);
   }
   if(buyProfitPips == 0 && SellProfitPips < NanpinWidth * -1)
   {
      res=OrderSend(Symbol(), OP_SELL, Lots, Bid, Slippage, 0,  0, "", MAGICMA, 0, Blue);
   }

}
void CheckForClose(){
   int res;
   double buyProfitPips = 0;
   double SellProfitPips = 0;
   
   for( int i=0; i<OrdersTotal(); i++ ){
      if( OrderSelect(i, SELECT_BY_POS) == true && OrderMagicNumber() == MAGICMA && OrderSymbol() == Symbol() ){
         if( OrderType() == OP_BUY ){
            buyProfitPips = (MarketInfo(Symbol(),MODE_BID) - OrderOpenPrice()) / (Point * 10); 
         }else if( OrderType() == OP_SELL ){
            SellProfitPips = (OrderOpenPrice() - MarketInfo(Symbol(),MODE_ASK)) / (Point * 10);
         }  
      }
   }
   if(TakeProfit <= buyProfitPips || LossCut <= buyProfitPips * -1){
      for( int i=OrdersTotal()-1; i>=0; i-- ){
         if( OrderSelect(i, SELECT_BY_POS) == true ){
            if( OrderType() == OP_BUY && OrderMagicNumber() == MAGICMA && OrderSymbol() == Symbol() ){
               res = OrderClose(OrderTicket(), OrderLots(), Bid, Slippage * 10,Green);
            }
         }
      }
   }
   if(TakeProfit <= SellProfitPips || LossCut <= SellProfitPips * -1){
      for( int i=OrdersTotal()-1; i>=0; i-- ){
         if( OrderSelect(i, SELECT_BY_POS) == true ){
            if( OrderType() == OP_SELL && OrderMagicNumber() == MAGICMA && OrderSymbol() == Symbol() ){
               res = OrderClose(OrderTicket(),OrderLots(),Ask,Slippage * 10,Green);
            }
         }
      }
   }
}
int CalculateCurrentOrders()
{
   int positions = 0;
   for(int i=0;i<OrdersTotal();i++)
   {
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) break;
      if(OrderSymbol()==Symbol() && OrderMagicNumber()==MAGICMA)
      {
      positions++;
      }
   }
   return positions;
}

EA(自動売買)を学びたい方へオススメコンテンツ

EA運用の注意点

OANDAではEA(自動売買)を稼働するプラットフォームMT4/MT5の基本的な使い方について、画像や動画付きで詳しく解説しています。MT4/MT5のインストールからEAの設定方法までを詳しく解説しているので、初心者の方でもスムーズにEA運用を始めることが可能です。またOANDAの口座をお持ちであれば、独自開発したオリジナルインジケーターを無料で利用することもできます。EA運用をお考えであれば、ぜひ口座開設をご検討ください。


本ホームページに掲載されている事項は、投資判断の参考となる情報の提供を目的としたものであり、投資の勧誘を目的としたものではありません。投資方針、投資タイミング等は、ご自身の責任において判断してください。本サービスの情報に基づいて行った取引のいかなる損失についても、当社は一切の責を負いかねますのでご了承ください。また、当社は、当該情報の正確性および完全性を保証または約束するものでなく、今後、予告なしに内容を変更または廃止する場合があります。なお、当該情報の欠落・誤謬等につきましてもその責を負いかねますのでご了承ください。

この記事をシェアする

ホーム » FX自動売買基礎と応用 » EA(自動売買)の精度を上げる方法とは?パラメータを最適化して検証