English Русский 中文 Español Deutsch Português
同時に2方向で機能するためのユニバーサル RSI インジケータ

同時に2方向で機能するためのユニバーサル RSI インジケータ

MetaTrader 5 | 22 10月 2018, 08:17
2 610 0
Anatoli Kazharski
Anatoli Kazharski

コンテンツ

イントロダクション

トレーディングアルゴリズムを開発するとき、ほとんど常にある問題が発生します。つまり、トレンド/レンジの開始と終わりを決定する方法です。 完璧なソリューションを見つけることは困難です。 ひとつの指標として、1つのアルゴリズムでトレンドベースとレンジベースの両方の戦略を組み合わせることが可能です。 この記事では、さまざまなタイプの戦略に対してシグナルを結合するユニバーサルインジケータを作成します。 EAのトレードシグナルの生成をできるだけ簡素化します。 1つのインジケータを組み合わせた例を挙げます。 オブジェクト指向プログラミングを使用し、各インジケータでメインプログラムファイルに含まれるクラスの形式で実装します。 

したがって、ここでのタスクは、2つのトレード戦略を組み合わせ、そのEAを書くことです。 2方向の同時トレードがより効果的であり、その戦略がより安定していると仮定します。 その後、両方の戦略タイプのシグナルを生成するEAに、1つだけインジケータを含めます。 内部では、売買シグナルを決定するための複雑なシステムを実装することができます。 異なる設定を持つ複数の同一のインジケータを1つにまとめる必要があるかもしれません。 または、インジケータにメインとフィルタリング (補助)などの異なるものを含めることも必要かもしれません。 このようなスキームを OOP を使用して実装すると便利です。

以下のスキームは、メイン (Indicator.mq5)に2つのインジケータクラス (CIndicator1CIndicator2) をインクルードすることを示します。 CIndicator2は補助インジケータで、 CIndicator1には計算結果が必要です。 ここでは、マルチシンボルインジケータの作成についての記事で前に説明した、真の足を決定するためのメソッドを適用します。この記事では、別のCFirstTrueBarクラスが作成されています。 現在の時間枠に属さない足の計算を避けるために、すべてのインジケータにインクルードします。

 図1. OOP を使用してインジケータを作成するための可能なスキームの1つ

図1. OOP を使用してインジケータを作成するための可能なスキームの1つ


インジケータの選択

シグナル生成には、標準端子パッケージのインジケータを選択できます。 ほとんどは同じような考え方であり、基本的に、他のよりも有用と言い切れません。 インジケータとフィルターの組み合わせは、一定の時間間隔で有効ですが、他の組み合わせも他の異なる間隔で有用です。

しかし、研究の利便性のため、オシレータータイプのインジケータを選択することをお勧めします。 トレンドとレンジの両方でシグナルを決定するために使用することができます。 また、オシレーターデータを使用して価格チャネルをプロットすることもできます。 結果として、複雑なトレード戦略を開発するときに便利です。ユニバーサルインジケータを作成する関数を取得します。

RSI (相対強度指数) インジケータは、この記事シリーズの例として使用します。 下記は、 AUDUSD H1チャートで8の期間を持つこのインジケータの計算結果です。 

 図2. 相対強度指数インジケータ

図2. 相対強度指数インジケータ

一見すると、このインジケータのシグナルに基づいて利益を受け取ることは簡単です。 でもそれは幻想です。 おおよその理解で次の新しいレベルに到達する前に、多くのやるべきタスクがあります。 同時に、目標を達成できる保証はありません。 

最もシンプルで最も明白なケースを考えてみましょう: インジケータ線がデフォルトのレベル70/30を越えるときに利益を受け取ることができると思います: 。 レベル70に下向きにクロスしている場合、売りシグナルと見なされます。 レベル30に上向きにクロスしている場合は、買いシグナルと見なされます。 しかし、ポジションを取った方向とは反対側に行ってしまい、ダマシシグナルになることもあります。 

このインジケータのシグナルを解析するもう1つの例を示します (図3参照)。 価格が長い間、下に移動していることがわかります。 赤い線は、インジケータがレベル30を上方に横切るときに形成されるシグナルです。 このアルゴリズムに基づいた買いポジションがある場合、フローティングドローダウンが発生します。 ポジションにSLを設定している場合、毎回損失が発生することになります。 同時に、直近の買いシグナルが現れてから、価格がプラスの結果になることはありません。 結果はマイナスです。 また、このチャートのこのセグメントは、明確なものがないことを意味し、トレンドに沿ってトレードしていることを示唆しています。

インジケータを使用する場合も同様の問題が発生します。 したがって、どれを選択するかという問題ではありません。

 図3. RSI インジケータによるシグナル

図3. RSI インジケータによるシグナル


RSI インジケータの変更

利便性のため、選択したインジケータに追加して、後でEAから稼働できるようにします。 RSI の5つのバージョンを作成し、連続的にシンプルから複雑にします (理解の容易さのため).

最初のバージョン シグナルバッファの追加

RSI の標準バージョンを、ターミナルのMQL5\Indicators\Examplesディレクトリにポジションします。 コピーを作成し、修正をしましょう。 固定パラメータの一覧に2つのインジケータバッファを追加します。 合計数も5に等しくなり、 3がチャートに表示されます。 2つのバッファは補助計算用に予約されたままです。 買いシグナルのラベルは、緑色 (clrMediumSeaGreen)、および赤色 (clrRed) でマークされ、売りシグナルになります。 

//---プロパティ
#property indicator_separate_window
#property indicator_minimum 0
#property indicator_maximum 100
#property indicator_buffers 5
#property indicator_plots   3
#property indicator_color1  clrSteelBlue
#property indicator_color2  clrMediumSeaGreen
#property indicator_color3  clrRed

シグナルラベルのコードを定義します。 ドットを表示する必要がある場合、コードは159です。 シグナルが矢印として表示される場合は、コード233234をそれぞれ使用します。

//---シグナルのための矢: 159-点;233/234-矢印
#define ARROW_BUY  159
#define ARROW_SELL 159

インジケータライン境界のクロスは、両方とも売買シグナルとして機能します。 したがって、外部パラメータには、インジケータシグナルのメソッドを指定するために使用できる列挙体が必要です。

  • Break in—範囲内の境界のブレイクアウト。 下の境界の上向きのブレイクアウトは買いシグナル、上の境界の下向きのブレイクアウトは売りシグナルです。
  • Break in reverse-範囲への境界のブレイクアウト (インパルスに対して)。 ' ブレイクイン ' モードと同じシグナルが、売買の条件が逆になります。
  • Break out—範囲外の境界のブレイクアウト。 上部の境界の上のブレイクアウトは買いシグナル、下の境界の下のブレイクアウトは売りシグナルです。 
  • Break out reverse—範囲外の境界のブレイクアウト (インパルスに対して)。 ' ブレイクアウト ' モードと同じシグナルですが、売買の条件が逆になります。

すべてのモードは、下のチャートに表示されます。

//---チャネル境界ブレイクアウトのモードを使用した列挙体
enum ENUM_BREAK_INOUT
  {
   BREAK_IN          =0, //ブレイクイン
   BREAK_IN_REVERSE  =1, //逆のブレイク
   BREAK_OUT         =2, //ブレイクアウト
   BREAK_OUT_REVERSE =3  //ブレイクアウトリバース
  };

このインジケータには、合計で3つの外部パラメータがあります。

  • RSI Period—インジケータ期間;
  • Signal Level—インジケータレベル;
  • Break Mode—レベルブレイクアウトモード。

//インプットパラメータ
input  int              PeriodRSI   =8;         //RSI の期間
input  double           SignalLevel =30;        //シグナルレベル
input  ENUM_BREAK_INOUT BreakMode   =BREAK_OUT; //モードブレイク

このインジケータのプロパティは、 SetPropertiesIndicator() 関数で設定されます。 補助配列は最後に設定されます。 すべてのインジケータ配列は、 ZeroIndicatorBuffers()関数のゼロ値で初期化されます。 次に、ゼロ値をチャートに表示しないように指定し、その値が空にします。

//+------------------------------------------------------------------+
//| Sets indicator properties                                        |
//+------------------------------------------------------------------+
void SetPropertiesIndicator(void)
  {
//--- Short name
   ::IndicatorSetString(INDICATOR_SHORTNAME,"RSI_PLUS1");
//--- Decimal places
   ::IndicatorSetInteger(INDICATOR_DIGITS,2);
//--- Indicator arrays
   ::SetIndexBuffer(0,rsi_buffer,INDICATOR_DATA);
   ::SetIndexBuffer(1,buy_buffer,INDICATOR_DATA);
   ::SetIndexBuffer(2,sell_buffer,INDICATOR_DATA);
   ::SetIndexBuffer(3,pos_buffer,INDICATOR_CALCULATIONS);
   ::SetIndexBuffer(4,neg_buffer,INDICATOR_CALCULATIONS);
//配列の初期化
   ZeroIndicatorBuffers();
//テキストラベルを設定
   string plot_label[]={"RSI","buy","sell"};
   for(int i=0; i<indicator_plots; i++)
      ::PlotIndexSetString(i,PLOT_LABEL,plot_label[i]);
//インジケータ配列の幅を設定
   for(int i=0; i<indicator_plots; i++)
      ::PlotIndexSetInteger(i,PLOT_LINE_WIDTH,1);
//インジケータ配列の型を設定
   ENUM_DRAW_TYPE draw_type[]={DRAW_LINE,DRAW_ARROW,DRAW_ARROW};
   for(int i=0; i<indicator_plots; i++)
      ::PlotIndexSetInteger(i,PLOT_DRAW_TYPE,draw_type[i]);
//---ラベルコード
   ::PlotIndexSetInteger(1,PLOT_ARROW,ARROW_BUY);
   ::PlotIndexSetInteger(2,PLOT_ARROW,ARROW_SELL);
//計算を開始する要素のインデックス
   for(int i=0; i<indicator_plots; i++)
      ::PlotIndexSetInteger(i,PLOT_DRAW_BEGIN,period_rsi);
//--- The number of the indicator's horizontal levels
   ::IndicatorSetInteger(INDICATOR_LEVELS,2);
//インジケータの水平レベルの値
   up_level   =100-SignalLevel;
   down_level =SignalLevel;
   ::IndicatorSetDouble(INDICATOR_LEVELVALUE,0,down_level);
   ::IndicatorSetDouble(INDICATOR_LEVELVALUE,1,up_level);
//線のスタイル
   ::IndicatorSetInteger(INDICATOR_LEVELSTYLE,0,STYLE_DOT);
   ::IndicatorSetInteger(INDICATOR_LEVELSTYLE,1,STYLE_DOT);
//何も描画されない場所をプロットするための空の値
   for(int i=0; i<indicator_buffers; i++)
      ::PlotIndexSetDouble(i,PLOT_EMPTY_VALUE,0);
//Y 軸に沿ってシフト
   if(BreakMode==BREAK_IN_REVERSE || BreakMode==BREAK_OUT_REVERSE)
     {
      ::PlotIndexSetInteger(0,PLOT_ARROW_SHIFT,arrow_shift);
      ::PlotIndexSetInteger(1,PLOT_ARROW_SHIFT,-arrow_shift);
     }
   else
     {
      ::PlotIndexSetInteger(0,PLOT_ARROW_SHIFT,-arrow_shift);
      ::PlotIndexSetInteger(1,PLOT_ARROW_SHIFT,arrow_shift);
     }
  }

便宜のため、 RSIインジケータの値の予備と主な計算は、 PreliminaryCalculations() とCalculateRSI() を分離した関数に移動されます。 その内容は、標準パッケージのRSIインジケータと同じです。 CalculateSignals() インジケータシグナルを決定するための関数だけを考えてみましょう。 ここでは、外部パラメータで設定されたモードに応じて、最初に条件をチェックします。 その後、条件が満たされている場合は、 RSIインジケータの値は、対応するインジケータ配列に格納されます。 条件が満たされない場合は、ゼロ値が保存され、チャートに表示されません。

//+------------------------------------------------------------------+
//| Calculate the indicator signals                                  |
//+------------------------------------------------------------------+
void CalculateSignals(const int i)
  {
   bool condition1 =false;
   bool condition2 =false;
//チャネルにブレイクアウト
   if(BreakMode==BREAK_IN || BreakMode==BREAK_IN_REVERSE)
     {
      condition1 =rsi_buffer[i-1]<down_level && rsi_buffer[i]>down_level;
      condition2 =rsi_buffer[i-1]>up_level && rsi_buffer[i]<up_level;
     }
   else
     {
      condition1 =rsi_buffer[i-1]<up_level && rsi_buffer[i]>up_level;
      condition2 =rsi_buffer[i-1]>down_level && rsi_buffer[i]<down_level;
     }
//条件が満たされた場合にシグナルを表示
   if(BreakMode==BREAK_IN || BreakMode==BREAK_OUT)
     {
      buy_buffer[i]  =(condition1)? rsi_buffer[i] : 0;
      sell_buffer[i] =(condition2)? rsi_buffer[i] : 0;
     }
   else
     {
      buy_buffer[i]  =(condition2)? rsi_buffer[i] : 0;
      sell_buffer[i] =(condition1)? rsi_buffer[i] : 0;
     }
  }

その結果、インジケータ関数のコード やOnCalculate ()OnInit()などは、きちんとして読みやすくなります。

//+------------------------------------------------------------------+
//|カスタムインジケータ初期化関数                                        |
//+------------------------------------------------------------------+
void OnInit(void)
  {
//外部パラメータ値を確認
   if(PeriodRSI<1)
     {
      period_rsi=2;
      Print("Incorrect value for input variable PeriodRSI =",PeriodRSI,
            "Indicator will use value =",period_rsi,"for calculations.");
     }
   else
      period_rsi=PeriodRSI;
//---設定インジケータプロパティ
   SetPropertiesIndicator();
  }
//+------------------------------------------------------------------+
//|相対強度指数                                                       |
//+------------------------------------------------------------------+
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[])
  {
//--- Leave, if the data are insufficient
   if(rates_total<=period_rsi)
      return(0);
//---予備計算
   PreliminaryCalculations(prev_calculated,close);
//メインの計算ループ
   for(int i=start_pos; i<rates_total && !::IsStopped(); i++)
     {
      //---RSI インジケータを計算する
      CalculateRSI(i,close);
      //シグナルを計算
      CalculateSignals(i);
     }
//最後に計算された要素数を返す
   return(rates_total);
  }

このインジケータをチャートに移して、何が起きたかを確認します。 すべての4つの操作モード (外部パラメータBreak Mode) の結果です。

 図4. 変更された RSI インジケータ操作のデモンストレーション。

図4. 変更された RSI インジケータ操作のデモンストレーション。

RSIインジケータのこの変更後、生成するダマシシグナルの数は減っています。 チャートを分析すると、このインジケータはレンジで、時にはトレンドのトレードに良いと結論づけています。 

  • モードBreak inは、 RSIの方向にレンジでトレードするために設計されています。
  • Break in reverseは、 RSIの方向に対するトレンドに沿ってトレードするために設計されています。
  • ブレイクアウトモードは、 RSIの方向にトレンドに沿ってトレードするために設計されています。
  • ブレイクアウトリバースモードは、 RSIの方向に対してレンジでトレードするために設計されています。

このインジケータのシグナルは、レンジでのみ、またはトレンドの間にのみトレードできるでしょうか? どのようにすればポジティブな結果とエントリの精度を高めることができるでしょうか? 連続シリーズの最初のシグナルによってポジションが開かれていなければ、結果を改善することができます。

2番目のバージョン シグナルカウンタのバッファの追加

1方向の連続シグナルの数が表示されるインジケータバッファを追加してみましょう。 逆のシグナルが表示されるとすぐに、前のバッファのカウンタがゼロになり、現在の一連のシグナルのカウンタがアクティブになります。 実装については、コードを少し補足します。

特定のパラメータに変更があります。 新しい数のバッファを指定し、プロット用のシリーズを作成し、色を設定します。

//---プロパティ
...
#property indicator_buffers 7
#property indicator_plots   5
...
#property indicator_color4  clrMediumSeaGreen
#property indicator_color5  clrRed

グローバルスコープで、カウンタと2つの対応する補助変数の値を表示する2つの追加の配列を追加します。

//--- Indicator arrays
...
double buy_counter_buffer[];
double sell_counter_buffer[];
//連続シグナルシーケンスのカウンタ
int buy_counter  =0;
int sell_counter =0;

残りの変更は、シグナルを決定するための関数だけを考慮します。 ここで、条件が満たされた場合 (次の売買シグナルが表示されます)、対応するカウンタがトリガされ、反対側のシグナル系列のカウンタがリセットされます。 現在の足のカウンタを増やさないためには、最後の不完全な足でのトリガをスキップする必要があります。

//+------------------------------------------------------------------+
//| Calculate the indicator signals                                  |
//+------------------------------------------------------------------+
void CalculateSignals(const int i,const int rates_total)
  {
   int last_index=rates_total-1;
//---
   bool condition1 =false;
   bool condition2 =false;
//チャネルにブレイクアウト
   if(BreakMode==BREAK_IN || BreakMode==BREAK_IN_REVERSE)
     {
      condition1 =rsi_buffer[i-1]<down_level && rsi_buffer[i]>down_level;
      condition2 =rsi_buffer[i-1]>up_level && rsi_buffer[i]<up_level;
     }
   else
     {
      condition1 =rsi_buffer[i-1]<up_level && rsi_buffer[i]>up_level;
      condition2 =rsi_buffer[i-1]>down_level && rsi_buffer[i]<down_level;
     }
//条件が満たされた場合にシグナルを表示
   if(BreakMode==BREAK_IN || BreakMode==BREAK_OUT)
     {
      buy_buffer[i]  =(condition1)? rsi_buffer[i] : 0;
      sell_buffer[i] =(condition2)? rsi_buffer[i] : 0;
      //--- Counter only by completely formed bars
      if(i<last_index)
        {
         if(condition1)
           {
            buy_counter++;
            sell_counter=0;
           }
         else if(condition2)
           {
            sell_counter++;

            buy_counter=0;
           }
        }
     }
   else
     {
      buy_buffer[i]  =(condition2)? rsi_buffer[i] : 0;
      sell_buffer[i] =(condition1)? rsi_buffer[i] : 0;
      //--- Counter only by completely formed bars
      if(i<last_index)
        {
         if(condition2)
           {
            buy_counter++;
            sell_counter=0;
           }
         else if(condition1)
           {
            sell_counter++;
            buy_counter=0;
           }
        }
     }
//最後の値の補正 (2番目に等しい)
   if(i<last_index)
     {
      buy_counter_buffer[i]  =buy_counter;
      sell_counter_buffer[i] =sell_counter;
     }
   else
     {
      buy_counter_buffer[i]  =buy_counter_buffer[i-1];
      sell_counter_buffer[i] =sell_counter_buffer[i-1];
     }
  }

変更が行われたら、インジケータをチャートにアタッチして、何が起こったかを確認します。 RSIはインジケータがより有益になりました。 カウンタの値は、買いと売りの追加条件を形成するためにEAで受け取ることができます。

 図5. 同じ方向の連続的なシグナルのカウンターが付いている変更された RSI のインジケータ。

図5. 同じ方向の連続的なシグナルのカウンターが付いている変更された RSI のインジケータ。

すべてが意図したとおりに動作するようにするには、テスターとリアルタイムで両方のインジケータを確認してください。 ストラテジーテスターの結果:

 図6. ストラテジーテスターで変更された RSI インジケータの動作を確認します。

図6. ストラテジーテスターで変更された RSI インジケータの動作を確認します。

次のスクリーンショットは、ブレイクモードパラメータのすべての操作モードのインジケータ値を示します。

 図7. ブレイクモードパラメータのすべての操作モードのインジケータ。

図7. ブレイクモードパラメータのすべての操作モードのインジケータ。

価格が2つの類似したシグナルの間でかなり大きな距離を移動する状況があるかもしれません。 これは非常に頻繁に、任意のタイムフレームで発生します。 同時に、シグナルレベルの外部パラメータを必要なだけ変更し、範囲の境界を設定することで、問題を解決することはできません。 この不確実性は明確なトレードロジックの作成を妨げ、追加条件を書く必要性を複雑にします。 

 図8. 大きな価格の動きのシグナルをスキップ

図8. 大きな価格の動きのシグナルをスキップ

次のバージョンでは、このようなスキップを排除し、インジケータをさらに詳しく説明します。  

3番目のバージョン シグナル数の増加、スキップの排除

このバージョンでは、インジケータバッファの数は変わりません。 しかし、インジケータがラインを横切るときにシグナルを生成するために、インジケータレベルの配列を追加する必要があります。  

//インジケータの水平レベルとそのの値
double up_level          =0;
double down_level        =0;
int    up_levels_total   =0;
int    down_levels_total =0;
//水平レベルの配列 
double up_levels[];
double down_levels[];

前のセクションで述べたように、シグナルのスキップを避けるために、レベルは5ポイントごとに設定されます。 つまり、シグナルレベルの外部パラメータに値30が指定されている場合、上位レベルに対して次の値が計算されます:70、75、80、85、90、95。 

インジケータレベルは、 GetLevelsIndicator() 関数によって計算されます。 2つの別個のループは、配列に配置されるレベルの値を計算します。 この関数は、レベルの合計数を返します。

//+------------------------------------------------------------------+
//| Return the indicator levels                                      |
//+------------------------------------------------------------------+
int GetLevelsIndicator(void)
  {
   int levels_counter=0;
   double level=down_level;
//--- Lower levels down to the lower limit
   while(level>0 && !::IsStopped())
     {
      int size=::ArraySize(down_levels);
      ::ArrayResize(down_levels,size+1);
      down_levels[size]=level;
      level-=5;
      levels_counter++;
     }
   level=up_level;
//--- Upper levels up to the upper limit
   while(level<100 && !::IsStopped())
     {
      int size=::ArraySize(up_levels);
      ::ArrayResize(up_levels,size+1);
      up_levels[size]=level;
      level+=5;
      levels_counter++;
     }
//---
   return(levels_counter);
  }

レベルは関数SetPropertiesIndicator() で設定されます。 その短縮版を以下に示します。 ここでは、上位と下位の範囲の初期レベルが最初に計算され、レベル配列がゼロになります。 次に、 GetLevelsIndicator() 関数を呼び出すことによって、インジケータレベルの合計数が設定されます。 その後、上位と下位の範囲の計算レベルが配列から設定されます。

//+------------------------------------------------------------------+
//| Sets indicator properties                                        |
//+------------------------------------------------------------------+
void SetPropertiesIndicator(void)
  {
...
//--- Calculation of the first levels
   up_level   =100-SignalLevel;
   down_level =SignalLevel;
//--- Zeroing the level arrays
   ::ArrayFree(up_levels);
   ::ArrayFree(down_levels);
//--- The number of the indicator's horizontal levels
   ::IndicatorSetInteger(INDICATOR_LEVELS,GetLevelsIndicator());
//--- Values of the indicator's horizontal levels of the lower level
   down_levels_total=::ArraySize(down_levels);
   for(int i=0; i<down_levels_total; i++)
      ::IndicatorSetDouble(INDICATOR_LEVELVALUE,i,down_levels[i]);
//--- Values of the indicator's horizontal levels of the upper level
   up_levels_total=::ArraySize(up_levels);
   int total=up_levels_total+down_levels_total;
   for(int i=down_levels_total,k=0; i<total; i++,k++)
      ::IndicatorSetDouble(INDICATOR_LEVELVALUE,i,up_levels[k]);
...
  }

したがって、 CalculateSignals() 関数に変更を加える必要があります。 関数の変更された部分だけがここにも表示されます。 サイクルで条件が満たされているかどうかを確認するには、配列内の少なくとも1つのレベルがクロスしているかどうかを参照してください。 

//+------------------------------------------------------------------+
//| Calculate the indicator signals                                  |
//+------------------------------------------------------------------+
void CalculateSignals(const int i,const int rates_total)
  {
   int last_index=rates_total-1;
//---
   bool condition1 =false;
   bool condition2 =false;
//チャネルにブレイクアウト
   if(BreakMode==BREAK_IN || BreakMode==BREAK_IN_REVERSE)
     {
      if(rsi_buffer[i]<50)
        {
         for(int j=0; j<down_levels_total; j++)
           {
            condition1=rsi_buffer[i-1]<down_levels[j] && rsi_buffer[i]>down_levels[j];
            if(condition1)
               break;
           }
        }
      //---
      if(rsi_buffer[i]>50)
        {
         for(int j=0; j<up_levels_total; j++)
           {
            condition2=rsi_buffer[i-1]>up_levels[j] && rsi_buffer[i]<up_levels[j];
            if(condition2)
               break;
           }
        }
     }
   else
     {
      for(int j=0; j<up_levels_total; j++)
        {
         condition1=rsi_buffer[i-1]<up_levels[j] && rsi_buffer[i]>up_levels[j];
         if(condition1)
            break;
        }
      //---
      for(int j=0; j<down_levels_total; j++)
        {
         condition2=rsi_buffer[i-1]>down_levels[j] && rsi_buffer[i]<down_levels[j];
         if(condition2)
            break;
        }
     }
//条件が満たされた場合にシグナルを表示
...
//最後の値の補正 (2番目に等しい)
...
  }

図9は、その外観を示しています。 

 図9. 複数のレベルをクロスするときにシグナルを形成します。

図9. 複数のレベルをクロスするときにシグナルを形成します。

一つの問題が解決するが、さらに2つ浮上します。 最初の必要性は、価格が以前の買いシグナルよりも高い場合にシグナルを除外することにあります。 図10は、一連の買いシグナルに関するこのような状況を示します。最後のシグナルの価格は前のシグナルの価格を上回っています。 これはBreak inブレイクアウトモードに関連しています。

 図10. シグナルの価格が前のシグナルの価格より高い状況

図10. シグナルの価格が前のシグナルの価格より高い状況

2番目の問題は、あまりにも頻繁にシグナルが発生し、時には足毎に連続して発生します。 この場合、価格はシグナル間のわずかな距離を通過します (図11参照)。

 図11. 些細な価格の動きを持つ頻繁なシグナル

図11. 些細な価格の動きを持つ頻繁なシグナル

この問題は、次のバージョンで解決されます。

4番目のバージョン インジケータをメインチャートウィンドウに移動

インジケータのこのバージョンは、メインのチャートに移動されます。 このインジケータの操作は、ここでよく視覚化されます: シグナルは価格に直接表示されます。 シグナル間の距離をポイント単位で制御するには、外部パラメータに固定値を指定するだけです。 しかし、今回は動的な亜種を作成し、ボラティリティインジケータ (ATR) にこの値を結び付けます。 便宜上、指標の計算は別のクラスで記述されます:CATRи CRsiPlus。 このメソッドを使用して任意の数のインジケータを組み合わせることで、計算結果を1つのプログラムにまとめることができます。 

真の足を決定する

このバージョンは、将来のEA開発向けだと想定されます。 したがって、過去のデータのより高い時間枠の影響を排除するために、現在の時間枠の足が十分ではないときに真の足を決定します。 これについてはマルチシンボルインジケータに関する記事で徹底的に説明されています。 最初の真の足を決定するために、別のクラスのCFirstTrueBarを記述します。 まず、このクラスを詳しく見てみましょう。

CFirstTrueBarクラスのメンバとメソッドの定義を以下に示します。 これについて、かんたんに考えてみましょう。 

//+------------------------------------------------------------------+
//| Class for determining the true bar                               |
//+------------------------------------------------------------------+
class CFirstTrueBar
  {
private:
   //--- Time of the true bar
   datetime          m_limit_time;
   //--- Number of the true bar
   int               m_limit_bar;
   //---
public:
                     CFirstTrueBar(void);
                    ~CFirstTrueBar(void);
   //--- Return (1) the time and (2) the number of the true bar
   datetime          LimitTime(void) const { return(m_limit_time); }
   int               LimitBar(void)  const { return(m_limit_bar);  }
   //--- Determine the first true bar
   bool              DetermineFirstTrueBar(void);
   //---
private:
   //--- Search for the first true bar of the current period
   void              GetFirstTrueBarTime(const datetime &time[]);
  };

プライベートメソッドCFirstTrueBar:: GetFirstTrueBarTime() は、真の足を検索するために使用します。 最初の真の足を検索するには、履歴足の時間の配列を渡す必要があります。 配列の先頭から繰り返し、現在の時間枠に対応する足が見つかったら、この足の時刻とインデックスを格納します。 真の足を識別すると、 CFirstTrueBar:: LimitTime() およびCFirstTrueBar:: LimitBar() メソッドを使用して、その時間とインデックスを取得できます。

//+------------------------------------------------------------------+
//|現在の期間の最初の真の足を検索                                        |
//+------------------------------------------------------------------+
void CFirstTrueBar::GetFirstTrueBarTime(const datetime &time[])
  {
//配列のサイズを取得
   int array_size=::ArraySize(time);
   ::ArraySetAsSeries(time,false);
//各足を1つずつチェック
   for(int i=1; i<array_size; i++)
     {
      //足が現在の時間枠に対応している場合
      if(time[i]-time[i-1]==::PeriodSeconds())
        {
         //---保存してループを終了する
         m_limit_time =time[i-1];
         m_limit_bar  =i-1;
         break;
        }
     }
  }

CFirstTrueBar:: GetFirstTrueBarTime() メソッドは、 CFirstTrueBar::D eterminefirsttruebar() メソッドで呼び出されます。 後で最初の真の足を検索するために使用し、足の時間の配列が得られるところです。

//+------------------------------------------------------------------+
//| Determine the first true bar                                     |
//+------------------------------------------------------------------+
bool CFirstTrueBar::DetermineFirstTrueBar(void)
  {
//--- Array of bar times
   datetime time[];
//--- Get the total number of bars for the symbol
   int available_bars=::Bars(_Symbol,_Period);
//--- Copy the bar time array. この操作が失敗した場合は、やり直し
   if(::CopyTime(_Symbol,_Period,0,available_bars,time)<available_bars)
      return(false);
//現在のタイムフレームに対応する最初の真の足の時刻を取得
   GetFirstTrueBarTime(time);
   return(true);
  }

ATR インジケータの追加

ATRインジケータは、標準パッケージと同じように計算されます。 このコードはここから取得されます: MQL5\Indicators\ExamplesCATRクラスのメンバとメソッドの宣言を以下に示します。 標準バージョンとの唯一の差は、最初の真の足がここで決定され、そこから計算が開始されることです。

CFirstTrueBarクラスを含むファイルをインクルードし、CATRクラスの本体でそのインスタンスを宣言します。 インジケータ配列は、ここでパブリックアクセスとして宣言されています。 メインインジケータファイルのインジケータバッファとして設定できるようにするために必要です。

//+------------------------------------------------------------------+
//|                                                          ATR.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "FirstTrueBar.mqh"
//+------------------------------------------------------------------+
//| The ATR indicator                                                |
//+------------------------------------------------------------------+
class CATR
  {
private:
   //--- Determine the first true bar
   CFirstTrueBar     m_first_true_bar;
   //---インジケータ期間
   int               m_period;
   //インジケータ値の計算におけるリミッタ
   int               m_limit;
   //---
public:
   //---インジケータバッファ
   double            m_tr_buffer[];
   double            m_atr_buffer[];
   //---
public:
                     CATR(const int period);
                    ~CATR(void);
   //---インジケータ期間
   void              PeriodATR(const int period) { m_period=period; }
   //ATR インジケータを計算 
   bool              CalculateIndicatorATR(const int rates_total,const int prev_calculated,const datetime &time[],const double &close[],const double &high[],const double &low[]);
   //インジケータバッファをゼロに
   void              ZeroIndicatorBuffers(void);
   //---
private:
   //---予備計算
   bool              PreliminaryCalc(const int rates_total,const int prev_calculated,const double &close[],const double &high[],const double &low[]);
   //ATR を計算 
   void              CalculateATR(const int i,const datetime &time[],const double &close[],const double &high[],const double &low[]);
  };

インジケータがさらに計算される最初の真の足は、予備計算のメソッドで決定されます。 決定されていない場合、プログラムは、メソッドを抜けます。

//+------------------------------------------------------------------+
//|予備計算                                                           |
//+------------------------------------------------------------------+
bool CATR::PreliminaryCalc(const int rates_total,const int prev_calculated,const double &close[],const double &high[],const double &low[])
  {
//---最初の計算であるか、または変更があった場合
   if(prev_calculated==0)
     {
      //真の足の数を決定
      m_first_true_bar.DetermineFirstTrueBar();
      //真の足が決定されていない場合、離脱
      if(m_first_true_bar.LimitBar()<0)
         return(false);
      //---
      m_tr_buffer[0]  =0.0;
      m_atr_buffer[0] =0.0;
      //計算する足
      m_limit=(::Period()<PERIOD_D1)? m_first_true_bar.LimitBar()+m_period : m_period;
      //範囲外 (足が不十分な場合)
      if(m_limit>=rates_total)
         return(false);
      //真の範囲の値を計算
      int start_pos=(m_first_true_bar.LimitBar()<1)? 1 : m_first_true_bar.LimitBar();
      for(int i=start_pos; i<m_limit && !::IsStopped(); i++)
         m_tr_buffer[i]=::fmax(high[i],close[i-1])-::fmin(low[i],close[i-1]);
      //最初の ATR 値が計算されない
      double first_value=0.0;
      for(int i=m_first_true_bar.LimitBar(); i<m_limit; i++)
        {
         m_atr_buffer[i]=0.0;
         first_value+=m_tr_buffer[i];
        }
      //最初の値の計算
      first_value/=m_period;
      m_atr_buffer[m_limit-1]=first_value;
     }
   else
      m_limit=prev_calculated-1;
//---
   return(true);
  }

RSI インジケータのシグナルフィルタリング

上記のインジケータバッファに加えて、 RSIのこのバージョンは、さらに2つ持つことになります。 継続的なレベルを個別に買いし、スプレッドを考慮して売りのシグナルの価格に基づいてプロットされます。 インジケータ ATRのデータを計算に含める関数を持つためには、メインプログラムファイルで作成されたATRクラスのインスタンスへのポインタを取得する必要があります。 したがって、 CATR型のポインターと、取得と設定に対応するメソッドがここで宣言されます。

コードを最適化するために、ブロックは別のメソッドとして実装されています。 条件の確認、カウンタの操作などが含まれます。 以前考慮されていない唯一の新しいメソッドは、 CRsiPlus::D irectioncontrol()です。 これは、移動方向を制御し、現在のボラティリティに基づいて過剰なシグナルを除去するメソッドです。 さらに、不要なシグナルを除去するための補助メソッドがあります— CRsiPlus::D eletebuysignal() とCRsiPlus::D eletesellsignal()。

//+------------------------------------------------------------------+
//|                                                          RSI.mqh |
//|                        Copyright 2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "FirstTrueBar.mqh"
#include "ATR.mqh"
//チャネルブレイクアウトモードの列挙体
enum ENUM_BREAK_INOUT
  {
   BREAK_IN          =0, //ブレイクイン
   BREAK_IN_REVERSE  =1, //逆のブレイク
   BREAK_OUT         =2, //ブレイクアウト
   BREAK_OUT_REVERSE =3  //ブレイクアウトリバース
  };
//+------------------------------------------------------------------+
//|ボラティリティフィルターと RSI インジケータ                            |
//+------------------------------------------------------------------+
class CRsiPlus
  {
private:
   //--- Determine the first true bar
   CFirstTrueBar     m_first_true_bar;
   //ATR へのポインタ
   CATR             *m_atr;
   
   //---インジケータ期間
   int               m_period;
   //---RSI レベル
   double            m_signal_level;
   //シグナルを生成するためのモード
   ENUM_BREAK_INOUT  m_break_mode;
      
   //同じ方向のシグナルのカウンタ
   int               m_buy_counter;
   int               m_sell_counter;
   //---インジケータレベル
   double            m_up_level;
   double            m_down_level;
   double            m_up_levels[];
   double            m_down_levels[];
   int               m_up_levels_total;
   int               m_down_levels_total;
   
   //インジケータ値の計算におけるリミッタ
   int               m_limit;
   //最後の足を決定する
   bool              m_is_last_index;
   //---
public:
   //---インジケータバッファ
   double            m_rsi_buffer[];
   double            m_pos_buffer[];
   double            m_neg_buffer[];
   //---
   double            m_buy_buffer[];
   double            m_sell_buffer[];
   double            m_buy_level_buffer[];
   double            m_sell_level_buffer[];
   double            m_buy_counter_buffer[];
   double            m_sell_counter_buffer[];
   //---
public:
                     CRsiPlus(const int period,const double signal_level,const ENUM_BREAK_INOUT break_mode);
                    ~CRsiPlus(void) {}
   //ATR へのポインタ
   void              AtrPointer(CATR &object) { m_atr=::GetPointer(object);  }
   CATR             *AtrPointer(void)         { return(::GetPointer(m_atr)); }
   //---RSI インジケータを計算する
   bool              CalculateIndicatorRSI(const int rates_total,const int prev_calculated,const double &close[],const int &spread[]);
   //すべてのインジケータバッファの初期化
   void              ZeroIndicatorBuffers(void);
   //---
private:
   //インジケータレベルを取得には
   int               GetLevelsIndicator(void);
   //---予備計算
   bool              PreliminaryCalc(const int rates_total,const int prev_calculated,const double &close[]);
   //---RSI シリーズを計算する
   void              CalculateRSI(const int i,const double &price[]);
   //インジケータシグナルを計算
   void              CalculateSignals(const int i,const int rates_total,const double &close[],const int &spread[]);

   //---チェック条件
   void              CheckConditions(const int i,bool &condition1,bool &condition2);
   //---チェックカウンタ
   void              CheckCounters(bool &condition1,bool &condition2);
   //売買カウンターを増やす
   void              IncreaseBuyCounter(const bool condition);
   void              IncreaseSellCounter(const bool condition);

   //移動方向の制御
   void              DirectionControl(const int i,bool &condition1,bool &condition2);
   //過度の売買シグナルを削除
   void              DeleteBuySignal(const int i);
   void              DeleteSellSignal(const int i);
   //インジケータバッファの指定された要素をゼロ
   void              ZeroIndexBuffers(const int index);
  };

CRsiPlus::D irectioncontrol() メソッドは、シグナルが過剰かどうかを判断する次の条件をチェックします。

  • シグナルがある場合は、インパルスのサイズは、現在のボラティリティよりも小さいです。
  • 生成されたシグナルが現在の系列の方向と反対である場合。

条件が合えばシグナルは消されます。

//+------------------------------------------------------------------+
//|移動方向の制御                                                      |
//+------------------------------------------------------------------+
void CRsiPlus::DirectionControl(const int i,bool &condition1,bool &condition2)
  {
   double atr_coeff     =0.0;
   double impulse_size  =0.0;
   bool   atr_condition =false;
//---
   bool buy_condition  =false;
   bool sell_condition =false;
//--- If the reversion is disabled
   if(m_break_mode==BREAK_IN || m_break_mode==BREAK_OUT)
     {
      buy_condition =condition1 && m_buy_counter>1;
      impulse_size  =::fabs(m_buy_buffer[i]-m_buy_level_buffer[i-1]);
      atr_condition =impulse_size<m_atr.m_atr_buffer[i];
      //---
      if((m_buy_counter>1 && atr_condition) || 
         (m_break_mode==BREAK_IN && buy_condition && m_buy_buffer[i]>m_buy_level_buffer[i-1]) ||
         (m_break_mode==BREAK_OUT && buy_condition && m_buy_buffer[i]<m_buy_level_buffer[i-1]))
        {
         DeleteBuySignal(i);
        }
      //---
      sell_condition =condition2 && m_sell_counter>1;
      impulse_size   =::fabs(m_sell_buffer[i]-m_sell_level_buffer[i-1]);
      atr_condition  =impulse_size<m_atr.m_atr_buffer[i];
      //---
      if((m_sell_counter>1 && atr_condition) || 
         (m_break_mode==BREAK_IN && sell_condition && m_sell_buffer[i]<m_sell_level_buffer[i-1]) ||
         (m_break_mode==BREAK_OUT && sell_condition && m_sell_buffer[i]>m_sell_level_buffer[i-1]))
        {
         DeleteSellSignal(i);
        }
     }
//--- Reversion is enabled     
   else
     {
      buy_condition =condition2 && m_buy_counter>1;
      impulse_size  =::fabs(m_buy_buffer[i]-m_buy_level_buffer[i-1]);
      atr_condition =impulse_size<m_atr.m_atr_buffer[i];
      //---
      if((m_buy_counter>1 && atr_condition) || 
         (m_break_mode==BREAK_IN_REVERSE && buy_condition && m_buy_buffer[i]<m_buy_level_buffer[i-1]) ||
         (m_break_mode==BREAK_OUT_REVERSE && buy_condition && m_buy_buffer[i]>m_buy_level_buffer[i-1]))
        {
         DeleteBuySignal(i);
        }
      //---
      sell_condition =condition1 && m_sell_counter>1;
      impulse_size   =::fabs(m_sell_buffer[i]-m_sell_level_buffer[i-1]);
      atr_condition  =impulse_size<m_atr.m_atr_buffer[i];
      //---      
      if((m_sell_counter>1 && atr_condition) || 
         (m_break_mode==BREAK_IN_REVERSE && sell_condition && m_sell_buffer[i]>m_sell_level_buffer[i-1]) ||
         (m_break_mode==BREAK_OUT_REVERSE && sell_condition && m_sell_buffer[i]<m_sell_level_buffer[i-1]))
        {
         DeleteSellSignal(i);
        }
     }
  }

インジケータのメインファイルを詳しく見てみましょう。 このバージョンのインジケータには既に11のバッファがあり、そのうち7つがメインで、 4つが補助です。

//---プロパティ
#property indicator_chart_window
#property indicator_buffers 11
#property indicator_plots   7
#property indicator_color1  clrMediumSeaGreen
#property indicator_color2  clrRed
#property indicator_color5  clrMediumSeaGreen
#property indicator_color6  clrRed

便宜上、クラスで使用するすべてのファイルは、インクルードフォルダー内のインジケータのディレクトリにあります。

 図12. インジケータディレクトリ

図12. インジケータディレクトリ

したがって、メインファイルに含めるには次のようになります。

//インジケータのクラスをインクルード
#include "Includes\ATR.mqh"
#include "Includes\RsiPlus.mqh"

外部パラメータには、 ATRインジケータ期間用にもう1つ追加します。

//インプットパラメータ
input int              PeriodRSI   =8;         //RSI の期間
input double           SignalLevel =30;        //シグナルレベル
input ENUM_BREAK_INOUT BreakMode   =BREAK_OUT; //ブレイクモード
input int              PeriodATR   =200;       //ATR 期間

インジケータは、コンストラクターに渡されたパラメータで宣言されます。

//タスク用インジケータのインスタンス
CATR     atr(PeriodATR);
CRsiPlus rsi(PeriodRSI,SignalLevel,BreakMode);

OnInit()初期化関数では、 ATRへのポインタをRSIインジケータに渡すことを忘れないでください。

//+------------------------------------------------------------------+
//|カスタムインジケータ初期化関数                                        |
//+------------------------------------------------------------------+
void OnInit(void)
  {
//インジケータのの初期化
   rsi.AtrPointer(atr);
//---設定インジケータプロパティ
   SetPropertiesIndicator();
  }

インジケータバッファに割り当てられた配列は各インジケータクラスで public として宣言されているため、メインファイルのインジケータへの追加は、通常の動的配列のインクルードのように見えます。

//+------------------------------------------------------------------+
//| Sets indicator properties                                        |
//+------------------------------------------------------------------+
void SetPropertiesIndicator(void)
  {
//--- Short name
   ::IndicatorSetString(INDICATOR_SHORTNAME,"RSI_PLUS_CHART");
//--- Decimal places
   ::IndicatorSetInteger(INDICATOR_DIGITS,::Digits());
//---インジケータバッファ
   ::SetIndexBuffer(0,rsi.m_buy_buffer,INDICATOR_DATA);
   ::SetIndexBuffer(1,rsi.m_sell_buffer,INDICATOR_DATA);
   ::SetIndexBuffer(2,rsi.m_buy_counter_buffer,INDICATOR_DATA);
   ::SetIndexBuffer(3,rsi.m_sell_counter_buffer,INDICATOR_DATA);
   ::SetIndexBuffer(4,rsi.m_buy_level_buffer,INDICATOR_DATA);
   ::SetIndexBuffer(5,rsi.m_sell_level_buffer,INDICATOR_DATA);
   ::SetIndexBuffer(6,atr.m_atr_buffer,INDICATOR_DATA);
   ::SetIndexBuffer(7,rsi.m_rsi_buffer,INDICATOR_CALCULATIONS);
   ::SetIndexBuffer(8,rsi.m_pos_buffer,INDICATOR_CALCULATIONS);
   ::SetIndexBuffer(9,rsi.m_neg_buffer,INDICATOR_CALCULATIONS);
   ::SetIndexBuffer(10,atr.m_tr_buffer,INDICATOR_CALCULATIONS);
//配列の初期化
   atr.ZeroIndicatorBuffers();
   rsi.ZeroIndicatorBuffers();
...
  }

追加の計算に使用する補助配列は、チャートとデータウィンドウには表示されません。 チャートにデータを表示しないでデータウィンドウに表示する必要がある場合は、そのような系列の DRAW_NONEプロパティを設定する必要があります。

//+------------------------------------------------------------------+
//| Sets indicator properties                                        |
//+------------------------------------------------------------------+
void SetPropertiesIndicator(void)
  {
...
//インジケータバッファの型を設定
   ENUM_DRAW_TYPE draw_type[]={DRAW_ARROW,DRAW_ARROW,DRAW_NONE,DRAW_NONE,DRAW_LINE,DRAW_LINE,DRAW_NONE};
   for(int i=0; i<indicator_plots; i++)
      ::PlotIndexSetInteger(i,PLOT_DRAW_TYPE,draw_type[i]);
...
  }

OnCalculate ()関数の内容は、 ATR インジケータと変更された RSIを計算するために、メソッドを呼び出します。 

//+------------------------------------------------------------------+
//| 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[])
  {
//ATR インジケータを計算
   if(!atr.CalculateIndicatorATR(rates_total,prev_calculated,time,close,high,low))
      return(0);
//---RSI インジケータを計算する
   if(!rsi.CalculateIndicatorRSI(rates_total,prev_calculated,close,spread))
      return(0);
//最後に計算された要素数を返す
   return(rates_total);
  }

チャートにインジケータをコンパイルして読み込むと、図13に示すような結果が表示されます。 インジケータシグナルに基づいてプロットされたレベルは、チャネルの種類を形成します。 前のシリーズの最後の反対のシグナルから価格が通過した距離を決定するために使用することは簡単です。

 図13. 主要な図表の変更された RSI のインジケータの操作の結果。

図13. 主要な図表の変更された RSI のインジケータの操作の結果。

トレーディングシステムを開発する際には、1つまたは別のインジケータバッファの正確な値を視覚化する必要があるかもしれません。 特に、値が見えなくなっているバッファのシリーズがチャートに表示されない場合に当てはまります。 値は、Data Windowに表示されます。 便宜上、マウスの中央ボタンを使用して十字を有効にすることができます。 図14では、標準パッケージからのATRインジケータがチャートに追加され、変更されたRSIで算出したものと比較することができるようになっています。

 図14. インジケータデータウィンドウで値を表示

図14. インジケータデータウィンドウで値を表示

5番目のバージョン 同時に2方向で機能するためのユニバーサル RSI インジケータ

また、シグナルを2方向に同時に表示するインジケータが必要な場合はどうすればよいでしょうか。 結局のところ、メタトレーダー 5では、異なる方向のポジションを持つシステムを開発することが可能であることを意味し、価格レートヘッジアカウントを開始する機能があります。 トレンドとレンジの両方でシグナルを生成するインジケータを持つことが有用でしょう。 

このようなインジケータを作成するメソッドを考えてみましょう。 すでに必要なものはあり、変更はメインファイルにのみ行われます。 このバージョンには、合計で20のバッファがあり、そのうち15を描画に使用します。

#property indicator_buffers 20
#property indicator_plots   15

トレンドベースのシグナルは、矢印として、レンジベースのシグナルのドットとして表示されます。 これにより、特定のシグナルが属するタイプを視覚化しやすくなり、理解することが容易になります。

//シグナルの矢印: 159-ドット;233/234-矢印;
#define ARROW_BUY_IN   233
#define ARROW_SELL_IN  234
#define ARROW_BUY_OUT  159
#define ARROW_SELL_OUT 159

以前のバージョンと同じファイルが、インジケータのローカルディレクトリにも含まれています。 その後、ファイルの単一のコピーを配置する関数を持つことになります。 

//インジケータのクラスをインクルード
#include "Includes\ATR.mqh"
#include "Includes\RsiPlus.mqh"

このバージョンのインジケータでは、外部パラメータでシグナルの種類を指定する必要はありません。 しかし、トレンドベースのシグナル (シグナルレベル) とレンジベースのもの (Signal level InSignal level Out) のレベルを個別に指定できるようにします。 

//インプットパラメータ
input int    PeriodRSI      =8;   //RSI の期間
input double SignalLevelIn  =35;  //シグナルレベル
input double SignalLevelOut =30;  //シグナルレベルアウト
input int    PeriodATR      =100; //ATR 期間

その後、 CRsiPlusクラスの2つのインスタンスを宣言する必要があります: 1 つは、トレンドベースのシグナル、および別のレンジのシグナルです。 どちらの場合も、逆のタイプのシグナルが使用します (BREAK_IN_REVERSEBREAK_OUT_REVERSE)。 つまり、チャネルのインパルスは、レンジのシグナルとして機能します。そして、エントリーはロールバックの後でトレンドの方向でなされます。

//タスク用インジケータのインスタンス
CATR atr(PeriodATR);
CRsiPlus rsi_in(PeriodRSI,SignalLevelIn,BREAK_IN_REVERSE);
CRsiPlus rsi_out(PeriodRSI,SignalLevelOut,BREAK_OUT_REVERSE);

ATRインジケータへのポインターは、 RSIの両方のインスタンスに渡される必要があります。

//+------------------------------------------------------------------+
//|カスタムインジケータ初期化関数                                        |
//+------------------------------------------------------------------+
void OnInit(void)
  {
//ATR にポインタを渡す  
   rsi_in.AtrPointer(atr);
   rsi_out.AtrPointer(atr);
//---設定インジケータプロパティ
   SetPropertiesIndicator();
  }

各インスタンスのインジケータバッファは、コード内での移動を容易にするために、このような順序で設定されます。 このシーケンスは、もちろん、問題ではありません。 後でエキスパートアドバイザの条件を形成するために、特定のシリーズの値を取得するには、バッファ番号を知る必要があります。

//+------------------------------------------------------------------+
//| Sets indicator properties                                        |
//+------------------------------------------------------------------+
void SetPropertiesIndicator(void)
  {
//--- Short name
   ::IndicatorSetString(INDICATOR_SHORTNAME,"RSI_PLUS2_CHART");
//--- Decimal places
   ::IndicatorSetInteger(INDICATOR_DIGITS,::Digits());
//---インジケータバッファ
   ::SetIndexBuffer(0,rsi_in.m_buy_buffer,INDICATOR_DATA);
   ::SetIndexBuffer(1,rsi_in.m_sell_buffer,INDICATOR_DATA);
   ::SetIndexBuffer(2,rsi_in.m_buy_counter_buffer,INDICATOR_DATA);
   ::SetIndexBuffer(3,rsi_in.m_sell_counter_buffer,INDICATOR_DATA);
   ::SetIndexBuffer(4,rsi_in.m_buy_level_buffer,INDICATOR_DATA);
   ::SetIndexBuffer(5,rsi_in.m_sell_level_buffer,INDICATOR_DATA);
   ::SetIndexBuffer(6,rsi_in.m_rsi_buffer,INDICATOR_CALCULATIONS);
   ::SetIndexBuffer(7,rsi_in.m_pos_buffer,INDICATOR_CALCULATIONS);
   ::SetIndexBuffer(8,rsi_in.m_neg_buffer,INDICATOR_CALCULATIONS);
//---
   ::SetIndexBuffer(9,rsi_out.m_buy_buffer,INDICATOR_DATA);
   ::SetIndexBuffer(10,rsi_out.m_sell_buffer,INDICATOR_DATA);
   ::SetIndexBuffer(11,rsi_out.m_buy_counter_buffer,INDICATOR_DATA);
   ::SetIndexBuffer(12,rsi_out.m_sell_counter_buffer,INDICATOR_DATA);
   ::SetIndexBuffer(13,rsi_out.m_buy_level_buffer,INDICATOR_DATA);
   ::SetIndexBuffer(14,rsi_out.m_sell_level_buffer,INDICATOR_DATA);
   ::SetIndexBuffer(15,rsi_out.m_rsi_buffer,INDICATOR_CALCULATIONS);
   ::SetIndexBuffer(16,rsi_out.m_pos_buffer,INDICATOR_CALCULATIONS);
   ::SetIndexBuffer(17,rsi_out.m_neg_buffer,INDICATOR_CALCULATIONS);
//---
   ::SetIndexBuffer(18,atr.m_atr_buffer,INDICATOR_DATA);
   ::SetIndexBuffer(19,atr.m_tr_buffer,INDICATOR_CALCULATIONS);
//配列の初期化
   atr.ZeroIndicatorBuffers();
   rsi_in.ZeroIndicatorBuffers();
   rsi_out.ZeroIndicatorBuffers();
...
  }

どのシグナルレベルがどのタイプのシグナルに属しているかを混同しないように、破線で描画します。

//+------------------------------------------------------------------+
//| Sets indicator properties                                        |
//+------------------------------------------------------------------+
void SetPropertiesIndicator(void)
  {
...
//指定したインジケータバッファの線のスタイルを設定
   ::PlotIndexSetInteger(13,PLOT_LINE_STYLE,STYLE_DOT);
   ::PlotIndexSetInteger(14,PLOT_LINE_STYLE,STYLE_DOT);
...
  }

OnCalculate ()関数のコードは、インジケータの各インスタンスのメソッドを呼び出すためだけに制限されています。

//+------------------------------------------------------------------+
//| 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[])
  {
//ATR インジケータを計算
   if(!atr.CalculateIndicatorATR(rates_total,prev_calculated,time,close,high,low))
      return(0);
//---RSI インジケータを計算する
   if(!rsi_in.CalculateIndicatorRSI(rates_total,prev_calculated,close,spread))
      return(0);
   if(!rsi_out.CalculateIndicatorRSI(rates_total,prev_calculated,close,spread))
      return(0);
//最後に計算された要素数を返す
   return(rates_total);
  }

図15は、チャートのインジケータ操作を示します。 すべてRSIを実行しているだけの結果です。

 図15. ユニバーサル RSI インジケータ

図15. ユニバーサル RSI インジケータ

 図16. ストラテジーテスターのユニバーサル RSI インジケータ。

図16. ストラテジーテスターのユニバーサル RSI インジケータ。

このフォームでは、インジケータはエントリーの場所だけでなく、ポジションのボリュームを増やすためのポイントを示します。 さらに、属する連続シリーズにおけるシグナルのインジケータを得ることができます。 また、価格レベルもあるので、前回のシリーズからの最後のシグナルから価格が移動した距離を取得することも可能です。 より複雑なトレードアルゴリズムも実装できます。 たとえば、トレンドによってポジションを開く場合、レンジで機能するために設計されたシグナルに基づいて、部分的または完全に閉じることができます。

チャートのインジケータの結果を考慮すると、最初にテスターでチェックする必要があるさまざまなトレードアルゴリズムを思い付くことができます。 また、これは大規模なタスクです: 無限に多くの亜種が存在する可能性があります。 各トレーディングモジュールは、別のトレード結果を受け取るために行うことができます。 つまり、トレンドトレーディングモジュールは、レンジトレーディングモジュールのトレード結果を受け取ることができ、その逆も同様です。 データに基づいて、現在の状況に適応し、調整することができます。 各モジュールは、お互いのトレード戦術に影響を与えることができます: ポジションを開く、閉じる、または管理するための条件を変更し、管理システムを変更します (保守的なモデルから積極的なものに変換)。 トレードシステムは非常に複雑になり、現在の価格アクションに適応することができます。

結論

ご覧の通り、シンプルなインジケータは、とんでもなく有用です。 代わりに、トレーディングアルゴリズムの開発を容易にします。 それ以外の場合、このすべてをエキスパートアドバイザ内で実行し、コードを複雑にする必要があります。 すべてが1つのプログラム内に隠され、その配列内の計算値を読み取ることができます.

誰もがこのアイデアを発展し続けることができます。 たとえば、トレンドまたはレンジステートを指定する新しい特性の追加バッファを導入することができます。 その計算は、現在のボラティリティ ( ATRインジケータ) に関連付けることができ、トレーリングストップのバッファを追加することができます。 その結果、必要な計算はすべてインジケータに実装され、EAはレベルを受け取ることができます。

ファイル名 中身
MQL5\Indicators\RSI\RSI_Plus1.mq5 変更された RSI インジケータの最初のバージョン
MQL5\Indicators\RSI\RSI_Plus2.mq5 変更された RSI インジケータの2番目のバージョン
MQL5\Indicators\RSI\RSI_Plus3.mq5 変更された RSI インジケータの3番目のバージョン
MQL5\Indicators\RSI\ChartRSI_Plus1\ChartRSI_Plus1.mq5 変更された RSI インジケータの4番目のバージョン
MQL5\Indicators\RSI\ChartRSI_Plus2\ChartRSI_Plus2.mq5 変更された RSI インジケータの5番目のバージョン


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

添付されたファイル |
MQL5.zip (31.59 KB)
MQLベースのエキスパートアドバイザとデータベースの統合 (SQL server、.NET、および C#) MQLベースのエキスパートアドバイザとデータベースの統合 (SQL server、.NET、および C#)
この記事では、MQL5 ベースのEAに対して Microsoft SQL server データベースサーバーを使用する方法について説明します。 DLL からの関数のインポートが使用します。 DLL は、Microsoft .NET プラットフォームと C# 言語を使用して作成します。 この記事で使用するメソッドは、マイナーな調整があり、MQL4で書かれているEAに適しています。
トレード履歴のカスタム表示とレポート図の作成 トレード履歴のカスタム表示とレポート図の作成
この記事では、トレード履歴を評価するためのカスタム・メソッドについて説明します。 2つのクラスが、ヒストリーを分析するために書かれ、ダウンロード可能です。 最初のトレード履歴を収集し、要約表として表します。 2番目は、統計情報を扱います。: 変数を計算し、トレード結果のより効率的な評価チャートを構築します。
デルタインジケータの例によるボリュームコントロールを特徴とする株式インジケータの開発 デルタインジケータの例によるボリュームコントロールを特徴とする株式インジケータの開発
この記事では、CopyTicks() および CopyTicksRange() 関数を使用して、実際のボリュームに基づいた株価インジケータを開発するアルゴリズムを扱います。 このようなインジケータの開発については、リアルタイムでの操作とストラテジーテスターにおける細かい側面も説明されています。
10のレンジトレーディング戦略の比較分析 10のレンジトレーディング戦略の比較分析
この記事はレンジ期間のトレードにおける利点および欠点について調査します。 この記事で作成およびテストされた10の戦略は、チャネル内の価格変動の追跡に基づいています。 各戦略は、ダマシの相場参入シグナルを回避することを目的としたフィルタリング機構を備えています。