FX自動売買基礎と応用

EA(自動売買)の精度を上げる方法とは?買いと売りを分類して検証


第13回では、ビジュアルモードを活用して負けやすいポイントを特定し、その苦手ポイントを克服するインジケーターを追加して精度を上げることに成功しました。

>第13回の記事はこちら

今回はさらに応用して、「買いエントリー」と「売りエントリー」で分けて、最適化を行います。

「買いエントリー」と「売りエントリー」で分けるバックテスト

これまでに作成したEAでは、「買いエントリー」と「売りエントリー」でテクニカル指標を変えずに使用してきました。
つまり、どちらも同じ期間、同じ種別の移動平均線を使用しています。
しかし、投資においては、上昇方向と下降方向の値動きを同列にしない考え方もあります。
そのため、今回は買いと売りでパラメータを分け、それぞれでバックテストを実行します。

パラメータを買いと売りで分ける

ひな形のプラグラムは前回作成したプログラムを使用します。
パラメータ部分を以下のように変更します。


input int                   MAGICMA = 23498721;                         // マジックナンバー
input double                Lots =0.01;                                 // 1ロット十万通貨単位
input int                   Slippage = 4;                               // エントリー見送りスリッページ
input double                MaxSpread = 5;                              // エントリー見送りスプレッド

//買い設定
input double                BuyTakeProfit = 5.0;                           // 利益確定幅(pips)
input double                BuyLossCut = 20.0;                             // 損切確定幅(pips)
input int                   BuyRSIPeriod=4;                                // 期間(RSI)
input   ENUM_APPLIED_PRICE  BuyRSIAppliedPrice = PRICE_CLOSE;              // 適用価格(RSI)
input int                   BuyUpLine = 90;                                // 上の線(RSI)
input int                   BuyDownLine = 20;                              // 下の線(RSI)
input int                   BuyMAPeriod=350;                               // 期間(MA)
input   ENUM_APPLIED_PRICE  BuyMAAppliedPrice = PRICE_CLOSE;               // 適用価格(MA)

//売り設定
input double                SellTakeProfit = 5.0;                           // 利益確定幅(pips)
input double                SellLossCut = 20.0;                             // 損切確定幅(pips)
input int                   SellRSIPeriod=4;                                // 期間(RSI)
input   ENUM_APPLIED_PRICE  SellRSIAppliedPrice = PRICE_CLOSE;              // 適用価格(RSI)
input int                   SellUpLine = 90;                                // 上の線(RSI)
input int                   SellDownLine = 20;                              // 下の線(RSI)
input int                   SellMAPeriod=350;                               // 期間(MA)
input   ENUM_APPLIED_PRICE  SellMAAppliedPrice = PRICE_CLOSE;               // 適用価格(MA)

input int                   TradeTime = 0;                              // トレードを行う時間
input int                   NanpinWidth = 7;                            //ナンピンの幅(pips)

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

このように、インジケーターの期間や利確、損切りpipsなどのパラメータの先頭に「Buy」「Sell」という文字列を付け、分類します。
このままだとコンパイルエラーが発生してしまうので、変更したパラメータを反映させる必要があります。

OnTick関数

まず指標部分を以下のように変更します。


//買い指標
   double BuyRSI = iRSI(Symbol(), 0, BuyRSIPeriod, BuyRSIAppliedPrice, 1);
   double BuyMA0 = iMA(Symbol(), Period(), BuyMAPeriod, 0, MODE_EMA, BuyRSIAppliedPrice, 0);
   double BuyMA1 = iMA(Symbol(), Period(), BuyMAPeriod, 0, MODE_EMA, BuyRSIAppliedPrice, 1);
   
   //売り指標
   double SellRSI = iRSI(Symbol(), 0, SellRSIPeriod, SellRSIAppliedPrice, 1);
   double SellMA0 = iMA(Symbol(), Period(), SellMAPeriod, 0, MODE_EMA, SellRSIAppliedPrice, 0);
   double SellMA1 = iMA(Symbol(), Period(), SellMAPeriod, 0, MODE_EMA, SellRSIAppliedPrice, 1);

もともとの式を複製し、片方を買い用の指標に、もう一方を売り用の指標に使用します。
これでOnTick関数内の変更は完了です。

CheckForClose関数

最後はCheckForClose関数です。
こちらは利確と損切りpipsを指定している部分を書き換えます。
決済なのですでに買いと売りに分かれており、TakeProfitとLossCutをそれぞれBuyTakeProfit、SellTakeProfit、BuyLossCut、SellLossCutと変更します。


if(BuyTakeProfit <= buyProfitPips || BuyLossCut <= 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(SellTakeProfit <= SellProfitPips || SellLossCut <= 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);
            }
         }
      }
   }

変更点はこれだけです。

バックテスト

バックテストの方法は、第12回で説明した方法と同じです。
今回は、以下のように最適化の設定をしました。

バックテスト 最適化の設定

以下は、最適化を実施して、精度の高かったパラメータセットのバックテスト結果です。

売りと買いを分ける前

バックテスト 売りと買いを分ける前

売りと買いを分けた後に最適化した結果

バックテスト 売りと買いを分けた後に最適化した結果

比較して分かる通り、PFを伸ばすことに成功しました。
グラフを見ると、大きなドローダウンもないように見えます。
買いと売りのエントリーポイントを分けるという方法は、今回のロジックに限らず有効です。
ぜひ他のロジックでも試してみてください。

注意点

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

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


#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                BuyTakeProfit = 5.0;                           // 利益確定幅(pips)
input double                BuyLossCut = 35.0;                             // 損切確定幅(pips)
input int                   BuyRSIPeriod=4;                                // 期間(RSI)
input   ENUM_APPLIED_PRICE  BuyRSIAppliedPrice = PRICE_CLOSE;              // 適用価格(RSI)
input int                   BuyUpLine = 90;                                // 上の線(RSI)
input int                   BuyDownLine = 20;                              // 下の線(RSI)
input int                   BuyMAPeriod=550;                               // 期間(MA)
input   ENUM_APPLIED_PRICE  BuyMAAppliedPrice = PRICE_CLOSE;               // 適用価格(MA)

//売り設定
input double                SellTakeProfit = 40.0;                           // 利益確定幅(pips)
input double                SellLossCut = 10.0;                             // 損切確定幅(pips)
input int                   SellRSIPeriod=10;                                // 期間(RSI)
input   ENUM_APPLIED_PRICE  SellRSIAppliedPrice = PRICE_CLOSE;              // 適用価格(RSI)
input int                   SellUpLine = 80;                                // 上の線(RSI)
input int                   SellDownLine = 20;                              // 下の線(RSI)
input int                   SellMAPeriod=150;                               // 期間(MA)
input   ENUM_APPLIED_PRICE  SellMAAppliedPrice = PRICE_CLOSE;               // 適用価格(MA)

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 BuyRSI = iRSI(Symbol(), 0, BuyRSIPeriod, BuyRSIAppliedPrice, 1);
   double BuyMA0 = iMA(Symbol(), Period(), BuyMAPeriod, 0, MODE_EMA, BuyRSIAppliedPrice, 0);
   double BuyMA1 = iMA(Symbol(), Period(), BuyMAPeriod, 0, MODE_EMA, BuyRSIAppliedPrice, 1);
   
   //売り指標
   double SellRSI = iRSI(Symbol(), 0, SellRSIPeriod, SellRSIAppliedPrice, 1);
   double SellMA0 = iMA(Symbol(), Period(), SellMAPeriod, 0, MODE_EMA, SellRSIAppliedPrice, 0);
   double SellMA1 = iMA(Symbol(), Period(), SellMAPeriod, 0, MODE_EMA, SellRSIAppliedPrice, 1);
   
   if(TradeTime == TimeHour(Time[1]))
   {
      if(BuyRSI < BuyDownLine && BuyMA0 > BuyMA1)
      {
         res=OrderSend(Symbol(), OP_BUY, Lots, Ask, Slippage, 0, 0,"", MAGICMA, 0, Red);
      }
      if(SellRSI > SellUpLine && SellMA0 < SellMA1)
      {
         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(BuyTakeProfit <= buyProfitPips || BuyLossCut <= 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(SellTakeProfit <= SellProfitPips || SellLossCut <= 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(自動売買)の精度を上げる方法とは?買いと売りを分類して検証