FX自動売買基礎と応用

ティックチャートと1分足を重ねて表示する自動売買の作成方法


ティック情報と時間情報を格納するバッファーを追加


この記事では、サブウィンドウにティックチャートと1分足のローソク足を重ねて表示する方法を解説します。まずはティックチャートを表示できるようにしましょう。

ファイルの新規作成で「カスタムインディケータ」を選択し、ファイル名を「Tick_and_Bar」とします。パラメーターは、計算するバーの本数を指定するようにします。名前は「BARS」、初期値は「1000」でOKです。

パラメーターは計算するバーの本数を指定

「カスタムインディケータのイベントハンドラ」の画面では、「OnTimer」「OnChartEvent」のどちらにもチェックを入れずに次へ進み、「カスタムインディケータの描画プロパティ」画面のプロット欄で、データを格納する二つのバッファーを登録します。

一つはティックの情報を格納するバッファーでラベルを「Tick」、カラーを「White」、もう一つは時間情報を格納するバッファーでラベルを「TimeM1」、カラーを「Red」としましょう。これで「完了」をクリックすれば、ひな形の完成です。

データを格納する二つのバッファーを登録

新規ファイルが開いたら、上部のプロパティを修正します。まず、サブウィンドウに表示するので「indicator_chart_window」を「indicator_separate_window」に変更します。


#property indicator_ separate _window

そして時間情報を記憶する「TimeM1」は表示しないので、タイプ(type)のところを「DRAW_LINE」から「DRAW_NONE」に変え、その下の「color」「style」「width」の3行は削除します。今回はオブジェクトを使うので、まとめて削除などができるように接頭辞「PREFIX」を定義しておきましょう。


#property indicator_type2   DRAW_NONE
#define PREFIX MQLInfoString(MQL_PROGRAM_NAME) + "_"

また、バッファーの名前が長いので「TickBuffer」→「Tick」という具合に該当する箇所の「Buffer」を省いて短くします。


double Tick[];
double TimeM1[];

同様にOnInit関数内で使われているバッファー名も、次のように変更します。


SetIndexBuffer(0,Tick);
SetIndexBuffer(1,TimeM1);


サブウィンドウにティックチャートを表示


続いて、サブウィンドウにティックチャートを表示しましょう。

OnCalculate関数配下に計算式を記述していきます。計算する本数は、指定した数「BARS」または「Bars-2」の小さい方とします。まず「Tick[0]」に「Bid」を入れますが、入れるたびに一つバッファーをずらしていかないと、同じ値がどんどん書き変わってしまいます。そこで、1本前の値を入れるようにしましょう。この1本ずつずらすという処理は、Tick[0]のデータが空ではなかったときに実行するようにします。


   int limit = MathMin(BARS, Bars - 2);
   if (Tick[0] != EMPTY_VALUE) {
      for (int i = limit; i > 0; i--) {
         Tick[i] = Tick[i - 1];
      }
   }
   
   Tick[0] = Bid;

これでコンパイルしてチャートにセットすると、サブウィンドウにティックチャートが表示されることが分かります。

サブウィンドウにティックチャートが表示


「TimeM1」に関する式を追加


同じように時間情報を格納する「TimeM1」についての記述を加えます。「TimeM1[0]」には1分足の時間を格納し、オブジェクトは毎回消すようにします。また、範囲外の値を表す「limit+1」をリセットするようにしておきましょう。


int limit = MathMin(BARS, Bars - 2);
if (Tick[0] != EMPTY_VALUE) {
   for (int i = limit; i > 0; i--) {
      Tick[i] = Tick[i - 1];
      TimeM1[i] = TimeM1[i - 1];
   }
}
Tick[0] = Bid;
TimeM1[0] = (double)iTime(NULL, PERIOD_M1, 0);
Tick[limit + 1] = EMPTY_VALUE;
TimeM1[limit + 1] = EMPTY_VALUE;
ObjectsDeleteAll(0, PREFIX);


1分足の4本値を取得


サブウィンドウにティックチャートを表示し、そこに1分足を重ねる準備が整いました。続いて、バッファーを利用して1分足のローソク足を描画していきます。

まずif文で「TimeM1」の値が空の場合は、処理をしないようにします。次に「iBarShift」で1分足のバー番号を出し、それを使って4本値をそれぞれ取得します。


for (int i = limit; i >= 0; i--) {
   if (TimeM1[i] == EMPTY_VALUE) continue;
   int bar = iBarShift(NULL, PERIOD_M1, (datetime)TimeM1[i]);
   double op = iOpen(NULL, PERIOD_M1, bar);
   double cl = iClose(NULL, PERIOD_M1, bar);
   double hi = iHigh(NULL, PERIOD_M1, bar);
   double lo = iLow(NULL, PERIOD_M1, bar);

ここでローソク足の色も定義しておきましょう。陽線のときは赤、陰線のときは青とします。


color clr = clrWhite;
if (cl > op) clr = clrRed;
else if (cl < op) clr = clrBlue;


オブジェクトのサンプルコードを流用


次に、if文を用いて1分足の情報が前の足と比べて変化したときに新しくバーを描き、前と同じときは足の情報を更新するようにします。


if (TimeM1[i] != TimeM1[i + 1]) {
} else {
}

まず、1分足の情報が前の足と比べて変化したときの処理を記述します。新しくバーを描くにあたり、オブジェクトのトレンドラインとレクタングルを利用するので、サンプルコードをMQL4リファレンスからコピーして使います。

MQL4リファレンスの目次にある「Constants, Enumerations and Structures」→「Objects Constants」→「Object Types」をクリックするとオブジェクトの一覧が表示されるので、その中から「OBJ_TREND」を選択し、あらかじめ用意されている「Create a trend line by the given coordinates」のコードをコピーしてファイル下部に貼り付けます。

同様にオブジェクトの一覧から「OBJ_RECTANGLE」を選択し、「Create rectangle by the given coordinates」のコードをコピーして、ファイル下部に貼り付けましょう。

どちらも背景表示設定にしたいので「// in the background」のところの「back = false」を「back = true」に、セレクションは必要ないので「// highlight to move」のところの「selection = true」を「selection = false」に変更します。

また、トレンドラインの方は「//--- set anchor points' coordinates if they are not set」から「ResetLastError();」までの4行と「Print(__FUNCTION__,」「": failed to create a trend line! Error code = ", GetLastError());」の2行を、レクタングルの方は「//--- set anchor points' coordinates if they are not set」から「ResetLastError();」までの4行と「Print(__FUNCTION__,」「": failed to create a rectangle! Error code = ", GetLastError());」の2行を不要なので削除してください。


//+------------------------------------------------------------------+
//| Create a trend line by the given coordinates                     |
//+------------------------------------------------------------------+
bool TrendCreate(const long            chart_ID = 0,      // chart's ID
                 const string          name = "TrendLine", // line name
                 const int             sub_window = 0,    // subwindow index
                 datetime              time1 = 0,         // first point time
                 double                price1 = 0,        // first point price
                 datetime              time2 = 0,         // second point time
                 double                price2 = 0,        // second point price
                 const color           clr = clrRed,      // line color
                 const ENUM_LINE_STYLE style = STYLE_SOLID, // line style
                 const int             width = 1,         // line width
                 const bool            back = true,      // in the background
                 const bool            selection = false,  // highlight to move
                 const bool            ray_right = false, // line's continuation to the right
                 const bool            hidden = true,     // hidden in the object list
                 const long            z_order = 0)       // priority for mouse click
{
//--- create a trend line by the given coordinates
   if(!ObjectCreate(chart_ID, name, OBJ_TREND, sub_window, time1, price1, time2, price2)) {
      return(false);
   }
//--- set line color
   ObjectSetInteger(chart_ID, name, OBJPROP_COLOR, clr);
//--- set line display style
   ObjectSetInteger(chart_ID, name, OBJPROP_STYLE, style);
//--- set line width
   ObjectSetInteger(chart_ID, name, OBJPROP_WIDTH, width);
//--- display in the foreground (false) or background (true)
   ObjectSetInteger(chart_ID, name, OBJPROP_BACK, back);
//--- enable (true) or disable (false) the mode of moving the line by mouse
//--- when creating a graphical object using ObjectCreate function, the object cannot be
//--- highlighted and moved by default. Inside this method, selection parameter
//--- is true by default making it possible to highlight and move the object
   ObjectSetInteger(chart_ID, name, OBJPROP_SELECTABLE, selection);
   ObjectSetInteger(chart_ID, name, OBJPROP_SELECTED, selection);
//--- enable (true) or disable (false) the mode of continuation of the line's display to the right
   ObjectSetInteger(chart_ID, name, OBJPROP_RAY_RIGHT, ray_right);
//--- hide (true) or display (false) graphical object name in the object list
   ObjectSetInteger(chart_ID, name, OBJPROP_HIDDEN, hidden);
//--- set the priority for receiving the event of a mouse click in the chart
   ObjectSetInteger(chart_ID, name, OBJPROP_ZORDER, z_order);
//--- successful execution
   return(true);
}
//+------------------------------------------------------------------+
//| Create rectangle by the given coordinates                        |
//+------------------------------------------------------------------+
bool RectangleCreate(const long            chart_ID = 0,      // chart's ID
                     const string          name = "Rectangle", // rectangle name
                     const int             sub_window = 0,    // subwindow index
                     datetime              time1 = 0,         // first point time
                     double                price1 = 0,        // first point price
                     datetime              time2 = 0,         // second point time
                     double                price2 = 0,        // second point price
                     const color           clr = clrRed,      // rectangle color
                     const ENUM_LINE_STYLE style = STYLE_SOLID, // style of rectangle lines
                     const int             width = 1,         // width of rectangle lines
                     const bool            fill = false,      // filling rectangle with color
                     const bool            back = true,      // in the background
                     const bool            selection = false,  // highlight to move
                     const bool            hidden = true,     // hidden in the object list
                     const long            z_order = 0)       // priority for mouse click
{
//--- create a rectangle by the given coordinates
   if(!ObjectCreate(chart_ID, name, OBJ_RECTANGLE, sub_window, time1, price1, time2, price2)) {
      return(false);
   }
//--- set rectangle color
   ObjectSetInteger(chart_ID, name, OBJPROP_COLOR, clr);
//--- set the style of rectangle lines
   ObjectSetInteger(chart_ID, name, OBJPROP_STYLE, style);
//--- set width of the rectangle lines
   ObjectSetInteger(chart_ID, name, OBJPROP_WIDTH, width);
//--- enable (true) or disable (false) the mode of filling the rectangle
   ObjectSetInteger(chart_ID, name, OBJPROP_FILL, fill);
//--- display in the foreground (false) or background (true)
   ObjectSetInteger(chart_ID, name, OBJPROP_BACK, back);
//--- enable (true) or disable (false) the mode of highlighting the rectangle for moving
//--- when creating a graphical object using ObjectCreate function, the object cannot be
//--- highlighted and moved by default. Inside this method, selection parameter
//--- is true by default making it possible to highlight and move the object
   ObjectSetInteger(chart_ID, name, OBJPROP_SELECTABLE, selection);
   ObjectSetInteger(chart_ID, name, OBJPROP_SELECTED, selection);
//--- hide (true) or display (false) graphical object name in the object list
   ObjectSetInteger(chart_ID, name, OBJPROP_HIDDEN, hidden);
//--- set the priority for receiving the event of a mouse click in the chart
   ObjectSetInteger(chart_ID, name, OBJPROP_ZORDER, z_order);
//--- successful execution
   return(true);
}

これでコンパイルしてチャート上をクリックすると、ローソク足の塗りつぶしが左から右に流れていきます。


オブジェクトで1分足を描く


中編では、1分足のローソク足を描くのに使用するトレンドラインとレクタングルの設定をするところまで解説しました。次はこれらのオブジェクトを使って1分足を描いていきましょう。

まず、レクタングルで1分足の実体を書きます。「name」を定義しておき、名前は「name + "Body"」、サブウィンドウは「ChartWindowFind」を使ってインジケーターがセットされているサブウィンドウの番号、時間は「Time[i]」、価格は始値から終値、カラーは定義した色とします。


string name = PREFIX + (string)bar + "_";
RectangleCreate(0, name + "Body", ChartWindowFind(), Time[i], op, Time[i], cl, clr);

次にトレンドラインです。名前を「name + "Pin"」、価格を高値から安値とし、その他はレクタングルと同じで問題ありません。


TrendCreate(0, name + "Pin", ChartWindowFind(), Time[i], hi, Time[i], lo, clr);

そして、1分足の情報が前の足と同じ場合の処理を記述します。足の情報を更新するため、「ObjectMove」を利用してオブジェクトの座標を変更します。価格に関しては、実体を表す「Body」は終値で、ヒゲを表す「Pin」は高値と安値で反映させます。ヒゲは実体の真ん中に描きたいので、for文の上に「int barStart = 0;」を、「TrendCreate(0, name + "Pin", ChartWindowFind(), Time[i], hi, Time[i], lo, clr);」の下に「barStart = i;」を追記し、最初にバーが現れた場所を記憶しておく必要があります。バーが始まったところから現在までを足して2で割ったところが実体の中央になるので、そこにヒゲを描くようにします。


ObjectMove(0, name + "Body", 1, Time[i], cl);
ObjectMove(0, name + "Pin", 0, Time[(barStart + i) / 2], hi);
ObjectMove(0, name + "Pin", 1, Time[(barStart + i) / 2], lo);

また、「ObjectSetInteger」を使って実体とヒゲの色も指定しましょう。


ObjectSetInteger(0, name + "Body", OBJPROP_COLOR, clr);
ObjectSetInteger(0, name + "Pin", OBJPROP_COLOR, clr);

これでコンパイルすると、サブウィンドウのティックチャートに1分足のローソク足が重ねて表示されます。

サブウィンドウのティックチャートに1分足のローソク足が重ねて表示


オブジェクトで1分足を描く


このままでは始値と終値が一致している場合に、実体(レクタングル)の高さが0になってしまい、十字線を表示することができません。そこで、始値と終値が同じときは終値に最低値動きのポイント(ドル円だと0.1銭)の半分を足して、十字線でも表示するようにしましょう。次のif文を「ObjectMove(0, name + "Body", 1, Time[i], cl);」の上に加えます。


if (cl ==op) cl += _Point / 2;

また、現状ではヒゲが細いので少し太くしましょう。トレンドラインの「// line width」の初期値を「width = 1」から「width = 3」に変えます。


const int width = 3, // line width

これでコンパイルすれば完成です。このようにティックチャートと1分足を重ねて表示すれば、1分足の中身を細かく確認できることが分かります。数秒単位で売買を繰り返す場合などに役立つインジケーターといえます。

ティックチャートと1分足を重ねて表示すれば、1分足の中身を細かく確認できる


ソースコード


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


//+------------------------------------------------------------------+
//|                                                 Tick_and_Bar.mq4 |
//|                        Copyright 2022, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   2
//--- plot Tick
#property indicator_label1  "Tick"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrWhite
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- plot TimeM1
#property indicator_label2  "TimeM1"
#property indicator_type2   DRAW_NONE
#define PREFIX MQLInfoString(MQL_PROGRAM_NAME) + "_"
//--- input parameters
input int      BARS = 1000;
//--- indicator buffers
double         Tick[];
double         TimeM1[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
//--- indicator buffers mapping
   SetIndexBuffer(0, Tick);
   SetIndexBuffer(1, TimeM1);

//---
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int 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 int &spread[])
{
//---
   int limit = MathMin(BARS, Bars - 2);
   if (Tick[0] != EMPTY_VALUE) {
      for (int i = limit; i > 0; i--) {
         Tick[i] = Tick[i - 1];
         TimeM1[i] = TimeM1[i - 1];
      }
   }

   Tick[0] = Bid;
   TimeM1[0] = (double)iTime(NULL, PERIOD_M1, 0);
   Tick[limit + 1] = EMPTY_VALUE;
   TimeM1[limit + 1] = EMPTY_VALUE;
   
   ObjectsDeleteAll(0, PREFIX);
   
   int barStart = 0;
   
   for (int i = limit; i >= 0; i--) {
      if (TimeM1[i] == EMPTY_VALUE) continue;
      int bar = iBarShift(NULL, PERIOD_M1, (datetime)TimeM1[i]);
      double op = iOpen(NULL, PERIOD_M1, bar);
      double cl = iClose(NULL, PERIOD_M1, bar);
      double hi = iHigh(NULL, PERIOD_M1, bar);
      double lo = iLow(NULL, PERIOD_M1, bar);

      color clr = clrWhite;
      if (cl > op) clr = clrRed;
      else if (cl < op) clr = clrBlue;
      
      string name = PREFIX + (string)bar + "_";
      
      if (TimeM1[i] != TimeM1[i + 1]) {
         RectangleCreate(0, name + "Body", ChartWindowFind(), Time[i], op, Time[i], cl, clr);
         TrendCreate(0, name + "Pin", ChartWindowFind(), Time[i], hi, Time[i], lo, clr);
         barStart = i;
      } else {
      if (cl ==op) cl += _Point / 2;
      ObjectMove(0, name + "Body", 1, Time[i], cl);
      ObjectMove(0, name + "Pin", 0, Time[(barStart + i) / 2], hi);
      ObjectMove(0, name + "Pin", 1, Time[(barStart + i) / 2], lo);

      ObjectSetInteger(0, name + "Body", OBJPROP_COLOR, clr);
      ObjectSetInteger(0, name + "Pin", OBJPROP_COLOR, clr);      
      }
   }
//--- return value of prev_calculated for next call
   return(rates_total);
}
//+------------------------------------------------------------------+
//| Create a trend line by the given coordinates                     |
//+------------------------------------------------------------------+
bool TrendCreate(const long            chart_ID = 0,      // chart's ID
                 const string          name = "TrendLine", // line name
                 const int             sub_window = 0,    // subwindow index
                 datetime              time1 = 0,         // first point time
                 double                price1 = 0,        // first point price
                 datetime              time2 = 0,         // second point time
                 double                price2 = 0,        // second point price
                 const color           clr = clrRed,      // line color
                 const ENUM_LINE_STYLE style = STYLE_SOLID, // line style
                 const int             width = 3,         // line width
                 const bool            back = true,      // in the background
                 const bool            selection = false,  // highlight to move
                 const bool            ray_right = false, // line's continuation to the right
                 const bool            hidden = true,     // hidden in the object list
                 const long            z_order = 0)       // priority for mouse click
{
//--- create a trend line by the given coordinates
   if(!ObjectCreate(chart_ID, name, OBJ_TREND, sub_window, time1, price1, time2, price2)) {
      return(false);
   }
//--- set line color
   ObjectSetInteger(chart_ID, name, OBJPROP_COLOR, clr);
//--- set line display style
   ObjectSetInteger(chart_ID, name, OBJPROP_STYLE, style);
//--- set line width
   ObjectSetInteger(chart_ID, name, OBJPROP_WIDTH, width);
//--- display in the foreground (false) or background (true)
   ObjectSetInteger(chart_ID, name, OBJPROP_BACK, back);
//--- enable (true) or disable (false) the mode of moving the line by mouse
//--- when creating a graphical object using ObjectCreate function, the object cannot be
//--- highlighted and moved by default. Inside this method, selection parameter
//--- is true by default making it possible to highlight and move the object
   ObjectSetInteger(chart_ID, name, OBJPROP_SELECTABLE, selection);
   ObjectSetInteger(chart_ID, name, OBJPROP_SELECTED, selection);
//--- enable (true) or disable (false) the mode of continuation of the line's display to the right
   ObjectSetInteger(chart_ID, name, OBJPROP_RAY_RIGHT, ray_right);
//--- hide (true) or display (false) graphical object name in the object list
   ObjectSetInteger(chart_ID, name, OBJPROP_HIDDEN, hidden);
//--- set the priority for receiving the event of a mouse click in the chart
   ObjectSetInteger(chart_ID, name, OBJPROP_ZORDER, z_order);
//--- successful execution
   return(true);
}
//+------------------------------------------------------------------+
//| Create rectangle by the given coordinates                        |
//+------------------------------------------------------------------+
bool RectangleCreate(const long            chart_ID = 0,      // chart's ID
                     const string          name = "Rectangle", // rectangle name
                     const int             sub_window = 0,    // subwindow index
                     datetime              time1 = 0,         // first point time
                     double                price1 = 0,        // first point price
                     datetime              time2 = 0,         // second point time
                     double                price2 = 0,        // second point price
                     const color           clr = clrRed,      // rectangle color
                     const ENUM_LINE_STYLE style = STYLE_SOLID, // style of rectangle lines
                     const int             width = 1,         // width of rectangle lines
                     const bool            fill = false,      // filling rectangle with color
                     const bool            back = true,      // in the background
                     const bool            selection = false,  // highlight to move
                     const bool            hidden = true,     // hidden in the object list
                     const long            z_order = 0)       // priority for mouse click
{
//--- create a rectangle by the given coordinates
   if(!ObjectCreate(chart_ID, name, OBJ_RECTANGLE, sub_window, time1, price1, time2, price2)) {
      return(false);
   }
//--- set rectangle color
   ObjectSetInteger(chart_ID, name, OBJPROP_COLOR, clr);
//--- set the style of rectangle lines
   ObjectSetInteger(chart_ID, name, OBJPROP_STYLE, style);
//--- set width of the rectangle lines
   ObjectSetInteger(chart_ID, name, OBJPROP_WIDTH, width);
//--- enable (true) or disable (false) the mode of filling the rectangle
   ObjectSetInteger(chart_ID, name, OBJPROP_FILL, fill);
//--- display in the foreground (false) or background (true)
   ObjectSetInteger(chart_ID, name, OBJPROP_BACK, back);
//--- enable (true) or disable (false) the mode of highlighting the rectangle for moving
//--- when creating a graphical object using ObjectCreate function, the object cannot be
//--- highlighted and moved by default. Inside this method, selection parameter
//--- is true by default making it possible to highlight and move the object
   ObjectSetInteger(chart_ID, name, OBJPROP_SELECTABLE, selection);
   ObjectSetInteger(chart_ID, name, OBJPROP_SELECTED, selection);
//--- hide (true) or display (false) graphical object name in the object list
   ObjectSetInteger(chart_ID, name, OBJPROP_HIDDEN, hidden);
//--- set the priority for receiving the event of a mouse click in the chart
   ObjectSetInteger(chart_ID, name, OBJPROP_ZORDER, z_order);
//--- successful execution
   return(true);
}


本記事の監修者・HT FX


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

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

EA運用の注意点

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


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

この記事をシェアする

ホーム » FX自動売買基礎と応用 » ティックチャートと1分足を重ねて表示する自動売買の作成方法