水平線や垂直線オブジェクトを使いチャートにグリッドを描画する方法
ひな形を作り接頭辞を登録
この記事では、水平線、垂直線オブジェクトを使ってチャートにグリッド(縦横線)を描画する方法を解説します。
まずファイルの新規作成で「カスタムインディケータ」を選択後、ファイル名を「Grid_test」とします。今回は「OnChartEvent」を使うので、「カスタムインディケータのイベントハンドラ」画面で「OnChartEvent」のみにチェックを入れます。次へ進み、「完了」をクリックすれば、ひな形の完成です。
オブジェクトを使うので、まとめて削除などができるように接頭辞を登録しておきます。ファイル上部のプロパティ「
#property indicator_chart_window」の下に次のように定義しましょう。
#define PREFIX "Grid_"
そして「Custom indicator initialization function」の下に「Custom indicator deinit function」を設けて、OnDeinit関数を使った次のコードを記述します。
void OnDeinit(const int reason)
{
ObjectsDeleteAll(0, PREFIX);
}
OBJ_VLINEで垂直線を設定
グリッドの描画処理に関しては、チャートに変更があったときに実行するようにします。毎回リセットして、その後グリッドを描画していきます。
まずは、垂直線から設定しましょう。縦のラインを描画するときは、左端のローソク足から右端のローソク足までの範囲を指定します。
「CHART_FIRST_VISIBLE_BAR」はチャート左端の最初に見えているローソク足、「CHART_WIDTH_IN_BARS」はチャート全体のローソク足の本数です。右端のローソク足に関しては、チャートのシフトで最新足の表示が右端にならない場合があり、マイナスの値になってしまうので、そのようなときは値を「0」にします。
int barF = (int)ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR);
int barR = barF - (int)ChartGetInteger(0, CHART_WIDTH_IN_BARS);
if (barR < 0) barR = 0;
そして、for文を使ってbarFからbarRまで計算させます。必要なところだけに垂直線を引きたいので、さらに条件を加えましょう。垂直線を引くときだけ「drawV」を「true」にするようにして、そのときに垂直線を描画する形とします。
for (int i = barF; i >= barR; i--) {
bool drawV = false;
if (drawV) {
}
}
垂直線のコードは、MQL4リファレンスからサンプルをコピーして使いましょう。MQL4リファレンスの目次にある「Constants, Enumerations and Structures」→「Objects Constants」→「Object Types」をクリックするとオブジェクトの一覧が表示されます。その中から「OBJ_VLINE」を選択し、あらかじめ用意されているコードをコピーしてファイルの下部に貼り付けます。
「//--- if the line time is not set, draw it via the last bar」から5行と、「Print(__FUNCTION__,」「": failed to create a vertical line! Error code = ",GetLastError());」の2行は不要なので削除します。
また、色を「clr = clrRed」から「clr = clrGray」に、スタイルを実線の「style = STYLE_SOLID」から点線の「style = STYLE_DOT」に、セレクションを「selection = true,」から「selection = false」に変更します。
ちなみに、こちらは垂直線専用になっていますが、水平線でも使えるように「VLineCreate」を「LineCreate」とし、時間の初期設定「datetime time = 0, // line time」の下に価格の初期設定「double price = 0, // line price」をdouble型で追加します。そして「if(!ObjectCreate(chart_ID, name, OBJ_VLINE, sub_window, time, 0)) {」の「0」を「price」に変えれば準備完了です。
//+------------------------------------------------------------------+
//| Create the vertical line |
//+------------------------------------------------------------------+
bool LineCreate(const long chart_ID = 0, // chart's ID
const string name = "VLine", // line name
const int sub_window = 0, // subwindow index
datetime time = 0, // line time
double price = 0, // line price
const color clr = clrGray, // line color
const ENUM_LINE_STYLE style = STYLE_DOT, // line style
const int width = 1, // line width
const bool back = false, // 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 vertical line
if(!ObjectCreate(chart_ID, name, OBJ_VLINE, sub_window, time, price)) {
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);
//--- 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);
}
switch処理で垂直線を描画
続いて、垂直線を描画するコードを追記していきます。
まず、「if (drawV) {」の下で「LineCreate」のパラメーターを次のように設定します。チャートIDは「0」、名前は「PREFIX+VL+通し番号」、ウィンドウ番号はメインウィンドウなので「0」、時間は許可が出た時間、価格は「0」なので省略とします。
LineCreate(0, PREFIX + "VL" + IntegerToString(i), 0, Time[i]);
ここで時間足ごとに設定を変えたいので、「switch文」を使います。if文と同様に、条件分岐を行うプログラムを作成する際に用いられることが多いです。Switchの後のカッコ内には「条件式」を入れ、その式の値に一致するcaseの箇所に飛んで処理が実行されます。
まずは表示しているチャートの時間足を示す「_Period」の値が1分足「PERIOD_M1」だった場合の処理を「if (drawV) {」の上に、次のように記述します。1分足に関しては、15分区切りで垂直線を描きたいので、時間を15分区切りで割った余りが「0」のとき「true」になるという式とします。
switch (_Period) {
case PERIOD_M1 :
drawV = Time[i] % PeriodSeconds(PERIOD_M15) == 0;
break;
}
これで1分足の設定は完了です。
時間足ごとに垂直線を引く間隔を指定
時間足は1分足の他に「5分足」「15分足」「30分足」「1時間足」「4時間足」「日足」「週足」「月足」と8個あるので、それぞれの条件を設定します。
5分足は1時間区切り、15分足と30分足は4時間区切り、1時間足は1日区切りとします。15分足と30分足は同じ4時間区切りという処理なので、次のように15分足の「break;」を消してまとめて記述することが可能です。
case PERIOD_M5 :
drawV = Time[i] % PeriodSeconds(PERIOD_H1) == 0;
break;
case PERIOD_M15 :
case PERIOD_M30 :
drawV = Time[i] % PeriodSeconds(PERIOD_H4) == 0;
break;
case PERIOD_H1 :
drawV = Time[i] % PeriodSeconds(PERIOD_D1) == 0;
break;
4時間足は1週間区切りに設定しましょう。指定した日付の曜日を取得する「TimeDayOfWeek」を使い、月曜日のときだけ処理を実行するようにします。これで1週間ごとに垂直線が引かれます。
case PERIOD_H4 :
drawV = Time[i] % PeriodSeconds(PERIOD_D1) == 0 && TimeDayOfWeek(Time[i]) == MONDAY;
break;
また、日足は1か月区切りとしたいので「MN1」を使っても良いのですが、これだと一日の足がない場合に垂直線がうまく引かれないようになってしまうので、ここでは式を変えて「TimeMonth」を利用し、月が切り替わったタイミングで引くようにします。
case PERIOD_D1 :
drawV = TimeMonth(Time[i]) != TimeMonth(Time[i + 1]);
break;
ただし、「i+1」を使っていて「barF」がチャートの一番左端のデータにいったときに配列の範囲からはみ出てしまうので注意が必要です。そのエラーをなくすために、「int barF = (int)ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR);」の下に次のif文を追記します。
if (barF > Bars - 2) barF = Bars - 2;
週足に関しては、3か月区切りにします。TimeMonth(Time[i])を3で割ったときの余りが1のとき、つまり1、4、7、10月だけ垂直線を引くようにしましょう。
case PERIOD_W1 :
drawV = TimeMonth(Time[i]) != TimeMonth(Time[i + 1]) && TimeMonth(Time[i]) % 3 == 1;
break;
最後に月足ですが、1年区切りとしたいので「TimeYear」を使って、年が切り替わったときに引くようにします。
case PERIOD_MN1 :
drawV = TimeYear(Time[i]) != TimeYear(Time[i + 1]);
break;
これで垂直線は完成です。コンパイルしてチャートにセットすると、設定した間隔で垂直線が引かれることが分かります。
水平線を時間足ごとに異なるピッチで描画
続いて、水平線の設定を行いましょう。
水平線も、垂直線と同じようにswitch文を使い、区切りのピッチを時間足ごとに変更していきます。
今回は1分足、5分足、15分足を50pips区切りとします。「_Point」は最小価格単位のことで、0.1pipsを意味するので、500を掛ければ50pipsです。また、30分足、1時間足、4時間足は100pips区切り、日足は200pips区切り、週足と月足は1000pips区切りとしましょう。
double pitch = 0;
switch (_Period) {
case PERIOD_M1 :
case PERIOD_M5 :
case PERIOD_M15 :
pitch = 500 * _Point;
break;
case PERIOD_M30 :
case PERIOD_H1 :
case PERIOD_H4 :
pitch = 1000 * _Point;
break;
case PERIOD_D1 :
pitch = 2000 * _Point;
break;
case PERIOD_W1 :
case PERIOD_MN1 :
pitch = 10000 * _Point;
break;
}
水平線の場合は、チャートの上から下までの範囲だけを対象としたいので、「ChartGetDouble」で1番上の価格と1番下の価格を取得します。一番下の価格に関しては、「MathMod」を使ってピッチで割った余りをミニマムから引いて数値を丸めましょう。例えば、一番下の価格が「104.2」だった場合、ピッチの50pipsで丸めると「104」です。
double max = ChartGetDouble(0, CHART_PRICE_MAX);
double min = ChartGetDouble(0, CHART_PRICE_MIN);
min -= MathMod(min, pitch);
区切りのいい数字を丸めるために「NormalizeDouble」を使い、そのミニマムのところから水平線をn本分引くようにします(「_Digits」は、その通貨ペアの価格の小数点以下の桁数を示します)。
min = NormalizeDouble(min, _Digits);
int n = int((max - min) / pitch) + 1;
for (int i = 0; i < n; i++) {
for文を使ってn本分の処理をさせるのですが、「Create the vertical line」の設定がVLINE専用になっているので、ここでタイプを指定できるように改修しましょう。「Create the vertical line」の「const string name = "VLine", // line name」の下に次の式を入れ、両方のタイプを指定できるようにしましょう。
const int type = OBJ_VLINE, // type
そして「if(!ObjectCreate(chart_ID, name, OBJ_VLINE, sub_window, time, price)) {」の「OBJ_VLINE」を「type」に変えます。
if(!ObjectCreate(chart_ID, name, type, sub_window, time, price)) {
これに伴い、垂直線を引く「LineCreate(0, PREFIX + "VL" + IntegerToString(i), 0, Time[i]);」の「0」の前に「OBJ_VLINE」を追加します。
LineCreate(0, PREFIX + "VL" + IntegerToString(i), OBJ_VLINE, 0, Time[i]);
水平線を描画するコードに関しては、上記の垂直線のものを流用し、先ほどのfor文に組み込みます。「VL」を「HL」に、「OBJ_VLINE」を「OBJ_HLINE」に変更しましょう。また、HLINEなので時間は「0」、価格は「min+i*pitch」とすればOKです。
LineCreate(0, PREFIX + "HL" + IntegerToString(i), OBJ_HLINE, 0, 0, min + i * pitch);
コンパイルしてチャートにセットすると、時間足ごとに設定した間隔で水平線が引かれます。これでグリッドが完成です。
ソースコード
今回、作成したソースコードは下記の通りです。
//+------------------------------------------------------------------+
//| Grid_test.mq4 |
//| Copyright 2021, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Software Corp."
#property link "https://www.mql5.com"
#property version "1.00"
#property strict
#property indicator_chart_window
#define PREFIX "Grid_"
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- indicator buffers mapping
//---
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinit function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
ObjectsDeleteAll(0, PREFIX);
}
//+------------------------------------------------------------------+
//| 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[])
{
//---
//--- return value of prev_calculated for next call
return(rates_total);
}
//+------------------------------------------------------------------+
//| ChartEvent function |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
if (id == CHARTEVENT_CHART_CHANGE) {
ObjectsDeleteAll(0, PREFIX);
int barF = (int)ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR);
if (barF > Bars - 2) barF = Bars - 2;
int barR = barF - (int)ChartGetInteger(0, CHART_WIDTH_IN_BARS);
if (barR < 0) barR = 0;
for (int i = barF; i >= barR; i--) {
bool drawV = false;
switch (_Period) {
case PERIOD_M1 :
drawV = Time[i] % PeriodSeconds(PERIOD_M15) == 0;
break;
case PERIOD_M5 :
drawV = Time[i] % PeriodSeconds(PERIOD_H1) == 0;
break;
case PERIOD_M15 :
case PERIOD_M30 :
drawV = Time[i] % PeriodSeconds(PERIOD_H4) == 0;
break;
case PERIOD_H1 :
drawV = Time[i] % PeriodSeconds(PERIOD_D1) == 0;
break;
case PERIOD_H4 :
drawV = Time[i] % PeriodSeconds(PERIOD_D1) == 0 && TimeDayOfWeek(Time[i]) == MONDAY;
break;
case PERIOD_D1 :
drawV = TimeMonth(Time[i]) != TimeMonth(Time[i + 1]);
break;
case PERIOD_W1 :
drawV = TimeMonth(Time[i]) != TimeMonth(Time[i + 1]) && TimeMonth(Time[i]) % 3 == 1;
break;
case PERIOD_MN1 :
drawV = TimeYear(Time[i]) != TimeYear(Time[i + 1]);
break;
}
if (drawV) {
LineCreate(0, PREFIX + "VL" + IntegerToString(i), OBJ_VLINE, 0, Time[i]);
}
}
double pitch = 0;
switch (_Period) {
case PERIOD_M1 :
case PERIOD_M5 :
case PERIOD_M15 :
pitch = 500 * _Point;
break;
case PERIOD_M30 :
case PERIOD_H1 :
case PERIOD_H4 :
pitch = 1000 * _Point;
break;
case PERIOD_D1 :
pitch = 2000 * _Point;
break;
case PERIOD_W1 :
case PERIOD_MN1 :
pitch = 10000 * _Point;
break;
}
double max = ChartGetDouble(0, CHART_PRICE_MAX);
double min = ChartGetDouble(0, CHART_PRICE_MIN);
min -= MathMod(min, pitch);
min = NormalizeDouble(min, _Digits);
int n = int((max - min) / pitch) + 1;
for (int i = 0; i < n; i++) {
LineCreate(0, PREFIX + "HL" + IntegerToString(i), OBJ_HLINE, 0, 0, min + i * pitch);
}
}
}
//+------------------------------------------------------------------+
//| Create the vertical line |
//+------------------------------------------------------------------+
bool LineCreate(const long chart_ID = 0, // chart's ID
const string name = "VLine", // line name
const int type = OBJ_VLINE, // type
const int sub_window = 0, // subwindow index
datetime time = 0, // line time
double price = 0, // line price
const color clr = clrGray, // line color
const ENUM_LINE_STYLE style = STYLE_DOT, // line style
const int width = 1, // line width
const bool back = false, // 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 vertical line
if(!ObjectCreate(chart_ID, name, type, sub_window, time, price)) {
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);
//--- 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運用をお考えであれば、ぜひ口座開設をご検討ください。
本ホームページに掲載されている事項は、投資判断の参考となる情報の提供を目的としたものであり、投資の勧誘を目的としたものではありません。投資方針、投資タイミング等は、ご自身の責任において判断してください。本サービスの情報に基づいて行った取引のいかなる損失についても、当社は一切の責を負いかねますのでご了承ください。また、当社は、当該情報の正確性および完全性を保証または約束するものでなく、今後、予告なしに内容を変更または廃止する場合があります。なお、当該情報の欠落・誤謬等につきましてもその責を負いかねますのでご了承ください。