
同時に2方向で機能するためのユニバーサル RSI インジケータ
コンテンツ
- イントロダクション
- インジケータの選択
- RSI インジケータの変更
- 最初のバージョン シグナルバッファの追加
- 2番目のバージョン シグナルカウンタのバッファの追加
- 3番目のバージョン シグナル数の増加、スキップの排除
- 4番目のバージョン インジケータをメインチャートウィンドウに移動
- 5番目のバージョン 同時に2方向で機能するためのユニバーサル RSI インジケータ
- 結論
イントロダクション
トレーディングアルゴリズムを開発するとき、ほとんど常にある問題が発生します。つまり、トレンド/レンジの開始と終わりを決定する方法です。 完璧なソリューションを見つけることは困難です。 ひとつの指標として、1つのアルゴリズムでトレンドベースとレンジベースの両方の戦略を組み合わせることが可能です。 この記事では、さまざまなタイプの戦略に対してシグナルを結合するユニバーサルインジケータを作成します。 EAのトレードシグナルの生成をできるだけ簡素化します。 1つのインジケータを組み合わせた例を挙げます。 オブジェクト指向プログラミングを使用し、各インジケータでメインプログラムファイルに含まれるクラスの形式で実装します。
したがって、ここでのタスクは、2つのトレード戦略を組み合わせ、そのEAを書くことです。 2方向の同時トレードがより効果的であり、その戦略がより安定していると仮定します。 その後、両方の戦略タイプのシグナルを生成するEAに、1つだけインジケータを含めます。 内部では、売買シグナルを決定するための複雑なシステムを実装することができます。 異なる設定を持つ複数の同一のインジケータを1つにまとめる必要があるかもしれません。 または、インジケータにメインとフィルタリング (補助)などの異なるものを含めることも必要かもしれません。 このようなスキームを OOP を使用して実装すると便利です。
以下のスキームは、メイン (Indicator.mq5)に2つのインジケータクラス (CIndicator1とCIndicator2) をインクルードすることを示します。 CIndicator2は補助インジケータで、 CIndicator1には計算結果が必要です。 ここでは、マルチシンボルインジケータの作成についての記事で前に説明した、真の足を決定するためのメソッドを適用します。この記事では、別のCFirstTrueBarクラスが作成されています。 現在の時間枠に属さない足の計算を避けるために、すべてのインジケータにインクルードします。
図1. OOP を使用してインジケータを作成するための可能なスキームの1つ
インジケータの選択
シグナル生成には、標準端子パッケージのインジケータを選択できます。 ほとんどは同じような考え方であり、基本的に、他のよりも有用と言い切れません。 インジケータとフィルターの組み合わせは、一定の時間間隔で有効ですが、他の組み合わせも他の異なる間隔で有用です。
しかし、研究の利便性のため、オシレータータイプのインジケータを選択することをお勧めします。 トレンドとレンジの両方でシグナルを決定するために使用することができます。 また、オシレーターデータを使用して価格チャネルをプロットすることもできます。 結果として、複雑なトレード戦略を開発するときに便利です。ユニバーサルインジケータを作成する関数を取得します。
RSI (相対強度指数) インジケータは、この記事シリーズの例として使用します。 下記は、 AUDUSD H1チャートで8の期間を持つこのインジケータの計算結果です。
図2. 相対強度指数インジケータ
一見すると、このインジケータのシグナルに基づいて利益を受け取ることは簡単です。 でもそれは幻想です。 おおよその理解で次の新しいレベルに到達する前に、多くのやるべきタスクがあります。 同時に、目標を達成できる保証はありません。
最もシンプルで最も明白なケースを考えてみましょう: インジケータ線がデフォルトのレベル70/30を越えるときに利益を受け取ることができると思います: 。 レベル70に下向きにクロスしている場合、売りシグナルと見なされます。 レベル30に上向きにクロスしている場合は、買いシグナルと見なされます。 しかし、ポジションを取った方向とは反対側に行ってしまい、ダマシシグナルになることもあります。
このインジケータのシグナルを解析するもう1つの例を示します (図3参照)。 価格が長い間、下に移動していることがわかります。 赤い線は、インジケータがレベル30を上方に横切るときに形成されるシグナルです。 このアルゴリズムに基づいた買いポジションがある場合、フローティングドローダウンが発生します。 ポジションにSLを設定している場合、毎回損失が発生することになります。 同時に、直近の買いシグナルが現れてから、価格がプラスの結果になることはありません。 結果はマイナスです。 また、このチャートのこのセグメントは、明確なものがないことを意味し、トレンドに沿ってトレードしていることを示唆しています。
インジケータを使用する場合も同様の問題が発生します。 したがって、どれを選択するかという問題ではありません。
図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です。 シグナルが矢印として表示される場合は、コード233と234をそれぞれ使用します。
//---シグナルのための矢: 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 インジケータ操作のデモンストレーション。
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 のインジケータ。
すべてが意図したとおりに動作するようにするには、テスターとリアルタイムで両方のインジケータを確認してください。 ストラテジーテスターの結果:
図6. ストラテジーテスターで変更された RSI インジケータの動作を確認します。
次のスクリーンショットは、ブレイクモードパラメータのすべての操作モードのインジケータ値を示します。
図7. ブレイクモードパラメータのすべての操作モードのインジケータ。
価格が2つの類似したシグナルの間でかなり大きな距離を移動する状況があるかもしれません。 これは非常に頻繁に、任意のタイムフレームで発生します。 同時に、シグナルレベルの外部パラメータを必要なだけ変更し、範囲の境界を設定することで、問題を解決することはできません。 この不確実性は明確なトレードロジックの作成を妨げ、追加条件を書く必要性を複雑にします。
図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. 複数のレベルをクロスするときにシグナルを形成します。
一つの問題が解決するが、さらに2つ浮上します。 最初の必要性は、価格が以前の買いシグナルよりも高い場合にシグナルを除外することにあります。 図10は、一連の買いシグナルに関するこのような状況を示します。最後のシグナルの価格は前のシグナルの価格を上回っています。 これはBreak inとブレイクアウトモードに関連しています。
図10. シグナルの価格が前のシグナルの価格より高い状況
2番目の問題は、あまりにも頻繁にシグナルが発生し、時には足毎に連続して発生します。 この場合、価格はシグナル間のわずかな距離を通過します (図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\Examples。 CATRクラスのメンバとメソッドの宣言を以下に示します。 標準バージョンとの唯一の差は、最初の真の足がここで決定され、そこから計算が開始されることです。
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. インジケータディレクトリ
したがって、メインファイルに含めるには次のようになります。
//インジケータのクラスをインクルード #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 のインジケータの操作の結果。
トレーディングシステムを開発する際には、1つまたは別のインジケータバッファの正確な値を視覚化する必要があるかもしれません。 特に、値が見えなくなっているバッファのシリーズがチャートに表示されない場合に当てはまります。 値は、Data Windowに表示されます。 便宜上、マウスの中央ボタンを使用して十字を有効にすることができます。 図14では、標準パッケージからのATRインジケータがチャートに追加され、変更されたRSIで算出したものと比較することができるようになっています。
図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_REVERSEとBREAK_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 インジケータ
図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





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索