ホーム » FX自動売買基礎と応用 » MT5版のレジサポラインツールを作成する【MQLプログラミングの基礎】

MT5版のレジサポラインツールを作成する【MQLプログラミングの基礎】

1.ファイルの新規作成

この記事では、チャート上にレジスタンスラインとサポートラインの候補となるラインを表示するツールを作成します。

※この記事はMQL5の内容です。MQL4に対応した類似記事は以下を参照してください。

レジスタンスラインとサポートラインの候補となるラインを表示する方法

レジスタンス・サポートの価格を決める条件として、MT5に標準で入っている、ビル・ウィリアムズ系のインジケーター「Fractals」を利用します。

MetaEditorの「新規作成」アイコンをクリックしてMQLウィザードを開始します。

MQLウィザード新規作成

「カスタムインディケータ」を選択して「次へ」をクリック。

一般プロパティ

ファイル名を「FractalsLine.mq5」とします。
今回のツールでは、計算するバーの本数を限定したいので、「BARS」という名前でパラメーターを追加し、初期値を「1000」に設定します。

イベントハンドラ

イベントハンドラは一番上のOnCalculateのみを選択して「次へ」をクリック。

描画プロパティ

「カスタムインディケータの描画プロパティ」では、レジスタンスとサポートラインを表示するためのバッファを登録します。

レジスタンスライン用(上側)を「UPPER」、サポートライン用(下側)を「LOWER」、タイプを「Arrow」、カラーを「Red」「DodgerBlue」とし、「完了」ボタンをクリックすると、ファイルのベースが作成されます。

2.インジケーターバッファの設定

インジケーターで表示させるマークを変更するために、OnInit関数内にある下記の行の「159」を、小さい点が表示される「158」へ変更します。


   PlotIndexSetInteger(0,PLOT_ARROW,159);
   PlotIndexSetInteger(1,PLOT_ARROW,159);

158等のコード番号を指定することで、特殊文字フォント「Wingdings」に相当するマークが表示されます。

インジケーター用バッファの配列の並びを、0番が最新となるように、「 PlotIndexSetInteger(1,PLOT_ARROW,158);」という行の下へ、下記の行を追記します。


   ArraySetAsSeries(UPPERBuffer, true);
   ArraySetAsSeries(LOWERBuffer, true);

3.iFractals関数で頂点情報を取得

iFractals関数を利用するために、ハンドル用の変数を定義します。

double LOWERBuffer[];」という行の下に、下記の行を追記します。


int handle;

定義した変数「handle」に、iFractals関数を入力します。

OnInit関数内の「 return(INIT_SUCCEEDED);」という行の上に、下記の行を追記します。


   handle = iFractals(NULL, 0);

OnCalculate関数内に、計算範囲を決める変数「limit」を定義します。


   int limit = MathMin(ArraySize(UPPERBuffer) - 3, BARS);

下記のfor文を追加し、Fractalsの頂点情報を取得します。


   for (int i = limit; i >= 0; i--) {
      double upper[], lower[];
      ArraySetAsSeries(upper, true);
      ArraySetAsSeries(lower, true);
      CopyBuffer(handle, 0, i, 1, upper);
      CopyBuffer(handle, 1, i, 1, lower);
   } 

double upper[], lower[]」の行で、取得したデータを書き込む配列を定義しています。

CopyBuffer関数を利用して、各ローソク足のiFractals関数の値を配列にコピーしています。
CopyBuffer(handle, 0, i, 1, upper); 」で上側の点をupper[0]へコピーし、「CopyBuffer(handle, 1, i, 1, lower); 」で下側の点をlower[0]へコピーしています。
handleに登録されているiFractals関数は、1つ目(0番)の情報として上側の点、2つ目(1番)の情報として下側の点を持ちます。
CopyBuffer(handle, 0, i, 1, upper); 」の関数内の引数の内容を順に説明すると、下記の通りとなります。
「handle(iFractals関数)の0番の情報のi本目の足の値1つを配列upper[]にコピーする。」

まずは、取得した情報をそのままインジケーターバッファで表示させるため、下記の行をfor文内(「CopyBuffer(handle, 1, i, 1, lower);」の下)に追記します。


      UPPERBuffer[i] = upper[0];
      LOWERBuffer[i] = lower[0];

ここまでの状態でコンパイルし、チャートに本インジケーターをセットするとFractalsインジケーターと同じ場所に、赤と青の点が表示されていることを確認できます。

上下点をそのまま表示

上記画像では、点を目視しやすいように、サイズ(幅)を「3」に変更して表示させています。

4.インジケーターバッファの条件設定

頂点情報を並べて点線表示とするため、下記の行を


      UPPERBuffer[i] = upper[0];
      LOWERBuffer[i] = lower[0];

次のように変更します。


      if (upper[0] != EMPTY_VALUE && i >= 3) UPPERBuffer[i] = upper[0];
      else UPPERBuffer[i] = UPPERBuffer[i + 1];
      if (lower[0] != EMPTY_VALUE && i >= 3) LOWERBuffer[i] = lower[0];
      else LOWERBuffer[i] = LOWERBuffer[i + 1];

取得したFractalsの頂点情報(upper[0]、lower[0])の値が空(EMPTY_VALUE)でない場合は、その値を頂点情報としてバッファに入力し、空の場合は1本前の足の情報を維持する回路となっています。
確定したFractals情報のみを扱う仕様とするため、if文の判定に「i >= 3」という式を入れて、最新部の3本の足は計算範囲から除外しています。

OnCalculate関数で定義されている終値の配列close[]を判定で利用するため、下記の行をfor文の前へ追記します。


   ArraySetAsSeries(close, true);

ローソク足の終値によってラインがブレイクされたときに、ラインがそこで停止するよう、下記の式を「else LOWERBuffer[i] = LOWERBuffer[i + 1];」の後へ追記します。


      if (UPPERBuffer[i] != EMPTY_VALUE && close[i] > UPPERBuffer[i]) UPPERBuffer[i] = EMPTY_VALUE;
      if (LOWERBuffer[i] != EMPTY_VALUE && close[i] < LOWERBuffer[i]) LOWERBuffer[i] = EMPTY_VALUE;

上記までの状態で、バッファを利用したライン表示機能までは完成です。

コンパイルしたときの表示は、下記のようになります。

ドットで水平線描画

5.トレンドラインオブジェクトでの延長

現状では、新たな次のラインが発生すると、ラインがブレイクされていなくても、旧ラインはそこで途切れてしまいます。

以降の作業で、ブレイクされるまではラインが延長される機能を、トレンドラインオブジェクトを利用して追加します。

MetaEditorメニューの「ヘルプ」から「MQL5リファレンス」を開いて「目次」タブを選択し、「標準的な定数、列挙と構造体」→「オブジェクト定数」→「オブジェクト型」にある「OBJ_TREND」を選択してください。

MQLリファレンスOBJ_TREND

OBJ_TREND内の「//| 与えられた座標で傾向線を作成する」以下にある「TrendCreate」関数をコピーして、今回のプログラムの最下部へ貼り付けます。

――――― コピーして貼り付けするコード(ここから) ―――――


//+------------------------------------------------------------------+ 
//| 与えられた座標で傾向線を作成する                                         | 
//+------------------------------------------------------------------+ 
bool TrendCreate(const long            chart_ID=0,       // チャート識別子 
                const string          name="TrendLine", // 線の名称 
                const int             sub_window=0,     // サブウィンドウ番号 
                datetime              time1=0,           // 1 番目のポイントの時間 
                double                price1=0,         // 1 番目のポイントの価格 
                datetime              time2=0,           // 2 番目のポイントの時間 
                double                price2=0,         // 2 番目のポイントの価格 
                const color           clr=clrRed,       // 線の色 
                const ENUM_LINE_STYLE style=STYLE_SOLID, // 線のスタイル 
                const int             width=1,           // 線の幅 
                const bool            back=false,       // 背景で表示する 
                const bool            selection=true,   // 強調表示して移動 
                const bool            ray_left=false,   // 線の左への継続 
                const bool            ray_right=false,   // 線の右への継続 
                const bool            hidden=true,       // オブジェクトリストに隠す 
                const long            z_order=0)         // マウスクリックの優先順位 
 { 
//--- 設定されてない場合アンカーポイントの座標を設定する 
  ChangeTrendEmptyPoints(time1,price1,time2,price2); 
//--- エラー値をリセットする 
  ResetLastError(); 
//--- 与えられた座標で傾向線を作成する 
  if(!ObjectCreate(chart_ID,name,OBJ_TREND,sub_window,time1,price1,time2,price2)) 
    { 
    Print(__FUNCTION__, 
          ": failed to create a trend line! Error code = ",GetLastError()); 
    return(false); 
    } 
//--- 線の色を設定する 
  ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr); 
//--- 線の表示スタイルを設定する 
  ObjectSetInteger(chart_ID,name,OBJPROP_STYLE,style); 
//--- 線の幅を設定する 
  ObjectSetInteger(chart_ID,name,OBJPROP_WIDTH,width); 
//--- 前景(false)または背景(true)に表示 
  ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back); 
//--- マウスで線を移動させるモードを有効(true)か無効(false)にする 
//--- ObjectCreate 関数を使用してグラフィックオブジェクトを作成する際、オブジェクトは 
//--- デフォルトではハイライトされたり動かされたり出来ない。このメソッド内では、選択パラメータは 
//--- デフォルトでは true でハイライトと移動を可能にする。 
  ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection); 
  ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection); 
//--- 線の表示を左に延長するモードを有効(true)か無効(false)にする 
  ObjectSetInteger(chart_ID,name,OBJPROP_RAY_LEFT,ray_left); 
//--- 線の表示を右に延長するモードを有効(true)か無効(false)にする 
  ObjectSetInteger(chart_ID,name,OBJPROP_RAY_RIGHT,ray_right); 
//--- オブジェクトリストのグラフィックオブジェクトを非表示(true)か表示(false)にする 
  ObjectSetInteger(chart_ID,name,OBJPROP_HIDDEN,hidden); 
//--- チャートのマウスクリックのイベントを受信するための優先順位を設定する 
  ObjectSetInteger(chart_ID,name,OBJPROP_ZORDER,z_order); 
//--- 実行成功 
  return(true); 
 }

――――― コピーして貼り付けするコード(ここまで) ―――――

上記の流用コードから、今回利用しない下記の行を削除します。


//--- 設定されてない場合アンカーポイントの座標を設定する 
  ChangeTrendEmptyPoints(time1,price1,time2,price2);

オブジェクトの名称の頭に共通の文字列を付けて管理するために、接頭辞を定義します。
ファイル上部のプロパティ「#property indicator_width2 1」の下に、下記を追記します。


#define PREFIX "FractalsLine_"

インジケーターの削除や再読み込み時に、トレンドラインオブジェクトを一括消去するため、OnDeinit関数を追加します。
下記をOnInit関数の下などへ追加してください(他の関数の中でなければ、どこへ追加しても構いません)。


void OnDeinit(const int reason)
{
   ObjectsDeleteAll(0, PREFIX);
}

トレンドラインを描画する際、OnCalculate関数で定義されている終値の配列time[]を利用したいので、下記の行をfor文の前へ追記します。


   ArraySetAsSeries(time, true);

OnCalculate関数のfor文内に、上側ラインの延長したい場所の判定をするため、下記を追記します。


      if (UPPERBuffer[i] != EMPTY_VALUE && UPPERBuffer[i + 1] != EMPTY_VALUE && UPPERBuffer[i] < UPPERBuffer[i + 1]) {
         
      }

その足および1本前の足の両方に値があり(空ではない)、1本前の足のラインの方が上にあるという判定になっています。
新たな上側ラインが発生したときに条件が成立することになります。
延長するラインの終点(右側の端点)の位置を決めるために、上記if文内に下記を追記します。


         int iEnd = 0;
         for (int j = i - 1; j >= 0; j--){
            if (close[j] > UPPERBuffer[i + 1]){
               iEnd = j;
               break;
            }
         }

下記の行を追記し、トレンドラインで上側ラインを描画します。


         string name = PREFIX + "U_" + TimeToString(time[i], TIME_DATE | TIME_MINUTES);
         TrendCreate(0, name, 0, time[i], UPPERBuffer[i + 1], time[iEnd], UPPERBuffer[i + 1], clrRed, STYLE_SOLID, 2, false, false);

トレンドラインの名称を、nameで定義しています。
始点の時間を文字列に変換し、名前として利用しています。
上記の追記で、赤の太さ2の実線がトレンドラインで描画されるようになります。
同様の方法で下側ラインを描画するため、下記を追記します。


      if (LOWERBuffer[i] != EMPTY_VALUE && LOWERBuffer[i + 1] != EMPTY_VALUE && LOWERBuffer[i] > LOWERBuffer[i + 1]) {
         int iEnd = 0;
         for (int j = i - 1; j >= 0; j--){
            if (close[j] < LOWERBuffer[i + 1]){
               iEnd = j;
               break;
            }
         }
         string name = PREFIX + "L_" + TimeToString(time[i], TIME_DATE | TIME_MINUTES);
         TrendCreate(0, name, 0, time[i], LOWERBuffer[i + 1], time[iEnd], LOWERBuffer[i + 1], clrDodgerBlue, STYLE_SOLID, 2, false, false);
      }

上記までの作業で完成となります。
コンパイルすると、トレンドラインで延長される表示が確認できます。

トレンドライン追加

6.ソースコード

今回、作成したソースコードは下記の通りです。


//+------------------------------------------------------------------+
//|                                                 FractalsLine.mq5 |
//|                                  Copyright 2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots   2
//--- plot UPPER
#property indicator_label1  "UPPER"
#property indicator_type1   DRAW_ARROW
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- plot LOWER
#property indicator_label2  "LOWER"
#property indicator_type2   DRAW_ARROW
#property indicator_color2  clrDodgerBlue
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1
#define PREFIX "FractalsLine_"
//--- input parameters
input int      BARS=1000;
//--- indicator buffers
double         UPPERBuffer[];
double         LOWERBuffer[];
int handle;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,UPPERBuffer,INDICATOR_DATA);
   SetIndexBuffer(1,LOWERBuffer,INDICATOR_DATA);
//--- setting a code from the Wingdings charset as the property of PLOT_ARROW
   PlotIndexSetInteger(0,PLOT_ARROW,158);
   PlotIndexSetInteger(1,PLOT_ARROW,158);
   ArraySetAsSeries(UPPERBuffer, true);
   ArraySetAsSeries(LOWERBuffer, true);
   
   handle = iFractals(NULL, 0);
//---
   return(INIT_SUCCEEDED);
  }

void OnDeinit(const int reason)
{
   ObjectsDeleteAll(0, PREFIX);
}

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int32_t rates_total,
                const int32_t prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int32_t &spread[])
  {
//---
   ArraySetAsSeries(close, true);
   ArraySetAsSeries(time, true);
   int limit = MathMin(ArraySize(UPPERBuffer) - 3, BARS);
   for (int i = limit; i >= 0; i--) {
      double upper[], lower[];
      ArraySetAsSeries(upper, true);
      ArraySetAsSeries(lower, true);
      CopyBuffer(handle, 0, i, 1, upper);
      CopyBuffer(handle, 1, i, 1, lower);
      
      if (upper[0] != EMPTY_VALUE && i >= 3) UPPERBuffer[i] = upper[0];
      else UPPERBuffer[i] = UPPERBuffer[i + 1];
      if (lower[0] != EMPTY_VALUE && i >= 3) LOWERBuffer[i] = lower[0];
      else LOWERBuffer[i] = LOWERBuffer[i + 1];
      
      if (UPPERBuffer[i] != EMPTY_VALUE && close[i] > UPPERBuffer[i]) UPPERBuffer[i] = EMPTY_VALUE;
      if (LOWERBuffer[i] != EMPTY_VALUE && close[i] < LOWERBuffer[i]) LOWERBuffer[i] = EMPTY_VALUE;
      
      if (UPPERBuffer[i] != EMPTY_VALUE && UPPERBuffer[i + 1] != EMPTY_VALUE && UPPERBuffer[i] < UPPERBuffer[i + 1]) {
         int iEnd = 0;
         for (int j = i - 1; j >= 0; j--){
            if (close[j] > UPPERBuffer[i + 1]){
               iEnd = j;
               break;
            }
         }
         string name = PREFIX + "U_" + TimeToString(time[i], TIME_DATE | TIME_MINUTES);
         TrendCreate(0, name, 0, time[i], UPPERBuffer[i + 1], time[iEnd], UPPERBuffer[i + 1], clrRed, STYLE_SOLID, 2, false, false);
      }
      
      if (LOWERBuffer[i] != EMPTY_VALUE && LOWERBuffer[i + 1] != EMPTY_VALUE && LOWERBuffer[i] > LOWERBuffer[i + 1]) {
         int iEnd = 0;
         for (int j = i - 1; j >= 0; j--){
            if (close[j] < LOWERBuffer[i + 1]){
               iEnd = j;
               break;
            }
         }
         string name = PREFIX + "L_" + TimeToString(time[i], TIME_DATE | TIME_MINUTES);
         TrendCreate(0, name, 0, time[i], LOWERBuffer[i + 1], time[iEnd], LOWERBuffer[i + 1], clrDodgerBlue, STYLE_SOLID, 2, false, false);
      }
   }
//--- return value of prev_calculated for next call
   return(rates_total);
  }

//+------------------------------------------------------------------+ 
//| 与えられた座標で傾向線を作成する                                         | 
//+------------------------------------------------------------------+ 
bool TrendCreate(const long            chart_ID=0,       // チャート識別子 
                const string          name="TrendLine", // 線の名称 
                const int             sub_window=0,     // サブウィンドウ番号 
                datetime              time1=0,           // 1 番目のポイントの時間 
                double                price1=0,         // 1 番目のポイントの価格 
                datetime              time2=0,           // 2 番目のポイントの時間 
                double                price2=0,         // 2 番目のポイントの価格 
                const color           clr=clrRed,       // 線の色 
                const ENUM_LINE_STYLE style=STYLE_SOLID, // 線のスタイル 
                const int             width=1,           // 線の幅 
                const bool            back=false,       // 背景で表示する 
                const bool            selection=true,   // 強調表示して移動 
                const bool            ray_left=false,   // 線の左への継続 
                const bool            ray_right=false,   // 線の右への継続 
                const bool            hidden=true,       // オブジェクトリストに隠す 
                const long            z_order=0)         // マウスクリックの優先順位 
 { 

//--- エラー値をリセットする 
  ResetLastError(); 
//--- 与えられた座標で傾向線を作成する 
  if(!ObjectCreate(chart_ID,name,OBJ_TREND,sub_window,time1,price1,time2,price2)) 
     { 
     Print(__FUNCTION__, 
           ": failed to create a trend line! Error code = ",GetLastError()); 
     return(false); 
     } 
//--- 線の色を設定する 
  ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr); 
//--- 線の表示スタイルを設定する 
  ObjectSetInteger(chart_ID,name,OBJPROP_STYLE,style); 
//--- 線の幅を設定する 
  ObjectSetInteger(chart_ID,name,OBJPROP_WIDTH,width); 
//--- 前景(false)または背景(true)に表示 
  ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back); 
//--- マウスで線を移動させるモードを有効(true)か無効(false)にする 
//--- ObjectCreate 関数を使用してグラフィックオブジェクトを作成する際、オブジェクトは 
//--- デフォルトではハイライトされたり動かされたり出来ない。このメソッド内では、選択パラメータは 
//--- デフォルトでは true でハイライトと移動を可能にする。 
  ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection); 
   ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection); 
//--- 線の表示を左に延長するモードを有効(true)か無効(false)にする 
  ObjectSetInteger(chart_ID,name,OBJPROP_RAY_LEFT,ray_left); 
//--- 線の表示を右に延長するモードを有効(true)か無効(false)にする 
  ObjectSetInteger(chart_ID,name,OBJPROP_RAY_RIGHT,ray_right); 
//--- オブジェクトリストのグラフィックオブジェクトを非表示(true)か表示(false)にする 
  ObjectSetInteger(chart_ID,name,OBJPROP_HIDDEN,hidden); 
//--- チャートのマウスクリックのイベントを受信するための優先順位を設定する 
  ObjectSetInteger(chart_ID,name,OBJPROP_ZORDER,z_order); 
//--- 実行成功 
  return(true); 
  } 
//+------------------------------------------------------------------+

本記事の監修者・HT FX

2013年にFXを開始し、その後専業トレーダーへ。2014年からMT4/MT5のカスタムインジケーターの開発に取り組む。ブログでは100本を超えるインジケーターを無料公開。投資スタイルは自作の秒足インジケーターを利用したスキャルピング。

EA(自動売買)を学びたい方へオススメコンテンツ

EA運用の注意点

OANDAではEA(自動売買)を稼働するプラットフォームMT4/MT5の基本的な使い方について、画像や動画付きで詳しく解説しています。MT4/MT5のインストールからEAの設定方法までを詳しく解説しているので、初心者の方でもスムーズにEA運用を始めることが可能です。またOANDAの口座をお持ちであれば、独自開発したオリジナルインジケーターを無料で利用することもできます。EA運用をお考えであれば、ぜひ口座開設をご検討ください。


本ホームページに掲載されている事項は、投資判断の参考となる情報の提供を目的としたものであり、投資の勧誘を目的としたものではありません。投資方針、投資タイミング等は、ご自身の責任において判断してください。本サービスの情報に基づいて行った取引のいかなる損失についても、当社は一切の責を負いかねますのでご了承ください。また、当社は、当該情報の正確性および完全性を保証または約束するものでなく、今後、予告なしに内容を変更または廃止する場合があります。なお、当該情報の欠落・誤謬等につきましてもその責を負いかねますのでご了承ください。

この記事をシェアする