今回作成するインジケータの概要
今回は、パラボリックを拡張したインジケータ(ソースあり)を紹介していきます。
パラボリックは人気の高いインジケータだと思いますが、EAで利用する際にはいくつか不便な点があるため、この不便な点を解消していきたいと思います。
当記事の最後にコンパイル済インジケータをダウンロードできるようにしていますので、すぐ使いたいという方は、当記事の最後からダウンロードしてください。
パラボリックの不便な点
パラボリックインジケータをEAで利用するにあたって、筆者が感じた不便な点を2点説明します。
インジケータバッファ値が1本しかない
チャート上でインジケータの描画内容を見ているだけだと気付きづらいですが、実際には上昇トレンド(ローソク足の下に描画される点)と、下降トレンド(ローソク足の上に描画される点)が、同じ1本のインジケータバッファ値で描画されています。
「それの何が問題なの?」という声が聞こえてきそうですが、EAに組み込む場合を考えると、「パラボリックのインジケータ値だけでは、今の状態が上昇トレンドなのか下降トレンドなのかロジックで判断できない」という事態が発生してしまいます。
つまり、上昇(下降)トレンドかどうかをロジックで判断するためには、別途ローソク足の情報も必要になってきてしまうんですね。これはちょっと不便です。
リペイント(再描画)してしまう
はじめのうちは、「パラボリックはリペイントしてないのでは?」と思っていましたが、よくよく調べると、しっかりとリペイントしてました(笑)。
順調に上昇・下降トレンドが継続している時は、「たまたま」リペイントしていないように見えますが、トレンド反転のタイミングでは、SAR値が上に描画されたり下に描画されたりと、しっかりとリペイントしています。これも、EAに組み込む際には不便ですね。
インジケータのMQL5コード
コード
上記で説明した2点の問題点を解消した、インジケータのコードを紹介します。今回は「パラボリックインジケータ(IndicatorParabolicSAR.mq5)」の1ファイルのみになります。
パラボリックインジケータ(IndicatorParabolicSAR.mq5)
//+------------------------------------------------------------------+
//| IndicatorParabolicSAR.mq5 |
//| Copyright 2023, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
//+------------------------------------------------------------------+
//| インジケータ関連定義
//+------------------------------------------------------------------+
#property indicator_chart_window
#property indicator_buffers 6
#property indicator_plots 2
//| No Type Buffer Visible Name Description
//|-------------------------------------------------------------------------------------------+
//| 1 DRAW_ARROW 0 Yes UpperSAR : 上部SAR(下降トレンド)
#define PARABOLICSAR_IDX_UPPER_SAR 0
#property indicator_label1 "上部SAR(下降トレンド)"
#property indicator_type1 DRAW_ARROW
#property indicator_style1 STYLE_SOLID
#property indicator_color1 clrRed
#define ARROW_UPPER_SAR 159
double bufUpperSAR[];
//| No Type Buffer Visible Name Description
//|-------------------------------------------------------------------------------------------+
//| 2 DRAW_ARROW 1 Yes LowerSAR : 下部SAR(上昇トレンド)
#define PARABOLICSAR_IDX_LOWER_SAR 1
#property indicator_label2 "下部SAR(上昇トレンド)"
#property indicator_type2 DRAW_ARROW
#property indicator_style2 STYLE_SOLID
#property indicator_color2 clrBlue
#define ARROW_LOWER_SAR 159
double bufLowerSAR[];
//| No Type Buffer Visible Name Description
//|-------------------------------------------------------------------------------------------+
//| 3 DRAW_NONE 2 No LastHigh : 前回反転時点以降の高値
#define PARABOLICSAR_IDX_LAST_HIGH 2
#property indicator_color3 clrNONE
#property indicator_type3 DRAW_NONE
double bufLastHigh[];
//| No Type Buffer Visible Name Description
//|-------------------------------------------------------------------------------------------+
//| 4 DRAW_NONE 3 No LastLow : 前回反転時点以降の安値
#define PARABOLICSAR_IDX_LAST_LOW 3
#property indicator_color4 clrNONE
#property indicator_type4 DRAW_NONE
double bufLastLow[];
//| No Type Buffer Visible Name Description
//|-------------------------------------------------------------------------------------------+
//| 5 DRAW_NONE 4 No EP : エクストリーム・プライス
#define PARABOLICSAR_IDX_EP 4
#property indicator_color5 clrNONE
#property indicator_type5 DRAW_NONE
double bufEP[];
//| No Type Buffer Visible Name Description
//|-------------------------------------------------------------------------------------------+
//| 6 DRAW_NONE 5 No AF : アクセラレーション・ファクター
#define PARABOLICSAR_IDX_AF 5
#property indicator_color6 clrNONE
#property indicator_type6 DRAW_NONE
double bufAF[];
//+------------------------------------------------------------------+
//| 変数
//+------------------------------------------------------------------+
datetime gdtOrgBeforeTime; // オリジナル時間足における、ターゲット時間足の前Bar時刻
int giOrgBeforeBar; // オリジナル時間足における、ターゲット時間足の前Bar位置
bool gbDirectionLong; // 「上昇トレンド中」フラグ
//+------------------------------------------------------------------+
//| インジケータ入力パラメータ
//+------------------------------------------------------------------+
input group "【 基本設定 】"
input ENUM_TIMEFRAMES inp_calc_timeframe = PERIOD_CURRENT; // 指標値計算対象の時間足
input group "【 パラメータ 】"
input double inp_sar_step = 0.02; // ステップ
input double inp_sar_max = 0.2; // 最大
//+------------------------------------------------------------------+
//| 【初期化関数】
//| ・チャートの初期表示時、または時間足変更等のチャート初期化が必要な
//| タイミングで呼び出される。
//+------------------------------------------------------------------+
int OnInit()
{
//+------------------------------------------------------------------+
//| インジケータバッファ設定
//+------------------------------------------------------------------+
//| No Type Buffer Visible Name Description
//|-------------------------------------------------------------------------------------------+
//| 1 DRAW_ARROW 0 Yes UpperSAR : 上部SAR(下降トレンド)
ArraySetAsSeries(bufUpperSAR, true);
ArrayInitialize(bufUpperSAR, 0);
SetIndexBuffer(PARABOLICSAR_IDX_UPPER_SAR, bufUpperSAR, INDICATOR_DATA);
PlotIndexSetInteger(PARABOLICSAR_IDX_UPPER_SAR, PLOT_ARROW, ARROW_UPPER_SAR);
//| No Type Buffer Visible Name Description
//|-------------------------------------------------------------------------------------------+
//| 2 DRAW_ARROW 1 Yes LowerSAR : 下部SAR(上昇トレンド)
ArraySetAsSeries(bufLowerSAR, true);
ArrayInitialize(bufLowerSAR, 0);
SetIndexBuffer(PARABOLICSAR_IDX_LOWER_SAR, bufLowerSAR, INDICATOR_DATA);
PlotIndexSetInteger(PARABOLICSAR_IDX_LOWER_SAR, PLOT_ARROW, ARROW_LOWER_SAR);
//| No Type Buffer Visible Name Description
//|-------------------------------------------------------------------------------------------+
//| 3 DRAW_NONE 2 No LastHigh : 前回反転時点以降の高値
ArraySetAsSeries(bufLastHigh, true);
ArrayInitialize(bufLastHigh, 0);
SetIndexBuffer(PARABOLICSAR_IDX_LAST_HIGH, bufLastHigh, INDICATOR_CALCULATIONS);
//| No Type Buffer Visible Name Description
//|-------------------------------------------------------------------------------------------+
//| 4 DRAW_NONE 3 No LastLow : 前回反転時点以降の安値
ArraySetAsSeries(bufLastLow, true);
ArrayInitialize(bufLastLow, 0);
SetIndexBuffer(PARABOLICSAR_IDX_LAST_LOW, bufLastLow, INDICATOR_CALCULATIONS);
//| No Type Buffer Visible Name Description
//|-------------------------------------------------------------------------------------------+
//| 5 DRAW_NONE 4 No EP : エクストリーム・プライス
ArraySetAsSeries(bufEP, true);
ArrayInitialize(bufEP, 0);
SetIndexBuffer(PARABOLICSAR_IDX_EP, bufEP, INDICATOR_CALCULATIONS);
//| No Type Buffer Visible Name Description
//|-------------------------------------------------------------------------------------------+
//| 6 DRAW_NONE 5 No AF : アクセラレーション・ファクター
ArraySetAsSeries(bufAF, true);
ArrayInitialize(bufAF, 0);
SetIndexBuffer(PARABOLICSAR_IDX_AF, bufAF, INDICATOR_CALCULATIONS);
//+------------------------------------------------------------------+
//| 変数初期化
//+------------------------------------------------------------------+
gdtOrgBeforeTime = NULL;
giOrgBeforeBar = 0;
gbDirectionLong = true;
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| 【計算イベント関数】
//| ・ローソク足に変化が発生する毎に呼び出される。
//+------------------------------------------------------------------+
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[])
{
//+------------------------------------------------------------------+
//| 1. 新規チャートのインジケータ値がまだ計算されていない場合
//| → 過去チャートのインジケータ値を計算する。
//+------------------------------------------------------------------+
if(prev_calculated == 0)
{
for(int i=rates_total-2; i>=0; i--)
{
setBufferValue(i,false);
}
}
//+------------------------------------------------------------------+
//| 2. 過去チャートのインジケータ値は計算済で、
//| 前回計算済Bar数 < チャートの最大Bar数 の場合 (Liveで新規Barが発生した)
//| → 新規Barのインジケータ値を計算する。
//+------------------------------------------------------------------+
else
{
if(prev_calculated < rates_total)
{
setBufferValue(0,true);
}
}
//+------------------------------------------------------------------+
//| 3. 同Bar内でのティック変更によって呼び出された場合
//| → インジケータ値は計算しない
//+------------------------------------------------------------------+
// NOP
//+------------------------------------------------------------------+
//| 今回の処理でインジケータ値を計算したBar数を返却する。
//| → このreturn値は、次回OnCalculate関数が呼び出された際、
//| 引数のprev_calculatedに設定される。
//+------------------------------------------------------------------+
return(rates_total);
}
//+------------------------------------------------------------------+
//| 【インデックスバッファ設定関数】
//| ・指定されたBar位置のインジケータ値を計算する。
//+------------------------------------------------------------------+
void setBufferValue(int iOrgCurrentBar, bool bIsNewBar)
{
//+------------------------------------------------------------------+
//| 変数定義
//+------------------------------------------------------------------+
// 時間足調整用
datetime dtOrgCurrentTime;
int iOrgBeforeBar = 0;
int iTgtCurrentBar = 0;
int iTgtCheckBar = 0;
// インジケータ計算用
bool bTrendChanged = false;
double dHighLastBar = 0.0;
double dLowLastBar = 0.0;
//+------------------------------------------------------------------+
//| 現在Bar位置のインジケータ値を初期化
//+------------------------------------------------------------------+
bufUpperSAR[iOrgCurrentBar] = 0;
bufLowerSAR[iOrgCurrentBar] = 0;
bufLastHigh[iOrgCurrentBar] = 0;
bufLastLow[iOrgCurrentBar] = 0;
bufEP[iOrgCurrentBar] = 0;
bufAF[iOrgCurrentBar] = 0;
//+------------------------------------------------------------------+
//| 計算用時間足のBar位置(T+0,T+1)を、現在時刻から算出
//+------------------------------------------------------------------+
dtOrgCurrentTime = iTime(Symbol(), PERIOD_CURRENT, iOrgCurrentBar);
iTgtCheckBar = iBarShift(Symbol(), inp_calc_timeframe, gdtOrgBeforeTime, false);
iTgtCurrentBar = iBarShift(Symbol(), inp_calc_timeframe, dtOrgCurrentTime, false);
//+------------------------------------------------------------------+
//| 計算用時間足のBar位置が変わってない場合(計算用時間足ではバー未確定)
//| 前Barのインジケータ値を引き継いで終了
//+------------------------------------------------------------------+
if(iTgtCheckBar == iTgtCurrentBar)
{
bufUpperSAR[iOrgCurrentBar] = bufUpperSAR[iOrgCurrentBar + 1];
bufLowerSAR[iOrgCurrentBar] = bufLowerSAR[iOrgCurrentBar + 1];
bufLastHigh[iOrgCurrentBar] = bufLastHigh[iOrgCurrentBar + 1];
bufLastLow[iOrgCurrentBar] = bufLastLow[iOrgCurrentBar + 1];
bufEP[iOrgCurrentBar] = bufEP[iOrgCurrentBar + 1];
bufAF[iOrgCurrentBar] = bufAF[iOrgCurrentBar + 1];
return;
}
//+------------------------------------------------------------------+
//| 計算用時間足のBar位置が変わった場合、インジケータ値を算出する。
//+------------------------------------------------------------------+
gdtOrgBeforeTime = dtOrgCurrentTime;
giOrgBeforeBar = iOrgCurrentBar + 1;
//+------------------------------------------------------------------+
//| 初回呼び出しの場合、「上昇トレンド」で初期化して終了する。
//+------------------------------------------------------------------+
dHighLastBar = iHigh(Symbol(),inp_calc_timeframe,iTgtCurrentBar+1);
dLowLastBar = iLow(Symbol(), inp_calc_timeframe,iTgtCurrentBar+1);
if(bufLastHigh[giOrgBeforeBar] == 0)
{
gbDirectionLong = true;
bufUpperSAR[iOrgCurrentBar] = 0.0;
bufLowerSAR[iOrgCurrentBar] = dLowLastBar;
bufLastHigh[iOrgCurrentBar] = dHighLastBar;
bufLastLow[iOrgCurrentBar] = dLowLastBar;
bufEP[iOrgCurrentBar] = dHighLastBar;
bufAF[iOrgCurrentBar] = inp_sar_step;
return;
}
//+------------------------------------------------------------------+
//| 2回目以降の呼び出しの場合、インジケータ値を計算する。
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 高値・安値更新
//+------------------------------------------------------------------+
bufLastHigh[iOrgCurrentBar] = bufLastHigh[giOrgBeforeBar] < dHighLastBar ? dHighLastBar : bufLastHigh[giOrgBeforeBar];
bufLastLow[iOrgCurrentBar] = bufLastLow[giOrgBeforeBar] > dLowLastBar ? dLowLastBar : bufLastLow[giOrgBeforeBar];
//+------------------------------------------------------------------+
//| 「上昇トレンド」の場合
//+------------------------------------------------------------------+
if(gbDirectionLong == true)
{
//+------------------------------------------------------------------+
//| トレンド転換あり
//| 下部SAR(上昇トレンド) > 安値 の場合、「下降トレンド」に変更
//+------------------------------------------------------------------+
if(bufLowerSAR[giOrgBeforeBar] > dLowLastBar)
{
gbDirectionLong = false;
bufUpperSAR[iOrgCurrentBar] = MathMax(bufLastHigh[iOrgCurrentBar],dHighLastBar);
bufLowerSAR[iOrgCurrentBar] = 0.0;
bufLastHigh[iOrgCurrentBar] = dHighLastBar;
bufLastLow[iOrgCurrentBar] = dLowLastBar;
bufEP[iOrgCurrentBar] = dHighLastBar;
bufAF[iOrgCurrentBar] = inp_sar_step;
}
//+------------------------------------------------------------------+
//| トレンド転換なし
//+------------------------------------------------------------------+
else
{
//+------------------------------------------------------------------+
//| EP < 直近高値の場合 → EP,AF更新
//+------------------------------------------------------------------+
if(bufEP[giOrgBeforeBar] < dHighLastBar)
{
bufEP[iOrgCurrentBar] = dHighLastBar;
bufAF[iOrgCurrentBar] = bufAF[giOrgBeforeBar] + inp_sar_step;
if(bufAF[iOrgCurrentBar] > inp_sar_max)
{
bufAF[iOrgCurrentBar] = inp_sar_max;
}
}
//+------------------------------------------------------------------+
//| その他 → EP,AF継続
//+------------------------------------------------------------------+
else
{
bufEP[iOrgCurrentBar] = bufEP[giOrgBeforeBar];
bufAF[iOrgCurrentBar] = bufAF[giOrgBeforeBar];
}
//+------------------------------------------------------------------+
//| SAR値算出
//+------------------------------------------------------------------+
bufUpperSAR[iOrgCurrentBar] = 0.0;
bufLowerSAR[iOrgCurrentBar] = bufLowerSAR[giOrgBeforeBar]
+ bufAF[iOrgCurrentBar] * (bufEP[giOrgBeforeBar] - bufLowerSAR[giOrgBeforeBar]);
//+------------------------------------------------------------------+
//| SAR値補正
//| SAR値よりも直近安値のほうが低い場合、直近安値で更新する
//+------------------------------------------------------------------+
if(dLowLastBar < bufLowerSAR[iOrgCurrentBar])
{
bufLowerSAR[iOrgCurrentBar] = dLowLastBar;
}
}
}
//+------------------------------------------------------------------+
//| 「下降トレンド」の場合
//+------------------------------------------------------------------+
else
{
//+------------------------------------------------------------------+
//| トレンド転換あり
//| 上部SAR(下降トレンド) < 高値 の場合、「上昇トレンド」に変更
//+------------------------------------------------------------------+
if(bufUpperSAR[giOrgBeforeBar] < dHighLastBar)
{
gbDirectionLong = true;
bufUpperSAR[iOrgCurrentBar] = 0.0;
bufLowerSAR[iOrgCurrentBar] = MathMin(bufLastLow[iOrgCurrentBar],dLowLastBar);
bufLastHigh[iOrgCurrentBar] = dHighLastBar;
bufLastLow[iOrgCurrentBar] = dLowLastBar;
bufEP[iOrgCurrentBar] = dLowLastBar;
bufAF[iOrgCurrentBar] = inp_sar_step;
}
//+------------------------------------------------------------------+
//| トレンド転換なし
//+------------------------------------------------------------------+
else
{
//+------------------------------------------------------------------+
//| EP > 直近安値の場合 → EP,AF更新
//+------------------------------------------------------------------+
if(bufEP[giOrgBeforeBar] > dLowLastBar)
{
bufEP[iOrgCurrentBar] = dLowLastBar;
bufAF[iOrgCurrentBar] = bufAF[giOrgBeforeBar] + inp_sar_step;
if(bufAF[iOrgCurrentBar] > inp_sar_max)
{
bufAF[iOrgCurrentBar] = inp_sar_max;
}
}
//+------------------------------------------------------------------+
//| その他 → EP,AF継続
//+------------------------------------------------------------------+
else
{
bufEP[iOrgCurrentBar] = bufEP[giOrgBeforeBar];
bufAF[iOrgCurrentBar] = bufAF[giOrgBeforeBar];
}
//+------------------------------------------------------------------+
//| SAR値算出
//+------------------------------------------------------------------+
bufUpperSAR[iOrgCurrentBar] = bufUpperSAR[giOrgBeforeBar]
+ bufAF[iOrgCurrentBar] * (bufEP[giOrgBeforeBar] - bufUpperSAR[giOrgBeforeBar]);
bufLowerSAR[iOrgCurrentBar] = 0.0;
//+------------------------------------------------------------------+
//| SAR値補正
//| SAR値よりも直近高値のほうが高い場合、直近高値で更新する
//+------------------------------------------------------------------+
if(bufUpperSAR[iOrgCurrentBar] < dHighLastBar)
{
bufUpperSAR[iOrgCurrentBar] = dHighLastBar;
}
}
}
}
//+------------------------------------------------------------------+
コード解説
パラボリックインジケータ(IndicatorParabolicSAR.mq5)
18~38行目
インジケータのSAR値を、上部SARと下部SARで2本のインジケータバッファに分けています。この対応により、上昇トレンドか下降トレンドかを、パラボリックのインジケータ値のみで判断できるようになります。
40~70行目
インジケータ値の計算用に使われるインデックスバッファです。これらのバッファ値は計算用に使われるため描画はされません。
75,76,148,149行目
マルチタイムフレーム対応用の変数と初期化処理です。画面に表示しているチャートの時間足を基準にして、計算すべき時間足チャートのBar時刻とBar位置を保持します。
173~211行目
新規ローソク足発生時のみ計算する処理になっていますが、解説はこちらの記事を参照してください。
247~273行目
ソースのコメントにも記載してありますが、計算用時間足のBar位置(T+0,T+1)を、表示中時間足の現在時刻から算出します。計算用時間足のBar位置が変わった場合のみ、インジケータ値を計算します。具体例で説明すると、
- 表示用時間足:5分足
- 計算用時間足:1時間足
の場合、49分から50分になったタイミングでは5分足は1Bar分進みますが、1時間足はまだ進まないため、新たなインジケータ値の計算は行わずに、前Barの値を引き継ぎます。
59分から00分になったタイミングでは、1時間足も1Bar分進むため、インジケータ値を計算します。
275~290行目
インジケータ値の初回Bar計算時点では、上昇トレンドでも下降トレンドでもないため、いったん上昇トレンドで初期化します。
293~429行目
2回目以降の呼び出し時には、必ず上昇トレンドか下降トレンドのどちらかになっているので、それぞれの状態に応じてインジケータ値を算出します。
トレンド変換が発生したかどうかを、SAR値が安値・高値に到達したかで判断し、トレンド変換していれば現在のトレンドを変更、トレンド変換していなければ、SAR値更新処理をしています。
実行結果
パラボリックインジケータの実行結果は以下のようになります。1本前の確定バーを基準にインジケータ値を算出しているため、一般的なパラボリックインジケータと比べると、微妙に描画位置がずれているので注意してください。
次は5分足チャートに「5分足のパラボリック(小さい丸)」と「1時間足のパラボリック(大きい丸)」を描画した結果です。マルチタイムフレームで計算されたインジケータ値が描画されている事が分かります。
まとめ
今回は、パラボリックを拡張したインジケータを紹介しました。
マルチタイムフレーム対応のパラボリックインジケータは、いろいろなサイトで見かける事がありますが、当記事で挙げた2点の問題点を解消しているインジケータは、なかなか無いのではと思います。
パラボリックは良いインジケータだと思うので、当記事で紹介したインジケータを利用して、良いEAを作るための手助けになってくれれば幸いです。
ダウンロード
今回紹介したインジケータはこちらからダウンロードできます。
コメント