カスタムインジケーターの作成を始めるにあたって、かんたんなインジケーターを作ってみて一度動かしてみるとイメージが湧いてきます。
もっともかんたんなインジケーターとして、高値と安値の中央値をラインで結ぶ例を作成してみましょう。
カスタムインジケーターのプロジェクトを追加する
まずはカスタムインジケーターのプロジェクトを追加しましょう。
プロジェクトの追加方法はこちらの記事を参照してください。
一般プロパティはそのままでも構いませんが、名前だけ変更しましょう。
ここでは既存のサンプルプログラムと混在しないよう「Indicators¥Work¥Test01」としています。
カスタムインディケータのイベントハンドラでは一番上のOnCalculate(…,open,high,low,close)を選択しておきます。
それ以外はチェックする必要はありません。
続く描画プロパティでは特に指定する必要はありません。
カスタムインジケーターを作成する大まかな流れ
カスタムインジケーターはかんたんなロジックだと次の3ステップで作成することができます。
コンパイラディレクティブでカスタムインジケーターの各種情報を宣言します。
OnInit関数でカスタムインジケーターを描画する前の各種初期化を行います。
OnCaluculator関数でイベントが発生するごとに指標バッファの値を更新します。
詳しく見ていきましょう。
カスタムインジケーターのコーディング
カスタムインジケーターの情報を宣言
作成するカスタムインジケーターはどんなタイプの指標プロットを何個描画するのか、そのための指標バッファは何個使用するのかをコンパイラディレクティブで宣言する必要があります。
#propertyを使って宣言するプログラムのプロパティ。
プロジェクト内のメインのプログラムファイル(プロジェクト名.mq5)で宣言する。
例えば、線を描画する指標プロットを1つ表示する最低限のプロパティを宣言する場合は次のように指定します。
#property indicator_buffers 1 // 指標プロットを描画するための指標バッファを1つ使用する
#property indicator_plots 1 // 描画する指標プロットは1つ
#property indicator_type1 DRAW_LINE // 指標プロットのタイプは「線」
また、併せて指標バッファを紐付ける配列変数も宣言します。
double m_val[];
カスタムインジケーターの初期化
カスタムインジケーターを描画していくための各種初期化を行います。
ここで使用するのはOnInit関数です。
OnInit関数は通常の関数とは異なり、Initイベントが発生したときに呼び出される関数。
OnInit関数のように、MQL5のイベントと紐づいており、イベント発生時の処理を記述できる関数をイベントハンドラと呼びます。
プロジェクト作成直後に、プログラム上に既に次のような記述があるはずです。
この中に初期化の処理を記述していきます。
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- indicator buffers mapping
//---
return(INIT_SUCCEEDED);
}
カスタムインジケーターを作成する上で、OnInit関数で必ず行う必要がある初期化が、指標バッファと動的配列変数の紐付け。
そして動的配列変数の初期化。
double m_val[]; // 指標バッファと紐づける動的配列を宣言
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// 動的配列と指標バッファを紐付け
SetIndexBuffer(0, m_val, INDICATOR_DATA);
// 動的配列変数を初期化。(0.0を設定)
ArrayInitialize(m_val, 0.0);
return(INIT_SUCCEEDED);
}
これで動的配列変数(ここではm_val)の値を更新すると自動でインジケーターが描画されます。
指標バッファの値を更新
動的配列変数の値の更新はOnCalculate関数に実装します。
OnCalculate関数はCalculateイベントのイベントハンドラ。
Initイベントの送信直後と、価格データの変更時に発生するイベント。
カスタムインジケーター作成時にのみ使用します。
こちらもプロジェクトを作成すると、次のように関数の枠だけ用意されています。
この中に指標バッファに紐付けた動的配列変数を更新する処理を記述していきます。
//+------------------------------------------------------------------+
//| 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);
}
この関数に次のようなコードを追加します。
//+------------------------------------------------------------------+
//| 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[])
{
// 処理済みの値はスキップ(インジケーター初回描画時には0番目の要素から)
int iStart = prev_calculated;
for(int i=iStart; i<rates_total; i++) {
// 高値と安値の中間に線を描画
m_val[i] = ((high[i] - low[i]) / 2) + low[i];
}
//--- return value of prev_calculated for next call
return(rates_total);
}
OnCalculate関数が呼び出されるたびに、最新の高値と安値の中間値( (高値-安値) ÷ 2 + 安値)を求め、動的配列変数に格納するだけです。
ただし、毎回配列内の全データを計算するのも処理量が大きいので、引数prev_calculatedを利用して、計算済みのインデックスはスキップするようにしています。
MQLの時系列配列は、通常インデックス0に最新の値が格納されており、インデックスが0から離れるほど過去の値を格納していますが、OnCalculate関数や動的配列変数は、配列のインデックスが大きいほど新しい(最新の)値が格納されています。
時系列配列については次の記事も参照してください。
カスタムインジケーターをデバッグ実行してみる
これまでに挙げたプログラムの全体像は次のようになります。
//+------------------------------------------------------------------+
//| Test01.mq5 |
//| |
//| |
//+------------------------------------------------------------------+
#property indicator_buffers 1 // 指標プロットを描画するための指標バッファを1つ使用する
#property indicator_plots 1 // 描画する指標プロットは1つ
#property indicator_type1 DRAW_LINE // 指標プロットのタイプは「線」
double m_val[]; // 指標バッファと紐づける動的配列を宣言
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- indicator buffers mapping
// 動的配列と指標バッファを紐付け
SetIndexBuffer(0, m_val, INDICATOR_DATA);
//---
// 動的配列変数を初期化。(0.0を設定)
ArrayInitialize(m_val, 0.0);
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[])
{
// 処理済みの値はスキップ(インジケーター初回描画時には0番目の要素から)
int iStart = prev_calculated;
for(int i=iStart; i<rates_total; i++) {
// 高値と安値の中間に線を描画
m_val[i] = ((high[i] - low[i]) / 2) + low[i];
}
//--- return value of prev_calculated for next call
return(rates_total);
}
このカスタムインジケーターをデバッグ実行してみましょう。
デバッグ実行するには、MetaEditorのメニューから次のいずれかを選択します。
- 「デバッグ」→「リアルデータでスタート」
- 「デバッグ」→「ヒストリカルデータでスタート」
リアルデータでスタートはその名のとおり、現在の実時間の価格データに対してデバッグを実行することができます。
デバッグもMT5上で実行できるため、実際の運用に近い形でデバッグすることが可能ですが、取引可能時間帯でないとMT5の価格データが更新されないため、デバッグすることができません。
一方でヒストリカルデータでスタートは過去の価格データ(ヒストリカルデータ)を基に、疑似的に処理を実行するため、どのような曜日/時間帯においてもデバッグすることができます。
しかしStrategy Testerで実行されるためMT5の設定に依存するような動作を確認することができません。
一長一短といったところはありますが、デバッグして確認したい処理ごとうまく使い分けましょう。
デバッグはMetaEditor上のアイコンからでも開始することができます。
下の図の赤枠部分(青い丸の実行ボタン)はヒストリカルデータでスタートと同じ意味。(緑色の実行ボタンは逆にリアルデータでスタートと同じ意味)
デバッグを開始すると、チャート上、高値と安値の中間地点を線で結ぶインジケーターが表示されます。
デバッグを停止するには、停止ボタンを押下します。
まとめ
MQLに限らずどんなプログラミング言語でも、かんたんな処理を実際に動かしていくことで、なんとなく感覚を掴んでいくと捗ります。
動かしてみながらたくさんプログラムを書いて慣れることが大事
ということで今回はただ線を引くだけというカスタムインジケーターを作成してみました。
チャート上への描画に関する処理はすべてMQL側で行ってくれるので、かんたんに作成することができちゃいます。
実際の取引に活用できるカスタムインジケーターは、言うまでもなく指標として有効となるロジックが大切になります。
高値と安値の間を線で結んだところで、注文は出せません…。
MQL5の書き方はサクッとしっかり覚えて、有効なロジックを考えることに注力しましょう。