ティックチャートと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分足を描く
このままでは始値と終値が一致している場合に、実体(レクタングル)の高さが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分足の中身を細かく確認できることが分かります。数秒単位で売買を繰り返す場合などに役立つインジケーターといえます。
ソースコード
今回、作成したソースコードは下記の通りです。
//+------------------------------------------------------------------+
//| 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(自動売買)を学びたい方へオススメコンテンツ

OANDAではEA(自動売買)を稼働するプラットフォームMT4/MT5の基本的な使い方について、画像や動画付きで詳しく解説しています。MT4/MT5のインストールからEAの設定方法までを詳しく解説しているので、初心者の方でもスムーズにEA運用を始めることが可能です。またOANDAの口座をお持ちであれば、独自開発したオリジナルインジケーターを無料で利用することもできます。EA運用をお考えであれば、ぜひ口座開設をご検討ください。
本ホームページに掲載されている事項は、投資判断の参考となる情報の提供を目的としたものであり、投資の勧誘を目的としたものではありません。投資方針、投資タイミング等は、ご自身の責任において判断してください。本サービスの情報に基づいて行った取引のいかなる損失についても、当社は一切の責を負いかねますのでご了承ください。また、当社は、当該情報の正確性および完全性を保証または約束するものでなく、今後、予告なしに内容を変更または廃止する場合があります。なお、当該情報の欠落・誤謬等につきましてもその責を負いかねますのでご了承ください。