【MT5/MQL5】【インジケータ】パラボリック(MTF対応、リペイント無し)

この記事は約38分で読めます。

今回作成するインジケータの概要

今回は、パラボリックを拡張したインジケータ(ソースあり)を紹介していきます。

パラボリックは人気の高いインジケータだと思いますが、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;
           }
        }
     }
  }
//+------------------------------------------------------------------+
Expand

コード解説

パラボリックインジケータ(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を作るための手助けになってくれれば幸いです。

ダウンロード

今回紹介したインジケータはこちらからダウンロードできます。

コメント

タイトルとURLをコピーしました