ZigZagEAをマルチタイムフレーム化する(サンプルソース)

EA
スポンサーリンク

FXで売買タイミングとして使うジグザグ(ZigZag)をマルチタイムフレーム化する、簡単なMT4のEAサンプルです。

概要

以前からコメントやお問合せで、ZigZagEAのマルチタイムフレーム化が要望としてありましたので、今回は出来るだけシンプルにマルチタイムフレーム対応してみました。

EA自体は5分足で動かしており、上位足の15分足も高値安値をチェックするような感じです。

※ベースとなったZigZagサンプルEAの記事はこちら

実際の動き

修正を加えた事で、実際に5分足の高値安値と15分足の高値安値を見ている事を確認しました。

以下は5分足のチャートです。

次に、以下は15分足のチャートです。

5分足でも高値安値の切り上げ、15分足でも高値安値切り上げしている部分(青〇の部分)でロングエントリーできている事がわかります。

EAレポート結果

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

2012年~2021年の期間の5分足でバックテスト確認してみました。まずまずの結果になりました!

今回はプロパティの値等特に変えず、最適化もしていないのでこれよりいい結果になるプロパティ値もあると思います。

ダウンロード

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

“ZigZagマルチEA” をダウンロード

20_ZigMulti.ex4 – 3227 回のダウンロード – 21.17 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   "2.00"
         
input int E_Depth = 12;
input int E_Deviation = 5;
input int E_Backstep = 3;
  
input int A_SPREAD = 10;
input double Lots = 0.01;
      
input int STOP_LOSS = 200;
input int TP = 200;
 
      
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;
 
double ZigTop_M15[5];     //ジグザグの山保存用(15分足用)
double ZigBottom_M15[5];  //ジグザグの谷保存用(15分足用)
int TopPoint_M15=0,BottomPoint_M15=0;
 
double ZigTop_M5[5];     //ジグザグの山保存用(5分足用)
double ZigBottom_M5[5];  //ジグザグの谷保存用(5分足用)
int TopPoint_M5=0,BottomPoint_M5=0;
 
 
//---
   //新しい足ができた時だけやる
   if(Time[0] != prevtime){
      prevtime = Time[0];
   }else{
      return(0);
   }
          
//***売買判断箇所***//
         
   //ZigZagの高値安値を構造体に再セット
   getZigZag(ZigTop_M15,ZigBottom_M15,TopPoint_M15,BottomPoint_M15,PERIOD_M15,1);   //15分足のZigZag値を取得
   getZigZag(ZigTop_M5,ZigBottom_M5,TopPoint_M5,BottomPoint_M5,PERIOD_M5,1);        //5分足のZigZag値を取得
    
 
   //高値切り上げ安値切り上げでロング(5分足)
   if(ZigTop_M5[0] > ZigTop_M5[1] && ZigTop_M5[1] > ZigTop_M5[2] && 
      ZigBottom_M5[0] > ZigBottom_M5[1] && ZigBottom_M5[1] > ZigBottom_M5[2] && BottomPoint_M5==1)
   {
      //高値切り上げ安値切り上げでロング(15分足)
      if(ZigTop_M15[0] > ZigTop_M15[1] && ZigTop_M15[1] > ZigTop_M15[2] && 
         ZigBottom_M15[0] > ZigBottom_M15[1])
      {
    
         orderPtn=1;
      }
   }
 
   //高値切り下げ安値切り下げでショート(5分足)
   else if(ZigTop_M5[0] < ZigTop_M5[1] && ZigTop_M5[1] < ZigTop_M5[2] &&
      ZigBottom_M5[0] < ZigBottom_M5[1] && ZigBottom_M5[1] < ZigBottom_M5[2] && TopPoint_M5==1)
   {
      //高値切り下げ安値切り下げでショート(15分足)   
      if(ZigTop_M15[0] < ZigTop_M15[1] && ZigTop_M15[1] < ZigTop_M15[2] &&
         ZigBottom_M15[0] < ZigBottom_M15[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);
         
   }
 
 
   //目視確認用コメント
   Comment(
          "│ ZigTOP0=", ZigTop_M15[0], "│ ZigBTM0=", ZigBottom_M15[0], "|\n",
          "│ ZigTOP1=", ZigTop_M15[1], "│ ZigBTM1=", ZigBottom_M15[1], "|\n",
          "│ ZigTOP2=", ZigTop_M15[2], "│ ZigBTM2=", ZigBottom_M15[2], "|\n",
          "│ ZigTOP3=", ZigTop_M15[3], "│ ZigBTM3=", ZigBottom_M15[3], "|\n",
          "│ TopP=", TopPoint_M15, "│ BottomP=", BottomPoint_M15, "|\n",
          "│ ZigTOP0=", ZigTop_M5[0], "│ ZigBTM0=", ZigBottom_M5[0], "|\n",
          "│ ZigTOP1=", ZigTop_M5[1], "│ ZigBTM1=", ZigBottom_M5[1], "|\n",
          "│ ZigTOP2=", ZigTop_M5[2], "│ ZigBTM2=", ZigBottom_M5[2], "|\n",
          "│ ZigTOP3=", ZigTop_M5[3], "│ ZigBTM3=", ZigBottom_M5[3], "|\n",
          "│ TopP=", TopPoint_M5, "│ BottomP=", BottomPoint_M5, "|\n"         
   );
 
            
   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;
         }else if(ea_order_entry_Type == OP_SELL){        
            ea_order_entry_price = Bid;               // 現在の売値でエントリー
            order_Color = clrRed;            
         }
         
         // 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               // オーダーアイコンカラー
            );
      }
                     
      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;
      }
   }
   return kekka;   
}
 
//+------------------------------------------------------------------+
//|【関数】ジグザグの値をセット
//|【引数(参照)】ZigTop[],ZigBottom[],TopPoint,BottomPoint
//|【引数】 時間足(PERIOD_M15,PERIOD_M5,PERIOD_D1等)  
//|【引数】 バーシフト         
//|
//|【備考】なし
//+------------------------------------------------------------------+
bool getZigZag(double &bufT[5],double &bufB[5],int &bufTP,int &bufBP,int t_Period,int i)
{
 
   int n=0,m=0,t=0;
   bool kekka=false;
 
 
   for(n=i;n<=i+200;n++)
   {
      //ZigZagの値を取得
      double Zg=NormalizeDouble(iCustom(NULL,t_Period,"ZigZag",E_Depth,E_Deviation,E_Backstep,0,n),5);
      
      //ZigZagの値と最高値が同じ場合、頂点なのでZigTopにセット      
      if(Zg!=0 && Zg==NormalizeDouble(iHigh(NULL,t_Period,n),5))
      {
         if(n==1){
            bufTP=1;
         }
               
         bufT[m++]=Zg;
         if(m>=4)break;
      }
            
      //ZigZagの値と最安値が同じ場合、底なのでZigBottomにセット            
      if(Zg!=0 && Zg==NormalizeDouble(iLow(NULL,t_Period,n),5))
      {
         if(n==1){
            bufBP=1;
         }
                     
         bufB[t++]=Zg;
         if(t>=4)break;
      }
   }
 
 
   kekka=true;
         
   return(kekka);   
        
}

関数の変更も含めた全体のサンプルソースコードです。

主な変更点

getZigZag()関数の変更

まず、ZigZagの値を複数の時間足(5分足と15分足)で取得したかったので、ZigZagの値を取得する関数『getZigZag()関数』を修正しました。ほぼ、この関数の修正だけで対応完了した感じです。

//+------------------------------------------------------------------+
//|【関数】ジグザグの値をセット
//|【引数(参照)】ZigTop[],ZigBottom[],TopPoint,BottomPoint
//|【引数】 時間足(PERIOD_M15,PERIOD_M5,PERIOD_D1等)  
//|【引数】 バーシフト         
//|
//|【備考】なし
//+------------------------------------------------------------------+
bool getZigZag(double &bufT[5],double &bufB[5],int &bufTP,int &bufBP,int t_Period,int i)
{
 
   int n=0,m=0,t=0;
   bool kekka=false;
 
 
   for(n=i;n<=i+200;n++)
   {
      //ZigZagの値を取得
      double Zg=NormalizeDouble(iCustom(NULL,t_Period,"ZigZag",E_Depth,E_Deviation,E_Backstep,0,n),5);
      
      //ZigZagの値と最高値が同じ場合、頂点なのでZigTopにセット      
      if(Zg!=0 && Zg==NormalizeDouble(iHigh(NULL,t_Period,n),5))
      {
         if(n==1){
            bufTP=1;
         }
               
         bufT[m++]=Zg;
         if(m>=4)break;
      }
            
      //ZigZagの値と最安値が同じ場合、底なのでZigBottomにセット            
      if(Zg!=0 && Zg==NormalizeDouble(iLow(NULL,t_Period,n),5))
      {
         if(n==1){
            bufBP=1;
         }
                     
         bufB[t++]=Zg;
         if(t>=4)break;
      }
   }
 
 
   kekka=true;
         
   return(kekka);   
        
}

以前は決め打ちの15分足で配列にZigZagの高値と安値をセットしていただけでしたが、この関数の呼び出し元から時間足の種類(int t_Period)を渡すようにしたことで全ての時間足のZigZagの値を取得できるように修正しています。

あと、ZigZagの配列等も参照渡し(呼び出し元の引数に反映)して 『getZigZag()関数』 内で元の配列にセットしなおす等の処理を省いています。

メイン処理の変更

メイン処理の主な変更点です。

①配列を追加

まず、15分足用の配列の他に5分足の配列を追加しました。さらに1時間足も見たいってなったら同様に配列を追加してください。

あと、ベースとなったZigZagEAはメイン処理ではない先頭部でこの配列を宣言してグローバル配列みたいになってましたが、今回はローカル配列に落ち着きました。

②呼び出し部分を変更

まず、15分足用ZigZagの値を取得する用と、 5分足用ZigZagの値を取得するため2つ書いています 。 さらに、引数も前述していた通り追加された分を渡すようにしています。

③チェック処理を変更

5分足のZigZagの値で高値の切り上げ・切り下げ等をチェックしつつ、15分足のZigZagの値でも高値の切り上げ・切り下げ等をチェックするように変更しています。

変更点は以上です。

さいごに

以上、「ZigZagEAをマルチタイムフレーム化する」でした。

ZigZagの値配列に各時間足の情報を持たせるのは大変ですが、一度日足ぐらいまで見たものを作ってみたいと思います。



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

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


コメント

  1. 名無し より:

    いつも参考にさせていただいております。
    ZigZagを複数の足において取得しようとすると必ずarray out of rangeとなってしまい、
    エラーについて調べてもイマイチどこが悪いのか気づけません。具体的には
    15分足と1H足について取得しようとしたさい、1H足はエラーとなり取得できない形です。
    もし原因など思いつきましたらご教授おねがいできませんでしょうか?

    • りょう りょう より:

      返信が遅れてしまい申し訳ありません。

      サンプルソースの通り5分足と15分足ならエラーにならないけど、15分足と1時間足だとエラーになるという事でしょうか?

      考えられることとしては、1時間足のテストデータが足りない(テスト開始時より200本前にさかのぼれない)かな?と思いますがテスト期間を最近だけにしても同じような問題がおきますでしょうか?