EAでZigZagと移動平均線を使う(サンプルソース)

EA
スポンサーリンク

FXで売買タイミングとして使うジグザグ(ZigZag)に移動平均線の条件も付け加えた、簡単なMT4のEAサンプルです。

概要

ZigZagの記事コメントで、移動平均線の条件も追加して欲しい旨のコメントを頂いたのでエントリー根拠として「ZigZagと移動平均線」を使ったEAを作っていきたいと思います。

コメントで頂いた要望をまとめると、こうです。

①ZigZagと移動平均線を判断材料にエントリーする
②ロングエントリーする条件として、ZigZagの谷が確定している&移動平均が上向いた時エントリーする

今回は『新たに条件をEAに追加する』に重点を置いています。どんな風に修正を加えていくのかも追って頂けたらEA作りの参考になるかもしれませんね!

それでは早速作っていきましょう。

ベースのZigZagだけのEA記事はこちら

所得が必要な情報を確認する

まず、1番初めに今回の目的を達成するために必要になってくるのが、移動平均線の情報です。

移動平均線の情報は簡単に取得できますよね。そう、iMA()関数です。

ソースコードを修正する

さて、とにもかくにも移動平均線の情報が必要なのでソースコードを修正しましょう。

//***売買判断箇所***//
         
   //チェック用変数の初期化
   TopPoint=0;
   BottomPoint=0;   

   //----MA値を取得      
   double MA_ATAI_1   = iMA(NULL, 0, E_MAKikan, 0, E_MAMode, PRICE_CLOSE, 1);    //移動平均線で1つ前の値を取得
   double MA_ATAI_2   = iMA(NULL, 0, E_MAKikan, 0, E_MAMode, PRICE_CLOSE, 2);    //移動平均線で2つ前の値を取得
   double MA_ATAI_3   = iMA(NULL, 0, E_MAKikan, 0, E_MAMode, PRICE_CLOSE, 3);    //移動平均線で3つ前の値を取得
      
   //ZigZagの高値安値を構造体に再セット
   getZigZag(1);
      
      

こんな感じで、移動平均線の情報を取得するように修正しました。移動平均線の情報がローソク足3つ分必要だったため、このように3つ取得しています。

また、移動平均線の期間と種類ぐらいはE_MAKikanとE_MAModeとしてパラメータ設定にしておくと便利ですね。

取得した値でエントリー条件を修正する

次に、移動平均線の情報を取得できたので、この情報を使ってエントリー条件を修正していきます。

エントリー条件はこんな感じでした。

・高値の切り上げ、安値の切り上げ中の、ZigZagの最新の底値が確定している
・移動平均線が下向きから上向きに変わる

上記2点の内容が合致した場合ロングエントリーを行います。

・高値切り下げ、安値切り下げ中の、ZigZagの最新の高値が確定している
・移動平均線が上向きから下向きに変わる

上記2点の内容が合致した場合ショートエントリーを行います。こんな感じで箇条書きでまとめると実際にif文を作る際役立ちます。

ソースコードを修正する

上記のエントリー条件に合致したif文になるよう、ソースコードを修正します。

   double MA_ATAI_2   = iMA(NULL, 0, E_MAKikan, 0, E_MAMode, PRICE_CLOSE, 2);    //短期移動平均線で2つ前の値を取得
   double MA_ATAI_3   = iMA(NULL, 0, E_MAKikan, 0, E_MAMode, PRICE_CLOSE, 3);    //短期移動平均線で3つ前の値を取得
      
   //ZigZagの高値安値を構造体に再セット
   getZigZag(1);
      
      
   //高値切り上げ安値切り上げでロング
   if(ZigTop[0] > ZigTop[1] && ZigTop[1] > ZigTop[2] && ZigBottom[0] > ZigBottom[1] && ZigBottom[1] > ZigBottom[2])
   {
      //ZigZag谷完成後、移動平均線が下向きから上向きに切り替わったタイミング
      if(BottomPoint>=1 && BottomPoint < TopPoint && MA_ATAI_3  > MA_ATAI_2 && MA_ATAI_2 < MA_ATAI_1)
      {
         orderPtn=1;
      }
   }
   //高値切り下げ安値切り下げでショート 
   else if(ZigTop[0] < ZigTop[1] && ZigTop[1] < ZigTop[2] && ZigBottom[0] < ZigBottom[1] && ZigBottom[1] < ZigBottom[2])
   {
      //ZigZag山完成後、移動平均線が上向きから下向きに切り替わったタイミング   
      if(TopPoint>=1 && BottomPoint > TopPoint && MA_ATAI_3  < MA_ATAI_2 && MA_ATAI_2 > MA_ATAI_1)
      {
         orderPtn=2;
      }
   }
   else
   {
      orderPtn=0;
   }
      
//***売買判断箇所***//
          
      
//***決済判断箇所***//
   total=OrdersTotal();

if文が少し長くなったので、2つにわけました。1つでも2つでも一緒ですが、自分がみてわかりやすい書き方にするのが良いです。if文の階層が深くなりすぎると読み辛いので適度にわけるのが良いでしょう。

BottomPoint>=1 && BottomPoint < TopPointとしているのは、次章で解説しますがZigZagの谷ができた後、次の山ができるまでという条件を達成するためのものです。簡単に図で表すとこんな感じです。

①BottomPoint==1 とだけした場合
②BottomPoint>=1 とだけした場合
③BottomPoint>=1 && BottomPoint < TopPoint とした場合

これで、if文も修正完了しました。あとはZigZagのBottomPointとTopPointの部分を修正します。

ZigZag関数を修正する

既存のZigZagのEAに使っていたgetZigZag()関数は、前図でいう①の状態しか考慮していませんでした。そのため、③を考慮した作りに作り替える必要があります。

ソースコードを修正する

すこしややこしいですが、ソースコードを修正するとこうなります。

//+------------------------------------------------------------------+
//|【関数】ジグザグの値をセット
//|
//|
//|【備考】なし
//+------------------------------------------------------------------+
bool getZigZag(int i)
{
      
int n,m,t;
      
bool kekka;
      
   m=0;
   t=0;
      
         
   kekka=false;
      
   for(n=i;n<=i+200;n++)
   {
      //ZigZagの値を取得
      double Zg=NormalizeDouble(iCustom(NULL,0,"ZigZag",E_Depth,E_Deviation,E_Backstep,0,n),5);
      
      //ZigZagの値と最高値が同じ場合、頂点なのでZigTopにセット      
      if(Zg!=0 && Zg==NormalizeDouble(High[n],5))
      {
         
         if(m==0){
            TopPoint=n;
         }
         
         ZigTop[m++]=Zg;
         if(m>=4)break;
      }
            
      //ZigZagの値と最安値が同じ場合、底なのでZigBottomにセット            
      if(Zg!=0 && Zg==NormalizeDouble(Low[n],5))
      {

         if(t==0){      
            BottomPoint=n;
         }
     
         ZigBottom[t++]=Zg;
         if(t>=4)break;
      }
   }
      
   kekka=true;
         
      
   //目視確認用コメント
   Comment(
          "│ ZigTOP0=", ZigTop[0], "│ ZigBTM0=", ZigBottom[0], "|\n",
          "│ ZigTOP1=", ZigTop[1], "│ ZigBTM1=", ZigBottom[1], "|\n",
          "│ ZigTOP2=", ZigTop[2], "│ ZigBTM2=", ZigBottom[2], "|\n",
          "│ ZigTOP3=", ZigTop[3], "│ ZigBTM3=", ZigBottom[3], "|\n",
          "│ TopP=", TopPoint, "│ BottomP=", BottomPoint, "|\n"
   );
            
         
   return(kekka);   
        

ちょっとここの修正は、この関数を理解する必要がありますが直近の山と谷があるローソク足の場所をTopPointとBottomPointにセットした形になります。

以上、でソースコードの修正は完了です。全体のソースコードを見て行きましょう。

ソースコード

//+------------------------------------------------------------------+
//|                                                     24_ZIGMA.mq4 |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, mef Software."
#property link      "https://fx-prog.com/"
#property version   "1.00"
         
input int E_Depth = 12;
input int E_Deviation = 5;
input int E_Backstep = 3;

input int E_MAKikan = 21;
input int E_MAMode  =  1; // 0 = sma, 1 = ema, 2 = smma, 3 = lwma
  
input int A_SPREAD = 20;
input double Lots = 0.01;
      
input int STOP_LOSS = 200;
input int TP = 200;
      
      
double ZigTop[5];     //ジグザグの山保存用
double ZigBottom[5];  //ジグザグの谷保存用
int TopPoint;
int BottomPoint;
      
  
datetime prevtime;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
  {
//---
             
//---
   return(INIT_SUCCEEDED);
  }
          
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
int start()
{
int orderPtn=0; //0:何もしない 1:買い 2:売り
int total=0;
          
double ea_order_stop_price=0,ea_order_good_price=0; //ストップロスレート,利確レート,エントリーレート
bool OrderKekka;
         
//---
   //新しい足ができた時だけやる
   if(Time[0] != prevtime){
      prevtime = Time[0];
   }else{
      return(0);
   }
          
//***売買判断箇所***//
         
   //チェック用変数の初期化
   TopPoint=0;
   BottomPoint=0;   

   //----MA値を取得      
   double MA_ATAI_1   = iMA(NULL, 0, E_MAKikan, 0, E_MAMode, PRICE_CLOSE, 1);    //短期移動平均線で1つ前の値を取得
   double MA_ATAI_2   = iMA(NULL, 0, E_MAKikan, 0, E_MAMode, PRICE_CLOSE, 2);    //短期移動平均線で2つ前の値を取得
   double MA_ATAI_3   = iMA(NULL, 0, E_MAKikan, 0, E_MAMode, PRICE_CLOSE, 3);    //短期移動平均線で3つ前の値を取得
      
   //ZigZagの高値安値を構造体に再セット
   getZigZag(1);
      
      
   //高値切り上げ安値切り上げでロング
   if(ZigTop[0] > ZigTop[1] && ZigTop[1] > ZigTop[2] && ZigBottom[0] > ZigBottom[1] && ZigBottom[1] > ZigBottom[2])
   {
      //ZigZag谷完成後、移動平均線が下向きから上向きに切り替わったタイミング
      if(BottomPoint>=1 && BottomPoint < TopPoint && MA_ATAI_3  > MA_ATAI_2 && MA_ATAI_2 < MA_ATAI_1)
      {
         orderPtn=1;
      }
   }
   //高値切り下げ安値切り下げでショート 
   else if(ZigTop[0] < ZigTop[1] && ZigTop[1] < ZigTop[2] && ZigBottom[0] < ZigBottom[1] && ZigBottom[1] < ZigBottom[2])
   {
      //ZigZag山完成後、移動平均線が上向きから下向きに切り替わったタイミング   
      if(TopPoint>=1 && BottomPoint > TopPoint && MA_ATAI_3  < MA_ATAI_2 && MA_ATAI_2 > MA_ATAI_1)
      {
         orderPtn=2;
      }
   }
   else
   {
      orderPtn=0;
   }
      
//***売買判断箇所***//
          
      
//***決済判断箇所***//
   total=OrdersTotal();
   if(total ==0 && orderPtn > 0)   
   {
      if(orderPtn == 1)
      {
         ea_order_stop_price = Ask - STOP_LOSS * Point;
         ea_order_good_price = Ask + TP * Point;          
      }
      else if(orderPtn == 2)
      {
         ea_order_stop_price = Bid + STOP_LOSS * Point;  
         ea_order_good_price = Bid - TP * Point;  
      }   
         
      //新規注文
      OrderKekka = funcOrder_Send(orderPtn - 1,ea_order_stop_price,ea_order_good_price,0,0);
         
   }
            
   return(0);
}
         
//+------------------------------------------------------------------+
//|【関数】新規注文関数                                                 |
//|                                                                  |
//|【引数】 ea_order_entry_Type:売買(0:買 1:売)                         |
//|【引数】 ea_order_stop_price:損切値  ea_order_good_price:利確値      |
//|【引数】 orderComment:オーダーコメント    ea_order_MagicNo:マジックNo       |
//|                                                                  |
//|【戻値】True:成功                                                   |
//|                                                                  |
//| 
bool funcOrder_Send(int ea_order_entry_Type, double ea_order_stop_price, double ea_order_good_price,int orderComment,int ea_order_MagicNo)
{
           
   int order_resend_num;        // エントリー試行回数
   int ea_ticket_res;           // チケットNo
   int errorcode;               // エラーコード
   double ea_order_entry_price; // エントリーレート
   color order_Color;
   bool kekka;
              
   for( order_resend_num = 0; order_resend_num < 10; order_resend_num++ ) {    // エントリー試行回数上限:10回
           
      if(MarketInfo(NULL,MODE_SPREAD) < A_SPREAD ){
           
         if(ea_order_entry_Type == OP_BUY){   
            ea_order_entry_price = Ask;               // 現在の買値でエントリー
            order_Color = clrBlue;
            Print(Ask+"でロングするよ!");            
         }else if(ea_order_entry_Type == OP_SELL){        
            ea_order_entry_price = Bid;               // 現在の売値でエントリー
            order_Color = clrRed;
            Print(Bid+"でショートするよ!");                       
         }
           
         // FXCMでは新規エントリー時にストップ/リミットを設定出来ない。
         ea_ticket_res = OrderSend(   // 新規エントリー注文
            NULL,                 // 通貨ペア
            ea_order_entry_Type,      // オーダータイプ[OP_BUY / OP_SELL]
            Lots ,                    // ロット[0.01単位](FXTFは1=10Lot)
            ea_order_entry_price,     // オーダープライスレート
            20,                       // スリップ上限    (int)[分解能 0.1pips]
            ea_order_stop_price,      // ストップレート
            ea_order_good_price,      // リミットレート
            orderComment,             // オーダーコメント
            ea_order_MagicNo,         // マジックナンバー(識別用)
            0,                        // オーダーリミット時間
            order_Color               // オーダーアイコンカラー
            );
  
         Print("結果:"+ea_ticket_res);
  
         if ( ea_ticket_res == -1) {            // オーダーエラー
            errorcode = GetLastError();      // エラーコード取得
              
            if( errorcode != ERR_NO_ERROR) { // エラー発生
               printf("エラー");
            }
              
            Sleep(2000);                                           // 1000msec待ち
            RefreshRates();                                        // レート更新
              
            printf("再エントリー要求回数:%d, 更新エントリーレート:%g",order_resend_num+1 ,ea_order_entry_price);
              
         } else {    // 注文約定
            Print("新規注文約定。 チケットNo=",ea_ticket_res," レート:",ea_order_entry_price);
            Sleep(300);                                           // 300msec待ち(オーダー要求頻度が多過ぎるとエラーになる為)
                          
            break;
         }
  
              
              
      }else{
         Print("スプレッドNG 許容スプレッド:"+A_SPREAD+"pips未満 環境スプレッド:"+MarketInfo(NULL,MODE_SPREAD)+"pips");
         Sleep(2000);                                           // 2000msec待ち
         RefreshRates();     
      }
                       
  
   }
   return kekka;   
}
         
//+------------------------------------------------------------------+
//|【関数】ジグザグの値をセット
//|
//|
//|【備考】なし
//+------------------------------------------------------------------+
bool getZigZag(int i)
{
      
int n,m,t;
      
bool kekka;
      
   m=0;
   t=0;
      
         
   kekka=false;
      
   for(n=i;n<=i+200;n++)
   {
      //ZigZagの値を取得
      double Zg=NormalizeDouble(iCustom(NULL,0,"ZigZag",E_Depth,E_Deviation,E_Backstep,0,n),5);
      
      //ZigZagの値と最高値が同じ場合、頂点なのでZigTopにセット      
      if(Zg!=0 && Zg==NormalizeDouble(High[n],5))
      {
         
         if(m==0){
            TopPoint=n;
         }
         
         ZigTop[m++]=Zg;
         if(m>=4)break;
      }
            
      //ZigZagの値と最安値が同じ場合、底なのでZigBottomにセット            
      if(Zg!=0 && Zg==NormalizeDouble(Low[n],5))
      {

         if(t==0){      
            BottomPoint=n;
         }
     
         ZigBottom[t++]=Zg;
         if(t>=4)break;
      }
   }
      
   kekka=true;
         
      
   //目視確認用コメント
   Comment(
          "│ ZigTOP0=", ZigTop[0], "│ ZigBTM0=", ZigBottom[0], "|\n",
          "│ ZigTOP1=", ZigTop[1], "│ ZigBTM1=", ZigBottom[1], "|\n",
          "│ ZigTOP2=", ZigTop[2], "│ ZigBTM2=", ZigBottom[2], "|\n",
          "│ ZigTOP3=", ZigTop[3], "│ ZigBTM3=", ZigBottom[3], "|\n",
          "│ TopP=", TopPoint, "│ BottomP=", BottomPoint, "|\n"
   );
            
         
   return(kekka);   
        
}

パラメータ設定など

設定値は以下の通りです。

  1. E_Depth = 12 ⇒ ZigZagの「Depth」設定です。
  2. E_Deviation = 5 ⇒ ZigZagの「Deviation」設定です。
  3. E_Backstep = 3 ⇒ ZigZagの「Backstep」設定です。
  4. E_MAKikan = 21 ⇒ 移動平均線の期間です。
  5. E_MAMode = 1⇒ 移動平均線のモードです。(0 = sma, 1 = ema, 2 = smma, 3 = lwma)
  6. A_SPREAD = 20 ⇒ エントリー時に許容するスプレッドの値です(20 = 2pips)
  7. Lots = 0.01 ⇒ ロット数です(0.01=1000通貨)
  8. STOP_LOSS = 200 ⇒ 損切り幅
  9. TP = 200 ⇒ 利確幅

既にポジションがある場合は追加でエントリーしません。

新しい足(バー)が出来た際に1度だけ処理を行います。※1時間足の場合だと1時間に1回処理する

ZigZagの設定値及び、エントリー時の指値、逆指値はパラメータ設定できます。

ダウンロード

上記のEAファイルになります。

“ZigZag-MAEA” をダウンロード

24_ZIGMA.ex4 – 1309 回のダウンロード – 15.41 KB

さいごに

以上、ZigZagと移動平均線を使ったEAのサンプルソースでした。

今回は、コメントにて質問があった内容を実現するためのEAサンプル作成となりました。私がEAを作る場合はこんな感じで簡単なところから肉付けする感じでEAを作っていってます。

あまり参考になるかどうかは分かりませんが、もしEAを修正する際等に少しでも参考になればと思います。

※ MT4・EAが使えるFX会社のおすすめ

※ EAのサンプルソースを一覧表にまとめました


コメント

  1. プリン より:

    早速の記事ありがとうございました。
    前の記事と同じようにエントリーの図解まであって、コードを理解する助けになり、勉強になりました!
    今後ともどうぞよろしくお願いいたします。

  2. プリン より:

    試してみたのですが、谷の仮確定でエントリするようになってしまいました。リペイントでしょうか。もしかすると、山と谷を確定するのは、もっと条件が必要なのかもしれません。

    • りょう りょう より:

      う~ん、イメージ図にもありますが元々①のようにピンポイントでしかエントリーしていなかったのを、③のように谷を形成した後なら次の山ができるまでどこでも移動平均線が上向いたらエントリーするようにエントリーポイントを拡大したのが原因かもしれないです。

      問題のエントリーポイントの画面キャプチャー等を私宛に送って頂ければ何か対策案を提示できるかもしれません。

      • プリン より:

        返信ありがとうございます。
        仰るとおり、ピンポイントだったものを変えてしまったためだと思います。
        山が仮確定したら、谷が確定するかと思いますので、そのあたりを考慮する必要がある!?という気がいたします。
        別途、キャプチャ等を送付させていただこうと思います。

  3. 竜馬 より:

    いつも勉強させて戴いています。ZIGZAGのナンピンマーチンをコンパイルしましたが、ポジションを持ちません。何か原因があるのでしょうか?そのままコンパイルしたのですが。外為ファイネスト、OANDA、デューカスコピーのデモで行いましたがポジションを持ちません。

    • りょう りょう より:

      はじめまして、バックテスト時のスプレッドが「現在値」等になっていないでしょうか?
      休日だとスプレッドが結構開いてしまうので、平日はバックテストできていても休日にバックテストが出来なくなることがあります。
      私が公開しているEAは基本1pips以上開くとエントリーしないように設定しているのでそれが原因ではないか?と思いました。

      パラメータ設定で以下の設定を変更して頂くとポジションを持つと思います。
      A_SPREAD = 10 ⇒ 50

      • 竜馬 より:

        早々のご返事ありがとうございます!月曜日にやってみます。ありがとうございました。

  4. 竜馬 より:

    連続質問ですみません。
    マジックナンバーについてですが、ZIGZAGのソースコードにマジックナンバーを適用する場合は初めに、input int MagicNo = 123456; //マジックナンバーを定義して内容もマジックナンバーに連動するようにしないといけないのですよね?それが良く分からないのですが。サンプルソースコードの中に一つだけ、マジックナンバー付があったと思いますが、どこがナジックナンバーと連動しているのか分かりません。ご教授願えませんでしょうか?