MT5版のZigZag頂点の軌跡描画ツールを作成する【MQLプログラミングの基礎】
1.ファイルの新規作成
この記事では、Textオブジェクトを利用してZigZagの上下頂点の軌跡にマークを表示するツールを作成します。
※この記事はMQL5の内容です。MQL4に対応した類似記事は以下を参照してください。
「ZigZagの軌跡から値動きの特性を確認する方法【MQLプログラミングの基礎】 」
ZigZagは、特定の条件を満たした高値・安値を直線で結ぶインジケーターで、MT5に標準で入っています。まずはチャートにZigZagをセットします。
ZigZagは、ナビゲータウィンドウ内の「指標」の「Examples」内にあります。
ラインを目視しやすいように線の幅を「3」とし、短時間で多くの頂点を確認するためにパラメーターの「Depth」の値を「5」へ変更しています。
ひな形となるファイルを新規作成します。
MetaEditorの「新規作成」をクリックし、「カスタムインディケータ」にチェックを入れて「次へ」ボタンを押します。
「カスタムインディケータの一般プロパティ」では、ファイル名を「ZigZagPoint.mq5」とします。
パラメーターは、ZigZagの仕様に合わせて「Depth」「Deviation」「Backstep」の3つを設定します。
タイプは全て「int」とし、初期設定は下記画像の通り、「5」「5」「3」とします。
設定後に「次へ」ボタンを押します。
「カスタムインディケータのイベントハンドラ」は一番上のOnCalculateのみを選択して「次へ」をクリック。
「カスタムインディケータの描画プロパティ」で、そのまま「完了」ボタンをクリックすれば、ひな形の完成です。
今回はインディケーターバッファを利用した表示はしないので、 「#property indicator_chart_window」の行の下に下記のコードを追加します。
#property indicator_buffers 0
#property indicator_plots 0
2.ZigZagの頂点情報を取得
iCustom関数を利用してZigZagの情報を取得します。
まずはハンドルを定義します。
パラメーターの最後の行「input int Backstep=3;」の下へ 「int handle;」を追記します。
定義したハンドルに、ZigZagをセットします。
OnInit関数に下記を追記します。
handle = iCustom(NULL, 0, "\\Indicators\\Examples\\ZigZag.ex5", Depth, Deviation, Backstep);
上記で、パラメーター(Depth、Deviation、Backstep)が適用されたZigZagのハンドルが定義されます。
ZigZag情報を配列にコピーするために、OnCalculate関数内に下記を追記します。
double zigzag[];
ArraySetAsSeries(zigzag, true);
CopyBuffer(handle, 0, 0, rates_total, zigzag);
上記の式にて、handleに定義されたインジケーターの0番目のバッファ情報の全てが、配列zigzagへコピーされます。
3.Textオブジェクト描画用の関数を準備
ZigZag頂点の軌跡を表示するためにTextオブジェクトを利用します。
オブジェクトを描画する準備として、サンプルファイルをMOL5リファレンスからコピーして流用します。
MetaEditorメニューの「ヘルプ」から「MOL5リファレンス」を開き、「目次」タブで「OBJ_TEXT」を選択すると、例として関数が表示されます。
(MQL5リファレンス/標準的な定数、列挙と構造体/オブジェクト定数/オブジェクト型/OBJ_TEXT)
上記画像のTextCreate関数をコピーし、プログラムの最下部へ貼り付けます。
貼り付けた関数内の不要な行を削除します。
「//— 設定されてない場合アンカーポイントの座標を設定する」の行から「 ResetLastError();」までの4行を削除します。
さらに「 Print(__FUNCTION__,」と
「 “: failed to create \”Text\” object! Error code = “,GetLastError());」
の2行も削除します。
関数の各種初期設定を変更します。
フォントは、記号を描画するために「Arial」から日本語フォントである「メイリオ」へ変更。
フォントサイズは「10」から「16」へ変更。
マークを指定価格の中央へ表示するために、アンカーの種類は「ANCHOR_LEFT_UPPER」から「ANCHOR_CENTER」へ変更。
関数を呼び出す際に、色を指定するために「色」の行を「テキスト」の行の下へ移動します。
このText描画関数は、繰り返しの描画処理を避けるため、既に同名のオブジェクトが存在する場合(新規作成に失敗した場合)は、そこで処理を終了するようになっています。
その判定式が下記のif文です。
if(!ObjectCreate(chart_ID,name,OBJ_TEXT,sub_window,time,price))
既にオブジェクトが描画済でも、変化した価格をオブジェクトの位置へ反映したいので、if文内を下記のように変更します。
if(!ObjectCreate(chart_ID,name,OBJ_TEXT,sub_window,time,price))
{
ObjectSetDouble(chart_ID,name,OBJPROP_PRICE,0,price);
return(false);
}
4.ZigZag頂点に〇×マークを描画
オブジェクトを削除する際などに管理しやすいよう、接頭辞(オブジェクト名の頭に共通で付ける文字列)を定義します。
ファイル上部のプロパティ「#property indicator_plots 0」の下に接頭辞として「PREFIX」を定義します。
#define PREFIX "ZigZagPoint_"
そして、OnInit関数の下に、インジケーター終了時等に機能するOnDeinit関数を追加します。
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
ObjectsDeleteAll(0, PREFIX);
}
上記を追加することで、インジケーター削除時や再読み込み時に、描画したオブジェクトが全削除されるようになります。
OnCalculate関数内にて、for文を利用し、先頭から2つ目までのZigZag頂点情報を取得する式を作成します。
int count = 0;
for (int i = 0; i < rates_total; i++) {
if (zigzag[i] != 0) {
count++;
}
if (count >= 2) break;
}
最新足から検索し、zigzag配列の値が0ではない(頂点の価格が存在する)場所を見つけてカウントしています。
カウントが2になったとき、for文を終了します。
オブジェクト描画の際、OnCalculate関数で定義された時間配列「time」を利用したいので、並び方を定義する下記の行をOnCalculate関数の最初に追加します。
ArraySetAsSeries(time, true);
ZigZagの頂点が見つかった場所へマークを表示するために、「count++;」の下へ、下記を追記します。
string name = PREFIX + (string)i + "_" + TimeToString(time[i], TIME_DATE | TIME_MINUTES);
string text = count == 1 ? "✕" : "〇";
color clr = count == 1 ? clrWhite : clrOrange;
TextCreate(0, name, 0, time[i], zigzag[i], text, clr);
オブジェクトの名前は、足1本ごとに変えたいので、時間を文字列にして利用しています。
countが1のとき、白の×、1でないとき(2のとき)オレンジの〇を描画するように指定しています。
ここまでの記述で軌跡描画機能までは完成しました。
コンパイルしてチャートへセットすることで、ZigZagの最新頂点および1つ前の頂点に〇×マークが描画されます。
もし、ZigZagがリペイントして変化しても〇×マークの表示は残るので、ラインが変化しながら確定するまでの特性を確認できるかと思われます。
5.一定時間経過でオブジェクトを削除する
〇×マークを描画できるようになりましたが、OnDeinit関数内にしかマークを削除する機能がないので、放置しておくとオブジェクトが無限に蓄積してしまいます。
無限に増えるのを避けるために、古いオブジェクトを削除する式をOnCalculate関数内に追加します。
for (int i = ObjectsTotal(0, 0, OBJ_TEXT) - 1; i >= 0; i--) {
string name = ObjectName(0, i, 0, OBJ_TEXT);
if (StringFind(name, PREFIX) != -1) {
datetime timeText = (datetime)ObjectGetInteger(0, name, OBJPROP_TIME, 0);
if (iBarShift(NULL, 0, timeText) > 1000) ObjectDelete(0, name);
}
}
for文でメインチャート上の全Textオブジェクト(ObjectsTotal(0, 0, OBJ_TEXT))を検索しています。
オブジェクト名に接頭辞(PREFIX)が含まれている場合のみに処理を実行します。
Textオブジェクトの時間情報(OBJPROP_TIME)を取得し、iBarShift関数を利用して足の位置を確認し、最新足より1000本以上経過していた場合、削除する式となっています。
6.ソースコード
今回、作成したソースコードは下記の通りです。
//+------------------------------------------------------------------+
//| ZigZagPoint.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 0
#property indicator_plots 0
#define PREFIX "ZigZagPoint_"
//--- input parameters
input int Depth=5;
input int Deviation=5;
input int Backstep=3;
int handle;
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- indicator buffers mapping
handle = iCustom(NULL, 0, "\\Indicators\\Examples\\ZigZag.ex5", Depth, Deviation, Backstep);
//---
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization 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[])
{
//---
ArraySetAsSeries(time, true);
double zigzag[];
ArraySetAsSeries(zigzag, true);
CopyBuffer(handle, 0, 0, rates_total, zigzag);
int count = 0;
for (int i = 0; i < rates_total; i++) {
if (zigzag[i] != 0) {
count++;
string name = PREFIX + (string)i + "_" + TimeToString(time[i], TIME_DATE | TIME_MINUTES);
string text = count == 1 ? "✕" : "〇";
color clr = count == 1 ? clrWhite : clrOrange;
TextCreate(0, name, 0, time[i], zigzag[i], text, clr);
}
if (count >= 2) break;
}
for (int i = ObjectsTotal(0, 0, OBJ_TEXT) - 1; i >= 0; i--) {
string name = ObjectName(0, i, 0, OBJ_TEXT);
if (StringFind(name, PREFIX) != -1) {
datetime timeText = (datetime)ObjectGetInteger(0, name, OBJPROP_TIME, 0);
if (iBarShift(NULL, 0, timeText) > 1000) ObjectDelete(0, name);
}
}
//--- return value of prev_calculated for next call
return(rates_total);
}
//+------------------------------------------------------------------+
//| テキストオブジェクトを作成する |
//+------------------------------------------------------------------+
bool TextCreate(const long chart_ID=0, // チャート識別子
const string name="Text", // オブジェクト名
const int sub_window=0, // サブウィンドウ番号
datetime time=0, // アンカーポイントの時刻
double price=0, // アンカーポイントの価格
const string text="Text", // テキスト
const color clr=clrRed, // 色
const string font="メイリオ", // フォント
const int font_size=16, // フォントサイズ
const double angle=0.0, // テキストの傾斜
const ENUM_ANCHOR_POINT anchor=ANCHOR_CENTER, // アンカーの種類
const bool back=false, // 背景で表示する
const bool selection=false, // 強調表示して移動
const bool hidden=true, // ブジェクトリストに隠れている
const long z_order=0) // マウスクリックの優先順位
{
//--- テキストオブジェクトを作成する
if(!ObjectCreate(chart_ID,name,OBJ_TEXT,sub_window,time,price))
{
ObjectSetDouble(chart_ID,name,OBJPROP_PRICE,0,price);
return(false);
}
//--- テキストを設定する
ObjectSetString(chart_ID,name,OBJPROP_TEXT,text);
//--- テキストフォントを設定する
ObjectSetString(chart_ID,name,OBJPROP_FONT,font);
//--- フォントサイズを設定する
ObjectSetInteger(chart_ID,name,OBJPROP_FONTSIZE,font_size);
//--- テキストの傾斜を設定する
ObjectSetDouble(chart_ID,name,OBJPROP_ANGLE,angle);
//--- アンカーの種類を設定
ObjectSetInteger(chart_ID,name,OBJPROP_ANCHOR,anchor);
//--- 色を設定
ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr);
//--- 前景(false)または背景(true)に表示
ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back);
//--- マウスでオブジェクトを移動させるモードを有効(true)か無効(false)にする
ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection);
ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection);
//--- オブジェクトリストのグラフィックオブジェクトを非表示(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(自動売買)を学びたい方へオススメコンテンツ

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