EAでZigZagを使う(サンプルソース)

記事内に商品プロモーションを含む場合があります
スポンサーリンク

今回は、MT4のEAサンプルソース(FX用)を公開します。

トレードに使うテクニカル分析はZigZagで、順張りエントリーをしています。

はじめに

ZigZagは、MACD等のようにiMACD関数のような物がないのでiCustomで取得するかつ、とってくるだけでは若干使いづらいという感じで少しだけ面倒です。

今回、以下の記事では順張り目線で高値切り上げ、又は安値更新でエントリーしていく(ダウ理論系の)サンプルを載せています。

EAの仕様

それでは、今回のソースコードのエントリー/決済/パラメータ設定についての仕様を説明します。

エントリータイミング

ロングエントリー

高値の切り上げ、安値の切り上げ中の、ZigZagの最新の底値が確定した後にロングしています。

ショートエントリー

高値切り下げ、安値切り下げ中の、ZigZagの最新の高値が確定した後にショートしています。

イメージ図

ZigZagの性質上、デメリットが発生する事があります。

決済タイミング

指値、逆指値共に20pipsに設定しています。

その他の仕様

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

  1. E_Depth = 12 ⇒ ZigZagの「Depth」設定です。
  2. E_Deviation = 5 ⇒ ZigZagの「Deviation」設定です。
  3. E_Backstep = 3 ⇒ ZigZagの「Backstep」設定です。
  4. A_SPREAD = 20 ⇒ エントリー時に許容するスプレッドの値です(20 = 2pips)
  5. Lots = 0.01 ⇒ ロット数です(0.01=1000通貨)
  6. STOP_LOSS = 200 ⇒ 損切り幅
  7. TP = 200 ⇒ 利確幅

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

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

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

スポンサーリンク

EAレポート結果

ZigZag結果
※サンプルEAの作り上、始値のみの確認となっています

2005年~2020年の期間の15分足でバックテスト確認してみました。ZigZagのDepth値がデフォルトの12だとそこまで若干のプラスにしかならなかったので、16に変えるといい感じのプラスになりました。結構粗削りなEAなので調整すればもっと良くなりそうです。

ダウンロード

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

“ZIGZAG-EA” をダウンロード 11_ZigZag.ex4 – 4364 回のダウンロード – 14.41 KB

以下サンプルソースコードになります。

ソースコード

//+------------------------------------------------------------------+
//|                                                ZigZag_Sample.mq4 |
//|                                    Copyright 2020, mef Software. |
//|                                             https://fx-prog.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 A_SPREAD = 120;
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;   
      
   //ZigZagの高値安値を構造体に再セット
   getZigZag(1);
      
      
   //高値切り上げ安値切り上げでロング
   if(ZigTop[0] > ZigTop[1] && ZigTop[1] > ZigTop[2] && 
      ZigBottom[0] > ZigBottom[1] && ZigBottom[1] > ZigBottom[2] && BottomPoint==1)
   {
      orderPtn=1;
   }
   //高値切り下げ安値切り下げでショート 
   else if(ZigTop[0] < ZigTop[1] && ZigTop[1] < ZigTop[2] &&
      ZigBottom[0] < ZigBottom[1] && ZigBottom[1] < ZigBottom[2] && TopPoint==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("エラー:"+ IntegerToString(errorcode));
            }
              
            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(n==1){
            TopPoint=1;
         }
               
         ZigTop[m++]=Zg;
         if(m>=4)break;
      }
            
      //ZigZagの値と最安値が同じ場合、底なのでZigBottomにセット            
      if(Zg!=0 && Zg==NormalizeDouble(Low[n],5))
      {
         if(n==1){
            BottomPoint=1;
         }
                     
         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);   
        
}

自作のgetZigZag関数で、ZigTop[]構造体にZigZagの高値側(頂点)をセットしています。

ZigTop[0]は最新の高値、ZigTop[1]は1つ前の高値・・・といったような感じです。

ZigBottom[]構造体にはZigZagの安値側(底)をセットしています。

アレンジ方法(切り上げ、切り下げ最中のエントリー)

高値切り上げ中にロングエントリーしたいという場合は、
「・・・&& ZigBottom[1] > ZigBottom[2] && BottomPoint==1」の所を
「・・・&& ZigBottom[1] > ZigBottom[2] 」に変えればOKです。

安値切り下げ中にショートエントリーしたいという場合は、
「・・・&& ZigBottom[1] < ZigBottom[2] && TopPoint==1」の所を
「・・・&& ZigBottom[1] < ZigBottom[2] 」に変えればOKです。

アレンジ方法(切り上げ、切り下げ上限の緩和)

3セット分の高値安値を見る場合、
「if(ZigTop[0] > ZigTop[1] && ZigTop[1] > ZigTop[2] && ZigTop[2] > ZigTop[3] &&ZigBottom[0] > ZigBottom[1] && ZigBottom[1] > ZigBottom[2] && ZigBottom[2] > ZigBottom[3]&& TopPoint==1)」

2セット分の高値安値を見る場合(サンプルソースの内容)
「if(ZigTop[0] > ZigTop[1] && ZigTop[1] > ZigTop[2] &&ZigBottom[0] > ZigBottom[1] && ZigBottom[1] > ZigBottom[2] && TopPoint==1)」

1セット分の高値安値を見る場合、
「if(ZigTop[0] > ZigTop[1] &&ZigBottom[0] > ZigBottom[1]&& TopPoint==1)」

スポンサーリンク

主な構文

iCustom()

【使用例】
iCustom(NULL,0,"ZigZag",12,5,3,0,0)

iCustom()関数でZigZagの値を取得します。

引数は以下の通りです。

  1. 通貨ペア(NULLで当該通貨)
  2. 時間軸(0で当該時間軸)
  3. インジケータ名称(ZigZagを使う場合はそのまま”ZigZag”です)
  4. Depthの値(デフォルト設定の12を使用しています)
  5. Deviationの値(デフォルト設定の5を使用しています)
  6. Backstepの値(デフォルト設定の3を使用しています)
  7. 取得する値(ZigZagの頂点を取得する場合0でOKです)
  8. バーシフト
※サブウインドウにZigZagの値を表示するインジケータを作成して表示しています
【ZigZagの確認】
◎値がセットされている箇所
①iCustom()関数でZigZagの値を取得する際に気を付けたいのが、上の図の①を見てほしいのですがサブウインドウ側の縦棒が伸びていない部分は0が返ってくるという点です。なので単純にiCustom()関数でZigZagの値を取得してきて使おうとしても、大体0が返ってきてしまうので現在時間から後ろに結構さかのぼって確認していく必要があります。
※縦棒が伸びている部分にはちゃんと頂点の値段がセットされています

②奇麗な安値切り下げが発生しています。これを見ようと思ったら矢印のように過去へさかのぼり、(頂点と底は分けて)値を保持しておくのが一番てっとりばやいと思います。

上記を踏まえ以下サンプルソースでは、for文で現在時間から後ろに200バー分さかのぼり、ZigZagの頂点と底をそれぞれ構造体にセットしていき、それを見てエントリーしていくような感じにしています。

さいごに

以上、ZigZagを使ったEAのサンプルソースでした。

このZigZagロジックの調整例としては、先ほど「ZigZagの性質上、デメリットが発生する」と記述していたデメリット部分が連続して発生している所が結構あったので、そこはデメリットが1回発生したら同一方向へのエントリーはしないようにしてあげたり、トレール等を追加すればさらによい成績になると思います。

あと、指値、逆指値も20pips固定なので、ZigTopとZigBottomという高値安値を使っていけばもっといい場所で指値、逆指値を設定できると思います。

※2021/02/18追記 
このZigZagロジックをベースに開発したEAをリアル口座にて稼働しました。

※2021/03/19追記 
このZigZagロジックをメンテして、ナンピンを行うZigZagEAのサンプルソースを公開しました。

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

※オンラインレッスンやってます


コメント

  1. よね より:

    はじめまして、よねと申します。

    EAの作成にあたって、本サイトに行き着きました。
    本記事のサンプルソースがとても参考になっています。
    自分も裁量トレードから勝てなくて、EAに行き着きました。
    自分のブログにも本サイトのリンクを貼らせて頂きました。
    問題があれば削除させて頂きますので、ご連絡ください。
    サイトの更新楽しみにしています。
    また遊びにこさせていただきます。
    よろしくお願いします。

    • りょう りょう より:

      はじめまして、よねさん。サイトを見て頂き有難うございます。
      裁量トレード難しいですよね・・・。EA運用ともに頑張りましょう!
      サイトにリンクを貼って頂くのはむしろ歓迎です、ありがとうございます!

  2. キクチワタル より:

    はじめまして、ジグザクの説明勉強になりました。例えば条件が4時間足、一時間足切り上げ。15分も切り上げたときエントリーすることなど可能でしょうか?他の足と連動させることは可能でしょうか?

    • りょう りょう より:

      はじめまして、キクチワタルさん。有難うございます!他の足と連動させる方法は難しいんですが、色んな方から結構ご質問頂いているので1度作ってみようと思います。また、参考に出来そうなソースコードが完成したら掲載しますね。

  3. ぷりんらぶ より:

    バックテストのデータですが、どちらを使われましたか?
    このコードを使用してみたのですが、
    結果が一致しないので、、、

  4. プリン より:

    掲載のコードを参考に、谷が確定した後の任意の位置でエントリーするようにコードを書き換えようとしていますが、なかなか上手くいきません。どうかご教授いただきたくコメント申し上げます。

    谷確定後で任意の位置でエントリーするという場合、TopPoint>=2を入れて考えています。例えば、次のような感じです。

    ZigBottom[0] MA2 && MA2 =2
     ※MA1:最新の足の1つ前の足の移動平均。
     MA2:最新の足の2つ前の足の移動平均。
     MA3:最新の足の3つ前の足の移動平均。

    最新の2つ前の足からZigBottom[0]の足の間に、ZigTop[0]があるという条件下のもと、谷確定後で「MA3 > MA2 && MA2 < MA1」を満足するような箇所でエントリーとなりそうだと思っていますが、どうもそうじゃないようです。

    何故上手くいかないのか分からなくて途方に暮れています。コメント頂ければ幸いです。

    • りょう りょう より:

      コメントありがとうございます。

      すこし、プリンさんが実現したい事を整理させてください。
      まず、ZigZagと移動平均線を判断材料にエントリーするという感じで合ってますでしょうか?

      その場合、『ロングエントリーする条件として、ZigZagの谷が確定した後にMA3 > MA2 && MA2 < MA1となっている事』という感じでしょうか? あと、この『MA3 > MA2 && MA2 < MA1となっている事』という判定は、移動平均線が下向いている事とかレンジで有る事とか上向いている事といった事を判断されるための条件でしょうか?

  5. プリン より:

    早速の返信ありがとうございます。
    仰るとおり、「ZigZagと移動平均線を判断材料にエントリーする」です。
    また、『ロングエントリーする条件として、ZigZagの谷が確定した後にMA3 > MA2 && MA2 < MA1となっている事』です。移動平均が上向いた時にエントリーしたいと考えています。
    どうぞよろしくお願いいたします。

    • りょう りょう より:

      わかりました。

      ちょうどZigZagを使ったEAで、新たに記事を書こうと思っていましたので「ZigZagと移動平均線を判断材料にエントリーする」EAのサンプルソース記事を書きます。よかったら参考にして頂ければと思います。
      ※明日明後日には書けると思いますので、もうしばらくお待ちください

      • プリン より:

        返信ありがとうございます。
        新しい記事作成して頂けるのですね、楽しみしています。また、参考にさせて頂きます。

  6. 3山(3川) より:

    有益な情報、ありがとうございます。
    3山(3川)でエントリーするプログラムを考えています。
    68行目以下を下記のように変更すれば、良いのでしょうか?

    //高値切り上げ安値切り上げで_ロング_long
    if(Close[0]>ZigTop[0] && ZigBottom[0] > ZigBottom[1] && ZigBottom[2] > ZigBottom[1])//3zan//3山
    /////////////////////////////////////////////////////////////////////////////////////
    //高値切り下げ安値切り下げで_ショート_
    else if(Close[0]<ZigBottom[0] && ZigTop[0] < ZigTop[1] && ZigTop[2] < ZigTop[1])//33sen//3川

    宜しくお願いします。

    • りょう りょう より:

      3山(3川)さん

      3山というのは具体的にはどのエントリーポイントになりますでしょうか?
      お問合せ の方からご質問頂ければ、メールでのやり取りができるのでイメージ添付等でエントリーポイントの情報を教えて頂ければ回答出来るかと思います。

  7. 3山(3川) より:

    3山というのは具体的にはどのエントリーポイントになりますでしょうか?
    _画像を付けてみました、宜しくお願いします。
    https://www2.x-feeder.info/ryou4trader/

    • りょう りょう より:

      画像有難うございました。
      ヘッド&ショルダー系という事ですね。
      記載されているif文の内容だと、ZigZagの最新の値が更新されてしまう場合『Close[0]>ZigTop[0]』の条件は成り立たなくなってしまうためエントリーポイントは極端に少なくなってしまいます。

      単純なエントリーポイントの形だとif文の箇所は以下のような感じで良いと思います。
      ※エントリーポイントの制御を細かくしてないので、ブレイク後沢山エントリーしちゃいますが・・・

      //高値切り上げ安値切り上げでロング
      if(Close[0] > ZigTop[1] && Close[0] > ZigTop[2])
      {
      if(ZigBottom[0] > ZigBottom[1] && ZigBottom[2] > ZigBottom[1])//3zan//3山
      {
      orderPtn=1;
      }
      }
      //高値切り下げ安値切り下げでショート
      else if(Close[0]

  8. 3山(3川) より:

    ご返答ありがとうございます。
    ヘッド&ショルダーで「エントリー」するより
    「だまし」」で「エントリー」した方が、
    良いような感じです、。

  9. グレープドラゴン より:

    zigzagについて質問失礼いたします。
    //安値切り上げ高値更新でエントリ
    {
    if(ZigTop[0] > ZigTop[1] && ZigBottom[0] > ZigBottom[1] )
    {
    orderPtn=1;
    }
    }

    この時、損切をZigBottom[0]に設定してリスクリワードを1:1にし利確されたとします。
    利確のあと、まだ上記のZigzag条件が成立しているままだと再度エントリーされてしまうのを防ぎたいです。条件が「はじめて」揃った時だけエントリーしたいです。利確後○○分間はエントリーしないという方法以外で、どのようなイメージでコードを書けばいいのでしょうか?
    よろしくお願いいたします。

  10. りょう りょう より:

    確かに損切りや利確幅が小さい場合は1つの山や1つの谷で複数回エントリーしてしまいますね。

    条件が「はじめて」揃った時だけエントリーしたい場合は、ZigZagの山が同じ山かどうかを見てあげれば解決します。

    例えばエントリー時のZigBottom[1]の値を別変数に保存しておき、次回のエントリー時に現在のZigBottom[1]の値と比較し同一なら同じとみなしてエントリーしないといった感じです。

    【例】
    ZigBottom[1]の値が150.511、ZigBottom_bak[1]の値が150.511なら同じ谷なのでエントリーしない。
    ZigBottom[1]の値が150.432、ZigBottom_bak[1]の値が150.511なら違う谷なのでエントリーする。
    ※ZigBottom_bak[1]はエントリー時に、ZigBottom[1]の値をセットする

    少し難しいので、月曜火曜あたりに記事にします。難しそうでしたら記事の方を参照ください。