【徹底解説】MQL5ソースコード解体新書-パラボリック編-【MQL勉強用】

ソースコード解説

※【おススメのMT5MQL5対応のFX業者】

MQL5にて開発した、MT5EAを実運用するにあたり、

当サイトでは以下のFX業者をおススメいたします。

外為ファイネスト

EAの利用制限なし。スキャルピングもOK

外為ファイネストに関する記事はコチラ

アヴァトレードジャパン

自動売買界最狭スプレッド水準

アヴァトレードジャパンに関する記事はコチラ

フィリップ証券

口座開設でMT5専用EAを無料プレゼント

フィリップ証券に関する記事はコチラ



—————————————

スポンサーリンク
スポンサーリンク

MQL5ソースコード解体新書とは?

MQL5ソースコード解体新書とは、端的に言えば

巷にある、MQL5ソースコードを色々と解析・解説していこうというシリーズです。

当サイト筆者は鋭意「MQL5でEA作ろう講座」シリーズという連続ものの記事を執筆中であり、基本的にはそれが一段落するまでは、そんなにたくさんの記事をアップすることはできませんが、プログラミング力向上の一助になるシリーズとして、細々と続けていけたらな、と思っています。

事の発端は・・・

なぜ、MQL5ソースコード解体新書シリーズを始めようと思ったのか?

ということについて簡単に触れておきたいと思います。

このブログ内では、

EA制作お役立ち記事 

という記事カテゴリーを設けていて、読者の方がEA制作をする上でのヒントになるような内容をサンプルコード付きで紹介しているのですが、その中の1つである↓

で、パラボリックSARに関するチャート上の挙動について質問を頂きました。

その質問に対してお答えする中で、ふと、思いました。

MT5に実装されているインディケータで、ソースコードが公開されているものを、解読・解説するような記事は需要があるんじゃないか?」

と。

自分の実体験に照らし合わせて考えても、

公開されているコードを見て、それについて自分で考えたり、ちょっとづつ変えてみたり・・・

という事を積極的に繰り返すようになってから、プログラミングの力が大いに伸びたのを実感しました。

MQL5勉強中の方は、今回の記事で、私がやったような方法で、でネット上に無料で公開されているサンプルコードを解読する作業を是非やってもらたいな、と思います。

そんなわけで、MQL5ソースコード解体新書の第1弾は、きっかけとなった質問をいただいたパラボリックSARについて取り上げたいと思います。

MT5に実装されているパラボリックSARのソースコード確認方法

パラボリックSARのソースコードに関する解説する前に、

そもそもパラボリックSARのソースコードにどうやってアクセスするの?

という話ですよね?

これから

MT5に実装されているパラボリックSARのソースコードを確認する方法をお伝えします。

MT5の「ナビゲーター」ウィンドウから

「指標」→「Examples」

とディレクトリーを展開していくと、その中に

ParabolicSAR

があるので、それを右クリックして表示メニューから

「変更」

を選択するとメタエディターが起動し、メタトレーダーに実装されているパラボリックSARのソースコードが確認できます。↓

MT5に実装されているパラボリックSARのソースコードは以下のようになっています。

//+------------------------------------------------------------------+
//|                                                 ParabolicSAR.mq5 |
//|                   Copyright 2009-2020, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2009-2020, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
//--- indicator settings
#property indicator_chart_window
#property indicator_buffers 3
#property indicator_plots   1
#property indicator_type1   DRAW_ARROW
#property indicator_color1  DodgerBlue
//--- input parametrs
input double InpSARStep=0.02;    // Step
input double InpSARMaximum=0.2;  // Maximum
//--- indicator buffers
double ExtSARBuffer[];
double ExtEPBuffer[];
double ExtAFBuffer[];

int    ExtLastRevPos;
bool   ExtDirectionLong;
double ExtSarStep;
double ExtSarMaximum;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
void OnInit()
  {
//--- checking input data
   if(InpSARStep<0.0)
     {
      ExtSarStep=0.02;
      PrintFormat("Input parametr InpSARStep has incorrect value. Indicator will use value %d for calculations.",
                  ExtSarStep);
     }
   else
      ExtSarStep=InpSARStep;
   if(InpSARMaximum<0.0)
     {
      ExtSarMaximum=0.2;
      PrintFormat("Input parametr InpSARMaximum has incorrect value. Indicator will use value %d for calculations.",
                  ExtSarMaximum);
     }
   else
      ExtSarMaximum=InpSARMaximum;
//--- indicator buffers
   SetIndexBuffer(0,ExtSARBuffer);
   SetIndexBuffer(1,ExtEPBuffer,INDICATOR_CALCULATIONS);
   SetIndexBuffer(2,ExtAFBuffer,INDICATOR_CALCULATIONS);
//--- set arrow symbol
   PlotIndexSetInteger(0,PLOT_ARROW,159);
//--- set indicator digits
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
//--- set label name
   string short_name=StringFormat("SAR(%.2f,%.2f)",ExtSarStep,ExtSarMaximum);
   PlotIndexSetString(0,PLOT_LABEL,short_name);

   ExtLastRevPos=0;
   ExtDirectionLong=false;
  }
//+------------------------------------------------------------------+
//| 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<3)
      return(0);
//--- detect current position
   int pos=prev_calculated-1;
//--- correct position
   if(pos<1)
     {
      //--- first pass, set as SHORT
      pos=1;
      ExtAFBuffer[0]=ExtSarStep;
      ExtAFBuffer[1]=ExtSarStep;
      ExtSARBuffer[0]=high[0];
      ExtLastRevPos=0;
      ExtDirectionLong=false;
      ExtSARBuffer[1]=GetHigh(pos,ExtLastRevPos,high);
      ExtEPBuffer[0]=low[pos];
      ExtEPBuffer[1]=low[pos];
     }
//---main cycle
   for(int i=pos; i<rates_total-1 && !IsStopped(); i++)
     {
      //--- check for reverse
      if(ExtDirectionLong)
        {
         if(ExtSARBuffer[i]>low[i])
           {
            //--- switch to SHORT
            ExtDirectionLong=false;
            ExtSARBuffer[i]=GetHigh(i,ExtLastRevPos,high);
            ExtEPBuffer[i]=low[i];
            ExtLastRevPos=i;
            ExtAFBuffer[i]=ExtSarStep;
           }
        }
      else
        {
         if(ExtSARBuffer[i]<high[i])
           {
            //--- switch to LONG
            ExtDirectionLong=true;
            ExtSARBuffer[i]=GetLow(i,ExtLastRevPos,low);
            ExtEPBuffer[i]=high[i];
            ExtLastRevPos=i;
            ExtAFBuffer[i]=ExtSarStep;
           }
        }
      //--- continue calculations
      if(ExtDirectionLong)
        {
         //--- check for new High
         if(high[i]>ExtEPBuffer[i-1] && i!=ExtLastRevPos)
           {
            ExtEPBuffer[i]=high[i];
            ExtAFBuffer[i]=ExtAFBuffer[i-1]+ExtSarStep;
            if(ExtAFBuffer[i]>ExtSarMaximum)
               ExtAFBuffer[i]=ExtSarMaximum;
           }
         else
           {
            //--- when we haven't reversed
            if(i!=ExtLastRevPos)
              {
               ExtAFBuffer[i]=ExtAFBuffer[i-1];
               ExtEPBuffer[i]=ExtEPBuffer[i-1];
              }
           }
         //--- calculate SAR for tomorrow
         ExtSARBuffer[i+1]=ExtSARBuffer[i]+ExtAFBuffer[i]*(ExtEPBuffer[i]-ExtSARBuffer[i]);
         //--- check for SAR
         if(ExtSARBuffer[i+1]>low[i] || ExtSARBuffer[i+1]>low[i-1])
            ExtSARBuffer[i+1]=MathMin(low[i],low[i-1]);
        }
      else
        {
         //--- check for new Low
         if(low[i]<ExtEPBuffer[i-1] && i!=ExtLastRevPos)
           {
            ExtEPBuffer[i]=low[i];
            ExtAFBuffer[i]=ExtAFBuffer[i-1]+ExtSarStep;
            if(ExtAFBuffer[i]>ExtSarMaximum)
               ExtAFBuffer[i]=ExtSarMaximum;
           }
         else
           {
            //--- when we haven't reversed
            if(i!=ExtLastRevPos)
              {
               ExtAFBuffer[i]=ExtAFBuffer[i-1];
               ExtEPBuffer[i]=ExtEPBuffer[i-1];
              }
           }
         //--- calculate SAR for tomorrow
         ExtSARBuffer[i+1]=ExtSARBuffer[i]+ExtAFBuffer[i]*(ExtEPBuffer[i]-ExtSARBuffer[i]);
         //--- check for SAR
         if(ExtSARBuffer[i+1]<high[i] || ExtSARBuffer[i+1]<high[i-1])
            ExtSARBuffer[i+1]=MathMax(high[i],high[i-1]);
        }
     }
//--- OnCalculate done. Return new prev_calculated.
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Find highest price from start to current position                |
//+------------------------------------------------------------------+
double GetHigh(int curr_pos,int start,const double& high[])
  {
   double result=high[start];
//---
   for(int i=start+1; i<=curr_pos; i++)
      if(result<high[i])
         result=high[i];
//---
   return(result);
  }
//+------------------------------------------------------------------+
//| Find lowest price from start to current position                 |
//+------------------------------------------------------------------+
double GetLow(int curr_pos,int start,const double& low[])
  {
   double result=low[start];
//---
   for(int i=start+1; i<=curr_pos; i++)
      if(result>low[i])
         result=low[i];
//---
   return(result);
  }
//+------------------------------------------------------------------+

いきなり、全体を見てしまうと「うっ・・・」となってしまうかもしれませんが、大丈夫!

一つ一つ順を追ってゆっくり見ていきましょう。

パラボリックSARコード解説-property命令部分-

まずは、#property命令部分からです。

とはいえ、さすがに著作権者や、ホームページリンク等に関する説明は、今回割愛させていただきます。

#property命令ってそもそもなに?」という人は↓の記事をご覧ください。

そんな訳で

著作権者、ホームページリンクより下の部分

から見ています。

書かれているコードの解説をコメントアウト部分に充てると以下のようになります。

//--- indicator settings
#property indicator_chart_window//メインチャートに描画する
#property indicator_buffers 3//使用バッファは3である
#property indicator_plots   1//プロットバッファは1である
#property indicator_type1   DRAW_ARROW//描画バッファタイプはアローである
#property indicator_color1  DodgerBlue//描画バッファのカラーはDodgerBlueである

#property indicator_chart_windowという記述をすることによって、このインジケータが描画する場所はメインチャート(ローソク足などの価格情報が表示される領域)であるという事が規定されます。

RSIやMACDといった相場における価格の過熱感を知らせる類のインジケータ情報(オシレーター系)はローソク足などの価格情報が表示される領域とは別のウィンドウに表示されます

これを一般的に「サブウィンドウ」と呼んでいます。

サブウィンドウに描画するタイプのインジケータの場合は、#property indicator_chart_windowの代わりに#indicator_separate_windowを使います。

その下の行の

#property indicator_buffers 3//使用バッファは3である

について。

バッファとは何か?

まずはバッファという言葉。

聞き馴染みのない人もいるでしょうし、

「余裕・空き・緩衝」といった意味合いで聞いたことがある人もいるんじゃないかと思います。

プログラミングの世界で使う「バッファ」というのは、「データの保存領域」という文脈で使われます。

「保存領域」と言われても、もっとピンと来なくなる人の方が多いかもしれません。

今回のパラボリックSARで言えば、「正常にチャート上に表示する為に必要な数値」の数と言い換えてもいいかと思います。

それが「3つ」という事を意味しています。

※なお、インジケータバッファについては以下の講座記事も参考にして頂ければと思います。

MQL5 EA講座 第108回「インジケータを使ってエントリーシグナルを生成する」

その下の、

property indicator_plots 1//プロットバッファは1である

について。

これは、前行で定めた、

使用するバッファは3である

に連動した命令で、

使用する3つのバッファのうち、チャート上に計算結果をなにかしらのマーク(オブジェクトと言ったりします)で描く目的で使うのが1つである

という命令です。

その下の、

property indicator_type1 DRAW_ARROW//描画バッファタイプはアローである

について。

これはチャート上に描くマークの種類を指定する命令文で、ここではアロー型のマークを指定しています。アロー型というと、つい飛び道具の「弓矢」を思い浮かべがちですが、アロー型のマークは↓のようにたくさんあり、この中から希望のマークを選択して使います。

アロー型のマークの選択方法については、後述します。

その下の、

#property indicator_color1 DodgerBlue//描画バッファのカラーはDodgerBlueである

については、描画するマークの色の指定命令です。DodgerBlueはこんな感じですね↓

パラボリックSARコード解説-input変数部分-

続いてinput変数部分です。

※「input変数ってなに?」という方は、↓の記事をご覧ください。

//--- パラメーター
input double InpSARStep=0.02;    // ステップ幅
input double InpSARMaximum=0.2;  //最大値

パラボリックSARの計算式に必要なステップ幅と最大値をパラメータ化して、ユーザーに自由に設定できるようにしています。

パラボリックSARの計算式についての解説はここでは割愛させていただきます<m(__)m>(それをやっていると4万字を超えてしまいますので(-_-;))

こちらの→マネーパートナーズのページなどで確認をお願いします。

パラボリックSARコード解説-バッファ格納用の配列部分-

続いては

バッファ格納用の配列部分についてです。

配列って何だっけ?」という方は↓の記事をご覧ください。

//バッファ格納用の配列を宣言
double ExtSARBuffer[];//描画バッファ
double ExtEPBuffer[];//最大値格納バッファ
double ExtAFBuffer[];//加速因子格納バッファ

後程、バッファの値を紐づけ、格納する配列を宣言します。必要なバッファの数が3つなので、配列も3つ用意します。

パラボリックSARコード解説-グローバル変数部分-

続いてはグローバル変数に関する部分です。

※「グローバル変数って何?」という方は↓の記事をご覧ください。

//各種グローバル変数の宣言
int    ExtLastRevPos;//リバースポイントを格納
bool   ExtDirectionLong;//上昇トレンドフラグを格納変数
double ExtSarStep;//ステップ幅を格納
double ExtSarMaximum;//最大値を格納

それぞれのグローバル変数には、リバースポイント(トレンドが変わるポイント)、上昇トレンドフラッグ、最大値、ステップ幅を格納します。

データ型については→コチラ  変数については→コチラ int型については→コチラ

double型については→コチラ  bool型については→コチラ

パラボリックSARコード解説-OnInit関数部分その1-

続いてOnInit関数部分についての説明に入ります。

「OnInit関数って何?」という方は、↓の記事をご覧ください。

まずは前半部分について↓

void OnInit()
  {
//もしステップパラが0.0以下になっていたら
   if(InpSARStep<0.0)
     {
      //ステップパラを0.02にする
      ExtSarStep=0.02;
      //パラメーター修正のログ出力
      PrintFormat("ステップ幅のパラメーターが不正確な値です。インディケータの計算値として %d を採用します。",
                  ExtSarStep);
     }
   else
      ExtSarStep=InpSARStep;//異常値ではないときは、インプットパラをステップ幅とする

   if(InpSARMaximum<0.0)//最大値パラが0.0以下になっていたら
     {
      ExtSarMaximum=0.2;//最大値パラを0.2にする
      //パラメーター修正のログ出力
      PrintFormat("最大値のパラメーターが不正確な値です。インディケータの計算値として %d を採用します。",
                  ExtSarMaximum);
     }
   else
      ExtSarMaximum=InpSARMaximum;//異常値ではないときは、インプットパラを最大値とする

最大値やステップ幅等の、パラメータで設定する項目が異常値だった場合の対応についての記述になっています。(ログ出力のコメントは英語から日本語に変えました)

パラボリックSARコード解説-OnInit関数部分その2-

OnInit関数部分の後半はバッファ配列の紐づけに関する設定記述が主となっています。↓

//バッファと配列の紐づけ設定
   SetIndexBuffer(0,ExtSARBuffer);//描画用バッファと配列の紐づけ
   SetIndexBuffer(1,ExtEPBuffer,INDICATOR_CALCULATIONS/*最大値:(中間計算のための補助バッファ*/);
   SetIndexBuffer(2,ExtAFBuffer,INDICATOR_CALCULATIONS/*加速因子:(中間計算のための補助バッファ*/);

//アロー設定(採用しているのはバリアント1)
   PlotIndexSetInteger(0,PLOT_ARROW,159);
//桁数に関する設定
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits);

//表示ラベルに関する設定(最大値とステップ幅に表示)
   string short_name=StringFormat("SAR(%.2f,%.2f)",ExtSarStep,ExtSarMaximum);
   PlotIndexSetString(0,PLOT_LABEL,short_name);

//グローバル変数の初期化
   ExtLastRevPos=0;//リバースインデックスを格納 初期値は0
   ExtDirectionLong=false;//上昇トレンドの検知フラグ 初期値はfalse
  }

3つのバッファ配列SetIndexBuffer関数によって、紐づけています。

バッファ番号0が、チャートに描画する値を格納するExtSARBuffer配列と紐づけ

バッファ番号1と2が、チャートに描画する値を計算する為の値を格納するExtEPBuffer、ExtAFBuffer配列とそれぞれ紐づけされています。

SetIndexBuffer関数の第3引数に、カスタム指標定数INDICATOR_CALCULATIONS」をあてがうことによって、ExtEPBuffer、ExtAFBuffer配列に中間計算のための補助バッファを格納する、という属性が付与されています。

関数については→コチラ    定数については→コチラ

PlotIndexSetInteger(0,PLOT_ARROW,159);

という箇所は、描画するアロー型のマークの種別を決定しています。

PlotIndexSetInteger関数第1引数に、バッファ番号、第2引数に、プロパティ識別子、第3引数には第2引数で指定した識別子に応じた設定値が入ります。

今回の場合だと、

番号0のバッファに対して、DRAW_ARROW スタイルの矢印コードに関する設定をすると決め、→コードを159番にした、

ということになります。

※アロー型の種類・定数を調べたい時はMQL5リファレンスの検索窓で、「wingdings」と検索し、検索結果の「オブジェクト定数」→「wingdingsフォント」と選択していけば確認できます。↓

仮に、第3引数を「159」から「40」に変更すると、↓のようにドットマークから電話マークに変わります。↓

//桁数に関する設定
IndicatorSetInteger(INDICATOR_DIGITS,_Digits);

の箇所は、

表示・描画するパラボリックSARの値を小数点以下何桁まで表示するか?

という設定を行っています。

IndicatorSetInteger関数の第1引数のプロパティ識別子に「INDICATOR_DIGITS」を充てると、値の小数点以下表示桁数に関する設定ができるようになります。なお、第2引数に充てられている「_Digits」は、通貨ペアの表示桁数を返す定義済み変数です。

ドル円などのクロス円通貨なら3桁表示、

ユーロドルなどのクロスドル通貨なら5桁表示になります。

ここを「3」とか「5」みたいに数字をいじると、表示桁数も変わります。

定義済み変数については→コチラ

//表示ラベルに関する設定(最大値とステップ幅に表示)
string short_name=StringFormat(“SAR(%.2f,%.2f)”,ExtSarStep,ExtSarMaximum);
PlotIndexSetString(0,PLOT_LABEL,short_name);

の箇所は、データウィンドウに表示する文字列に関する設定です。

データウィンドウというのは

「表示」→「データウィンドウ」メニューを選択することによって表示される、時系列情報や価格情報、チャートにセットしているインディケータ情報を掲載しているウィンドウのことです。↓

PlotIndexSetString関数の第2引数には、指標定数であるPLOT_LABELを記述し、第3引数にはStringFormat関数で整理した文字列を格納したstring型変数「short_name」を記述しています。

その下の、

//グローバル変数の初期化
ExtLastRevPos=0;//リバースインデックスを格納 初期値は0
ExtDirectionLong=false;//上昇トレンドの検知フラグ 初期値はfalse

の部分は、

コメントアウトに書いてあるまんまの、グローバル変数の初期化を行っているんですが、

ExtLastRevPosはトレンドが変わったときの読込足の位置

ExtDirectionLongは現在のトレンドが上昇トレンドなのか、下降トレンドなのかを切り替える

重要な役割をそれぞれが今後果たしていきます。

パラボリックSARコード解説-OnCalculate関数部分その1-

いろいよメインプログラム内であるOnCalculate関数内の処理記述に入ります。

OnCalculate関数は、価格の値動きがあるたびに{}内の処理を行う関数です。

OnTick関数と同じような処理ですが、戻り値引数が違います。

OnTick関数戻り値void型(型なし)で、引数もありませんが、

OnCalculate関数戻り値int型(rates_total→読み込んだ足の数)になっており、引数も実に10個あります。

OnTick関数EAを作るときに用いるイベントハンドラー

OnCalculate関数カスタムインジケ-タを作るときに用いるイベントハンドラー、と覚えておくとよいでしょう。

 {
   if(rates_total<3)//過去データ数が3より小さければ
      return(0);//終了

//読み込み位置を格納する変数を定義
   int pos=prev_calculated-1;//読み込み位置の定義
//--- correct position

   if(pos<1)
     {
      //最初に通るときは下降トレンド用にセット
      pos=1;
      ExtAFBuffer[0]=ExtSarStep;//加速因子用バッファの最新にステップ幅を格納
      ExtAFBuffer[1]=ExtSarStep;//加速因子用バッファの1本前にステップ幅を格納
      ExtSARBuffer[0]=high[0];//描画用バッファ(バッファ0)に最新足高値を代入
      ExtLastRevPos=0;//リバースポジションは0に
      ExtDirectionLong=false;//上昇トレンドフラグはfalseに
      ExtSARBuffer[1]
         =GetHigh(pos,ExtLastRevPos,high);
      //1本前のSAR値は下降トレンド時のパラボリック関数 第1引数は読込足、第2引数はリバースポイント、第3引数は高値
      //描画用バッファ(バッファ0)の1本前はオリジナル関数で取得した値を代入

      ExtEPBuffer[0]=low[pos];//最大値格納バッファ(バッファ2)には最安値を格納
      ExtEPBuffer[1]=low[pos];//最大値格納バッファ(バッファ2)には最安値を格納
     }

この部分に書いていることは、

  • ヒストリカルデータが不足している時には、returnで処理を終了する事
  • ティックごとに読み込む足の数に関する定義
  • パラボリックSARインジケータを初めてMT5チャートに挿入したときに、各変数や配列に代入する値に関する記述

となっています。

 if(rates_total<3)//過去データ数が3より小さければ
      return(0);//終了

↑の部分ですが、rates_totalはチャート上の全バー数を格納している変数です。

OnCalculate関数の第1引数であり、戻り値にもなっていますね。これが「3より小さい」場合、つまりバーの数が2以下の時は、returnで強制終了するようにしています。

//読み込み位置を格納する変数を定義
   int pos=prev_calculated-1;//読み込み位置の定義

↑の部分ですが、prev_calculatedOnCalculate関数の第2引数になっていますね。

prev_calculatedというのは、前回呼び出し時に読み込んだバー数が格納されています。(前回呼び出し時、戻り値として返されたrates_totalの値が格納されるようになっています)

インジケータ挿入直後は、読込がされていないので、例えばチャートに存在するバー数が5だったとしても、挿入直後はprev_calculatedの値は0です。

その後、値動き(tick)を受信することによって、OnCalculate関数が呼び出され、その時点でのバー数が5だったら、prev_calculatedにも5が入るわけです。

int pos=prev_calculated-1というのは、

(読み込んだ足の数-1)ということですから、仮に読み込んだ足の数が5だったら、4が変数pos」に代入されます。

  if(pos<1)

変数pos」には「prev_calculated-1の結果」が格納されています。prev_calculatedには、「前回呼び出し時のバー数」が格納されていますので、変数「pos」が1より小さいときというのは、インジケータをチャートに挿入した直後の状態であることを意味します。

パラボリックSARインジケータを初めてMT5チャートに挿入したときに、各変数や配列に代入する値については、暫定的に、最初は下降トレンドとして、各値を代入しています。

パラボリックSARを描画する値を格納するExtSARBuffer配列に、値を代入しているGetHigh関数は、OnCalculate関数の下のグローバル領域で宣言・定義しているオリジナル関数です。

この関数については後程、解説します。

returnキーワードについては→コチラ

パラボリックSARコード解説-OnCalculate関数部分その2 for文1-

続いて、for文内のループ記述についてです。パラボリックSARの挙動の肝となる記述はほぼこの中に書かれれています。

for文については→コチラ

for(int i=pos; i<rates_total-1 && !IsStopped()/*強制シャットダウンフラグがたっていなければ*/; i++)
     {

↑まずは、繰り返す範囲についての規定記述です。

ループの始点である変数「i」 に代入している 変数「pos」には

prev_calculated-1、すなわち、前回読込時のバー本数マイナス1の数が格納されています。

ループの終点はrates_total-1、すなわち最新足までとなります。

ArraySetAsSeries関数による、時系列セットの記述がないので、配列の時系列は

[0]→[n] =[過去の時間]→[最新の時間]という流れでデータが格納されています。そのため、ループ終了ごとの処理式もデクリメントではなくインクリメントになっています。

インクリメントとデクリメントについては→コチラ

したがって、基本的には最新足の一本前から最新足に向かって、繰り返し処理をするという仕様になっています。

ArraySetAsSeries関数については→コチラ

ループの終点条件に、rates_total-1と一緒に、論理積(&&)で連なっているIsStopped関数は強制シャットダウンフラグを返す関数です。異常を検知した時に、ループを終了できるよう堅牢性を高める為に記述しているものと思われます。

IsStopped関数については→コチラ

論理積(&&)については→コチラ

パラボリックSARコード解説-OnCalculate関数部分その2 for文2-

続いて、for文内の{}実際の繰り返し処理の記述を見ていきましょう。

//反転フェーズのチェック
      if(ExtDirectionLong)//上昇トレンドフラグがtrueなら
        {
         if(ExtSARBuffer[i]>low[i])//描画バッファ値が安値より大きければ(実際の価格が下がって、上昇トレンドドットにぶつかる)
           {
            // 下降トレンドモードへ。
            ExtDirectionLong=false;//上昇トレンドフラグをfalseへ
            ExtSARBuffer[i]=GetHigh(i,ExtLastRevPos,high);//描画バッファ値はオリジナル関数で取得する。
           
            ExtEPBuffer[i]=low[i];//計算用バッファ(バッファ2)には最安値を格納
            ExtLastRevPos=i;//チェック中の足のインデックスをExtLastRevPosに代入
            ExtAFBuffer[i]=ExtSarStep;//計算用バッファ(バッファ1)にステップ幅を代入
           }
        }

最初は、上昇トレンド中に、下降トレンドに変換する動きがあったときの記述です。

まず一番外側のif文で、変数「ExtDirectionLong」がtrueかfalseを確認し、現在上昇トレンドフラグがたっているのか、下降トレンドフラグがたっているのかを確認します。

if文については→コチラ

ExtDirectionLongがtrueなら上昇トレンド中だということになります。

if(ExtSARBuffer[i]>low[i])

↑というのは

実際の価格が下がって、最新価格が上昇トレンドドットにぶつかったことを意味しますから、これがすなわち上昇トレンドから下降トレンドへの転換点になります。

{}内に、下降トレンド用の値を変数配列に、代入する処理を書き込んでいきます。

代入する値の内容は、基本的にコメントアウト部分に書いている通りなのですが、一番特筆すべきなのは、変数「ExtLastRevPos」に、繰り返し処理用の変数[i]の値が代入されているところでしょうか?

変数「ExtLastRevPos」にはトレンドの転換点のインデックスが格納されているという所を押さえておくことが、このインジケータのコードを理解する上で重要です。

パラボリックSARコード解説-OnCalculate関数部分その2 for文3-

今度は、下降トレンド中だったのが、上昇トレンドに転換した時の記述になります。↓

else//上昇トレンドフラグがfalseなら(下降トレンド中)
        {
         if(ExtSARBuffer[i]<high[i])//描画バッファ値が高値より小さければ(実際の価格が上がって、価格トレンドドットにぶつかる)
           {
            //上昇トレンドモードに転換

            ExtDirectionLong=true;//フラグ値をfalseからtrueへ(上昇トレンド発生)
            ExtSARBuffer[i]=GetLow(i,ExtLastRevPos,low);//オリジナル関数で獲得する安値を描画バッファに代入
            
            ExtEPBuffer[i]=high[i];//計算用バッファ(バッファ2)には最大値には高値を設定
            ExtLastRevPos=i;//チェック中の足のインデックスをExtLastRevPosに代入
            ExtAFBuffer[i]=ExtSarStep;//計算用バッファ(バッファ1)にステップ幅を代入
           }
        }

このセクションの一番上にある else は

if(ExtDirectionLong)//上昇トレンドフラグがtrueなら

に対応した記述になります。

すなわち、

if(ExtDirectionLong==false)//上昇トレンドフラグがfalseなら

と読み替えることができます。

上昇トレンドフラグがfalse→今まで下降トレンド中だったということですね。

if(ExtSARBuffer[i]<high[i])

の記述箇所は、最新の価格が上がって、下降トレンド中のドットにぶつかったということです。

これは下降トレンドから上昇トレンドへの転換を意味します。

{}内にて、変数配列に上昇トレンド用の値を代入していくのは、上昇→下降転換時と同様です。

{}内に、GetLowというオリジナル関数が初めて出てきますが、これは上昇トレンド中のパラボリックSAR描画値を算出するためのものです。

GetHigh関数とまとめて、後程解説します。

elseについては→コチラ

パラボリックSARコード解説-OnCalculate関数部分その2 -for文4-

for文2 と for文3 はトレンドが変わった時の記述でした。

次は、トレンドが継続した場合の処理です。

まずは上昇トレンド中の継続に関する記述↓

//トレンド継続パターンの記述(上昇)
      if(ExtDirectionLong)//継続して上昇トレンドフラグがtrueなら
        {
         //--- check for new High
         if(high[i]>ExtEPBuffer[i-1]//高値が一本前の最大値より大きく、
            &&
            i!=ExtLastRevPos)//チェックインデックスとリバースポイントがイコールでなければ
           {
            ExtEPBuffer[i]=high[i];//最大値は現在の高値とする
            ExtAFBuffer[i]=ExtAFBuffer[i-1]+ExtSarStep;//加速因子は1本前の値にステップ幅をプラスする
            if(ExtAFBuffer[i]>ExtSarMaximum)//現在の加速因子がExtSarMaximum(基本パラの指定した最大値)より大きくなったら
               ExtAFBuffer[i]=ExtSarMaximum;//加速因子は最大値とする
           }
         else//( high[i]>ExtEPBuffer[i-1]&&i!=ExtLastRevPos)の論理積条件を満たさないとき→リバース条件を満たさないとき
           {
            //--- when we haven't reversed
            if(i!=ExtLastRevPos)//チェックインデックスとリバースポイントがイコールではないとき(トレンド転換ではない)
              {
               ExtAFBuffer[i]=ExtAFBuffer[i-1];//加速因子は一本前の加速因子となる
               ExtEPBuffer[i]=ExtEPBuffer[i-1];//最大値は一本前の最大値となる
              }
           }
         //--- calculate SAR for tomorrow 次足のSAR計算
         //次足のSAR描画バッファ= 読込足のSAR描画バッファ + <読込足の加速因子*(読込足の最大値-読込足のSAR描画バッファ)>
         ExtSARBuffer[i+1]=ExtSARBuffer[i]+ExtAFBuffer[i]*(ExtEPBuffer[i]-ExtSARBuffer[i]);
         //--- check for SAR
         if(ExtSARBuffer[i+1]>low[i] || ExtSARBuffer[i+1]>low[i-1])
            //次足のSAR描画バッファが読込足の安値より大きい、もしくは//次足のSAR描画バッファが読込足のひとつ前の安値より大きい時
            ExtSARBuffer[i+1]=MathMin(low[i],low[i-1]);
         //読込足のSAR描画バッファ値は、読込足の安値と、読込足のひとつ前の安値のより小さい方の値となる。
        }

上昇トレンドが継続するパターンも2パターンあります。

1.高値が最大値を更新しているパターン

2.高値が最大値を更新していないけれども、トレンド転換は検知していないパターン

このセクションでは、1と2どちらのパターンかを場合分けし、それに応じた最大値と加速因子を代入しています。

最後に、今読み込んでいる足の次に表示するパラボリックSAR描画値を計算し、上昇トレンドにおけるシークエンスは全て終了です。

最後の行に出てくる、MathMin関数引数1と引数2のうち、より小さい方の値を戻り値として返す関数です。

パラボリックSARコード解説-OnCalculate関数部分その2 -for文5-

上昇トレンドが継続した場合の記述~次足のパラボリックSAR描画値の計算記述が終わりました。

次は

下降トレンドが継続した場合の記述~次足のパラボリックSAR描画値の計算記述

となります。

この記述終了と同時に、for文記述も終了です。

for文記述の終了に伴い、OnCalculate関数の記述も終了です。

メインプログラムの記述は全て終了となるので、残すのはグローバル領域でのGetHigh関数・GetLow関数の宣言・定義のみとなります。

else////トレンド継続パターンの記述(下降)
        {
         //--- check for new Low
         if(low[i]<ExtEPBuffer[i-1] //安値が1本前の最大値より小さく、
            &&//
            i!=ExtLastRevPos)////チェックインデックスとリバースポイントがイコールでなければ
           {
            ExtEPBuffer[i]=low[i];//最大値は読込足の安値になる
            ExtAFBuffer[i]=ExtAFBuffer[i-1]+ExtSarStep;//読込足の加速因子は1本前の加速因子にパラの加速因子を足したものになる
            if(ExtAFBuffer[i]>ExtSarMaximum)//加速因子が最大値を超えた場合は
               ExtAFBuffer[i]=ExtSarMaximum;//加速因子は最大値となる。
           }
         else//(価格が上昇し下降トレンドドットにタッチし、かつ読込足がリバースポイントでないとき)を満たしていないとき
           {
            //--- when we haven't reversed
            if(i!=ExtLastRevPos)//価格が上昇し下降トレンドドットにタッチしたが、読込足がリバースポイントでないとき
              {
               ExtAFBuffer[i]=ExtAFBuffer[i-1];//加速因子は一本前の加速因子となる
               ExtEPBuffer[i]=ExtEPBuffer[i-1];//最大値は1本前の最大値となる
              }
           }
         //--- calculate SAR for tomorrow次足のSAR計算
         //次足のSAR描画バッファ= 読込足のSAR描画バッファ + <読込足の加速因子*(読込足の最大値-読込足のSAR描画バッファ)>
         ExtSARBuffer[i+1]=ExtSARBuffer[i]+ExtAFBuffer[i]*(ExtEPBuffer[i]-ExtSARBuffer[i]);
         
         //--- check for SAR
         if(ExtSARBuffer[i+1]<high[i] || ExtSARBuffer[i+1]<high[i-1])
          //次足のSAR描画バッファが読込足の高値より小さい、もしくは//次足のSAR描画バッファが読込足のひとつ前の高値より小さい時
            ExtSARBuffer[i+1]=MathMax(high[i],high[i-1]);
          //読込足のSAR描画バッファ値は、読込足の高値と、読込足のひとつ前の高値のより大きい方の値となる。
        }
     }
//--- OnCalculate done. Return new prev_calculated.
   return(rates_total);
  }

下降トレンドが継続するパターンの記述は基本的に上昇トレンド継続パターンと逆のことをやっているだけです。

1.安値が最大値を更新しているパターン

2.安値が最大値を更新していないけれども、トレンド転換は検知していないパターン

やはり1と2どちらのパターンかを場合分けし、それに応じた最大値と加速因子を代入しています。

最後に、今読み込んでいる足の次に表示するパラボリックSAR描画値を計算し、下降トレンドにおけるシークエンスを終了させています。

最後の行に出てくる、MathMax関数引数1と引数2のうち、より大きい方の値を戻り値として返す関数です。

GetHigh関数とGetLow関数の宣言・定義

これが、最後のセクションです。

GetHigh関数は、上昇トレンド→下降トレンド変換時のパラボリックSAR描画値を算出する関数です。

逆に

GetLow関数は、下降トレンド→上昇トレンド変換時のパラボリックSAR描画値を算出する関数になります。

//+------------------------------------------------------------------+
//| Find highest price from start to current position                |
//+------------------------------------------------------------------+
double GetHigh(int curr_pos,int start,const double& high[])
//第1引数は読込足、第2引数はリバースポイント、第3引数は高値(OnCalculateで引数にあてがわれている配列)
  {
   double result=high[start];//リバースポイントの高値を格納
//ループチェック範囲はリバースポイントの次から読み込み済み足マイナス1まで(要は直近付近まで)
   for(int i=start+1; i<=curr_pos; i++)
      if(result<high[i])//読込足の高値がリバースポイントの高値より大きければ
         result=high[i];//読込足の高値をresultに格納
//---
   return(result);
  }
//+------------------------------------------------------------------+
//| Find lowest price from start to current position                 |
//+------------------------------------------------------------------+
double GetLow(int curr_pos,int start,const double& low[])
//第1引数は読込足、第2引数はリバースポイント、第3引数は安値(OnCalculateで引数にあてがわれている配列)
  {
   double result=low[start];
//ループチェック範囲はリバースポイントの次から読み込み済み足マイナス1まで(要は直近付近まで)
   for(int i=start+1; i<=curr_pos; i++)
      if(result>low[i])//読込足の安値がリバースポイントの安値より小さければ
         result=low[i];//読込足の安値をresultに格納
//---
   return(result);
  }

引数は3つで、

第1仮引数のint curr_pos→読込足、メインプログラムでは変数posが実引数

第2仮引数のint start→リバースポイント、メインプログラムでは変数ExtLastRevPosが実引数

第3仮引数のconst double& low[]→描画値

をそれぞれ格納します。

第3引数のconst double& low[]はOnCalculate関数で使われているものですね。

読み取り専用であるconstキーワード参照渡しであることを示すアンパサンド(&)がついています。

参照渡しについては→コチラ

constキーワードについては→コチラ

※仮引数と実引数については、MQL5 EA講座 第25回「関数について」の<補足  -仮引数と実引数->をお読みください<m(__)m>

for文のループチェック範囲は、

ループチェック範囲はリバースポイントの次から読み込み済み足マイナス1まで、ということになります。

メインプログラムで使われている変数「ExtLastRevPos」というのは、トレンド転換時のインデックスを格納しているのですが、

上昇トレンド→下降トレンド転換時は、その直前まで高値を切り上げる動きがあるので、それを

if(result<high[i])//読込足の高値がリバースポイントの高値より大きければ
result=high[i];//読込足の高値をresultに格納

という記述で表しています。

変数resultに格納された値をreturnで返して、メインプログラムにてリバースポイントのパラボリックSAR描画値として利用されています。

GetLow関数も仮引数や記述構造はGetHigh関数と同じです。


引数は3つで、

第1仮引数のint curr_pos→読込足、メインプログラムでは変数posが実引数

第2仮引数のint start→リバースポイント、メインプログラムでは変数ExtLastRevPosが実引数

第3仮引数のconst double& high[]→描画値

をそれぞれ格納します。

下降トレンド→上昇トレンド転換時は、その直前まで安値を切り下げる動きがあるので、それを

if(result>low[i])//読込足の安値がリバースポイントの安値より小さければ
result=low[i];//読込足の安値をresultに格納

という記述で表しています

変数resultに格納された値をreturnで返して、メインプログラムにてリバースポイントのパラボリックSAR描画値として利用されています。

まとめ

全体のコードに、和訳文をあてたものは以下の通りになります。

ログ出力のメッセージを日本語に置き換えた以外は、コメントアウトして和訳文を付け加えただけです。メインのコード部分は元のものをいじっていません。



//--- indicator settings
#property indicator_chart_window//メインチャートに描画する
#property indicator_buffers 3//使用バッファは3である
#property indicator_plots   1//プロットバッファは1である
#property indicator_type1   DRAW_ARROW//描画バッファタイプはアローである
#property indicator_color1  DodgerBlue//描画バッファのカラーはDodgerBlueである

//--- パラメーター
input double InpSARStep=0.02;    // ステップ幅
input double InpSARMaximum=0.2;  //最大値

//バッファ格納用の配列を宣言
double ExtSARBuffer[];//描画バッファ
double ExtEPBuffer[];//最大値格納バッファ
double ExtAFBuffer[];//加速因子格納バッファ

//各種グローバル変数の宣言
int    ExtLastRevPos;//リバースポイントを格納
bool   ExtDirectionLong;//上昇トレンドフラグを格納変数
double ExtSarStep;//ステップ幅を格納
double ExtSarMaximum;//最大値を格納


//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
void OnInit()
  {
//もしステップパラが0.0以下になっていたら
   if(InpSARStep<0.0)
     {
      //ステップパラを0.02にする
      ExtSarStep=0.02;
      //パラメーター修正のログ出力
      PrintFormat("ステップ幅のパラメーターが不正確な値です。インディケータの計算値として %d を採用します。",
                  ExtSarStep);
     }
   else
      ExtSarStep=InpSARStep;//異常値ではないときは、インプットパラをステップ幅とする

   if(InpSARMaximum<0.0)//最大値パラが0.0以下になっていたら
     {
      ExtSarMaximum=0.2;//最大値パラを0.2にする
      //パラメーター修正のログ出力
      PrintFormat("最大値のパラメーターが不正確な値です。インディケータの計算値として %d を採用します。",
                  ExtSarMaximum);
     }
   else
      ExtSarMaximum=InpSARMaximum;//異常値ではないときは、インプットパラを最大値とする

//バッファと配列の紐づけ設定
   SetIndexBuffer(0,ExtSARBuffer);//描画用バッファと配列の紐づけ
   SetIndexBuffer(1,ExtEPBuffer,INDICATOR_CALCULATIONS/*最大値:(中間計算のための補助バッファ*/);
   SetIndexBuffer(2,ExtAFBuffer,INDICATOR_CALCULATIONS/*加速因子:(中間計算のための補助バッファ*/);

//アロー設定(採用しているのはバリアント1)
   PlotIndexSetInteger(0,PLOT_ARROW,159);
//桁数に関する設定
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits);

//表示ラベルに関する設定(最大値とステップ幅に表示)
   string short_name=StringFormat("SAR(%.2f,%.2f)",ExtSarStep,ExtSarMaximum);
   PlotIndexSetString(0,PLOT_LABEL,short_name);

//グローバル変数の初期化
   ExtLastRevPos=0;//リバースインデックスを格納 初期値は0
   ExtDirectionLong=false;//上昇トレンドの検知フラグ 初期値はfalse
  }

//+------------------------------------------------------------------+
//| 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<3)//過去データ数が3より小さければ
      return(0);//終了

//読み込み位置を格納する変数を定義
   int pos=prev_calculated-1;//読み込み位置の定義
//--- correct position

   if(pos<1)
     {
      //最初に通るときは下降トレンド用にセット
      pos=1;
      ExtAFBuffer[0]=ExtSarStep;//加速因子用バッファの最新にステップ幅を格納
      ExtAFBuffer[1]=ExtSarStep;//加速因子用バッファの1本前にステップ幅を格納
      ExtSARBuffer[0]=high[0];//描画用バッファ(バッファ0)に最新足高値を代入
      ExtLastRevPos=0;//リバースポジションは0に
      ExtDirectionLong=false;//上昇トレンドフラグはfalseに
      ExtSARBuffer[1]
         =GetHigh(pos,ExtLastRevPos,high);
      //1本前のSAR値は下降トレンド時のパラボリック関数 第1引数は読込足、第2引数はリバースポイント、第3引数は高値
      //描画用バッファ(バッファ0)の1本前はオリジナル関数で取得した値を代入

      ExtEPBuffer[0]=low[pos];//最大値格納バッファ(バッファ2)には最安値を格納
      ExtEPBuffer[1]=low[pos];//最大値格納バッファ(バッファ2)には最安値を格納
     }

//ループチェック(pos=prev_calculated-1 から始まり、rates_total-1まで読み込む)   
   /*ArraySetAsSeries()を設定していないので、直近の方がインデックスが大きくなっている*/

   for(int i=pos; i<rates_total-1 && !IsStopped()/*強制シャットダウンフラグがたっていなければ*/; i++)
     {
      //反転フェーズのチェック
      if(ExtDirectionLong)//上昇トレンドフラグがtrueなら
        {
         if(ExtSARBuffer[i]>low[i])//描画バッファ値が安値より大きければ(実際の価格が下がって、上昇トレンドドットにぶつかる)
           {
            // 下降トレンドモードへ。
            ExtDirectionLong=false;//上昇トレンドフラグをfalseへ
            ExtSARBuffer[i]=GetHigh(i,ExtLastRevPos,high);//描画バッファ値はオリジナル関数で取得する。
           
            ExtEPBuffer[i]=low[i];//計算用バッファ(バッファ2)には最安値を格納
            ExtLastRevPos=i;//チェック中の足のインデックスをExtLastRevPosに代入
            ExtAFBuffer[i]=ExtSarStep;//計算用バッファ(バッファ1)にステップ幅を代入
           }
        }
      else//上昇トレンドフラグがfalseなら(下降トレンド中)
        {
         if(ExtSARBuffer[i]<high[i])//描画バッファ値が高値より小さければ(実際の価格が上がって、価格トレンドドットにぶつかる)
           {
            //上昇トレンドモードに転換

            ExtDirectionLong=true;//フラグ値をfalseからtrueへ(上昇トレンド発生)
            ExtSARBuffer[i]=GetLow(i,ExtLastRevPos,low);//オリジナル関数で獲得する安値を描画バッファに代入
            
            ExtEPBuffer[i]=high[i];//計算用バッファ(バッファ2)には最大値には高値を設定
            ExtLastRevPos=i;//チェック中の足のインデックスをExtLastRevPosに代入
            ExtAFBuffer[i]=ExtSarStep;//計算用バッファ(バッファ1)にステップ幅を代入
           }
        }

      //トレンド継続パターンの記述(上昇)
      if(ExtDirectionLong)//継続して上昇トレンドフラグがtrueなら
        {
         //--- check for new High
         if(high[i]>ExtEPBuffer[i-1]//高値が一本前の最大値より大きく、
            &&
            i!=ExtLastRevPos)//チェックインデックスとリバースポイントがイコールでなければ
           {
            ExtEPBuffer[i]=high[i];//最大値は現在の高値とする
            ExtAFBuffer[i]=ExtAFBuffer[i-1]+ExtSarStep;//加速因子は1本前の値にステップ幅をプラスする
            if(ExtAFBuffer[i]>ExtSarMaximum)//現在の加速因子がExtSarMaximum(基本パラの指定した最大値)より大きくなったら
               ExtAFBuffer[i]=ExtSarMaximum;//加速因子は最大値とする
           }
         else//( high[i]>ExtEPBuffer[i-1]&&i!=ExtLastRevPos)の論理積条件を満たさないとき→リバース条件を満たさないとき
           {
            //--- when we haven't reversed
            if(i!=ExtLastRevPos)//チェックインデックスとリバースポイントがイコールではないとき(トレンド転換ではない)
              {
               ExtAFBuffer[i]=ExtAFBuffer[i-1];//加速因子は一本前の加速因子となる
               ExtEPBuffer[i]=ExtEPBuffer[i-1];//最大値は一本前の最大値となる
              }
           }
         //--- calculate SAR for tomorrow 次足のSAR計算
         //次足のSAR描画バッファ= 読込足のSAR描画バッファ + <読込足の加速因子*(読込足の最大値-読込足のSAR描画バッファ)>
         ExtSARBuffer[i+1]=ExtSARBuffer[i]+ExtAFBuffer[i]*(ExtEPBuffer[i]-ExtSARBuffer[i]);
         //--- check for SAR
         if(ExtSARBuffer[i+1]>low[i] || ExtSARBuffer[i+1]>low[i-1])
            //次足のSAR描画バッファが読込足の安値より大きい、もしくは//次足のSAR描画バッファが読込足のひとつ前の安値より大きい時
            ExtSARBuffer[i+1]=MathMin(low[i],low[i-1]);
         //読込足のSAR描画バッファ値は、読込足の安値と、読込足のひとつ前の安値のより小さい方の値となる。
        }
      else////トレンド継続パターンの記述(下降)
        {
         //--- check for new Low
         if(low[i]<ExtEPBuffer[i-1] //安値が1本前の最大値より小さく、
            &&//
            i!=ExtLastRevPos)////チェックインデックスとリバースポイントがイコールでなければ
           {
            ExtEPBuffer[i]=low[i];//最大値は読込足の安値になる
            ExtAFBuffer[i]=ExtAFBuffer[i-1]+ExtSarStep;//読込足の加速因子は1本前の加速因子にパラの加速因子を足したものになる
            if(ExtAFBuffer[i]>ExtSarMaximum)//加速因子が最大値を超えた場合は
               ExtAFBuffer[i]=ExtSarMaximum;//加速因子は最大値となる。
           }
         else//(価格が上昇し下降トレンドドットにタッチし、かつ読込足がリバースポイントでないとき)を満たしていないとき
           {
            //--- when we haven't reversed
            if(i!=ExtLastRevPos)//価格が上昇し下降トレンドドットにタッチしたが、読込足がリバースポイントでないとき
              {
               ExtAFBuffer[i]=ExtAFBuffer[i-1];//加速因子は一本前の加速因子となる
               ExtEPBuffer[i]=ExtEPBuffer[i-1];//最大値は1本前の最大値となる
              }
           }
         //--- calculate SAR for tomorrow次足のSAR計算
         //次足のSAR描画バッファ= 読込足のSAR描画バッファ + <読込足の加速因子*(読込足の最大値-読込足のSAR描画バッファ)>
         ExtSARBuffer[i+1]=ExtSARBuffer[i]+ExtAFBuffer[i]*(ExtEPBuffer[i]-ExtSARBuffer[i]);
         
         //--- check for SAR
         if(ExtSARBuffer[i+1]<high[i] || ExtSARBuffer[i+1]<high[i-1])
          //次足のSAR描画バッファが読込足の高値より小さい、もしくは//次足のSAR描画バッファが読込足のひとつ前の高値より小さい時
            ExtSARBuffer[i+1]=MathMax(high[i],high[i-1]);
          //読込足のSAR描画バッファ値は、読込足の高値と、読込足のひとつ前の高値のより大きい方の値となる。
        }
     }
//--- OnCalculate done. Return new prev_calculated.
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Find highest price from start to current position                |
//+------------------------------------------------------------------+
double GetHigh(int curr_pos,int start,const double& high[])
//第1引数は読込足、第2引数はリバースポイント、第3引数は高値(OnCalculateで引数にあてがわれている配列)
  {
   double result=high[start];//リバースポイントの高値を格納
//ループチェック範囲はリバースポイントの次から読み込み済み足マイナス1まで(要は直近付近まで)
   for(int i=start+1; i<=curr_pos; i++)
      if(result<high[i])//読込足の高値がリバースポイントの高値より大きければ
         result=high[i];//読込足の高値をresultに格納
//---
   return(result);
  }
//+------------------------------------------------------------------+
//| Find lowest price from start to current position                 |
//+------------------------------------------------------------------+
double GetLow(int curr_pos,int start,const double& low[])
//第1引数は読込足、第2引数はリバースポイント、第3引数は安値(OnCalculateで引数にあてがわれている配列)
  {
   double result=low[start];
//ループチェック範囲はリバースポイントの次から読み込み済み足マイナス1まで(要は直近付近まで)
   for(int i=start+1; i<=curr_pos; i++)
      if(result>low[i])//読込足の安値がリバースポイントの安値より小さければ
         result=low[i];//読込足の安値をresultに格納
//---
   return(result);
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+

割と思い付きで書き始めたのですが、結構なボリュームになってしまいました。

一つのプログラム完成物を解説していこうとすれば、必然的に網羅的にいろんな知識・概念について話が及ぶであろうことは予測しておくべきでしたが、書いていて楽しくもありました(^^♪

最後までお読みいただきありがとうございました <m(__)m>

パラボリックSARを使って、動的トレーリングストップを実現させる記述を解説している講座記事も宜しければご参照ください↓

※また、インジケ-タに関しては当サイトのメインコンテンツである、「MQL5でEAを作ろう講座」でも、以下の記事群で丁寧に解説をしておりますので、参考にして頂ければと思います↓

———————————————————————————-

—————————————————————

-<PR>-

※【おススメのMT5MQL5対応のFX業者】

MQL5を使って自作したEAシステムトレードに利用するには、取引プラットフォームとしてMT5を提供しているFX会社に口座を開設しなくてはいけません。

当サイトでは以下のFX会社での口座開設・EA運用をおススメしています。

おススメする理由の詳細につきましては、各FX会社について解説する記事を書いておりますので、下記のリンク記事を参考にしていただければと思います。


外為ファイネスト

EAの利用制限なし。スキャルピングもOK

外為ファイネストに関する記事は↓をご覧ください。

アヴァトレードジャパン

自動売買界最狭スプレッド水準

アヴァトレードジャパンに関する記事は↓をご覧ください。

フィリップ証券

口座開設でMT5専用EAを無料プレゼント

フィリップ証券に関する記事は↓をご覧ください。

コメント

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