EAでトレンドラインとZigZagを使う(サンプルソース)

EA
スポンサーリンク

FXの売買タイミングとして使うトレンドラインとジグザグ(ZigZag)を組み合わせた、簡単なMT4のEAサンプルです。

概要

今回のメインはトレンドラインを使ったEAです。

トレンドラインは始点と終点を決める必要がありますが、今回はZigZagの山の頂点(谷の頂点)を結んでトレンドラインを引き、それを上抜けたり下抜けたりした場合にエントリーしていくEAのサンプルを載せています。

エントリータイミング

ロングエントリー

ローソク足の終値が3Pipsを超えてトレンドライン(上)を上抜けた場合、ロングしています。

ただし、ローソク足の終値がトレンドライン(下)より下の場合、ローソク足の終値がトレンドライン(上)から20pipsを超える上抜けの場合はエントリーしません。

ショートエントリー

ローソク足の終値が3Pipsを超えてトレンドライン(下)を下抜けた場合、ショートしています。

ただし、ローソク足の終値がトレンドライン(上)より上の場合、ローソク足の終値がトレンドライン(下)から20pipsを超える下抜けの場合はエントリーしません。

イメージ図

決済タイミング

ロングエントリー時

トレンドライン(上)を4pipsを超えて下抜けた場合や、トレンドライン(下)を下抜けた場合に決済します。

ショートエントリー時

トレンドライン(下)を4pipsを超えて上抜けた場合や、トレンドライン(上)を上抜けた場合に決済します。

その他の仕様

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

  1. E_Depth = 16 ⇒ 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. TaikiChi= 12⇒ 最新のZigZagの頂点ができてからトレンドラインを引くまでのタイムラグです(1にするとすぐ線を引き直します)
  7. TP = 200 ⇒ 利確幅

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

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

EAレポート結果

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

2021年~2023年現在の期間の15分足でバックテスト確認してみました。こう見ると良さそうに見えますが、これ以前の期間だと今の売買ルールではあまり機能しませんでした。

今回はトレンドラインの処理を作る目的で作ったため、今後もう少しいい売買ルールも考えていきたいと思います。

ダウンロード

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

“トレンドラインEA” をダウンロード

22_TrendLineZig.ex4 – 2165 回のダウンロード – 19.29 KB

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

ソースコード

//+------------------------------------------------------------------+
//|                                              22_TrendLineZig.mq4 |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict

input int E_Depth = 16;
input int E_Deviation = 5;
input int E_Backstep = 3;

input int TaikiChi = 12; //ライン引き待ち数

input int A_SPREAD = 20; // 許容スプレッド
input double Lots = 0.01; // 取引量(ロット数)

double ZigTop[5];     //ジグザグの山保存用
double ZigBottom[5];  //ジグザグの谷保存用
int ZigTopIdx[5];     //ジグザグの山インデックス保存用
int ZigBottomIdx[5];  //ジグザグの谷インデックス保存用

int TopPoint;
int BottomPoint;

datetime prevtime;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
  
// エントリー関数
void OnTick()
{
int orderPtn=0; //0:何もしない 1:買い 2:売り
int total=0;

double ea_order_stop_price=0,ea_order_good_price=0; //ストップロスレート,利確レート,エントリーレート
bool OrderKekka;

// トレンドラインの2つのポイント用変数
int first_high_index;
double first_high_price;
int second_high_index;
double second_high_price;
   
int first_botm_index;
double first_botm_price;
int second_botm_index;
double second_botm_price;


   //新しい足ができた時だけやる
   if(Time[0] != prevtime){
      prevtime = Time[0];
   }else{
      return;
   }
   
   //チェック用変数の初期化
   TopPoint=0;
   BottomPoint=0;
   
   //ZigZagの高値安値を構造体に再セット
   getZigZag(TaikiChi);
    
   //トレンドライン(上)ポイントの設定
   if(ZigTop[2] <  ZigTop[1]){
      first_high_index = ZigTopIdx[1];
      first_high_price = ZigTop[1];
   }else{
      first_high_index = ZigTopIdx[2];
      first_high_price = ZigTop[2];
   }

   second_high_index = ZigTopIdx[0];
   second_high_price = ZigTop[0];

   //トレンドライン(下)ポイントの設定
   if(ZigBottom[2] >  ZigBottom[1]){
      first_botm_index = ZigBottomIdx[1];
      first_botm_price = ZigBottom[1];
   }else{
      first_botm_index = ZigBottomIdx[2];
      first_botm_price = ZigBottom[2];
   }
   
   second_botm_index = ZigBottomIdx[0];
   second_botm_price = ZigBottom[0];
   
   //トレンドライン(上)ポイントの描画
   string obj_name = "Trendline_T";
   int    chart_id = 0;
   
   //以下先程のオブジェクト描画、初期化関数
   ObjectDelete(chart_id,obj_name);
    
   ObjectCreate(chart_id,obj_name,OBJ_TREND,0,Time[first_high_index], first_high_price, Time[second_high_index], second_high_price);      

   // トレンドラインのプロパティを設定する
   ObjectSetInteger(chart_id,obj_name,OBJPROP_COLOR,clrYellow);    // ラインの色設定
   ObjectSetInteger(chart_id,obj_name,OBJPROP_STYLE,STYLE_SOLID);  // ラインのスタイル設定
   ObjectSetInteger(chart_id,obj_name,OBJPROP_WIDTH,2);              // ラインの幅設定
   ObjectSetInteger(chart_id,obj_name,OBJPROP_BACK,false);           // オブジェクトの背景表示設定
   ObjectSetInteger(chart_id,obj_name,OBJPROP_SELECTABLE,true);     // オブジェクトの選択可否設定
   ObjectSetInteger(chart_id,obj_name,OBJPROP_SELECTED,true);       // オブジェクトの選択状態
   ObjectSetInteger(chart_id,obj_name,OBJPROP_HIDDEN,true);         // オブジェクトリスト表示設定
   ObjectSetInteger(chart_id,obj_name,OBJPROP_ZORDER,0);     // オブジェクトのチャートクリックイベント優先順位
   ObjectSetInteger(chart_id,obj_name,OBJPROP_RAY_RIGHT,true);      // ラインの延長線(右)

   //トレンドライン(下)ポイントの描画
    obj_name = "Trendline_B";
    chart_id = 0;

     //以下先程のオブジェクト描画、初期化関数
   ObjectDelete(chart_id,obj_name);
    
   ObjectCreate(chart_id,obj_name,OBJ_TREND,0, Time[first_botm_index], first_botm_price, Time[second_botm_index], second_botm_price);      

   // トレンドラインのプロパティを設定する
   ObjectSetInteger(chart_id,obj_name,OBJPROP_COLOR,clrYellow);    // ラインの色設定
   ObjectSetInteger(chart_id,obj_name,OBJPROP_STYLE,STYLE_SOLID);  // ラインのスタイル設定
   ObjectSetInteger(chart_id,obj_name,OBJPROP_WIDTH,2);              // ラインの幅設定
   ObjectSetInteger(chart_id,obj_name,OBJPROP_BACK,false);           // オブジェクトの背景表示設定
   ObjectSetInteger(chart_id,obj_name,OBJPROP_SELECTABLE,true);     // オブジェクトの選択可否設定
   ObjectSetInteger(chart_id,obj_name,OBJPROP_SELECTED,true);       // オブジェクトの選択状態
   ObjectSetInteger(chart_id,obj_name,OBJPROP_HIDDEN,true);         // オブジェクトリスト表示設定
   ObjectSetInteger(chart_id,obj_name,OBJPROP_ZORDER,0);     // オブジェクトのチャートクリックイベント優先順位
   ObjectSetInteger(chart_id,obj_name,OBJPROP_RAY_RIGHT,true);      // ラインの延長線(右)


   // 引いたトレンドラインの現在の価格を取得する
   double trendlinePrice_T = NormalizeDouble(ObjectGetValueByShift("Trendline_T", 0),5);
   double trendlinePrice_B = NormalizeDouble(ObjectGetValueByShift("Trendline_B", 0),5);


   //トレンドラインを上ブレイクでロング
   if(Ask > trendlinePrice_T + 30 * Point && Ask > trendlinePrice_B && Ask < trendlinePrice_T + 200 * Point)
   {
      orderPtn=1;
   }
   //高値切り下げ安値切り下げでショート 
   else if(Bid < trendlinePrice_B - 30 * Point && Bid < trendlinePrice_T && Bid > trendlinePrice_B - 200 * Point)
   {
      orderPtn=2;
   }
   else
   {
      orderPtn=0;
   }


   total = OrdersTotal();
   if(total ==0 && orderPtn > 0)   
   {
      if(orderPtn == 1)
      {
         ea_order_stop_price  = Ask - 300 * Point; // ストップロス
         ea_order_good_price  = Ask + 1000 * Point; // 利益確定      
      }
      else if(orderPtn == 2)
      {
         ea_order_stop_price  = Bid + 300 * Point; // ストップロス
         ea_order_good_price  = Bid - 1000*Point; // 利益確定
      }   
         
      //新規注文
      OrderKekka = funcOrder_Send(orderPtn - 1,ea_order_stop_price,ea_order_good_price,"Trend Line",0);
         
   }

//***売判断箇所***//
   if(OrdersTotal() > 0){

      OrderKekka = OrderSelect(0,SELECT_BY_POS,MODE_TRADES);
       
      //エンベロープの決済判断値よりローソク足の終値が下だった場合、決済(ロング時)
      if(OrderType() == OP_BUY && (Ask < trendlinePrice_T - 40 * Point || Ask < trendlinePrice_B)){
         OrderKekka = funcOrder_Close();
      }
      //エンベロープの決済判断値よりローソク足の終値が上だった場合、決済(ショート時)
      else if(OrderType() == OP_SELL && (Bid > trendlinePrice_T|| Bid > trendlinePrice_B + 40 * Point )){
         OrderKekka = funcOrder_Close();
      }
      
   }

}

//+------------------------------------------------------------------+
//|【関数】新規注文関数                                                 |
//|                                                                  |
//|【引数】 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,string orderComment,int ea_order_MagicNo)
{
           
   int order_resend_num;        // エントリー試行回数
   int ea_ticket_res;           // チケットNo
   int errorcode;               // エラーコード
   double ea_order_entry_price=0; // エントリーレート
   color order_Color=0;
   bool kekka = false;
              
   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               // オーダーアイコンカラー
            );
  
         Print("結果:"+ IntegerToString(ea_ticket_res,10));
  
         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 許容スプレッド:"+ IntegerToString(A_SPREAD,5)+"pips未満 環境スプレッド:"+DoubleToString(MarketInfo(NULL,MODE_SPREAD),5)+"pips");
         Sleep(2000);                                           // 2000msec待ち
         RefreshRates();     
      }
                       
  
   }
   return kekka;   
}
//+------------------------------------------------------------------+
//|【関数】決済関数
//|
//|【引数】 なし
//|
//|【戻値】True:成功 False:失敗
//|
//|
bool funcOrder_Close()
{
   bool kekka,OrderKekka;
   int total,order_resend_num;
   double ea_order_entry_price = 0; // エントリーレート
   color order_Color = 0;
     
   kekka=false;
  
   for(order_resend_num = 0; order_resend_num < 100; order_resend_num++ ) {
      if(MarketInfo(NULL,MODE_SPREAD) < A_SPREAD){
  
         total=OrdersTotal();
           
         if(total >0 && OrderSelect(0,SELECT_BY_POS,MODE_TRADES)==true){
  
            if(OrderType() == OP_BUY){   
               ea_order_entry_price = Bid;               // 現在の売値で決済
               order_Color = clrLightBlue;
            }else if(OrderType() == OP_SELL){
               ea_order_entry_price = Ask;               // 現在の買値で決済
               order_Color = clrLightPink;            
            }
  
            OrderKekka = OrderClose(OrderTicket(),OrderLots(),ea_order_entry_price,10,order_Color);
  
            if(OrderKekka==false){
               Print("決済失敗 エラーNo=",GetLastError()," リトライ回数=",order_resend_num+1);
                 
               RefreshRates();
               Sleep(1000);
            }else{
               Print("決済注文約定。 レート=",ea_order_entry_price);
              
               kekka=true;
               break;
            }
         }
      }else{
          Print("スプレッド拡大中(銭)=",MarketInfo(NULL,MODE_SPREAD) / 10," リトライ回数=",order_resend_num+1);
            
          Sleep(5000);                                           // msec待ち
          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;
         }
         
         ZigTopIdx[m]=n;
         ZigTop[m++]=Zg;

         if(m>=4)break;
      }
            
      //ZigZagの値と最安値が同じ場合、底なのでZigBottomにセット            
      if(Zg!=0 && Zg==NormalizeDouble(Low[n],5))
      {
         if(n==1){
            BottomPoint=1;
         }
         ZigBottomIdx[t]=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);   
        
}

アレンジポイント①

アレンジポイントとしては、今回ZigZagの頂点を始点と終点としているためこの部分を自分好みにアレンジすると違ったエントリーポイントが作れます。(83~105行目)

アレンジポイント②

ロングエントリー条件(154行目)や、ショートエントリー条件(159行目)を変えるのも有りです。

アレンジポイント③

ロング決済条件(194行目)や、ショート決済条件(199行目)を変えるのも有りです。

主な構文

ObjectCreate()

ObjectCreate()関数を使いトレンドラインを引いています。トレンドラインになっているのは第三引数をOBJ_TRENDとしているためです。


※以下のような感じでOBJ_CHANNELにするとチャネルラインが引けます

    ObjectCreate(chart_id,obj_name,                                     // オブジェクト作成
                 OBJ_CHANNEL,                                           // オブジェクトタイプ
                 0,                                                       // サブウインドウ番号
                  Time[first_high_index], 
                  first_high_price, 
                  Time[second_high_index], 
                  second_high_price,
                  Time[first_botm_index], 
                  first_botm_price
                 );  

また、ローソク足が出来る毎にトレンドラインを引き直しているので、バックテストで見てみると分かりますがかなり流動的にトレンドラインが引かれている事がわかります。

引きっぱなしだと線が増える一方なので、ObjectDelete()関数で随時トレンドラインを削除してから引き直しています。(112行目~145行目で上下のトレンドラインを引いています)

ObjectGetValueByShift()関数

ObjectGetValueByShift()関数は、主にグラフィックオブジェクト(水平線、垂直線、トレンドラインなど)の値を取得するために使用されます。今回はこれを使ってトレンドライン上の価格情報を取得しています。(148行目~149行目)

引数は以下の通りです。

  1. オブジェクト名
  2. バーシフト

さいごに

以上、トレンドラインとZigZagを使ったEAのサンプルソースでした。

オブジェクト(トレンドライン)を自分で引いて取引するEAは初だったので中々難しかったです。チャネルラインでもチャレンジしたのですが、付随する線(水平になってくれる線)の価格情報を取る手段が見つからず断念しました。現状だとトレンドラインを水平になるように引いて疑似チャネルラインとしるしか方法がないのかなと思っています。(いい方法あったら教えてください)

どの始点と終点でトレンドラインを引くかや、どのタイミングでトレンドラインを引き直すかによってトレード内容もガラりと変わってくるので、トレンドライン取引に興味のある方は是非色々試してみて下さい。

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

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


コメント

  1. ぱらです より:

    わたくしも、作った事が有りますが勝てませんわ。
    わたくしの作品、
    https://www.gogojungle.co.jp/systemtrade/fx/14082
    チャネルラインの作品、待ってまぁ~す。

    • りょう りょう より:

      ぱらですさん、はじめまして。
      お作りになったEAもZigZag使われてるんですね!トレンドラインで勝負するのは難しいんですかね・・・。
      チャネルラインは、ChatGPT頼みで頑張ってみま~す。