グランビルの法則を使ったインジケーターのコードをそのままコピペしたら、タイムフレームを指定するパラメーターのところでコンパイラに警告を出された——という経験はありませんか。
グランビルの法則は、ジョセフ・E・グランビル(Joseph E. Granville)が考案した、移動平均線と価格(ローソク足)の位置関係から売買のタイミングを判断する8つのパターンです。この記事では、その中でも「2番」のシグナルを、MTF(マルチタイムフレーム)の移動平均線と組み合わせて矢印表示するインジケーターを作ります。パラメーターの書き方から、見落としやすい型指定の落とし穴、実際にMetaEditorでコンパイルできる完全なコードまで解説します。
グランビルの法則の2番を表示するインジケーター
グランビルの法則はご存知ですかね?
ジョセフ・E・グランビル(Joseph E. Granville)が考案した買いと売りの8つのパターンを言います。
具体的には
- 移動平均線
- ローソク足
を活用して、価格との位置関係やチャートパターンと組み合わせて見ていきます。
今回はその中でも2番を表示するインジケーターです。
また、グランビルをただ表示するだけではなく、
MTFの移動平均線を目安にしたグランビルのインジケーターとなります。
- グランビルの法則2番のシグナルを矢印で表示
- MTF(上位足)の移動平均線でトレンド方向を判定してからシグナルを絞り込み
- アラートのON/OFFを切り替え可能
メタエディタ(MetaEditor)を立ち上げる
メタエディタ(MetaEditor)を立ち上げましょう。操作は以下に記載しています。

今回は名前を「Granville」で作成します。
プロパティを記述する
プロパティを記述しましょう。
まず、シグナル2つを表示させるのでindicator_buffersは2つです。
#property indicator_buffers 2//バッファ2つ
それぞれの色を指定します。
#property indicator_color1 clrBlue//青色 #property indicator_color2 clrRed//赤色
幅を指定します。
#property indicator_width1 2//幅2 #property indicator_width2 2//幅2
これでプロパティを記述は完了です。
#property indicator_buffers 2//バッファ2つ #property indicator_color1 clrBlue//青色 #property indicator_color2 clrRed//赤色 #property indicator_width1 2//幅2 #property indicator_width2 2//幅2
バッファを定義する
バッファを定義しましょう。
今回は上矢印のupArrow[]と下矢印のdownArrow[]の2つを用意します。
double upArrow[], downArrow[];//上矢印と下矢印
これで、バッファを定義は完了です。
/*バッファ*/ double upArrow[], downArrow[];//上矢印と下矢印
パラメータを記述する
パラメータを記述しましょう。
maPeriodで移動平均線の期間を設定します。
input int maPeriod = 21;//移動平均線の期間
timeFrameはMTFで参照する上位足のタイムフレームです。今回は1時間足を使いたいので、時間軸を表す型(ENUM_TIMEFRAMES)の変数として宣言します。
input ENUM_TIMEFRAMES timeFrame = PERIOD_H1;//MTFで参照するタイムフレーム
mtfMaPeriodでMTF(マルチタイムフレーム)用の移動平均線の期間を設定します。
input int mtfMaPeriod = 21;//MTF移動平均線の期間
最後にsignAlertでアラートのON/OFFを設定します。
input bool signAlert = false;//アラートのON/OFF
これでパラメータを記述は完了です。
/*パラメーター*/ input int maPeriod = 21;//移動平均線の期間 input ENUM_TIMEFRAMES timeFrame = PERIOD_H1;//MTFで参照するタイムフレーム input int mtfMaPeriod = 21;//MTF移動平均線の期間 input bool signAlert = false;//アラートのON/OFF
タイムフレームの指定はPERIOD_〇〇を使う
timeFrameをENUM_TIMEFRAMESという時間軸専用の型で宣言しているのに、初期値を60のような生の数字で直接代入すると、コンパイラが「数値から列挙型への暗黙の変換」として警告を出したり、環境によってはコンパイルが通らない原因になります。
MQL4では、1時間足ならPERIOD_H1、4時間足ならPERIOD_H4、日足ならPERIOD_D1というように、あらかじめ用意されている列挙定数を使うのが正しい書き方です。数字を直接書くよりも、あとでコードを読み返したときにどの時間軸を指しているか一目でわかるという利点もあります。
OnInit()関数に記述する
OnInit()関数に記述しましょう。
それぞれのインデックスに紐付けします。
SetIndexBuffer(0, upArrow);//インデックス0にupArrowを紐付け SetIndexBuffer(1, downArrow);//インデックス1にdownArrowを紐付け
スタイルを矢印の実線を選択しましょう。
SetIndexStyle(0, DRAW_ARROW, STYLE_SOLID);//矢印の実線 SetIndexStyle(1, DRAW_ARROW, STYLE_SOLID);//矢印の実線
最後に矢印の種類を決めましょう。
SetIndexArrow(0, 233);//矢印の種類 SetIndexArrow(1, 234);//矢印の種類
これで、OnInit()関数に記述は完了です。
SetIndexBuffer(0, upArrow);//インデックス0にupArrowを紐付け SetIndexBuffer(1, downArrow);//インデックス1にdownArrowを紐付け SetIndexStyle(0, DRAW_ARROW, STYLE_SOLID);//矢印の実線 SetIndexStyle(1, DRAW_ARROW, STYLE_SOLID);//矢印の実線 SetIndexArrow(0, 233);//矢印の種類 SetIndexArrow(1, 234);//矢印の種類
OnCalculate()関数に記述する
OnCalculate()関数に記述しましょう。矢印はバッファ(SetIndexBufferで紐付けた配列)に値を入れるだけで自動的に描画されるので、ObjectCreate関数を自分で呼ぶ必要はなく、チャートを再読み込みしても矢印が重複して増えていく心配もありません。
if(rates_total == prev_calculated) return(rates_total);
int limit = rates_total-maPeriod-1;//バー総数-maPeriod-1
int flag = 0;//フラッグを0にする
for(int i=limit; i>=1; i--)
{
upArrow[i] = EMPTY_VALUE;//配列を空にする
downArrow[i] = EMPTY_VALUE;//配列を空にする
int shift = iBarShift(NULL, timeFrame, time[i]);//MTFの設定
double mtfMa1 = iMA(NULL, timeFrame, mtfMaPeriod, 0, MODE_SMA, PRICE_CLOSE, shift+1);//MTFのMAのシフト1
double mtfMa2 = iMA(NULL, timeFrame, mtfMaPeriod, 0, MODE_SMA, PRICE_CLOSE, shift+2);//MTFのMAのシフト2
double ma = iMA(NULL, 0, maPeriod, 0, MODE_SMA, PRICE_CLOSE, i);//MA
double ma1 = iMA(NULL, 0, maPeriod, 0, MODE_SMA, PRICE_CLOSE, i+1);//MAのシフト1
if(mtfMa2 < mtfMa1 && mtfMa1 <= close[i])//MTFのMAシフト2の期間よりもMTFのMAシフト1の方が大きく、終値がMTFのMAシフト1以上の場合
{
if(flag == 1)//フラッグが1であれば
{
if(ma1 <= ma && close[i+1] <= ma1 && ma < close[i])//MAのシフト1がMA以下で終値のシフト1がMAのシフト1以下でMAが終値よりも小さい場合
{
upArrow[i] = low[i];//上矢印を安値に表示
}
}
else//フラッグが1以外であれば
{
if(ma < close[i]) flag = 1;//MAが終値よりも小さい場合はフラッグを1にする
}
}
if(mtfMa2 > mtfMa1 && mtfMa1 >= close[i])//MTFのMAのシフト1の期間よりもMTFのMAのシフト2の方が大きく、MTFのMAのシフト1が終値以上の場合
{
if(flag == -1)//フラッグが-1であれば
{
if(ma1 >= ma && close[i+1] >= ma1 && ma > close[i])//MAのシフト1がMA以上で終値のシフト1がMAシフト1以上でMAが終値よりも大きい場合
{
downArrow[i] = high[i];//下矢印を高値に表示
}
}
else//フラッグが-1以外であれば
{
if(ma > close[i]) flag = -1;//MAが終値より大きい場合はフラッグを-1にする
}
}
}
if(signAlert)//signAlertがTrueの場合
{
if(upArrow[1] != EMPTY_VALUE) Alert("Granville 2nd UP");//upArrowが空ではない場合にアラート
if(downArrow[1] != EMPTY_VALUE) Alert("Granville 2nd DOWN");//downArrowが空ではない場合にアラート
}
コンパイルする
コンパイルしましょう。シグナルが表示されていたら完了です。

移動平均線を表示した場合は以下のようになります。

ソースコード全体
ここまでの修正(タイムフレーム指定をPERIOD_H1に変更)をすべて反映した、実際にMetaEditorでコンパイルしてそのまま動かせる状態のコードです。
//+------------------------------------------------------------------+
//| Granville.mq4 |
//| Copyright 2020, FX-EA System Project Creator |
//| https://creator.fx-ea-system-project.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, FX-EA System Project Creator"
#property link "https://creator.fx-ea-system-project.com/"
#property version "1.01"
#property strict
#property indicator_chart_window
/*プロパティ*/
#property indicator_buffers 2//バッファ2つ
#property indicator_color1 clrBlue//青色
#property indicator_color2 clrRed//赤色
#property indicator_width1 2//幅2
#property indicator_width2 2//幅2
/*バッファ*/
double upArrow[], downArrow[];//上矢印と下矢印
/*パラメーター*/
input int maPeriod = 21;//移動平均線の期間
input ENUM_TIMEFRAMES timeFrame = PERIOD_H1;//MTFで参照するタイムフレーム
input int mtfMaPeriod = 21;//MTF移動平均線の期間
input bool signAlert = false;//アラートのON/OFF
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
SetIndexBuffer(0, upArrow);//インデックス0にupArrowを紐付け
SetIndexBuffer(1, downArrow);//インデックス1にdownArrowを紐付け
SetIndexStyle(0, DRAW_ARROW, STYLE_SOLID);//矢印の実線
SetIndexStyle(1, DRAW_ARROW, STYLE_SOLID);//矢印の実線
SetIndexArrow(0, 233);//矢印の種類
SetIndexArrow(1, 234);//矢印の種類
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(rates_total == prev_calculated) return(rates_total);
int limit = rates_total-maPeriod-1;//バー総数-maPeriod-1
int flag = 0;//フラッグを0にする
for(int i=limit; i>=1; i--)
{
upArrow[i] = EMPTY_VALUE;//配列を空にする
downArrow[i] = EMPTY_VALUE;//配列を空にする
int shift = iBarShift(NULL, timeFrame, time[i]);//MTFの設定
double mtfMa1 = iMA(NULL, timeFrame, mtfMaPeriod, 0, MODE_SMA, PRICE_CLOSE, shift+1);//MTFのMAのシフト1
double mtfMa2 = iMA(NULL, timeFrame, mtfMaPeriod, 0, MODE_SMA, PRICE_CLOSE, shift+2);//MTFのMAのシフト2
double ma = iMA(NULL, 0, maPeriod, 0, MODE_SMA, PRICE_CLOSE, i);//MA
double ma1 = iMA(NULL, 0, maPeriod, 0, MODE_SMA, PRICE_CLOSE, i+1);//MAのシフト1
if(mtfMa2 < mtfMa1 && mtfMa1 <= close[i])//MTFのMAシフト2の期間よりもMTFのMAシフト1の方が大きく、終値がMTFのMAシフト1以上の場合
{
if(flag == 1)//フラッグが1であれば
{
if(ma1 <= ma && close[i+1] <= ma1 && ma < close[i])//MAのシフト1がMA以下で終値のシフト1がMAのシフト1以下でMAが終値よりも小さい場合
{
upArrow[i] = low[i];//上矢印を安値に表示
}
}
else//フラッグが1以外であれば
{
if(ma < close[i]) flag = 1;//MAが終値よりも小さい場合はフラッグを1にする
}
}
if(mtfMa2 > mtfMa1 && mtfMa1 >= close[i])//MTFのMAのシフト1の期間よりもMTFのMAのシフト2の方が大きく、MTFのMAのシフト1が終値以上の場合
{
if(flag == -1)//フラッグが-1であれば
{
if(ma1 >= ma && close[i+1] >= ma1 && ma > close[i])//MAのシフト1がMA以上で終値のシフト1がMAシフト1以上でMAが終値よりも大きい場合
{
downArrow[i] = high[i];//下矢印を高値に表示
}
}
else//フラッグが-1以外であれば
{
if(ma > close[i]) flag = -1;//MAが終値より大きい場合はフラッグを-1にする
}
}
}
if(signAlert)//signAlertがTrueの場合
{
if(upArrow[1] != EMPTY_VALUE) Alert("Granville 2nd UP");//upArrowが空ではない場合にアラート
if(downArrow[1] != EMPTY_VALUE) Alert("Granville 2nd DOWN");//downArrowが空ではない場合にアラート
}
return(rates_total);
}
//+------------------------------------------------------------------+
まとめ
グランビルの法則2番のシグナルを、MTFの移動平均線でトレンド方向を確認しながら矢印表示するインジケーターの作り方を解説しました。タイムフレームを指定するパラメーターは、生の数字ではなくPERIOD_H1のような列挙定数を使うことで、コンパイル時の警告を避け、コードの意図も読み取りやすくなります。
コメント