初心者のためのMQL5におけるデジタルフィルタの実践的実装

Nikolay Kositsin | 5 10月, 2015

はじめに

私は 前稿で、シンプルなインディケータの分析をし、このインディケータに関しMetaTrader 5のクライアント端末との連携について少し触れました。ここでは、先に進む前に"Toolbox"ウィンドウの『エラー』タブでのエクスパートコンパイル結果についてじっくり見る必要があります。ここから、以前にご提案したSMAインディケータコードについて詳しい学習を始めることができます。


インディケータのコンパイルエラー

この場合、2つのコードのあらゆるバージョンをコンパイルするとき、変更を加えないのであれば、コンパイル手順はおおいにスムーズに進み、期待の結果を得ることができます。

図1 インディケータコードコンパイルの成功

エラーはなく.mq5拡張子を伴うインディケータファイルと共に.ex5拡張子を伴なう似通ったファイルが作成されます。

よくあるようにコードで作業するときはエラーを避けることはできません。それらは定期的にプログラムによって作成されます。この目的のために、MetaEditorにはあらゆるエラーに備えコンパイルされたコードをチェックするための内蔵メカニズムがあります。それはエラーを認めると発生したエラーの全リストを提供します。

図2 コンパイルエラー

エラーの位置を検出するためには"Toolbox"ウィンドウのエラー内容に従い適切な行をダブルクリックします。たいていコンパイラが適切なアイコンを使用して正確にエラーが認められたコード行を示してくれます。

図3 コンパイルエラーの位置特定

ここで考慮すべき点が一点あります。コードにある一つのエラーがコンパイルエラーを次々に引き起こす可能性があるということです。それで、エラーの連鎖を除くには、コンパイラにエラーが認められた最初の行に行き、コードを修正すれば十分です。当然、そういったコンパイルエラーの連鎖は複数存在しえます。そこで、コード内エラーを一つ修正したあとで、それを再びコンパイルし、もしコンパイルエラーが見つかったら、そこで"Toolbox"ウィンドウの『エラー』タブの最初の行を探す必要があります。

図4 Toolboxウィンドウのエラータブ内で最初の行を選択

おそらく、これを最も効果的に理解するには、コンパイラの反応を学習するため、われわれのコードに破壊的な影響を与えるエラーを意識的に起こすのがよいでしょう。やり方は極めてシンプルです。コードの特定箇所にエラーを起こし、MetaEditor の『コンパイル』ボタンを押します。そして、コンパイルの結果をよく見ます。コードに壊滅的影響を与えるような結果を直感的に記憶していればなお良いでしょう。いずれにせよ、これはその後実際にMQL5コードで作業するのに有用です。

以下はインディケータのソースコードに多大な影響を起こしうる変更のリストです。

  1. オペレータや変数へのスペース挿入
  2. セミコロン";"の消去
  3. コードの異なる箇所にセミコロン";"を挿入
  4. オペレータの削除
  5. 括弧の消去や挿入
  6. コンマ","の消去
  7. OnCalculate()関数への余分なパラメータ追加
  8. 変数のゼロ除算
  9. "if"オペレータ行における"=="の"="への置き換え
  10. ++バーから--バーへの増分変数方向変更

当然、コンパイラは常にエラー発生と同時に発生個所を指摘するものではありません。そのため、この予備作業がそういう状況への対応に役立つのです。エラーに関して説明することがもう一つあります。MetaEditorコンパイラはMQL5言語のエラーだけを判断するだけで、通常プログラムのロジカルな エラーは検出しない、ということです。


MQL5のボキャブラリ

特定の個人に耳を傾けると、人類の言語は豊かなのに、個人の考えやニーズを表現するのにはほんのわずかなツールの一部を使っているだけだということが判ります。たいていの状況において、実際使用されているボキャブラリは使用可能な分量からするとごくわずかだということがわかるのです。MQL5についても同じ原理が働きます。まず、MQL5言語を学習している間は、共通して使用されるオペレータやこのプログラム言語の表現に慣れることです。そして、この言語を習得したら、次第に実用的なボキャブラリの幅を増やしていくことができます。

たとえば、4種類の異なるタイプの変数を使うことができます(intdoubleboolstring)。if-else条件付けオペレータ、 forループオペレータ、{} compoundオペレータ、 そしてreturnオペレータです。また、セミコロン";"やコンマ","の使い方も徹底的に学習します。数学関数および三角関数を学習するのもよいでしょう。これらは初期段階でプログラム技能を練習するのに十分すぎるツールです。


インディケータの改良

MetaTraderクライアント端末に表示されるインディケータを改良するMQL5の能力はきわめてシンプルで標準的なものです。 それらは グローバルレベル オペレータで構成されます。

//---- Indicator's author
#property copyright "2010, MetaQuotes Software Corp."
//---- Author's web-site link
#property link      "https://www.mql5.com"
//---- Indicator version number
#property version   "1.00"
//---- Drawing the indicator in the main window
#property indicator_chart_window
//---- One buffer is used for calculating and drawing the indicator
#property indicator_buffers 1
//---- Only one graphical plotting is used
#property indicator_plots   1
//---- Drawing the indicator as line
#property indicator_type1   DRAW_LINE
//---- Red is used as indicator's line color
#property indicator_color1  Red
//---- Indicator line is continuous curve
#property indicator_style1  STYLE_SOLID
//---- Indicator line thickness is equal to 1
#property indicator_width1  1
//---- Displaying indicator's label
#property indicator_label1  "SMA"

of 関数が of OnInit()を呼びます。

//---- Variable initialization for indicator's short name
   string shortname;
   StringConcatenate(shortname,"FATL(",FATLShift,")");
//--- Creating labels to display in Data Window
   PlotIndexSetString(0,PLOT_LABEL,shortname);
//--- Creating name to display in a separate window and in tool-tip
   IndicatorSetString(INDICATOR_SHORTNAME,shortname);
//--- Defining accuracy of displaying indicator's values
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits+1);
//--- Prohibition of displaying blank values
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
StringConcatenate()関数はこのフォーミュラを用いてインディケータ名を組み合わせます。
   shortname = shortname + "SMA(" + MAPeriod + "," + MAShift + ")";

ひとつのインディケータを他のインディケータへ適用の項目によるとPlotIndexSetInteger()関数呼び出しを OnCalculate()関数に追加しても問題はないだろうとのことです。

//---- Calculating the 'first' starting number for the bars recalculation loop
   if(prev_calculated==0)        // Checking the first start of the indicator calculation
     {
      first=FATLPeriod-1+begin;  // Starting number for calculation of all bars
      //--- Increasing the start of data position by 'begin' bars, because the 
      //    calculations are based on data of another indicator
      if(begin>0)
         PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,begin+FATLPeriod);
     }
   else first=prev_calculated-1; // Starting number for calculation of new bars

コード内の追加行を含めたあと、インディケータサイズは多少増えわずかに複雑化しますが、そのためより使用感が良くなったというのはよくあることです。


新規インディケータ作成テンプレートとしての前段階作業の結果

これらはすべて確かに興味深いものですが、ここでごく当然の疑問がわきます。インディケータコードをわざわざ繰り返す理由はなにか?それはすでに2バージョンのクライアント端末で利用可能なはずなのに、です。Moving Average.mq5テクニカルインディケータ形式とCustom Moving Average.mq5 カスタムインディケータ形式です。応えは簡単です。似通ったインディケータコードを速く書くことを覚えるには、以前に提案したSMAインディケータコードをテンプレートとして用いることです。それで出来る限り知的リソースも節約することです!たとえば、FinwareからのFATLのようにMQL5でデジタルフィルタのコードを書こうとすることができます。

一般にデジタルフィルタの計算に使う式は以下のようなものです。

FILTER = SUM (K(i) * CLOSE (i), FilterPeriod)

ここで

この式はSMAインディケータコードとたいして変わりません。

SMA = SUM ((1 / MAPeriod ) * CLOSE (i), MAPeriod)

違いは、デジタルフィルタで計算が行われる期間が厳密に固定されており、特定のデジタルフィルタに固有のものとなっている点です。K(i) weighting係数と同様です。重み付け係数自体とデジタルフィルタ期間は特殊なアルゴリズムで計算されます。そのアルゴリズムを分析するのは本稿の範疇ではないので、FATLデジタルフィルタに対しすぐに使用可能な値を使っていくことにします。デジタル信号フィルタリングにご興味のある方はDigital Methods Generatorウェブサイト(ロシア語)を参照してください。 MQL4におけるバリアントFATLインディケータの公式は秘密ではありません。

     FATL =  0.4360409450 * Close[bar + 0]
           + 0.3658689069 * Close[bar + 1]
           + 0.2460452079 * Close[bar + 2]
           + 0.1104506886 * Close[bar + 3]
           - 0.0054034585 * Close[bar + 4]
           - 0.0760367731 * Close[bar + 5]
           - 0.0933058722 * Close[bar + 6]
           - 0.0670110374 * Close[bar + 7]
           - 0.0190795053 * Close[bar + 8]
           + 0.0259609206 * Close[bar + 9]
           + 0.0502044896 * Close[bar + 10]
           + 0.0477818607 * Close[bar + 11]
           + 0.0249252327 * Close[bar + 12]
           - 0.0047706151 * Close[bar + 13]
           - 0.0272432537 * Close[bar + 14]
           - 0.0338917071 * Close[bar + 15]
           - 0.0244141482 * Close[bar + 16]
           - 0.0055774838 * Close[bar + 17]
           + 0.0128149838 * Close[bar + 18]
           + 0.0226522218 * Close[bar + 19]
           + 0.0208778257 * Close[bar + 20]
           + 0.0100299086 * Close[bar + 21]
           - 0.0036771622 * Close[bar + 22]
           - 0.0136744850 * Close[bar + 23]
           - 0.0160483392 * Close[bar + 24]
           - 0.0108597376 * Close[bar + 25]
           - 0.0016060704 * Close[bar + 26]
           + 0.0069480557 * Close[bar + 27]
           + 0.0110573605 * Close[bar + 28]
           + 0.0095711419 * Close[bar + 29]
           + 0.0040444064 * Close[bar + 30]
           - 0.0023824623 * Close[bar + 31]
           - 0.0067093714 * Close[bar + 32]
           - 0.0072003400 * Close[bar + 33]
           - 0.0047717710 * Close[bar + 34]
           + 0.0005541115 * Close[bar + 35]
           + 0.0007860160 * Close[bar + 36]
           + 0.0130129076 * Close[bar + 37]
           + 0.0040364019 * Close[bar + 38]; 

MQL5ではインディケータバッファのバーは、MQL4とは逆の方向で計算されます。ゆえに、この式をMQL5インディケータで使用するには、 括弧内のインクリメント処理をデクリメント処理に置き換える必要があります。MQL5ではClose[]時間連続配列が使われていないため、それもより適切なvariantprice[]に置き換える必要があります。MetaEditorのメニューコマンドを使うことでこのタスクを自動化することはいたって当然です。

図5 MetaEditor 5で行われる式の検索と置き換え

よく見るClose [bar +式はprice [bar -と置換する必要があります。

図6 ダイアログボックスの置換

このダイアログボックスで「全てを置き換え」をクリックします。結果、MQL5でFATLインディケータ計算に必要な式を取得します。

     FATL =  0.4360409450 * price[bar - 0]
           + 0.3658689069 * price[bar - 1]
           + 0.2460452079 * price[bar - 2]
           + 0.1104506886 * price[bar - 3]
           - 0.0054034585 * price[bar - 4]
           - 0.0760367731 * price[bar - 5]
           - 0.0933058722 * price[bar - 6]
           - 0.0670110374 * price[bar - 7]
           - 0.0190795053 * price[bar - 8]
           + 0.0259609206 * price[bar - 9]
           + 0.0502044896 * price[bar - 10]
           + 0.0477818607 * price[bar - 11]
           + 0.0249252327 * price[bar - 12]
           - 0.0047706151 * price[bar - 13]
           - 0.0272432537 * price[bar - 14]
           - 0.0338917071 * price[bar - 15]
           - 0.0244141482 * price[bar - 16]
           - 0.0055774838 * price[bar - 17]
           + 0.0128149838 * price[bar - 18]
           + 0.0226522218 * price[bar - 19]
           + 0.0208778257 * price[bar - 20]
           + 0.0100299086 * price[bar - 21]
           - 0.0036771622 * price[bar - 22]
           - 0.0136744850 * price[bar - 23]
           - 0.0160483392 * price[bar - 24]
           - 0.0108597376 * price[bar - 25]
           - 0.0016060704 * price[bar - 26]
           + 0.0069480557 * price[bar - 27]
           + 0.0110573605 * price[bar - 28]
           + 0.0095711419 * price[bar - 29]
           + 0.0040444064 * price[bar - 30]
           - 0.0023824623 * price[bar - 31]
           - 0.0067093714 * price[bar - 32]
           - 0.0072003400 * price[bar - 33]
           - 0.0047717710 * price[bar - 34]
           + 0.0005541115 * price[bar - 35]
           + 0.0007860160 * price[bar - 36]
           + 0.0130129076 * price[bar - 37]
           + 0.0040364019 * price[bar - 38];

これで、インディケータ コードを書き始めることができます。その計算アルゴリズムはすでに考察されました。このためにはまず、MetaEditorのSMA_1_en.mq5インディケータを開き、FATL_en.mq5として保存します。インディケータ テンプレートの準備が整いました。それでは、その中のインディケータ計算アルゴリズムを置き換え、変数にいくらか変更を加えます。見栄え良くするのです。先に述べたFATLフィルタ計算用の式のブロックをまるごと選択し、ウィンドウズのクリップボードにコピーします。それから、FATL.mq5インディケータコードで、インディケータバッファの最終初期化を除きループ操作内にあるコードをすべて削除します。

//---- Main loop of indicator calculation
   for(bar=first; bar<rates_total; bar++)
     {
     


      //---- Indicator buffer's cell initialization with FATL value
      ExtLineBuffer[bar]=FATL;
     }

削除されたコードの代わりにFATLデジタルフィルタ計算アルゴリズムをウィンドウズのクリップボードから貼り付けます。次述の置換手順によりSMAワードを より適切なFATLに置き換えます。まったく同じようにMAPeriod変数とMAShift input変数もそれぞれ FATLPeriodとFATLShft に置き換えます。FATLPeriod変数は外部変数からは削除する必要があります。それというのは39に等しい固定値だからです。同じ理由でOnInit()関数のStringConcatenate()オペレータからも削除する必要があります。さて、iiiローカル変数は必要なくなりましたから、それも削除してしまいます。最後にインディケータ行の色を青に変え、線をやや太くします。

SMA_1_en.mq5コードを用いて簡単に操作したら、希望のインディケータコードFATL_en.mq5を取得します。

//+------------------------------------------------------------------+
//|                                                      Fatl_en.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
//---- Indicator's author
#property copyright "2010, MetaQuotes Software Corp."
//---- Author's web-site link
#property link      "https://www.mql5.com"
//---- Indicator version number
#property version   "1.00"
//---- Drawing the indicator in the main window
#property indicator_chart_window
//---- One buffer is used for calculating and drawing the indicator
#property indicator_buffers 1
//---- Only one graphical plotting is used
#property indicator_plots   1
//---- Drawing the indicator as line
#property indicator_type1   DRAW_LINE
//---- Blue is used as indicator's line color
#property indicator_color1  Blue
//---- Indicator line is continuous curve
#property indicator_style1  STYLE_SOLID
//---- Indicator line thickness is equal to 2
#property indicator_width1  2
//---- Displaying indicator's label
#property indicator_label1  "FATL"

//---- Input parameters of indicator
input int FATLShift=0; // FATL horizontal shift in bars

//---- Declaring and initializing a variable to store the number of calculated bars
int FATLPeriod=39;

//---- Declaration of dynamic array, which will be 
//     used later as indicator buffer
double ExtLineBuffer[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+  
void OnInit()
  {
//----+
//---- Transformation of ExtLineBuffer dynamic array into indicator buffer
   SetIndexBuffer(0,ExtLineBuffer,INDICATOR_DATA);
//---- Horizontal shift of indicator by FATLShift
   PlotIndexSetInteger(0,PLOT_SHIFT,FATLShift);
//---- Setting the position from which the drawing of indicator will start
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,FATLPeriod);
//---- Variable initialization for indicator's short name
   string shortname;
   StringConcatenate(shortname,"FATL(",FATLShift,")");
//--- Creating labels to display in Data Window
   PlotIndexSetString(0,PLOT_LABEL,shortname);
//--- Creating name to display in a separate window and in tool-tip
   IndicatorSetString(INDICATOR_SHORTNAME,shortname);
//--- Defining accuracy of displaying indicator's values
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits+1);
//--- Prohibition of displaying blank values
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
//----+
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(
                const int rates_total,     // amount of history in bars at the current tick
                const int prev_calculated, // amount of history in bars at the previous tick
                const int begin,           // beginning number of reliable count of bars
                const double &price[]      // price array for indicator calculation
                )
  {
//----+   
//---- Check if the number of bars is sufficient for calculation
   if(rates_total<FATLPeriod-1+begin)
      return(0);

//---- Declaring local variables
   int first,bar;
   double Sum,FATL;

//---- Calculating the 'first' starting number for the bars recalculation loop
   if(prev_calculated==0)        // Checking the first start of the indicator calculation
     {
      first=FATLPeriod-1+begin;  // Starting number for calculation of all bars
      //--- Increasing the start of data position by 'begin' bars, because the 
      //    calculations are based on data of another indicator
      if(begin>0)
         PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,begin+FATLPeriod);
     }
   else first=prev_calculated-1; // Starting number for calculation of new bars

//---- Main loop of indicator calculation
   for(bar=first; bar<rates_total; bar++)
     {
      //---- 
      FATL=0.4360409450*price[bar-0]
           + 0.3658689069 * price[bar - 1]
           + 0.2460452079 * price[bar - 2]
           + 0.1104506886 * price[bar - 3]
           - 0.0054034585 * price[bar - 4]
           - 0.0760367731 * price[bar - 5]
           - 0.0933058722 * price[bar - 6]
           - 0.0670110374 * price[bar - 7]
           - 0.0190795053 * price[bar - 8]
           + 0.0259609206 * price[bar - 9]
           + 0.0502044896 * price[bar - 10]
           + 0.0477818607 * price[bar - 11]
           + 0.0249252327 * price[bar - 12]
           - 0.0047706151 * price[bar - 13]
           - 0.0272432537 * price[bar - 14]
           - 0.0338917071 * price[bar - 15]
           - 0.0244141482 * price[bar - 16]
           - 0.0055774838 * price[bar - 17]
           + 0.0128149838 * price[bar - 18]
           + 0.0226522218 * price[bar - 19]
           + 0.0208778257 * price[bar - 20]
           + 0.0100299086 * price[bar - 21]
           - 0.0036771622 * price[bar - 22]
           - 0.0136744850 * price[bar - 23]
           - 0.0160483392 * price[bar - 24]
           - 0.0108597376 * price[bar - 25]
           - 0.0016060704 * price[bar - 26]
           + 0.0069480557 * price[bar - 27]
           + 0.0110573605 * price[bar - 28]
           + 0.0095711419 * price[bar - 29]
           + 0.0040444064 * price[bar - 30]
           - 0.0023824623 * price[bar - 31]
           - 0.0067093714 * price[bar - 32]
           - 0.0072003400 * price[bar - 33]
           - 0.0047717710 * price[bar - 34]
           + 0.0005541115 * price[bar - 35]
           + 0.0007860160 * price[bar - 36]
           + 0.0130129076 * price[bar - 37]
           + 0.0040364019 * price[bar - 38];

      //---- Indicator buffer's cell initialization with FATL value
      ExtLineBuffer[bar]=FATL;
     }
//----+     
   return(rates_total);
  }
//+------------------------------------------------------------------+

インディケータのコンパイルができたら、クライアント端末のチャートで検証します。

図7 FATL.ex5インディケータ動作のチャート上での結果

FATLインディケータから得られたコードは当然他に似通ったフィルタを構築する際使用するのは可能です。しかし、問題はもっと簡単です。われわれのコードにおいては、フィルタ計算式の置換、FATLワードのDIGFILTERとの置換、DIGFILTERPeriod変数のデジタルフィルタの必要なディメンションでの初期化(今) で十分です。


クライアント端末におけるデジタルフィルタ作成の共通ソリューション

考察したばかりのインディケータはデジタル信号フィルタの一般的な問題を解決する唯一のバリアントです。一般的ソリューションを表し、インディケータをただ一つ使うだけでデジタルフィルタを構築できるインディケータがあればよいと思います。この問題は、MetaTrader 4クライアント端末では、Sergei Ilyuhin氏考案のDF.dllモジュールを使うことでかなり以前に解決されました。ですから、MetaTrader 5クライアント端末における問題解決にもそれを使うのが簡単な方法でしょう。このモジュールではDigitalFilter()関数が導入されています。

DigitalFilter(int FType, int P1, int D1, int A1, int P2, int D2, int A2, double Ripple, int Delay, double& array[]);

それにより、array[]配列としてデジタルフィルタ係数を受け取ることができます。この関数により、参照(本配列にこの変数宣言をしたのち'&' を入れます。)を用いてサイズ1500のこの配列にデジタルフィルタ係数を書き込むのです。関数は10個の入力値を受け取り、デジタルフィルタのサイズを返します。これはユニバーサルなデジタルフィルタを構築するには十分です。問題は今や、グローバルレベルの既存インディケータにDLLインポートを作成し、コードのインディケータの初期化ブロック内係数配列を取得、これら係数に基づきOnCalculate()関数内でユニバーサルな計算を実行することです。DigitalFilter()関数のインプット変数はインディケータのインプット変数に配置する必要があります。ここですぐにやってみます。

DF.dllファイルのインポートはなにも難しくありません。コードを3行書くだけです。

//---- DLL import
#import "DF.dll"
int DigitalFilter(int FType, int P1, int D1, int A1, int P2, int D2, int A2, double Ripple, int Delay, double& array[]); 
#import

このあと、インディケータのインプット変数として DigitalFilter()関数の外部変数を作成します。

//---- Input parameters of indicator
input FType_ FType=LPF;     //Filter Type
                            //0 - Low-Pass Filter (FATL / SATL / KGLP), 1 - High-Pass Filter (KGHP), 
                            //2 - Band-Pass Filter (RBCI / KGBP), 3 - Band-Stop Filter (KGBS)
input int    P1 = 28;       //Cut-off period 1, in bars
input int    D1 = 19;       //Transient process cut-off period 1, in bars
input int    A1 = 40;       //Fading in delay band 1, in dB
input int    P2 = 0;        //Cut-off period 2, in bars
input int    D2 = 0;        //Transient process cut-off period 2, in bars
input int    A2 = 0;        //Fading in delay band 2, in dB
input int    Delay=0;       //Delay, in bars
input double Ripple=0.08;   //Beats in bandwidth, in dB
input int    FILTERShift=0; //Moving Average horizontal shift, in bars

グローバルレベルでは FILTERPeriod変数宣言に初期化は必要ありません。

//---- Declaring and initializing a variable to store the number of calculated bars
int FILTERPeriod;

グローバルレベルでは 動的配列を宣言し、フィルタ係数を保存します。

//---- Declaration of dynamic array, which will be 
//     used later as indicator buffer
double FILTERTable[];

それでは、OnInit()関数のブロックに移動します。DigitalFilter()関数のパラメータとしてFILTERTable[]配列を使用するのは論理にかなっていません。このためには、サイズを1500エレメントまで増やします。そのうちOnCalculate()関数ではたった100 ~200だけを使用します。その状況ではOnInit()関数内のローカルで宣言されたArray[1500]配列を使うのが良いでしょう。この配列から必要とするデータ量はFILTERTable[]配列に書き込まれます。OnInit()関数から抜けたら、Array[]配列は破壊され、必要なデータは、FILTERPeriodデジタルフィルタと同量のサイズと長さのFILTERTable[]配列に保持されます。以下はこの目的に使用するコードのバリアントです。

//---- Calculation of digital filter coefficients and determining the size of FILTERTable[] buffer
   double Array[1500];
   FILTERPeriod=DigitalFilter(FType,P1,D1,A1,P2,D2,A2,Ripple,Delay,Array);
//----  Changing the size of FILTERTable[] buffer for required number of digital filter coefficients
   if(FILTERPeriod<=0)
     {
      Print("Input parameters are incorrect. Indicator can't operate!");
      return;
     }
//---- Copying data from temporary array with size of 1500 to the main array with size of FILTERPeriod
   ArrayCopy(FILTERTable,Array,0,0,FILTERPeriod);

OnCalculate()関数内ではフィルタ計算用のコードは極めてシンプルです。

      //---- Digital filter calculation formula
      FILTER=0.0;
      for(iii = 0; iii<FILTERPeriod; iii++)
         FILTER+= FILTERTable[iii] * price[bar - iii];

このインディケータコードの最終バージョンはDFilter_en.mq5ファイルにあります。このインディケータのインターフェースはすこし改善が可能です。それはインディケータの変数が0~3の値を取るという点です。

input int FType = 0; //Тип фильтра
                     //0 - ФНЧ (FATL/SATL/KGLP), 1 - ФВЧ (KGHP), 2 - полосовой (RBCI/KGBP), 3 - режекторный (KGBS)

これら値は数値形式でなくフィルタ名として簡単に判断されます。0 - ローパスフィルタ (FATL/SATL/KGLP)1 - ハイパスフィルタ (KGHP)2 - バンドパスフィルタ(RBCI/KGBP)3 - バンドストップフィルタ(KGBS)です。その場合MQL5では、特別なタイプの変数があり、 列挙と呼ばれます。われわれの場合は、インディケータのパラメータ入力以前に列挙を宣言し初期化する必要があります。

//---- Declaration and initialization of digital filters types
enum FType_ //Filter Type
  {
   LPF, //Low-Pass Filter (FATL/SATL/KGLP)
   HPF, //High-Pass Filter (KGHP)
   BPF, //Band-Pass Filter (RBCI/KGBP)
   BSF, //Band-Stop Filter (KGBS)
  };

その後、インディケータの外部パラメータ宣言で使用済み変数のタイプを置き換える必要があります。

input FType_ FType = LPF; //Filter Type

その結果、インディケータ ダイアログボックスでこのパラメータ値を選択すると以下のようになります。

図8 インディケータ入力パラメータダイアログボックス

列挙宣言同様、名前のつけられた定数の宣言には一行コメントが続き、定数は入力パラメータとして選択されます。これがユニバーサルなデジタルフィルタソースコードの最終バージョンです。

//+------------------------------------------------------------------+
//|                                                      ProjectName |
//|                                      Copyright 2010, CompanyName |
//|                                       http://www.companyname.net |
//+------------------------------------------------------------------+
/*
 * <<< DIGITAL FILTERS FOR METATRADER 5 >>> *
 *
 * DF.dll file should be placed in "\MetaTrader 5\MQL5\Libraries\" folder.
 * DF.dll requires three additional DLLs, containing a block of mathematical
 * processing - bdsp.dll, lapack.dll, mkl_support.dll.
 *  These DLLs must be installed in "C:\Windows\System32\" folder for
 * Windows 32-bit operating systems or in "C:\Windows\SysWOW64\" folder
 * for Windows 64-bit operating systems.
 *
 * Before using, make sure that:
 *
 * 1. "Allow DLL import" option is enabled in Client Terminal settings
 *     (Tools->Options->Expert Advisors tab).
 * 2. In "C:\Windows\System32\" or in "C:\Windows\SysWOW64\" folders the
 *    Bdsp.dll, lapack.dll and mkl_support.dll auxiliary math libraries are present.
 *
 * Description of input parameters:
 *
 * Ftype  - Filter Type: 0 - Low-Pass Filter (FATL/SATL/KGLP), 1 - High-Pass Filter (KGHP),
 *          2 - Band-Pass Filter (RBCI / KGBP), 3 - Band-Stop Filter (KGBS)
 * P1     -  Cut-off period P1, in bars
 * D1     - Transient process cut-off period D1, in bars
 * A1     - Fading in delay band A1, in dB
 * P2     - Cut-off period P2, in bars
 * D2     - Transient process cut-off period D2, in bars
 * A2     - Fading in delay band A2, in dB
 * Ripple - Beats in bandwidth, in dB
 * Delay  - Delay, in bars
 *
 * For Low-Pass Filter and HPF the values of P2, D2, A2 are ignored
 *
 * Conditions:
 * Low-Pass Filter:                         P1>D1
 * High-Pass Filter:                     P1<D1
 * Band-Pass Filter and Band-Stop Filter: D2>P2>P1>D1
 */
//+------------------------------------------------------------------+
//|      Digital Low Pass (FATL/SATL, KGLP) Filter    DFilter_en.mq5 | 
//|                    Digital Filter: Copyright (c) Sergey Ilyukhin |
//|                           Moscow, qpo@mail.ru  http://fx.qrz.ru/ |
//|                              MQL5 CODE: 2010,   Nikolay Kositsin |
//|                              Khabarovsk,   farria@mail.redcom.ru | 
//+------------------------------------------------------------------+
//---- Indicator's author
#property copyright "2005, Sergey Ilyukhin, Moscow"
//---- Author's web-site link
#property link      "http://fx.qrz.ru/"
//---- Indicator version number
#property version   "1.00"
//---- Drawing the indicator in main window
#property indicator_chart_window
//---- One buffer is used for calculating and drawing the indicator
#property indicator_buffers 1
//---- Only one graphical plotting is used
#property indicator_plots   1
//---- Drawing the indicator as line
#property indicator_type1   DRAW_LINE
//---- Blue is used as indicator's line color
#property indicator_color1  DarkViolet
//---- Indicator line is continuous curve
#property indicator_style1  STYLE_SOLID
//---- Indicator line thickness is equal to 2
#property indicator_width1  2
//---- Displaying indicator's label
#property indicator_label1  "DFilter"
//---- Declaration and initialization of digital filters types
enum FType_ //Filter Type
  {
   LPF, //Low-Pass Filter (FATL/SATL/KGLP)
   HPF, //High-Pass Filter (KGHP)
   BPF, //Band-Pass Filter (RBCI/KGBP)
   BSF, //Band-Stop Filter (KGBS)
  };

//---- Input parameters of indicator
input FType_ FType=LPF;     //Filter Type
                            //0 - Low-Pass Filter (FATL / SATL / KGLP), 1 - High-Pass Filter (KGHP), 
                            //2 - Band-Pass Filter (RBCI / KGBP), 3 - Band-Stop Filter (KGBS)
input int    P1 = 28;       //Cut-off period 1, in bars
input int    D1 = 19;       //Transient process cut-off period 1, in bars
input int    A1 = 40;       //Fading in delay band 1, in dB
input int    P2 = 0;        //Cut-off period 2, in bars
input int    D2 = 0;        //Transient process cut-off period 2, in bars
input int    A2 = 0;        //Fading in delay band 2, in dB
input int    Delay=0;       //Delay, in bars
input double Ripple=0.08;   //Beats in bandwidth, in dB
input int    FILTERShift=0; //Moving Average horizontal shift, in bars

//---- DLL Import
#import "DF.dll"
int DigitalFilter(int FType,int P1,int D1,int A1,int P2,int D2,int A2,double Ripple,int Delay,double &array[]);
#import

//---- Declaring and initializing a variable to store the number of calculated bars
int FILTERPeriod;

//---- Declaration of dynamic array, which will be 
//     used later as indicator buffer
double ExtLineBuffer[];

//---- Declaring and initializing an array for the digital filter coefficients
double FILTERTable[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+  
void OnInit()
  {
//----+
//---- Transformation of ExtLineBuffer dynamic array into indicator buffer
   SetIndexBuffer(0,ExtLineBuffer,INDICATOR_DATA);
//---- Horizontal shift of indicator by FILTERShift
   PlotIndexSetInteger(0,PLOT_SHIFT,FILTERShift);
//---- Setting the position from which the drawing of indicator will start
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,FILTERPeriod);
//---- Variable initialization for indicator's short name
   string shortname;
   StringConcatenate(shortname,"FILTER(",FILTERShift,")");
//---- Creating label to display in Data Window
   PlotIndexSetString(0,PLOT_LABEL,shortname);
//---- Creating name to display in a separate window and in tool-tip
   IndicatorSetString(INDICATOR_SHORTNAME,shortname);
//---- Defining accuracy of displaying indicator's values
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits+1);
//---- Prohibition of empty values plotting
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
//---- Calculation of digital filter coefficients and determining the size of FILTERTable[] buffer
   double Array[1500];
   FILTERPeriod=DigitalFilter(FType,P1,D1,A1,P2,D2,A2,Ripple,Delay,Array);
//----  Changing the size of FILTERTable[] buffer for required number of digital filter coefficients
   if(FILTERPeriod<=0)
     {
      Print("Input parameters are incorrect. Indicator can't operate!");
      return;
     }
//---- Copying data from temporary array with size of 1500 to the main array with size of FILTERPeriod
   ArrayCopy(FILTERTable,Array,0,0,FILTERPeriod);
//----+
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(
                const int rates_total,     // amount of history in bars at the current tick
                const int prev_calculated, // amount of history in bars at the previous tick
                const int begin,           // beginning number of reliable count of bars
                const double &price[]      // price array for indicator calculation
                )
  {
//----+   
//---- Check if the number of bars is sufficient for calculation
   if(rates_total<FILTERPeriod-1+begin)
      return(0);

//---- Declaring local variables
   int first,bar,iii;
   double Sum,FILTER;

//---- Calculating the 'first' starting number for the bars recalculation loop
   if(prev_calculated==0)         // Checking the first start of the indicator calculation
     {
      first=FILTERPeriod-1+begin; // Starting number for calculation of all bars
      //---- Increasing the start of data position by 'begin' bars, 
      //     because the calculations are based on data of another indicator
      if(begin>0)
         PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,begin+FILTERPeriod);
     }
   else first=prev_calculated-1;  // Starting number for calculation of new bars

//---- Main loop of indicator calculation
   for(bar=first; bar<rates_total; bar++)
     {
      //---- Digital filter calculation formula
      FILTER=0.0;
      for(iii = 0; iii<FILTERPeriod; iii++)
         FILTER+= FILTERTable[iii] * price[bar - iii];

      //---- Indicator buffer's cell initialization with FILTER value
      ExtLineBuffer[bar]=FILTER;
     }
//----+     
   return(rates_total);
  }
//+------------------------------------------------------------------+

クライアント端末という手段によってユニバーサル デジタルィルタのMQL5への実装することで、FinWare企業のデジタルフィルタへのニーズを閉ざしてしまいます。これは、かなり便利で、これらインディケータを使用する新しい可能性を開くものです。


おわりに

これまでのコードを使った操作を行い多くの詳細情報を取得しました。しかしこの手順の詳細をじっくり見ると、最もシンプルなものから分析を始め、シンプルから複雑へ意味深い配慮ある移行を続けたら、すべては理論的に作用し、理解可能です。