English Русский 中文 Español Deutsch Português
拡散と収束の自動探索

拡散と収束の自動探索

MetaTrader 5 | 9 11月 2017, 07:39
2 044 0
Dmitry Fedoseev
Dmitry Fedoseev

内容

はじめに

「拡散」という用語は、ラテン語の"divergere" (「不一致を検出する」)に由来します。拡散は、通常、指標の読みと価格の動きの不一致として定義されます。その反意語は、ラテン語の"convergo"(「集結する」)に由来する「収束」です。「隠れ拡散」、「拡張拡散」、A、B、Cクラスの拡散などの定義を含む、より広範な拡散/収束分類システムが存在します。

本稿ではまず、拡散と収束という基本的な用語について説明します。次に、他の分類方法に取り組み、比較分析を行い、それらの長所と短所を特定します。最後に、より完璧で明らかな欠点を持たない独自の分類だけでなく、チャート上の収束/拡散を検索して表示するための普遍的な指標を開発します。 

拡散と収束(概念の定義)

拡散とは指標値と価格の動きが一致しないことです。収束は逆の意味を持つ2番目の用語です。この論理によれば、拡散が指標値と価格の動きの不一致であるならば、収束は一致を意味します。しかし、一致(conformity)は収束(convergence)と同等ではないため、これは違います。 

拡散と収束の両方の用語がより正確な意味を持つためには、より狭い定義が必要です。価格チャートと指標チャートを見てみましょう。指標が下降しているのに価格が上昇すると、拡散が生じます。指標が上昇しているのに価格が下降すると、収束が生じます(図1)。


図1 価格と指標の動きの不一致:左では価格が上昇し
指標が下降(拡散)、右では価格が下降し指標が上昇(収束)

我々は指標が通常価格チャートの下に置かれているという事実に慣れているので、この定義は一目で受け入れられるようです。しかし、指標チャートと価格チャートの位置を反対にすると、すべてが根本的に変化します。つまり、拡散は収束になります(図2)。


図2 価格と指標の動きの不一致:左では価格が上昇し
指標が下降(収束)、右では価格が下降し指標が上昇(拡散)

さて、取引の方向を選択するという観点から、図1と図2を見てください。指標が下降している間に価格が上昇して、売ることにしたとします。同じように、指標が上昇している間に価格が下降したときには売るべきです(図3)。


図3 左:売り条件、価格と指標が拡散。右:買い条件(売り条件と同様)、価格と指標が収束  

売買の条件は同じで逆ですが、価格と指数は売りの場合に拡散して買いの場合は収束します。よって、条件の1つを弱気と呼び、2つ目は強気と呼ぶことにします。これは、収束と拡散を定義するには価格チャートの下の指標の位置では不十分であることを意味します。 

提供されるすべての定義は売り方向についてで、買いの場合とは反対です。しかし、テクニカル分析の本質に基づいたより簡単でより正確な定義のバージョンがあります。拡散と収束の定義は、将来的な価格変動の仮定を加えることによって単純かつ簡潔になるのです。

拡散とは、指標値と価格の移動方向の不一致という形での価格反転シグナルです。

これは反転シグナルなので、価格はまず買いのために上昇してから売りのために下降するべきです。上記の不一致が現れるようにするには、指標は上下に動く必要があります。この定義では、例として売り方向が使用され、指標チャートは価格チャートの下にあるべきです。図3に示すような拡散はこの定義に従います。 

収束は逆のケースを表します。指標が上に移動している間に価格が下降するはずです。しかし、価格方向の予測は変わりません。したがって、以下が収束の定義となります。

収束とは、指標値と価格変動の方向が一致しないという形でのトレンド継続シグナルです。

これは継続シグナルなので、価格は売りのために下降してから買いのために上昇するべきです。指標はそれぞれの場合に上と下に移動するべきです(図4)。


図4 収束シグナル

もちろん、拡散が逆転シグナルであり、収束は継続シグナルであると主張することはできますが、これはすでにテクニカル分析の可能性の実用化の問題です。

図5では、用語を理解できるように拡散シグナルと収束シグナルを同時に表示します。


図5 拡散及び収束シグナル

 

価格と指標の移動方向を決定する方法

指標と価格チャートのラインはこれまでのところまっすぐでした。しかし、これは、実質価格の動きとは関係のない抽象的なものです。したがって、価格と指標の方向を定義し、拡散を検出するために使用できる方法を検討してみましょう。次に、実際の拡散分類システムを見ていきます。

一般的には、まずチャート上の価格や上端と下端を特定し、それらの値を比較する必要があります。強気の拡散(買いシグナル)は下端によって検出されます。指標はある下端が前の下端よりも高い場合に上向きになり、その逆も当てはまります。弱気の拡散(売りシグナル)は上端によって検出されます。    

チャート上の極端点を検出する方法は3つあります。

  1. バー
  2. 最後の高値/安値からの閾値が突破される
  3. 最大/最小値が指標の中心線の上/下 

バーによる上端/下端の検出:ここでは上端/下端バーの数を使います。たとえば、パラメータが2の場合、上端バーの指標値は、左右2つずつのバーを超えているはずです。したがって、下端の値は、隣接するバーの値よりも低いはずです(図6)。


図6 2つのバーを使った上端と下端の定義:左が上端の定義(上端であることは、矢印のついたバーで
明らかになる)、右が下端の定義

上端/床の左右に必要なバーの数は、たとえば左の5、右の2など、さまざまです(図7)。


図7 左の5バーと右の2バーで定義された上端 

閾値を使った上端/床の定義:指標が上に移動すると、システムは最大値を追跡します。新しい極値のないバーでは、現在の値が以前の上端/床と比較されます。差が外部パラメータによって設定された閾値を超えると指標が方向を変えたとみなされ、最大/最小値に達したバーは上端/床とみなされます(図8)。


図8 閾値を使った上端/床の定義:閾値は左上に表示
指標はバー2まで上昇してバー2で最大値に達し、バー5では閾値分だけ値が減少し、その逆転が意味される。
指標はバー6では再び閾値を突破して逆転する。

バーを使った定義は指標に全く依存しないので最も便利です。<逆に、閾値の値は指標の種類に依存します。例えば、振動範囲が0〜100のRSIの場合、閾値は約5にすることができ、モメンタムでは、指標が100のレベル付近でわずかに変動するため、閾値は0.1-1です。その上、これらの変動の大きさは時間枠に依存するため、閾値の使用はさらに複雑になります。

最大値/最小値が指標の中心線の上/下:この方法は、他の方法より使用されません。すべての指標の平均値が0であるわけではない(RSIの平均値は50など)ため、この方法は適用する指標にも依存します。しかし、その主な欠点は遅れのひどさです(図9)。 


図9 中心線の横切りによる上端/下端の定義:上端1は中心線がバー2を横切った後で初めて明らかになり、
下端3はバー2を横切った後で初めて明らかになる。


拡散分類システム

拡散に関する多くの記事はweb上で見つけることができます。それらは、拡散と収束を体系化する上で用語と原則の両方で異なる様々なアプローチを記述しています。シンプルで拡散、古典的拡散、隠れ拡散、そして拡張拡散がみられます。これはA、B、Cのクラスに分けられます。本稿では、主要な情報源を分析するつもりはありません。代わりに、特定されたタイプのいくつかを見てみましょう。

古典的拡散:このタイプはすでに上で説明されており、図5に示されています。

隠れ拡散:隠れ拡散は、古典拡散とは、価格変動と指標の動きの方向によって異なります。言い換えれば、隠れ拡散は収束と似ています。

拡張拡散:これまでのところ、価格と指標の動きの上下方向についてのみ議論してきました。水平移動を追加すると、オプションの数が増えます。価格の動きの3つの方向と指標の動きの3つの方向を組み合わせることによって得られる複数のオプションにもかかわらず、拡張拡散の1つのバージョンのみが選別されています。

  • 水平方向の価格変動、指標の下降 - 弱気の拡張拡散(売りシグナル)
  • 水平方向の価格変動、指標の上昇 - 強気の拡張拡散(買いシグナル)

クラス:A、B、C :Аクラスは古典的拡散ですが、BクラスとCクラスは拡張拡散バージョンです。

Bクラス: 

    • 水平方向の価格変動、指標の下降 - 弱気(売りシグナル)
    • 水平方向の価格変動、指標の上昇 - 強気(買いシグナル)

Сクラス: 

    • 価格上昇、指標の上端が1つのレベルにとどまる - 弱気(売りシグナル)
    • 価格下降、指標の下端が1つのレベルにとどまる - 強気(買いシグナル)

ご覧のように、BクラスとСクラスは拡張拡散の方法です。Bクラスの定義は上記と完全に同じです。

拡散についての利用できる資料に目を通しながら私が下した主な結論は、明確な用語が不足しているということと可能なバージョンの範囲が不完全ということです。したがって、ここでは価格と指標の方向性を組み合わせるためのさまざまな選択肢を分析し、それらを体系化します。

価格と指標の動きの完全体系化

まず、価格と指標の2つの可能な動きの方向を定義しましょう。

  1. 2つの移動方向:上下 
  2. 3つの移動方向:上下及び水平方向

1.で可能な組み合わせは4つのみです。売りシグナルを例にして見てみましょう。

  1. 価格上昇、指標上昇 
  2. 価格上昇、指標下降(拡散)
  3. 価格下降、指標上昇(収束)
  4. 価格下降、指標下降  

方向を定義したので、これらのオプションを視覚化しましょう(図10)。 


図10 2方向が利用できる場合の価格と指標の動きのすべての可能な組み合わせ 

3方向の場合は9つの組み合わせが存在する可能性があります。

  1. 価格上昇、指標上昇
  2. 価格上昇、指標水平方向
  3. 価格上昇、指標下降(拡散)
  4. 価格水平方向、指標上昇
  5. 価格水平方向、指標水平方向
  6. 価格水平方向、指標下降
  7. 価格下降、指標上昇(収束)
  8. 価格下降、指標水平方向
  9. 価格下降、指標下降

これらの組み合わせはすべて図11に示されています。


図11 3方向が利用できる場合の価格と指標の動きの可能な組み合わせ 

考慮されたオプションのすべてを選択できる指標を作成すると、正しいと思われる拡散、収束、隠れ拡散または拡張拡散を選択することができます。言い換えれば、本稿で提示されている体系化と定義に同意しない人々にも役立つような普遍的な指標の作成が可能です。

トリプル拡散

これまでのところ、価格の方向性と指標の動きは、上下の2つの点と水平方向で定められていました。これに3つ目の点を追加して、価格と指標の動きに可能なオプションの数を増やすことがあります。オプションは合計で9あります。 

  1. 上昇、上昇
  2. 上昇、水平方向
  3. 上昇、下降
  4. 水平方向、上昇
  5. 水平方向、水平方向
  6. 水平方向、下降
  7. 下降、上昇
  8. 下降、水平方向
  9. 下降、下降

この場合、方向ではなく移動として話すほうが正しいでしょう。図12に3つの上端によって定義された移動タイプを示します。


図12 3つの上端に基づく様々な可能な動き 

下端に基づく適切な動きは図13に示されています。

 
図13 3つの下端に基づく様々な可能な動き 

9種類の価格の動きと9種類の指標の動きを組み合わせると、トリプル拡散には81のバリエーションがあることになります。

したがって、動きは任意の数の点で判断することができます。4つ目の点を追加すると、価格の動きと指標の動きは81種類、可能な組み合わせバージョンは6561(81 * 81)です。もちろん、可能なオプションが多いほど、その可能性は低いです。4つ目の点を適用する意味はおそらくないかもしれませんが、本稿の指標では、移動タイプを定義するために使用される点の数に制限はありません。

拡散を定義するためのユニバーサル指標

理論はここまでにして、指標を開発しましょう。 

オシレータの選択:拡散の定義が単一のオシレータによって制限されないようにこの記事で説明されている汎用指標を使用します。iUniOsc(汎用指標)とiUniOscGUI(同じ指標でグラフィカルインターフェース付き)が添付されていますが、ここでは基本的なiUniOscを使います。

新しい指標の作成: MetaEditorで新しいiDivergence指標を作成しましょう。OnCalculate関数を使用しますが、OnTimer()関数は必要ありません。"Indicator in separate window" (別ウィンドウでの指標)オプションにチェックを入れます。オシレータの表示ラインと、拡散が発生したときに矢印を描くための2つの矢印バッファの、計3つのバッファを作成します。新しいファイルをエディタで開いて、バッファ名を1 — buf_osc、2 — buf_buy、3 — buf_sellに変更します。 バッファ名は、配列が宣言されている場所、及びOnInit()関数でも変更される必要があります。バッファープロパティー(indicator_label1、indicator_label2、indicator_label3)を調整することもできます。これらのプロパティ値はデータウィンドウで表示されるとともに、ラインや指標ラベルの上にマウスを置くとツールヒントに表示されます。 これらを"osc"、"buy"、"sell"と呼ぶことにしましょう。

汎用指標の適用: iUniOsc指標のすべての外部パラメータを新しい指標に挿入します。ColorLine1、ColorLine2、及びColorHistoパラメータは、プロパティウィンドウでは必要ないので非表示にします。Typeパラメータには、UniOsc/UniOscDefines.mqhファイルで説明されているカスタムOscUni_RSIタイプがあります。このファイルをインクルードします。TypeパラメータはデフォルトではOscUni_ATR(ATR指標)に設定されていますが、ATRは価格の移動方向に依存しないので、拡散を定義するのには適していません。よって、デフォルトをOscUni_RSI(RSI指標)にします。

#include <UniOsc/UniOscDefines.mqh>

input EOscUniType          Type              =  OscUni_RSI;
input int                  Period1           =  14;
input int                  Period2           =  14;
input int                  Period3           =  14;
input ENUM_MA_METHOD       MaMethod          =  MODE_EMA;
input ENUM_APPLIED_PRICE   Price             =  PRICE_CLOSE;   
input ENUM_APPLIED_VOLUME  Volume            =  VOLUME_TICK;   
input ENUM_STO_PRICE       StPrice           =  STO_LOWHIGH;   
      color                ColorLine1        =  clrLightSeaGreen;
      color                ColorLine2        =  clrRed;
      color                ColorHisto        =  clrGray;

汎用オシレータのハンドルを外部変数の下に宣言します。

int h;

OnInit()関数の先頭に汎用オシレータをダウンロードします。

h=iCustom(Symbol(),Period(),"iUniOsc", Type,
                                       Period1,
                                       Period2,
                                       Period3,
                                       MaMethod,
                                       Price,
                                       Volume,
                                       StPrice,
                                       ColorLine1,
                                       ColorLine2,
                                       ColorHisto);
if(h==INVALID_HANDLE){
   Alert("Can't load indicator");
   return(INIT_FAILED);
}

OnCalculate()関数で、汎用オシレータのデータをbuf_oscバッファにコピーします。

int cnt;   

if(prev_calculated==0){
   cnt=rates_total;
}
else{ 
   cnt=rates_total-prev_calculated+1; 
}

if(CopyBuffer(h,0,0,cnt,buf_osc)<=0){
   return(0);
}  

この段階で、iDivergence指標をチャートに取り付けることで、実行されたアクションの正確性を検証できます。すべてが正しく行われると、サブウィンドウにオシレーターのラインが表示されます。 

オシレータの極値の定義:極値を定義する3つの方法は既に考察されました。指標にそれらを含め、いずれかが選択できます(ドロップダウンリスト付きの外部変数)。IncludeフォルダにUniDiverフォルダを作成し、コードを含むすべての追加ファイルを中に保存します。UniDiver/UniDiverDefines.mqhインクルードファイルを作成してEExtrType列挙を書き入れます。

enum EExtrType{
   ExtrBars,
   ExtrThreshold,
   ExtrMiddle
};

列挙オプションは下記の通りです。

  • ExtrBars — バー
  • ExtrThreshold — 最後の高値/安値から閾値を突破
  • ExtrMiddle — 指標の中心線の上/下の最大値/最小値 

指標内でExtremum型の外部パラメータを作成してそれを他のすべての外部パラメータの上に挿入します。極値をバーで定義するには極値の左右のバーの数を表す2つのパラメータが必要な一方、閾値で定義するには閾値を計算するためのパラメータが必要です。

input EExtrType            ExtremumType      =  ExtrBars; // 極値の種類
input int                  LeftBars          =  2;        // ExtrBarsの左のバー数
input int                  RightBars         =  -1;       // ExtrBarsの右のバー数
input double               MinMaxThreshold   =  5;        // ExtrThresholdの閾値 

一度にRightBarsパラメータとLeftBarsパラメータの2つを使用することに加えて、そのうち1つを使用する可能性を実装しましょう。RightBarsはデフォルトで-1に等しいです。つまり、それは使用されておらず、2番目のパラメータの値が割り当てられます。

極値定義クラス:指標の操作中に極値定義メソッドを変更する必要はないため、 'if'オペレータと 'switch'オペレータの代わりにOOPを使用する方が合理的です。極値を定義する3つの方法について、基本クラスと3つの派生クラスを作成します。 指標を起動するときに、これらの派生クラスの1つを選択します。 これらのクラスは、極値を定義するためと拡散を見つけるために必要な作業の行うために使用されます。 それらは極値を定義する方法だけにおいて異なり、収束の定義はすべてで完全に同じです。したがって、拡散定義関数は基本クラスに配置され、派生クラスから呼び出されることになります。しかしまず、すべての指標の極値に簡単にアクセスできるようにする必要があります(「Wolfe波」本稿のジグザグの上端で行われたように)。

SExtremum構造体は、1つの極値に関するデータを格納するために使用されます。構造の説明はUniDiverDefinesにあります。

struct SExtremum{
   int SignalBar;
   int ExtremumBar;
   datetime ExtremumTime;
   double IndicatorValue;
   double PriceValue;
};

下記が構造体のフィールドです。

  • SignalBar — 極値の形成が明らかになったバー
  • ExtremumBar — 極値を有するバー
  • ExtremumTime — 極値を有するバーの時間
  • IndicatorValue — 極値での指標値
  • PriceValue — 極値指標のバーの価格

これらの構造体の2つの配列は、すべての上端と床についてのデータを格納するために使用され、基本クラスのメンバーとなります。

極値を定義するクラスはUniDiver/CUniDiverExtremums.mqhファイルにあり、基本クラス名はCDiverBaseです。今のところは、基本的メソッドだけを持つクラスの構造を考えてみましょう。残りは必要に応じて後で追加されます。

class CDiverBase{
   protected: 
      
      SExtremum m_upper[];
      SExtremum m_lower[];

      void AddExtremum( SExtremum & a[],
                        int & cnt,
                        double iv,
                        double pv,
                        int mb,
                        int sb,
                        datetime et);
   
      void CheckDiver(  int i,
                        int ucnt,
                        int lcnt,
                        const datetime & time[],
                        const double &high[],
                        const double &low[],                     
                        double & buy[],
                        double & sell[],
                        double & osc[]
      );

   public:
      virtual void Calculate( const int rates_total,
                              const int prev_calculated,
                              const datetime &time[],
                              const double &high[],
                              const double &low[],
                              double & osc[],
                              double & buy[],     
                              double & sell[]
      );
}; 

Calculate()仮想メソッドは極値定義オプションの選択を可能にします。AddExtremum()メソッドとCheckDiver()メソッドは 'protected'セクションに配置されて、派生クラスの Calculate()メソッドから呼び出されます。AddExtremum()メソッドは、上端と床に関するデータをm_upper[]及びm_lower[]配列に集めます。CheckDiver()メソッドは、拡散の条件が満たされているかどうかを確認し、指標の矢印を設定します。以下ではこれらのすべてのメソッドをより詳細に見ていきますが、先に他の極値定義メソッドの派生クラスについて学びましょう。

バーでの極値の定義:バーで極値を定義するクラス

class CDiverBars:public CDiverBase{
   private:   
   
      SPseudoBuffers1 Cur;
      SPseudoBuffers1 Pre;  
           
      int m_left,m_right,m_start,m_period;   
   
   public:
         
      void CDiverBars(int Left,int Right);
   
      void Calculate( const int rates_total,
                      const int prev_calculated,
                      const datetime &time[],
                      const double &high[],
                      const double &low[],
                      double & osc[],
                      double & buy[],
                      double & sell[]
      );
}; 

極値を定義するためのパラメータ(外部変数LeftBars及びRightBars)がクラスコンストラクタに渡され、必要に応じて、その値が検証され、変更され、追加のパラメータが計算されます。

void CDiverBars(int Left,int Right){
   m_left=Left;
   m_right=Right;   
   if(m_left<1)m_left=m_right;   // 左パラメータが設定されていない
   if(m_right<1)m_right=m_left;  // 右パラメータが設定されていない
   if(m_left<1 && m_right<1){    // パラメータが1つも設定されていない
      m_left=2;
      m_right=2;
   }
   m_start=m_left+m_right;       // 間隔の開始点をシフトする
   m_period=m_start+1;           // 間隔バーの数
}

初めにパラメータ値が検証されます。それらのいくつかが正ではない(設定されていない)場合、第2パラメータの値が割り当てられます。パラメータが1つも設定されていない場合は、デフォルト値が割り当てられます (2)。極値を検索するための初期バー (m_start) のインデックスと極限バーの総数 (m_period) は、その後計算されます。

Calculate()メソッドは、標準のOnCalculate()関数と同じですが、すべてではなく必要な time[]、high[]、low[]価格配列とosc[]指標バッファ(オシレータデータ)、buy[]及びsell[](矢印)を受け取ります。いつものように、計算されたバーの範囲はOnCalculte()関数で定義されています。指標の極値(ArrayMaximum()及びArrayMinimum()関数)は、その後の標準指標ループで定義されます。極値が検出された後は、AddExtremum()メソッドが呼び出されてデータがm_upper[]またはm_lower[]配列に追加されます。最終的にはCheckDiver()メソッドが呼び出され、配列のデータを極値で分析します。拡散が検出されると矢印が配置されます。

void Calculate( const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &high[],
                const double &low[],
                double & osc[],
                double & buy[],
                double & sell[]
){

   int start; // 初期バーインデックスのための変数
   
   if(prev_calculated==0){ // 完全な指標計算
      start=m_period; // 初期計算バーを定義する
      m_LastTime=0;   // 新しいバーを定義する変数をリセットする
      Cur.Reset();    // 補助的構造体をリセットする
      Pre.Reset();    // 補助的構造体をリセットする
   }
   else{ // 新しいバーのみを計算する場合
      start=prev_calculated-1; // 計算が再開されるバーのインデックスを計算する
   }

   for(int i=start;i<rates_total;i++){ // 指標のメインループ
      
      if(time[i]>m_LastTime){ // 新しいバー
         m_LastTime=time[i];
         Pre=Cur;
      }
      else{ // 同じバーを再計算する
         Cur=Pre;
      }
      
      // High/Low検索パラメータを計算する 
      int sb=i-m_start; // 間隔が始まるバーのインデックス
      int mb=i-m_right; // High/Lowを持つバーのインデックス
      
      if(ArrayMaximum(osc,sb,m_period)==mb){ // 上端がある
         // 配列に上端を追加する
         this.AddExtremum(m_upper,Cur.UpperCnt,osc[mb],high[mb],mb,i,time[mb]);
      }
      if(ArrayMinimum(osc,sb,m_period)==mb){ // 下端がある
         // 配列に下端を追加する
         this.AddExtremum(m_lower,Cur.LowerCnt,osc[mb],low[mb],mb,i,time[mb]);
      }
      
      // 拡散があるかを確認する
      this.CheckDiver(i,Cur.UpperCnt,Cur.LowerCnt,time,high,low,buy,sell,osc);
   } 
}

コードをもっと詳しくみてみましょう。指標ループの先頭には下記があります。

if(time[i]>m_LastTime){ // 新しいバー
   m_LastTime=time[i];
   Pre=Cur;
}
else{ // 同じバーの再計算
   Cur=Pre;
}

m_LastTime変数は基本クラスで宣言されています。time[i]バー時間が変数を超過すると、バーは初めて計算されます。バー時間はm_LastTime変数に割り当てられ、Cur変数の値はPre変数に割り当てられます。逆に、同じバーを再計算すると、Pre変数の値がCur変数に割り当てられます。Cur変数とPre変数の使用はこの記事で詳しく説明されています。 Pre変数とCur変数は、UniDiverDefinesファイルに記述されているSPseudoBuffers1型の変数です。

struct SPseudoBuffers1{
   int UpperCnt;
   int LowerCnt;
   void Reset(){
      UpperCnt=0;
      LowerCnt=0;  
   }   
};

構造体には2つのフィールドがあります。

  • UpperCount — 使用されるm_upper[]配列要素の数
  • LowerCount — 使用されるm_lower[]配列要素の数

Reset()メソッドはすべての構造フィールドを迅速にリセットするために作成されました。

Cur変数とPre変数を使用した後は、極値を検索するためのバーインデックスが計算されます。

// 上端/床を検索するためのパラメータを計算する 
int sb=i-m_start; // 間隔が始まるバーのインデックス
int mb=i-m_right; // 上端/床を持つバーのインデックス

上端/床の検索が始まるバーのインデックスが 'sb'変数に割り当てられます。High/Lowが配置されるバーのインデックスが'mb'変数に割り当てられます。

ArrayMaximum()/ArrayMinimum()関数を使って上端/下端を定義します。

if(ArrayMaximum(osc,sb,m_period)==mb){ // 上端がある
   // 配列に上端を追加する
   this.AddExtremum(m_upper,Cur.UpperCnt,osc[mb],high[mb],mb,i,time[mb]);
}
if(ArrayMinimum(osc,sb,m_period)==mb){ // 下端がある
   // 配列に下端を追加する
   this.AddExtremum(m_lower,Cur.LowerCnt,osc[mb],low[mb],mb,i,time[mb]);
}

ArrayMaximum()/ArrayMinimum()関数が 'mb'を返す場合、これは指定された数のバーが上端/下端の左右にあることを意味します。これは、所望の上端/下端が形成されたことを示しています。AddExtremum()メソッドが呼び出され、データがm_upper[]配列またはm_lower[]配列に追加されます。

単純なAddExtremum()メソッドを考えてみましょう。

void AddExtremum( SExtremum & a[], // データが追加される配列
                  int & cnt,       // 使用される配列要素数
                  double iv,       // 指標値
                  double pv,       // 価格
                  int mb,          // 極値を有するバーのインデックス
                  int sb,          // 極値が明らかになるバーのインデックス
                  datetime et      // 極値を有するバーの時間
){
   if(cnt>=ArraySize(a)){ // 配列がいっぱいになっている
      // 配列サイズを増加する
      ArrayResize(a,ArraySize(a)+1024);
   }
   // 新しいデータを追加する
   a[cnt].IndicatorValue=iv; // 指標値
   a[cnt].PriceValue=pv;     // 価格値
   a[cnt].ExtremumBar=mb;    // 極値を有するバーのインデックス
   a[cnt].SignalBar=sb;      // 極値が明らかになるバーのインデックス 
   a[cnt].ExtremumTime=et;   // 極値を有するバーの時間
   cnt++;                    // 使用済みの要素数のカウンタを増加する
}

新しいデータが追加されるa []配列は、パラメータを介してメソッドに渡されます。これはm_upper[]配列またはm_lower[]配列です。a[] 配列の使用された要素の数は、 'cnt'変数を介して渡されます。これはCur.UpperCnt変数またはCur.LowerCnt変数です。a[]配列と'cnt'変数はメソッドで変更されるので参照によって渡されます。 

iv - 極値に基づく指標値、pv - 極値を有するバーの価格、mb - 極値を有するバーのインデックス、sb-極値バー(極値の到着について知られている)、et - 極値を有するバーの時間

配列サイズはAddExtremum()メソッドの先頭で確認されます。配列がいっぱいになると、そのサイズは最高1024要素増加します。データが追加され、後で 'cnt'変数が増加します。

CheckDiver()メソッドはあとで見ます。

閾値を使った上端/床の定義

閾値で定義するクラスは、主にCur及びPre変数の型において、バーによって定義するクラスとは異なります。これはUniDiverDefines.mqhファイルで説明されているSPseudoBuffers2型です。

struct SPseudoBuffers2{
   int UpperCnt;
   int LowerCnt;
   double MinMaxVal;
   int MinMaxBar;   
   int Trend;
   void Reset(){
      UpperCnt=0;
      LowerCnt=0;  
      MinMaxVal=0;
      MinMaxBar=0;
      Trend=1;
   }   
};

SPseudoBuffers2構造体にはSPseudoBuffers1と同じフィールドといくつかの追加フィールドがあります。

  • MinMaxVal — 最大値または最小値のための変数
  • MinMaxBar — 指標の最大値または最小値が見つかったバーのインデックスのための変数
  • Trend — 指標の動きの方向のための変数1の値は、指標が上に移動し、その最大値が追跡されることを意味します。-1の場合は最小値が追跡されます。

閾値(MinMaxThreshold変数)を持つ外部パラメータは、クラスコンストラクタに渡されます。その値は 'private'セクションで宣言されているm_threshold変数に保存されます。  

このクラスのCalculate()メソッドは極値定義メソッドにおいて異なります。

switch(Cur.Trend){ // 指標の現在の方向
   case 1: // 上
      if(osc[i]>Cur.MinMaxVal){ // 新しい最大値
         Cur.MinMaxVal=osc[i];
         Cur.MinMaxBar=i;
      }
      if(osc[i]<Cur.MinMaxVal-m_threshold){ // 閾値が超えられた
         // 配列に上端を追加する
         this.AddExtremum(m_upper,Cur.UpperCnt,Cur.MinMaxVal,high[Cur.MinMaxBar],Cur.MinMaxBar,i,time[Cur.MinMaxBar]);
         Cur.Trend=-1;          // 追跡される方向を変更する
         Cur.MinMaxVal=osc[i];  // 初期の最小値
         Cur.MinMaxBar=i;       // 初期の最小値を有するバー
      }
   break;
   case -1: // 下
      if(osc[i]<Cur.MinMaxVal){ // 新しい最小値
         Cur.MinMaxVal=osc[i];
         Cur.MinMaxBar=i;
      }
      if(osc[i]>Cur.MinMaxVal+m_threshold){ // 閾値が超えられた
         // 配列に下端を追加する
         this.AddExtremum(m_lower,Cur.LowerCnt,Cur.MinMaxVal,low[Cur.MinMaxBar],Cur.MinMaxBar,i,time[Cur.MinMaxBar]);
         Cur.Trend=1;           // 追跡される方向を変更する
         Cur.MinMaxVal=osc[i];  // 初期の最大値
         Cur.MinMaxBar=i;       // 初期の最大値を有するバー 
      }         
   break;
}

Cur.Trend変数が1の場合、オシレータ値はCurve.MinMaxValue値と比較されます。新しいオシレータ値が変数からの値を超えると、変数値が更新されます。新しいHighが検出されたバーのインデックスは、Cur.MinMaxBar変数に割り当てられます。また、オシレータの値が最後の既知の最大値からm_threshold減少していないことも保証されます。もしそうであれば、オシレータの方向が逆転したことになります。AddExtremum()メソッドが呼び出され、新しい極値のデータが配列に保存され、Cur.Trend値が反対の値に置き換えられ、新しい最小値の初期パラメータがCur.MinMaxVal及びCur.MinMaxBarに設定されます。Cur.Trend変数の値が変更されているので、もう1つの 'case'セクション(オシレータの最小値を追跡して閾値を上回る)が実行されます。

オシレータの中点に対する相対位置による極値の定義:適用されたオシレータの型がクラスコンストラクタに渡されます。オシレータの中間値は型に応じて定義されます。

void CDiverMiddle(EOscUniType type){
   if(type==OscUni_Momentum){
      m_level=100.0;
   }
   else if(type==OscUni_RSI || type==OscUni_Stochastic){
      m_level=50.0;
   }
   else if(type==OscUni_WPR){
      m_level=-50.0;
   }
   else{
      m_level=0.0;
   }
}

値はモメンタムのでは100、RSIとストキャスティクスでは50、WPRでは-50、その他のオシレータでは0です。

極値を決定する方法は、多くの点で閾値方法と似ています。

switch(Cur.Trend){
   case 1:
      if(osc[i]>Cur.MinMaxVal){
         Cur.MinMaxVal=osc[i];
         Cur.MinMaxBar=i;
      }
      if(osc[i]<m_level){
         this.AddExtremum(m_upper,Cur.UpperCnt,Cur.MinMaxVal,high[Cur.MinMaxBar],Cur.MinMaxBar,i,time[Cur.MinMaxBar]);
         Cur.Trend=-1;
         Cur.MinMaxVal=osc[i];
         Cur.MinMaxBar=i;               
      }
   break;
   case -1:
      if(osc[i]<Cur.MinMaxVal){
         Cur.MinMaxVal=osc[i];
         Cur.MinMaxBar=i;
      }
      if(osc[i]>m_level){
         this.AddExtremum(m_lower,Cur.LowerCnt,Cur.MinMaxVal,low[Cur.MinMaxBar],Cur.MinMaxBar,i,time[Cur.MinMaxBar]);
         Cur.Trend=1;
         Cur.MinMaxVal=osc[i];
         Cur.MinMaxBar=i;
      }         
   break;
}

唯一の違いは、方向変更がオシレータの中間レベルとの比較によって行われることです(osc [i]<m_levelまたはosc [i]>m_level)。

拡散型設定メソッド:特定された拡散の型を選択するための変数を外部パラメータに追加します。

input int                  Number            =  3;

パラメータ値はデフォルトでは3です。これは図11によれば古典的拡散です。図11には、価格と指標の動きの合計9つの組み合わせが表示されています。もう1つのオプション 「未確認」を追加しましょう。これで組み合わせは10になります。したがって、通常の10進数を使用して、任意の数の異なる動きの任意の組み合わせを記述できます(トリプル拡散を含む)。1桁の数字は単純な拡散(2つの上端/下端)に対応し、2桁の数字は3つの数字などであることが分かります。例えば、13の数字は、図14に表示された組み合わせに対応します。


図14 Number=13 の場合の、売りのための指標と価格動向の組み合わせ 

拡散条件確認クラス:拡散条件を確認するための基本クラスと派生クラスが作成されます。Numberパラメータ値は指標の起動時に分析されます。最も重要な属性は、クラスへのポインタの配列のサイズに影響を及ぼす長さです。次に、条件の種類に従って各配列要素に対して適切なオブジェクトが生成されます。    

条件検証クラスはUniDiver/CUniDiverConditions.mqhファイルにあります。基本クラスはCDiverConditionsBaseと呼ばれています。そのコードを見てみましょう。

class CDiverConditionsBase{
   protected:
      double m_pt;
      double m_it;
   public:
   void SetParameters(double pt,double it){
      m_pt=pt;
      m_it=it;
   }
   virtual bool CheckBuy(double i1,double p1,double i2,double p2){
      return(false);
   }
   virtual bool CheckSell(double i1,double p1,double i2,double p2){
      return(false);
   }
};

クラスには、2つの隣接する上端を比較する2つの仮想メソッドがあります。それらのパラメータは中で渡されます。: 

  • i1 — ポイント1での指標値
  • p1 —ポイント1での価格値
  • i2 — ポイント2での指標値
  • p2 — ポイント2での価格値

ポイントは右から左に1から始まります。

追加の比較パラメータを設定するにはSetParameters()メソッドを使用します。pt は、価格ポイントが単一のレベルにあると想定される価格値の利用可能な差異で、it は指標の上端を比較するための同様のパラメータです。これらのパラメータ値はプロパティウィンドウで設定します。

input double               IndLevel          =  0;
input int                  PriceLevel        =  0;

派生クラスの1つのコードを以下に示します。

class CDiverConditions1:public CDiverConditionsBase{
   private:
   public:
   bool CheckBuy(double i1,double p1,double i2,double p2){
      return((p1>p2+m_pt) && (i1>i2+m_it));
   }
   bool CheckSell(double i1,double p1,double i2,double p2){
      return((p1<p2-m_pt) && (i1<i2-m_it));
   }
};

CheckBuy()メソッドでは、ポイント1の価格がポイント2を超過しているかどうかが確認されます。指標も同じです。ポイント1の価格はポイント2の値を超えなければなりません。CheckSell()メソッドはCheckBuy()メソッド と鏡像対称です。CDiverConditions0以外の他のすべてのクラスは、論理式でのみ異なります。CheckSell()及びCheckBuy()メソッドはクラスで一度に'true'を返します。それは条件確認が無効になっている場合(任意のオプションが可能)に使用されます。

拡散条件確認の準備:配列とそのサイズの変数は、CDiverBaseクラスの 'protected'セクションで宣言されています。

CDiverConditionsBase * m_conditions[];
int m_ccnt;  

SetConditions()メソッドでは、m_conditions配列サイズは変更され拡散条件検証オブジェクトが作成されます。

void SetConditions(int num,      // 拡散番号
                   double pt,    // PriceLevelパラメータ
                   double it){   // IndLevelパラメータ
   if(num<1)num=1; // 拡散番号は1以上である必要がある
   ArrayResize(m_conditions,10); // 条件の最大可能数
   m_ccnt=0; // 実際の条件数のカウンタ
   while(num>0){
      int cn=num%10; // 新しい極値対の間の拡散のバリエーション
      m_conditions[m_ccnt]=CreateConditions(cn); // オブジェクトを作成する
      m_conditions[m_ccnt].SetParameters(pt,it); // 条件検証パラメータを設定する
      num=num/10; // 次の条件に移動する
      m_ccnt++; // 条件数を数える
   }
   // 実際の条件数に従って配列サイズを修正する 
   ArrayResize(m_conditions,m_ccnt);    
}

メソッドには以下のパラメータが渡されます。

  • num — 外部パラメータ数
  • pt — PriceLevel外部パラメータ
  • it — IndLevel外部パラメータ

'num'パラメータが初めに確認されます。

if(num<1)num=1; // 拡散番号は1以上である必要がある

次に、m_conditions配列が可能な最大サイズ(10 — 'int'変数の最大値の長さ)まで増加されます。その後、CreateConditions()メソッドによる条件検証のオブジェクトが作成され、 'num'の各桁の値に応じてSetParameters()パラメータを使用してパラメータが 'while'ループで設定されます。 11786>配列サイズはループの後で適用された条件の実数に従って変化します。

CreateConditions()メソッドを考察しましょう。

CDiverConditionsBase * CreateConditions(int i){
   switch(i){
      case 0:
         return(new CDiverConditions0());      
      break;
      case 1:
         return(new CDiverConditions1());      
      break;
      case 2:
         return(new CDiverConditions2());      
      break;
      case 3:
         return(new CDiverConditions3());      
      break;
      case 4:
         return(new CDiverConditions4());      
      break;
      case 5:
         return(new CDiverConditions5());      
      break;      
      case 6:
         return(new CDiverConditions6());      
      break;
      case 7:
         return(new CDiverConditions7());      
      break;
      case 8:
         return(new CDiverConditions8());      
      break;
      case 9:
         return(new CDiverConditions9());
      break;
   }
   return(new CDiverConditions0()); 
}

このメソッドは簡単です。iパラメータに応じて適切なオブジェクトが作成され、そのオブジェクトへの参照が返されます。

拡散の定義: ここでCDiverBaseクラスのCheckDivergence()メソッドを考察します。まず、すべてのメソッドコードを検討し、それを1つずつ考えます。

void CheckDiver(  int i,                    // 計算されたバーのインデックス
                  int ucnt,                 // m_upper配列での上端の数
                  int lcnt,                 // m_lower配列での上端の数
                  const datetime & time[],  // バーの時間の配列 
                  const double &high[],     // 高バー価格の配列
                  const double &low[],      // 低バー価格の配列
                  double & buy[],           // 上向き矢印の指標バッファ
                  double & sell[],          // 下向き矢印の指標バッファ
                  double & osc[]            // オシレータ値の指標バッファ
){

   // 矢印バッファを消去する
   buy[i]=EMPTY_VALUE;
   sell[i]=EMPTY_VALUE;
   
   // グラフィカルオブジェクトを削除する
   this.DelObjects(time[i]);
   
   // 上端(売りシグナル)
   if(ucnt>m_ccnt){ // 十分な数の上端
      if(m_upper[ucnt-1].SignalBar==i){ // 計算されたバーで検出された上端

         bool check=true; // 拡散が生じたと仮定する
         
         for(int j=0;j<m_ccnt;j++){ // 上端のすべての部分で
            // 新しい上端の対の条件を満たしていることを確認する
            bool result=m_conditions[j].CheckSell( m_upper[ucnt-1-j].IndicatorValue,
                                                   m_upper[ucnt-1-j].PriceValue,
                                                   m_upper[ucnt-1-j-1].IndicatorValue,
                                                   m_upper[ucnt-1-j-1].PriceValue
                                                );
            if(!result){ // 条件が満たされない 
               check=false; // 拡散なし
               break; 
            } 
                                
         }
         if(check){ // 拡散が生じた
            // 指標バッファー矢印の設定
            sell[i]=osc[i];
            // 価格チャートの追加線や矢印を描画する
            this.DrawSellObjects(time[i],high[i],ucnt);
         }
      }
   }
   
   // 下端(買いシグナル)
   if(lcnt>m_ccnt){
      if(m_lower[lcnt-1].SignalBar==i){
         bool check=true;
         for(int j=0;j<m_ccnt;j++){
            bool result=m_conditions[j].CheckBuy(  m_lower[lcnt-1-j].IndicatorValue,
                                                   m_lower[lcnt-1-j].PriceValue,
                                                   m_lower[lcnt-2-j].IndicatorValue,
                                                   m_lower[lcnt-2-j].PriceValue
                                                );
            if(!result){
               check=false;
               break;
            }                                          
         }
         if(check){
            buy[i]=osc[i];
            this.DrawBuyObjects(time[i],low[i],lcnt);
         }
      }
   }    
}

メソッドには以下のパラメータが渡されます。

  • i — 現在の計算されたバーのインデックス
  • ucnt — 適用されたm_upper[]要素の数
  • lcnt — 適用されたm_lower[]要素の数
  • time[] — バーの時間の配列
  • high[] — 高バー価格の配列
  • low[] — 低バー価格の配列                  
  • buy[] — 買い矢印の指標バッファ
  • sell[] — 売り矢印の指標バッファ
  • osc[] — オシレータ値の指標バッファ

矢印バッファは初めに消去されます。

// 矢印バッファを消去する
buy[i]=EMPTY_VALUE;
sell[i]=EMPTY_VALUE;

計算されたバーに対応するグラフィカルオブジェクトが削除されます。

// グラフィカルオブジェクトを削除する
this.DelObjects(time[i]);

矢印とは別に、iDivergenceはグラフィックオブジェクトを適用して価格チャート上に矢印を表示し、価格チャートの極値とオシレータグラフの上端/下端を結ぶ線を表示します。 

次に、売りと買いの条件を確認するコードに2つの同一セクションがあります。最初に売りのセクションを考えてみましょう。使用可能なオシレータの上端数は、確認された条件の数より1多いはずです。よって、下記の確認がとられます。  

// 上端(売りシグナル)
if(ucnt>m_ccnt){ // 十分な数の上端がある 

}

その後、計算されたバーの上端が存在するかどうかを確認します。これは、計算されたバーのインデックスと、上端/下端のデータを含む配列からのインデックスとの間の対応によって決定されます。

if(m_upper[ucnt-1].SignalBar==i){ // 計算されたバーで検出された上端

}

条件を確認した結果には補助変数が必要です。

bool check=true; // 拡散が生じたと仮定する

'for'ループでは、上端のデータを渡す条件をすべて調べます。

for(int j=0;j<m_ccnt;j++){ // 全ての上端の対を通し
   // 次の上端の対が条件を満たしているかを確認する
   bool result=m_conditions[j].CheckSell( m_upper[ucnt-1-j].IndicatorValue,
                                          m_upper[ucnt-1-j].PriceValue,
                                          m_upper[ucnt-1-j-1].IndicatorValue,
                                          m_upper[ucnt-1-j-1].PriceValue
                                        );
   if(!result){ // 条件が満たされない 
      check=false; // 拡散なし
      break; 
   } 
}

いずれかの条件が満たされていない場合は、ループを終了します。'false'が 'check'変数に割り当てられます。すべての条件が満たされている場合は、'check'に'true'が保存され、矢印が設定され、グラフィックオブジェクトが作成されます。

if(check){ // 拡散が生じた
   // 指標バッファ矢印を配置する
   sell[i]=osc[i];
   // 価格チャートに補助線及び/または矢印を描く
   this.DrawSellObjects(time[i],high[i],ucnt);
}

グラフィカルオブジェクトの表示は、指標のプロパティウィンドウで有効または無効にすることができます。以下の変数はそのために宣言されています。

input bool                 ArrowsOnChart     =  true;
input bool                 DrawLines         =  true;
input color                ColBuy            =  clrAqua;
input color                ColSell           =  clrDeepPink;
  • ArrowsOnChart — 価格チャートでグラフィックオブジェクトからの矢印を有効にする
  • DrawLines — 価格と指標の上端と下橋をつなぐ線の描写を有効にする
  • ColBuy and ColSell — 買いシグナルと売りシグナルのグラフィックオブジェクトの色

対応する変数はCDiverBaseクラスの'protected'で宣言されます。

bool m_arrows;  // ArrowsOnChart変数に対応
bool m_lines;   // DrawLines変数に対応
color m_cbuy;   // ColBuy変数に対応
color m_csell;  // ColSell変数に対応

これらの変数の値はSetDrawParmeters()メソッドで設定されます。

void SetDrawParmeters(bool arrows,bool lines,color cbuy,color csell){
   m_arrows=arrows;
   m_lines=lines;
   m_cbuy=cbuy;
   m_csell=csell;
}

グラフィカルオブジェクトで動作するメソッドを考察します。削除:

void DelObjects(datetime bartime){ // 線の描画が有効
   if(m_lines){
      // 共通接頭辞を形成する
      string pref=MQLInfoString(MQL_PROGRAM_NAME)+"_"+IntegerToString((long)bartime)+"_";
      for(int j=0;j<m_ccnt;j++){ // 拡散条件数による
         ObjectDelete(0,pref+"bp_"+IntegerToString(j)); // 買いシグナルの場合の価格チャートの線
         ObjectDelete(0,pref+"bi_"+IntegerToString(j)); // 買いシグナルの場合の指標グラフの線
         ObjectDelete(0,pref+"sp_"+IntegerToString(j)); // 売りシグナルの場合の価格チャートの線
         ObjectDelete(0,pref+"si_"+IntegerToString(j)); // 売りシグナルの場合の指標グラフの線
      }            
   }
   if(m_arrows){ // 価格チャートで矢印が有効
      // 
      ObjectDelete(0,MQLInfoString(MQL_PROGRAM_NAME)+"_"+IntegerToString((long)bartime)+"_ba");
      // 
      ObjectDelete(0,MQLInfoString(MQL_PROGRAM_NAME)+"_"+IntegerToString((long)bartime)+"_sa");
   }
}

上端/下端と矢印を結ぶ線は別々に削除されます。すべてのグラフィカルオブジェクトの名前は、指標名から始まり、拡散が検出されたバーの時刻が続きます。名前は、m_ccnt条件の数に対応するループ内で形成され続けます。買いシグナルは、価格チャートでは"bp"、指標グラフでは"bi"とともに表示されます。似たように、売りシグナルには"sp"と"ip"が追加されます。名前の最後には jインデックスが追加されます。矢印の名前には"_ba"(買いシグナル矢印)または"_sa"(売りシグナル矢印)が追加されます。

グラフィカルオブジェクトの作成は、DrawSellObjects()及びDrawBuyObjects()メソッドで実行されます。そのうちの1つを考えてみましょう。

void DrawSellObjects(datetime bartime,double arprice,int ucnt){
   if(m_lines){ // 線の表示を有効にする
      
      // 共通接頭辞を形成する
      string pref=MQLInfoString(MQL_PROGRAM_NAME)+"_"+IntegerToString((long)bartime)+"_";
      
      for(int j=0;j<m_ccnt;j++){  // すべての拡散条件による
                  
         // 価格チャート上の線
         fObjTrend(  pref+"sp_"+IntegerToString(j),
                     m_upper[ucnt-1-j].ExtremumTime,
                     m_upper[ucnt-1-j].PriceValue,
                     m_upper[ucnt-2-j].ExtremumTime,
                     m_upper[ucnt-2-j].PriceValue,
                     m_csell);
                     
         // 指標グラフ上の線
         fObjTrend(  pref+"si_"+IntegerToString(j),
                     m_upper[ucnt-1-j].ExtremumTime,
                     m_upper[ucnt-1-j].IndicatorValue,
                     m_upper[ucnt-2-j].ExtremumTime,
                     m_upper[ucnt-2-j].IndicatorValue,
                     m_csell,
                     ChartWindowFind(0,MQLInfoString(MQL_PROGRAM_NAME)));  
      }
   }
      
   if(m_arrows){ // 価格チャートで矢印が有効
      fObjArrow(MQLInfoString(MQL_PROGRAM_NAME)+"_"+IntegerToString((long)bartime)+"_sa",
               bartime,
               arprice,
               234,
               m_csell,
               ANCHOR_LOWER); 
   }            
} 

オブジェクト名は削除時と同じ方法で形成されます。その後、fObjTrend()及びfObjArrow()関数を使用してグラフィカルオブジェクトが作成されます。 これらはUniDiver/UniDiverGObjects.mqhインクルードファイルにあります。これらの関数はかなりシンプルなので、解析する必要はありません。

指標の完成:ここでは作成されたクラスを指標に適用するだけです。選択された極値を定義する型に応じて、OnInit() 関数で適切なオブジェクトを作成します。

switch(ExtremumType){
   case ExtrBars:
      diver=new CDiverBars(LeftBars,RightBars);
   break;
   case ExtrThreshold:
      diver=new CDiverThreshold(MinMaxThreshold);
   break;      
   case ExtrMiddle:
      diver=new CDiverMiddle(Type);
   break;      
}

外部パラメータの1つ(PriceLevel)はポイント単位で測定されるので、小数点以下の桁数に応じて修正するのが便利です。これを行うには、この修正を無効にできるように別の変数を宣言します。

input bool                 Auto5Digits       =  true;

次に、修正されたパラメータの補助変数を宣言し、OnInit()関数で調整します。

int pl=PriceLevel;   
if(Auto5Digits && (Digits()==5 || Digits()==3)){
   pl*=10;
}  

拡散パラメータと表示パラメータを設定します。

diver.SetConditions(Number,Point()*pl,IndLevel);
diver.SetDrawParmeters(ArrowsOnChart,DrawLines,ColBuy,ColSell);

OnCalculate()関数にはいくつかの文字列が残ります。基本的なCalculate()メソッドを呼び出します。

diver.Calculate(  rates_total,
                  prev_calculated,
                  time,
                  high,
                  low,
                  buf_osc,
                  buf_buy,
                  buf_sell);

図形オブジェクトを適用する場合は、描画を高速化する必要があります。

if(ArrowsOnChart || DrawLines){
   ChartRedraw();
}

指標の作業が完了したら、図形オブジェクトを削除する必要があります。これはCDiverBaseクラスのデストラクタで行われます。拡散条件検証オブジェクトもそこで削除されます。

void ~CDiverBase(){
   for(int i=0;i<ArraySize(m_conditions);i++){ // すべての条件による
      if(CheckPointer(m_conditions[i])==POINTER_DYNAMIC){
         delete(m_conditions[i]); // オブジェクトを削除する
      }      
   }  
   // グラフィカルオブジェクトを削除する
   ObjectsDeleteAll(0,MQLInfoString(MQL_PROGRAM_NAME));
   ChartRedraw();          
}   

この時点で、指標開発の主要段階は終わりです。図15は、(サブウィンドウ内に)指標が取り付けられたチャートを表示し、価格チャート上の矢印の表示を有効にし、上端の間に線をプロットします。 


図15 価格チャートに表示された、矢印と極値を結ぶ線を持った拡散指標

これでアラート機能を追加するだけです。それは非常に簡単で、すでに他の記事で説明されています。以下の添付ファイルでは、アラート機能と指標に必要なすべてのファイルを含む既製の指標が見つかります。

終わりに

指標の多用途性にもかかわらず、その欠点にも目が行きます。 主な欠点は、IndLevelパラメータの適用されたオシレータの型への依存性、及びPriceLevelパラメータの時間枠への依存性です。これらのパラメータのデフォルト値は、依存関係を除外するために0となっています。同時に、価格の動きと指標の動きの特定の組み合わせの条件を満たすことはほとんど不可能です。 拡散検証に水平移動の確認が含まれている場合、その実装は考えられません。この場合、拡散オプション1、3、7、9が残ります。 これは、指標を使用してEAを最適化するためにテスターを適用する場合にのみの問題かもしれません。

最適化は通常1つの銘柄と時間枠で実行されるため、アプローチが正しければこれは問題にはなりません。 まず、適用する銘柄と時間枠を決定し、PriceLevelパラメータに適切な値を設定しなければなりません。 次に、適用するオシレーターを選択し、IndLevelパラメーターに適切な値を設定します。適用するオシレータの型とPriceLevel及びIndLevelパラメータ値の自動最適化は、他にも多くの最適化パラメータがあるため、それほど重要ではありません。一番主要なのは、拡散の型(Number変数)とオシレータの期間です。

添付ファイル

iDivergenceの動作には「GUIによる汎用的なオシレーター」の汎用オシレータが必要です。オシレータと必要なすべてのファイルは、アプリケーション内にあります。

下記はすべての添付ファイルです。

  • Include/InDiver/CUniDiverConditions.mqh — 拡散条件を確認するためのクラスを含むファイル
  • Include/InDiver/CUniDiverExtremums.mqh — 極値を定義するためのクラスを持つファイル 
  • Include/InDiver/UniDiverDefines.mqh — 構造体と列挙体の説明
  • Include/InDiver/UniDiverGObjects.mqh — グラフィカルオブジェクトを操作するための関数
  • Indicators/iDivergence.mq5 — 指標
  • Indicators/iUniOsc.mq5 — 汎用オシレータ
  • Include/UniOsc/CUniOsc.mqh — 汎用オシレータクラスのファイル
  • Include/UniOsc/UniOscDefines.mqh — 汎用オシレータの構造体と列挙体の説明

MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/3460

添付されたファイル |
MQL5.zip (13.96 KB)
クロスプラットフォームEA: CExpertAdvisor と CExpertAdvisors クラス クロスプラットフォームEA: CExpertAdvisor と CExpertAdvisors クラス
この記事では、クロスプラットフォームのEAについて扱っています。主にクラス CExpertAdvisor と CExpertAdvisors は、この記事で説明した他のすべてのコンポーネントのコンテナとして機能します。
クロスプラットフォームEA: カスタムストップ、ブレイクイーブン、トレーリング クロスプラットフォームEA: カスタムストップ、ブレイクイーブン、トレーリング
この記事では、クロスプラットフォームEAでのカスタムストップレベルの設定方法について説明します。 また、時間の経過とともにストップレベルを設定するメソッドについても説明します。
適応型相場の実用的評価法 適応型相場の実用的評価法
この記事で提案するトレーディングシステムは、株価を分析するための数学的ツールです。 ディジタルフィルタリングと離散時系列のスペクトル推定を適用します。 戦略の理論的側面について説明し、テストEAを作成します。
CGraphic ライブラリを使用したスキャルピング相場深度の実装 CGraphic ライブラリを使用したスキャルピング相場深度の実装
この記事では、スキャルピング相場深度ツールの基本的な関数を作成します。 また、CGraphic ライブラリーをベースにしたティック・チャートを開発し、オーダーブックと統合します。 記述された相場深度を使用して、短期トレードの強力なアシスタントツールを作成することが可能になります。