マルチタイムフレーム移動平均線(MA)を方式別に比較する方法【MQLプログラミングの基礎】
1.ファイルの新規作成時にパラメーターとバッファーを追加
この記事では、マルチタイムフレーム移動平均線(MA)を方式別に比較する方法を説明します。今回は、次の四つの方式でマルチタイムフレームMAを比較します。
①単純に上位足のMAの値を利用したもの。iBarShift関数を使って上位足の値をそのまま表示する階段状のマルチタイムフレーム方式。
②上記①で作ったMAの確定点を結んだもの。いわゆる平滑化するパターン。
③MAの期間に上位時間足との比率を掛けた疑似的マルチタイムフレーム方式。例えば上位足が1時間足で、表示する足が15分足の場合、1時間足は60分なので15分足の4倍です。1時間足の期間20のMAを15分足で表示するなら、期間を4倍して80MAとします。
④確定した足までは上位時間足の終値を利用し、確定していない最新足については表示中の足の終値を利用する複合方式。(この④が最もリアルに近い表示内容です)
まずファイルの新規作成で「カスタムインディケータ」を選択したあと、ファイル名を「MTF_MA_test」としてパラメーターを追加します。今回はMAの期間を、名前「PERIOD」、タイプ「int」、初期値「20」で登録します。
「カスタムインディケータのイベントハンドラ」の画面では、「OnTimer」と「OnChartEvent」のどちらにもチェックを入れず次へ進み、「カスタムインディケータの描画プロパティ」画面のプロット欄で、MA用の四つのバッファーを追加します。
一つ目はラベルを「MA0」、カラーを「White」、二つ目はラベルを「MA1」、カラーを「Magenta」、三つ目はラベルを「MA2」、カラーを「DodgerBlue」、四つ目はラベルを「MA3」、カラーを「YellowGreen」にして登録し、「完了」をクリックすれば、ひな形の完成です。
2.MAの線種を変更し時間足を指定
続いて、ファイル上部のプロパティで、MA0、MA1、MA2の線種を変更します。MA0は「STYLE_DOT」、MA1は「STYLE_DASH」、MA2は「STYLE_DASHDOT」とします。
MA1に関しては、描画タイプも「DRAW_LINE」から「DRAW_SECTION」に変えておきましょう。LINEのままでは途中の点の値を消すと線が繋がらなくなってしまいますが、SECTIONにすれば飛び飛びの点を棒線で結んでくれます。
//--- plot MA0
#property indicator_style1 STYLE_DOT
//--- plot MA1
#property indicator_type2 DRAW_SECTION
#property indicator_style2 STYLE_DASH
//--- plot MA2
#property indicator_style3 STYLE_DASHDOT
また、バッファーの名前が長いので「MA0Buffer」→「MA0」という具合に該当箇所の「Buffer」を省いて短くします。
double MA0[];
double MA1[];
double MA2[];
double MA3[];
同様にOnInit関数内の「//— indicator buffers mapping」にあるバッファー名も、次のように短く変更します。
SetIndexBuffer(0,MA0);
SetIndexBuffer(1,MA1);
SetIndexBuffer(2,MA2);
SetIndexBuffer(3,MA3);
そして、時間足を指定するパラメーターを追加します。今回は列挙型の「ENUM_TIMEFRAMES」を使い、1時間足を指定します。「//— input parameters」の下に次のコードを加えましょう。
input ENUM_TIMEFRAMES TF = PERIOD_H1;
3.階段状のマルチタイムフレームMAを表示
上記では、MAの線種を変更してバッファー名を短縮し、時間足を指定するパラメーターを追加しました。続いて、OnCalculate関数内にメインの回路を作り、マルチタイムフレームMAを表示させましょう。
今回は上位に相当する時間足を1時間として、表示しているチャートが1時間足より大きい場合はそこで処理を終了します。計算する足の最大値は「limit」、最小値は「min」として定義します。limitがminより小さいときはminまで計算を続けて、上位足に相当する本数だけは最低限毎回計算するようにします。
if (_Period > TF) return 0;
int limit = Bars - prev_calculated -2;
int min = PeriodSeconds(TF) / PeriodSeconds();
if (limit < min) limit = min;
そして、定義したlimitから最新足まで計算させます。上位足にあたる足の番号を「iBarShift」で出し、それを利用してマルチタイムフレームMAを表示します。
for (int i = limit; i >= 0; i--) {
int bar = iBarShift(NULL, TF, Time[i]);
MA0[i] = iMA(NULL, TF, PERIOD, 0, MODE_SMA, PRICE_CLOSE, bar);
}
これでコンパイルしてチャートにセットすると、マルチタイムフレームMAが階段状に表示されることが分かります。
4.MA0の確定点を結んだ平滑化パターンのマルチタイムフレームMAを表示
次に、階段状のマルチタイムフレームMAの確定点を結ぶ、平滑化パターンのMAを表示します。途中まではMA0と同じ処理で、そのあと1本前の足のときに相当する上位足の番号を出し、今の足から前の足を見て上位足が同じだった場合に、前の足の値を空にします。これで途中の値をなくして、確定した点だけが残るようになります。
MA1[i] = iMA(NULL, TF, PERIOD, 0, MODE_SMA, PRICE_CLOSE, bar);
int barPre = iBarShift(NULL, TF, Time[i + 1]);
if (bar == barPre) MA1[i + 1] = EMPTY_VALUE;
これでコンパイルすると、MA0の確定点だけが結ばれたマルチタイムフレームMAが追加表示されます。
5.疑似的マルチタイムフレームMAを表示
今度は、MAの期間に上位時間足との比率を掛けた、疑似的マルチタイムフレームMAを表示します。計算する時間足は現在の時間足なので「0」、期間は上位時間足との比率を掛けて「PERIOD * min」とします。今回、MAを表示している時間足は15分足、上位足が1時間足なのでmin=4、つまり期間を4倍するということです。
MA2[i] = iMA(NULL, 0, PERIOD * min, 0, MODE_SMA, PRICE_CLOSE, i);
これでコンパイルすると、水色の疑似的マルチタイムフレームMAが追加表示されます。平滑化されたMA1よりも少し遅れて動くことが分かります。
6.複合方式のマルチタイムフレームMAを表示
上記では、上位足のMAの値を利用した階段状のマルチタイムフレームMA、その確定点を結んだ平滑化パターンのマルチタイムフレームMA、期間に上位時間足との比率を掛けた疑似的マルチタイムフレームMAの3種類を表示することができました。最後に、複合方式のマルチタイムフレームMAを表示します。
この複合方式のマルチタイムフレームMAは、確定した足までは上位時間足の終値を、最新の未確定足については表示中の時間足の終値を利用する計算方式です。まずは終値を合計する「sum」という変数を定義し、1からPERIODまで上位足19本の終値を合計していきます。その合計に、表示中の時間足の終値を足して、PERIODで割ります。
double sum = 0;
for (int j = 1; j < PERIOD; j++) {
sum += iClose(NULL, TF, bar + j);
}
MA3[i] = (sum + Close[i]) / PERIOD;
これでコンパイルすると、緑色の複合方式のマルチタイムフレームMAが追加表示されます。
実際の上位足の値を利用し、きちんと計算して求めた複合方式のマルチタイムフレームMAは、平滑化したラインよりも階段状のラインに似た軌跡を描きます。この結果から、MAを平滑化してしまうと実際の値とのずれが大きくなることが分かります。
7.ソースコード
今回、作成したソースコードは下記の通りです。
//+------------------------------------------------------------------+
//| MTF_MA_test.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_chart_window
#property indicator_buffers 4
#property indicator_plots 4
//--- plot MA0
#property indicator_label1 "MA0"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrWhite
#property indicator_style1 STYLE_DOT
#property indicator_width1 1
//--- plot MA1
#property indicator_label2 "MA1"
#property indicator_type2 DRAW_SECTION
#property indicator_color2 clrFuchsia
#property indicator_style2 STYLE_DASH
#property indicator_width2 1
//--- plot MA2
#property indicator_label3 "MA2"
#property indicator_type3 DRAW_LINE
#property indicator_color3 clrDodgerBlue
#property indicator_style3 STYLE_DASHDOT
#property indicator_width3 1
//--- plot MA3
#property indicator_label4 "MA3"
#property indicator_type4 DRAW_LINE
#property indicator_color4 clrYellowGreen
#property indicator_style4 STYLE_SOLID
#property indicator_width4 1
//--- input parameters
input ENUM_TIMEFRAMES TF = PERIOD_H1;
input int PERIOD = 20;
//--- indicator buffers
double MA0[];
double MA1[];
double MA2[];
double MA3[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- indicator buffers mapping
SetIndexBuffer(0, MA0);
SetIndexBuffer(1, MA1);
SetIndexBuffer(2, MA2);
SetIndexBuffer(3, MA3);
//---
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[])
{
//---
if (_Period > TF) return 0;
int limit = Bars - prev_calculated - 2;
int min = PeriodSeconds(TF) / PeriodSeconds();
if (limit < min) limit = min;
for (int i = limit; i >= 0; i--) {
int bar = iBarShift(NULL, TF, Time[i]);
MA0[i] = iMA(NULL, TF, PERIOD, 0, MODE_SMA, PRICE_CLOSE, bar);
MA1[i] = iMA(NULL, TF, PERIOD, 0, MODE_SMA, PRICE_CLOSE, bar);
int barPre = iBarShift(NULL, TF, Time[i + 1]);
if (bar == barPre) MA1[i + 1] = EMPTY_VALUE;
MA2[i] = iMA(NULL, 0, PERIOD * min, 0, MODE_SMA, PRICE_CLOSE, i);
double sum = 0;
for (int j = 1; j < PERIOD; j++) {
sum += iClose(NULL, TF, bar + j);
}
MA3[i] = (sum + Close[i]) / PERIOD;
}
//--- return value of prev_calculated for next call
return(rates_total);
}
本記事の監修者・HT FX
2013年にFXを開始し、その後専業トレーダーへ。2014年からMT4/MT5のカスタムインジケーターの開発に取り組む。ブログでは100本を超えるインジケーターを無料公開。投資スタイルは自作の秒足インジケーターを利用したスキャルピング。
EA(自動売買)を学びたい方へオススメコンテンツ

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