初心者のための複数インディケータバッファの作成
はじめに
前稿、『初心者のためのMQL5のカスタムインディケータ』および『初心者のためのMQL5へのデジタルフィラーの実践的実装』でひとつのインディケータバッファを使いインディケータのストラクチャ詳細に着目しました。
あきらかに、そういう手法はカスタムインディケータを書くのに広く採用されています。しかし、現実にはそれを使用する制限はほとんどなく、そのためより複雑な手法でインディケータコードを構築する方法を考える時がきたのではないでしょうか。さいわいにも、MQL5の能力は尽きることなく、 RAMやわれわれの PCが制限を持つにすぎません。
コードをコピーする例としてのアルーン指標
このインディケータの式には2つの構成部位があります。bullishと bearishインディケータです。これらは個別のチャートウィンドウにプロットされます。
BULLS = (1 - (bar - SHIFT(MAX(HIGH(), AroonPeriod)))/AroonPeriod) * 100
BEARS = (1 - (bar - SHIFT(MIN (LOW (), AroonPeriod)))/AroonPeriod) * 100
ここで
- BULLS - Bull長
- BEARS - Bear長
- SHIFT() - バーのインデックス一を判断する関数
- MAX() - AroonPeriod 期間に対する最大値を検索する関数
- MIN() - AroonPeriod 期間に対する最小値を検索する関数
- HIGH() and LOW() - 適切な価格は入れ酢
まさにインディケータの式からインディケータを構築するのに必要なインディケータバッファは2つだけだと結論づけることができます。インディケータの構築は前稿で考察したSMA_1.mq5の構築とさほど変わるところはありません。
それは、単純に、異なるインディケータ番号を持つ同じ重複コードです。MetaEditorでこのインディケータコードを開いて、Aroon.mq5として保存しましょう。最初の11行は、著作権とそのバージョン番号に関する内容ですが、そこにはインディケータの名前を置き換えるだけです。
//+------------------------------------------------------------------+ //| Aroon.mq5 | //| Copyright 2010, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ //---- copyright #property copyright "2010, MetaQuotes Software Corp." //---- link to the author's site #property link "http://www.mql5.com" //---- version number #property version "1.00"
次は12行目です。基本のチャートウィンドウから個別のウィンドウにインディケータのプロットを変更する必要があります。
//---- plot indicator in the separate window #property indicator_separate_window
このインディケータには完全に異なる値の範囲が入っているので、そのプロットは別のウィンドウで作成されます。
その後の4行(一般的インディケータプロパティ)では、使用されるインディケータ数を2に変更します。
//---- two buffers are used #property indicator_buffers 2 //---- two plots are used #property indicator_plots 2
続く10行は特別なインディケータバッファからのインディケータのプロットに関する部分です。そのラベルは重複する必要があり、その後インデックスをすべて1から2に置き換えます。また、インディケータバッファのレベルをすべて変更する必要があります。
//+----------------------------------------------+ //| bullish strength indicator parameters | //+----------------------------------------------+ //---- drawing style = line #property indicator_type1 DRAW_LINE //---- drawing color = Lime #property indicator_color1 Lime //---- line style = solid line #property indicator_style1 STYLE_SOLID //---- line width = 1 #property indicator_width1 1 //---- label of the BullsAroon indicator #property indicator_label1 "BullsAroon" //+----------------------------------------------+ //| bearish strength indicator parameters | //+----------------------------------------------+ //---- drawing style = line #property indicator_type2 DRAW_LINE //---- drawing color = Red #property indicator_color2 Red //---- line style = solid line #property indicator_style2 STYLE_SOLID //---- line width = 1 #property indicator_width2 1 //---- label of the BearsAroon indicator #property indicator_label2 "BearsAroon"
このインディケータは30、50、70、と3つの水平レベルを使用しています。
この3つのレベルをプロットするには、インディケータコードにもう5行追加する必要があります。
//+----------------------------------------------+ //| Horizontal levels | //+----------------------------------------------+ #property indicator_level1 70.0 #property indicator_level2 50.0 #property indicator_level3 30.0 #property indicator_levelcolor Gray #property indicator_levelstyle STYLE_DASHDOTDOT
インディケータの入力パラメータについては、前のインディケータと比べると、タイトルのちょっとした変更以外はすべて同じです。
//+----------------------------------------------+ //| Indicator input parameters | //+----------------------------------------------+ input int AroonPeriod = 9; // Period input int AroonShift = 0; // Horizontal shift of the indicator in bars
ただし、配列が2つあります。どちらもインディケータバッファに使用され、適切な名前が付けられます。
//--- declare the dynamic arrays used further as indicator buffers double BullsAroonBuffer[]; double BearsAroonBuffer[];
OnInit()関数のコードとまったく同じように進めていきます。
まず、0番目のバッファのコード行に変更を加えます。
//--- set BullsAroonBuffer dynamic array as indicator buffer SetIndexBuffer(0, BullsAroonBuffer, INDICATOR_DATA); //--- horizontal shift (AroonShift) of the indicator 1 PlotIndexSetInteger(0, PLOT_SHIFT, AroonShift); //--- plot draw begin (AroonPeriod) of the indicator 1 PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, AroonPeriod); //--- label shown in DataWindow PlotIndexSetString(0, PLOT_LABEL, "BearsAroon");
そののち、このコード全体をウィンドウズのクリップボードにコピーし同じコードのすぐ後ろに貼り付けます。
そして、貼りつけたコード上でインディケータバッファの値を0から1に変え、インディケータ配列の名前とインディケータラベルを変更します。
//--- set BearsAroonBuffer dynamic array as indicator buffer SetIndexBuffer(1, BearsAroonBuffer, INDICATOR_DATA); //--- horizontal shift (AroonShift) of the indicator 2 PlotIndexSetInteger(1, PLOT_SHIFT, AroonShift); //--- plot draw begin (AroonPeriod) of the indicator 2 PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, AroonPeriod); //--- label shown in DataWindow PlotIndexSetString(1, PLOT_LABEL, "BullsAroon");
インディケータの短縮名もちょっとした変更です。
//--- initialization of the variable for a short indicator name string shortname; StringConcatenate(shortname, "Aroon(", AroonPeriod, ", ", AroonShift, ")");
それでは、インディケータのプロットの正確性について考察します。実際のインディケータ範囲は1~100で、この範囲は常に表示されています。
今回の場合、チャートにプロットされたインディケータの整数値のみを使用するのは可能です。この理由により、インディケータプロットとして、小数点の後に0を使います。
//--- set accuracy of drawing of indicator values IndicatorSetInteger(INDICATOR_DIGITS, 0);
SMA_1.mq5インディケータではOnCalculate()関数呼び出しの最初のフォームを使いました。
アルーンインディケータではそれは適切ではありません。high[]とlow[]の価格配列がないからです。これら配列はこの関数を二度目に呼び出したとき使用可能となります。そのため、関数のヘッダを変更する必要があります。
int OnCalculate( const int rates_total, // total bars on the current tick const int prev_calculated,// total bars on the previous tick const datetime& time[], const double& open[], const double& high[], // price array of the maximum prices for the indicator calculations const double& low[], // price array of the minimum prices for the indicator calculations const double& close[], const long& tick_volume[], const long& volume[], const int& spread[] )
この変更後、始まりのパラメータ使用は意味をなさなくなります。そのためコードから削除します。
処理サイクルの変数変更制限を計算するコードは、計算の有効性を検証するデータですが、変更なしでそのまま残します。
//--- checking the number of bars if (rates_total < AroonPeriod - 1) return(0); //--- declare the local variables int first, bar; double BULLS, BEARS; //--- calculation of the first (staring index) for the main loop if (prev_calculated == 0) // checking for the first call of the OnCalculate function first = AroonPeriod - 1; // starting index for calculating all of the bars else first = prev_calculated - 1; // starting index for calculating new bars
ただしインディケータ値を計算するアルゴリズムにはある種の問題が発生します。その問題とはMQL5には、減少インデックスの方向で現在バーからの期間に対する最大、最小のインデックスを判断する関数が組み込まれていないことです。
解決法のひとつは自分でこの関数を書くことです。さいわいにも、その関数はすでカスタムインディケータのZigZag.mq5インディケータに存在しており、"MetaTrader5\MQL5\Indicators\Examples"フォルダにあります。
一番簡単な解決法は、ZigZag.mq5インディケータ内でこれら関数のコードを検索し、ウィンドウズのクリップボードにコピーし、 グローバルレベルのOnInit()関数のすぐあとに貼り付けることです。
//+------------------------------------------------------------------+ //| searching index of the highest bar | //+------------------------------------------------------------------+ int iHighest(const double &array[], // array for searching for the index of the maximum element int count, // number of the elements in the array (in the decreasing order), int startPos // starting index ) { //---+ int index = startPos; //---- checking the starting index if (startPos < 0) { Print("Incorrect value in the function iHighest, startPos = ", startPos); return (0); } //---- checking the startPos values if (startPos - count < 0) count = startPos; double max = array[startPos]; //---- index search for(int i = startPos; i > startPos - count; i--) { if(array[i] > max) { index = i; max = array[i]; } } //---+ return of the index of the largest bar return(index); } //+------------------------------------------------------------------+ //| searching index of the lowest bar | //+------------------------------------------------------------------+ int iLowest( const double &array[], // array for searching for the index of the maximum element int count, // number of the elements in the array (in the decreasing order), int startPos // starting index ) { //---+ int index = startPos; //--- checking the stating index if (startPos < 0) { Print("Incorrect value in the iLowest function, startPos = ",startPos); return(0); } //--- checking the startPos value if (startPos - count < 0) count = startPos; double min = array[startPos]; //--- index search for(int i = startPos; i > startPos - count; i--) { if (array[i] < min) { index = i; min = array[i]; } } //---+ return of the index of the smallest bar return(index); }
そののち、OnCalculate() 関数のコードは以下のようになります。
//+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate( const int rates_total, // total number of bars on the current tick const int prev_calculated,// number of calculated bars on the previous tick const datetime& time[], const double& open[], const double& high[], // price array for the maximum price for the indicator calculation const double& low[], // price array for the minimum price for the indicator calculation const double& close[], const long& tick_volume[], const long& volume[], const int& spread[] ) { //---+ //--- checking the number of bars if (rates_total < AroonPeriod - 1) return(0); //--- declare the local variables int first, bar; double BULLS, BEARS; //--- calculation of the starting bar number if (prev_calculated == 0) // checking for the first start of the indicator calculation first = AroonPeriod - 1; // starting number for the calculation of all of the bars else first = prev_calculated - 1; // starting number for the calculation of new bars //--- main loop for(bar = first; bar < rates_total; bar++) { //--- calculation of values BULLS = 100 - (bar - iHighest(high, AroonPeriod, bar) + 0.5) * 100 / AroonPeriod; BEARS = 100 - (bar - iLowest (low, AroonPeriod, bar) + 0.5) * 100 / AroonPeriod; //--- filling the indicator buffers with the calculated values BullsAroonBuffer[bar] = BULLS; BearsAroonBuffer[bar] = BEARS; } //---+ return(rates_total); } //+------------------------------------------------------------------+
軸の対称性のため、コードをすこし修正し、インディケータの垂直移動を追加しました。元のものにくらべると値を0.5としています。
チャート上にこのインディケータの動作による結果が表示されています
現在バーからAroonPeriod以上遠くない距離にある最高または最小の値を伴うエレメントの位置を見つけるには、MQL5内臓のArrayMaximum()またはArrayMinimum()を使用することができます。それはまた極値の検索をしますが、これら関数は昇順でのみ検索を行います。
ただ、検索はインデックスの降順でも行う必要があります。この最も簡単な解決法は、インディケータと価格バッファ内のインデックスの方向を変えることです。このとき、ArraySetAsSeries()関数を使用します。
しかし、計算ループにあるバー順序の方向も変える必要があり、最初の変数計算のアルゴリズムも変えます。
この場合、結果OnCalculate() 関数は以下のように見えます。
//+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate( const int rates_total, // total number of bars on the current tick const int prev_calculated,// number of calculated bars on the previous tick const datetime& time[], const double& open[], const double& high[], // price array for the maximum price for the indicator calculation const double& low[], // price array for the minimum price for the indicator calculation const double& close[], const long& tick_volume[], const long& volume[], const int& spread[] ) { //---+ //--- checking the number of bars if (rates_total < AroonPeriod - 1) return(0); //--- set indexation as timeseries ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); ArraySetAsSeries(BullsAroonBuffer, true); ArraySetAsSeries(BearsAroonBuffer, true); //--- declare the local variables int limit, bar; double BULLS, BEARS; //--- calculation of the starting bar index if (prev_calculated == 0) // check for the first call of OnCalculate function limit = rates_total - AroonPeriod - 1; // starting index for the calculation of all of the bars else limit = rates_total - prev_calculated; // starting index for the calculation of new bars //--- main loop for(bar = limit; bar >= 0; bar--) { //--- calculation of the indicator values BULLS = 100 + (bar - ArrayMaximum(high, bar, AroonPeriod) - 0.5) * 100 / AroonPeriod; BEARS = 100 + (bar - ArrayMinimum(low, bar, AroonPeriod) - 0.5) * 100 / AroonPeriod; //--- filling the indicator buffers with the calculated values BullsAroonBuffer[bar] = BULLS; BearsAroonBuffer[bar] = BEARS; } //----+ return(rates_total); } //+------------------------------------------------------------------+
変数名を『一番目』から『限定』に変えました。今の場合その方が適切だからです。
ここでは、メインループのコードMQL4のときと似ています。それで、この OnCalculate() 関数の書き方は、SMQL4からMQL5にインディケータを変換するのにコードの最小変更として使うことができます。
おわりに
もう終わりました!2種類のバージョンでインディケータは書かれました。
右の場合には、保守的でスマートな方法での問題解決でしたが、子供のおもちゃ、レゴを使って何かを作ることよりもほんの少し複雑なだけでした。
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/48
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索