Praktische Implementierung digitaler Filter in MQL5 für Anfänger

Nikolay Kositsin | 12 Februar, 2016

Einleitung

In meinem vorigen Beitrag habe ich eine Code-Analyse eines einfachen Indikators beschrieben und bin kurz auf die Interaktion dieses Indikators mit dem MetaTrader 5 Client-Terminal eingegangen. Bevor wir uns hier mit unserem neuen Thema befassen, sollten wir uns zunächst die Ergebnisse der Expert-Erstellung im "Fehler"-Tab oder dem "Toolbox"-Fenster in MetaEditor ansehen. Von dort kann man dann den Code des SMA-Indikators, den ich eingangs vorgeschlagen habe, weiter studieren.

Fehler bei der Indikator-Erstellung

Bei der Erstellung jeder der beiden Versionen dieses Codes, wie in unserem Fall, und wenn nichts verändert wird, ist der Erstellungsvorgang ziemlich einfach und liefert das erwartete Ergebnis


Es gibt keine Fehler und zusammen mit der Indikator-Datei mit der Erweiterung .mq5 ersheint auch eine ähnliche Datei mit der Erweiterung .ex5.

Bei der Arbeit mit dem Code kommt es jedoch zwangsläufig zu Fehlern. Und das liegt regelmäßig an den Programmierern. Aus diesem Grund verfügt der MetaEditor über einen eingebauten Mechanismus zur Prüfung des erstellten Codes auf alle möglichen Fehler. Wenn er welche findet, zeigt er eine komplette Liste aller Fehler bei der Erstellung an.

Um den Ort zu finden, wo sich ein Fehler befindet, geht man einfach im "Toolbox"-Fenster mit Doppelklick auf die entsprechende Zeile mit den Inhalten des Fehlers. Der Compiler zeigt meistens die Codezeile wo ein Fehler gefunden wurde, exakt an und zwar mit dem Symbol.

Hierbei sollte man eines bedenken Ein Fehler im Code kann eine ganze Reihe von Fehlern bei der Erstellung nach sich ziehen Um also diese Fehlerabfolge zu beseitigen, ist es ausreichend, zur ersten Zeile zu gehen, wo der Compiler einen Fehler gefunden hat und dort den Code zu berichtigen. Natürlich kann es eine ganze Menge solcher Fehlersequenzen bei der Erstellung geben. Wenn man also einen Fehler im Code behoben hat, müssen wir ihn erneut erstellen. Findet der Compiler weitere Fehler, dann muss man im "Fehler"-Tab des "Toolbox"-Fensters nach der ersten Zeile suchen:

Am besten versteht man wahrscheinlich wie das vonstatten geht, wenn wir unseren Code absichtlich mit "Fehlern versehen", um beobachten zu können, wie der Compiler auf absichtlich gemachte Fehler reagiert. Und das geht ziemlich einfach: man macht den Fehler in einem bestimmten Teil des Codes, drückt dann im MetaEditor auf "Compile" und sieht sich das Ergebnis der Code-Erstellung an. Noch besser ist es, wenn man sich intuitiv ein solches Ergebnis einer destruktiven Einwirkung auf den Code merkt. Das kann bei weiterer Anwendung auf jeden Fall nützlich sein, wenn man mit dem MQL5 Code arbeitet.

Dies ist eine Aufzählung möglicher destruktiver Änderungen im Quellcode des Indikators:

  1. Leerraum bei jedem Operator oder Variable.
  2. Löschen von Strichpunkten ";".
  3. Hinzufügen von Strichpunkten ";" in verschiedenen Teilen des Codes.
  4. Löschen eines Operators.
  5. Entfernen oder Hinzufügen einer Klammern oder runden Klammer.
  6. Entfernen eines Kommas ",".
  7. Hinzufügen eines extra Eingabeparameters in der OnCalculate() Funktion.
  8. Teilen einer Variable durch Null.
  9. Ersetzen von "==" durch "=" in der "falls" Operatorzeile.
  10. Änderung der Richtung des schrittweisen Zuwachses in einer Variable vpn Balken++ zu Balken--.

Natürlich findet der Compiler den Ort eines Fehlers nicht immer dort, wo er urspränglich gemacht wurde. Deswegen ist diese Vorbereitung wichtig, um verstehen zu lernen, wie man mit solchen Situationen umgeht. Noch eine letzte Erklärung in Bezug auf Fehler: Der MetaEditor Compiler stellt nur die Fehler in der MQL5-Sprache an sich fest und findet meistens logische Programmierfehler eben nicht!

Ihr MQL5 Wortschatz

Hört man einem x-beliebigen Menschen zu, dann stellt man fest, dass er, selbst angesichts der vielen Facetten der menschlichen Sprache, nur eine kleine Anzahl sprachlicher "Werkzeuge" benutzt, um seinen Gedanken und Bedürfnissen Ausdruck zu verleihen. In den meisten Fällen verwenden wir einen viel, viel kleineren Wortschatz las uns zur Verfügung steht. Und das Gleiche kann man auch auf die MQL5-Sprache anwenden. Sie sollten sich als zunächst beim Erlernen und Beherrschen der MQL5-Sprache mit den am häufigsten verwendeten Operatoren und Ausdrücken dieser Programmiersprache vertraut machen. Während Sie dann diese Sprache weiter erlernen, können Sie die Grenzen Ihres tatsächlichen Wortschatzes stetig erweitern.

Sie können z.B. vier Arten von Variablen verwenden (int, double, bool, string), if-else Bedingungs-Operator, for Schleifen-Operator, {} compound Operator und return Operator. Sie sollten auch genau wissen, wie man einen Strichpunkt ";" und ein Komma "," verwendet. Vielleicht wäre es auch nicht schlecht, die mathematischen und trigonometrischen Funktionen zu lernen. Diese Werkzeuge sind mehr als ausreichend, um Ihre Programmierfähigkeiten als Anfänger zu schulen und zu üben!

Weitere Verfeinerung des Indikators

Die im MetaTrader Client-Terminal dargestellten MQL5-Fähigkeiten zur Verfeinerung eines Indikators sind ziemlich einfach und allgemeiner Standard. Sie bestehen aus Operatoren globaler Ebene:

//---- 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"

und aus Funktionsaufrufen von 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);
Die StringConcatenate() Funktion setzt den Namens-String des Indikators mit Hilfe folgender Formel zusammen:
   shortname = shortname + "SMA(" + MAPeriod + "," + MAShift + ")";

Gemäß den im Beitrag Einen Indikator auf einen anderern anwenden gegebenen Empfehlungen, kann es auch nicht schaden, wenn man den PlotIndexSetInteger() Funktionsaufruf in OnCalculate() hinzufügt:

//---- 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
Klar wird unser Indikator dadurch ein bisschen größer, nachdem wir all diese zusätzlichen Codezeilen hinzugefügt haben, und wird auch ein bisschen komplizierter, doch jetzt hat er eben auch ein weitaus benutzerfreundlicheres Interface.

Das Ergebnis der vorangegangenen Arbeit als Template zur Erzeugung neuer Indikatoren

All das ist sicherlich interessant, dennoch bleibt eine ganz einfache Frage offen: Warum das Rad neu erfinden und den Indikator-Code wiederholen, der doch schon in zwei Versionen im Client-Terminal vorhanden ist, und zwar in Form des Moving Average.mq5  technischen Indikators und des ustom Moving Average.mq5  angepassten Indikators)? Die Antwort ist simpel. Um so schnell wie möglich das Schreiben von Codes für ähnliche Indikatoren zu erlernen, einfach mit Hilfe meines gerade vorgeschlagenen Codes für einen SMA-Indikator als Template, um Ihre intellektuellen Fähigkeiten so wenig wie möglich zu vergeuden! Sie können z.B. versuchen eine Code in MQL5 für einen digitalen Filter zu schreiben, wie z.B. FATL aus Finware.

Die Formel zur Berechnung des digitalen Filters lautet meistens:

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

wobei:

  • SUM — die Summe ist.
  • K(i) — der gewichtende Koeffizient ist.
  • CLOSE (i) — der Abschlusspreis des aktuellen Balkens ist.
  • FilterPeriod — die Balkenzahl für den Durchschnitt ist. 

Diese Formel unterscheidet sich nicht sehr von der Formel eines SMA-Indikators:

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

Der Unterschied ist, dass der Zeitraum in dem die Berechnungen mit einem digitalen Filter gemacht werden, strikt festgelegt ist und für spezifische digitale Filter sowie auch K(i) gewichtende Koeffizienten individuell ist. Die gewichtenden Koeffizienten ihrerseits und der Zeitraum des digitalen Filters werden auf Grundlage spezieller Algorithmen berechnet. Diese Algorithmen zu analysieren würde den Umfang dieses Beitrags sprengen, daher beschränken wir uns mit den fertigen Werten für den FATL digitalen Filter zu arbeiten. All diejenigen, die sich für den Gedanken einer Filterung digitaler Signale interessieren, können die Website Digital Methods Generator (auf Russisch) besuchen. Die Formel einer Variante des FATL-Indikators in MQL4 ist kein Geheimnis:

     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]; 

In MQL5 werden die Balken in Indikator-Buffern nach Richtung berechnet, genau umgekehrt als in MQL4. Um also diese Formel auf MQL5 Indikatoren anwenden zu können, müssen wir den Inkrementierungs-Vorgang innerhalb der Klammern durch den Dekrementierungs-Vorgang ersetzen. Da es in MQL5 kein Close[] Zeitreihen-Array gibt, müssen wir es durch einer passendere Variante ersetzen - price[]. Diese Aufgabe wird gemeinhin automatisiert und zwar mit Hilfe des folgenden Menü-Befehls im MetaEditor:

Der regelmäßig erfüllte Ausdruck Schließen [Balken + Ausdruck sollte ersetzt werden durchPreis [Balken -:

Klicken Sie in diesem Dialogfenster auf "Alle ersetzen". Als Ergebnis erhalten wir die zur Berechnung des FATL-Indikators in MQL5 notwendige Formel:

     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];

Jetzt können wir mit dem kodieren des Indikators beginnen, dessen Berechnungsalgorithmus ja gerade berücksichtigt wurde. Dazu müssen wir als Erstes den SMA_1_en.mq5 Indikator in MetaEditor öffnen und ihn alsFATL_en.mq5 speichern. Das Indikator-Template ist fertig. Nun müssen wir den Berechnungsalgorithmus des Indikators im Template ersetzen und einige, meist kosmetische, Änderungen an den Variablen vornehmen. Sie sollten den gesamten Block der zuletzt erwähnten Formel zur FATL-Filterberechnung auswählen und ihn in die Windows-Zwischenablage kopieren. Entfernen Sie nun im FATL.mq5 Indikator-Code den Code im Schleifen-Operator, außer der letzten Initialisierung des Indikator-Buffers:

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


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

Statt dieses gerade gelöschten Codes kopieren wir hier nun den Berechnungsalgorithmus für den FATL digitalen Filter aus der Windows-Zwischenablage hinein. Dann müssen wir mit Hilfe der gerade beschriebenen Methode das Wort SMA durch das besser geeignete FATL ersetzen. Und auf genau dieselbe Weise müssen wir die Namen MAPeriod und MAShift der Eingabe-Variablen durch FATLPeriod und FATLShft ersetzen. Die FATLPeriod Variable muss nun aus den externe Variablen entfernt werden, da sie einen festen Wert = 39 besitzt. Aus dem gleichen Grund muss sie auch aus dem StringConcatenate() Operator in der OnInit() Funktion verschwinden. Die iii lokale Variable brauchen wir nicht, also kann sie ebenfalls entfernt werden. Und abschließend können Sie die Farbe der Indikatorlinie zu Blau verändern und die Linie selbst etwas dicker anlegen.

Nach diesen einfachen Änderungen im SMA_1_en.mq5 Code erhalten wir den gewünschten FATL_en.mq5 Indikator-Code

//+------------------------------------------------------------------+
//|                                                      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);
  }
//+------------------------------------------------------------------+

Nach Erstellung des Indikators kann er auf dem Chart im Client-Terminal getestet werden:

Natürlich kann der so entstandene Code des FATL Indikators als Template zum Aufbau weiterer, ähnlicher Filter verwendet werden. Denn jetzt ist das Problem viel weniger komplex. In unserem Code reicht es vollkommen aus, die Filter-Berechnungsformel zu ersetzen, das Wort FATL durch DIGFILTER zu tauschen und (dann) die DIGFILTERPeriod Variable mit der erforderlichen Dimension des digitalen Filters zu initialisieren.

Allgemeine Lösung zur Erzeugung digitaler Filter im Client-Terminal

Der soeben besprochene Indikator ist eine Variante, das generelle Problem der Filterung digitaler Signale zu lösen. Es wäre schön, wenn es einen Indikator für eine allgemeine Lösung gäbe, sodass man nur mit Hilfe eines Indikators jeden digitalen Filter bauen könnte. Und Sergei Ilyuhin hat dieses Problem schon lange für den MetaTrader 4 Client-Terminal mit Hilfe des DF.dll Moduls gelöst Also nichts leichter, als mit diesem Module auch unser Problem im MetaTrader 5 Client-Terminal zu lösen. In diesem Modul wird die DigitalFilter() Funktion eingeführt:

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

Damit kann man die digitalen Filter-Koeffizienten als das array[] Array erhalten. Die Funktion schreibt die digitalen Filter-Koeffizienten mit der Größe 1500 in dieses Array und verwendet dazu den Verweis (das '&'-Zeichen nach der Deklarierung dieses Variablentyps in diesem Array). Die Funktion akzeptiert die Werte von 10 Eingabeparametern und liefert die Größe des digitalen Filters. Das genügt vollkommen um den universellen digitalen Filter zu bauen. Das ganze Problem läuft darauf hinaus, einen DLL-Import in den bestehenden Indikator auf einer globalen Ebene zu organisieren, das Koeffizienten-Array in den Codeblock zur Indikator-Initialisierung zu bekommen und auf Basis dieser Koeffizienten eine universelle Filterberechnung OnCalculate() ablaufen zu lassen. Die Eingabe-Variablen der DigitalFilter() Funktion müssen in die Eingabe-Variablen des Indikators platziert werden. Das machen wir gleich.

Das Importieren der DF.dll -Datei bereitet keine Probleme Sind ja nur drei Codezeilen:

//---- 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

Anschließend machen wir alle externen variablen der DigitalFilter() Funktion als Eingabe-Variablen des Indikators:

//---- 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

Auf globaler Ebene deklarieren wir die FILTERPeriod Variable ohne Initialisierung:

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

Auf der gleichen Ebene deklarieren wir das dynamische Array zum Speichern der Filter-Koeffizienten:

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

Jetzt begeben wir uns in den Block der OnInit() Funktion. Es ist nicht besonders logisch, das FILTERTable[] Array las einen Parameter der DigitalFilter() Funktion zu verwenden. Dafür könnten wir es auf 1500 Elemente Fassungsvermögen vergrößern, von denen im OnCalculate() Funktionsblock nur 100 - 200 verwendet werden. In solchen Fällen empfiehlt sich die Verwendung des lokal deklariertenArray[1500] Arrays innerhalb der OnInit() Funktion. Die von diesem Array notwendige Datenmenge wird nun auf das FILTERTable[] Array geschrieben. Nach Verlassen der OnInit() Funktion, wird das große Array[] Array zerstört und die notwendigen Daten bleiben im FILTERTable[] Array zurück, dessen Größe der Länge des FILTERPeriod digitalen Filters entspricht. Das ist die Code-Variante, die für diesen Zweck verwendet wurde:

//---- 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);

Der Code zur Filterberechnung innerhalb der OnCalculate() Funktion ist recht einfach:

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

Die Endversion dieses Indikator-Codes wird in der DFilter_en.mq5 FDAtei dargestellt. Man aknn das Interface dieses Indikators noch leicht verbessern. Die Eingabe-Variable des Indikators nimmt Werte von 0 - 3 an.

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

Diese Werte sind wesentlich leichter zu lesen, wenn sie nicht als Ziffern, sondern als Namen des Filters dargestellt werden: 0 - Tief-Übertragung-Filter (FATL/SATL/KGLP), 1 - Hoch-Übertragung-Filter (KGHP), 2 - Band-Übertragung-Filter (RBCI/KGBP), 3 - Band-Stop Filter (KGBS). Es gibt für so einen Fall in MQL5 eine spezielle Art von Variablen, namens Aufzählungen. In unserem Fall müssen wir die Aufzählung vor den Eingabe-Parameter des Indikators deklarieren und initialisieren:

//---- 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)
  };

Danach müssen wir den Typ der in der Deklarierung der externen Parameter des Indikators verwendeten Variable ersetzen:

input FType_ FType = LPF; //Filter Type

Und als Ergebnis sieht die Wahl der Werte dieses Parameters in der Dialogbox des Indikators so aus:

Wie bei der Deklarierung der Aufzählung sind die benannten Konstanten von einzeiligen Kommentaren gefolgt und müssen dann als Eingabeparameter gewählt werden. Jetzt haben wir die Endversion des universellen digitalen Filter-Quellcodes:

//+------------------------------------------------------------------+
//|                                                      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);
  }
//+------------------------------------------------------------------+
Die Implementierung in MQL5 eines solchen universellen digitalen Filters nur mit Hilfe des Client-Terminals macht die Notwendigkeit jedweder digitaler Filter der Firma FinWare hinfällig. Und dies bedeutet einen erheblichen Vorteil, der uns neue Möglichkeiten im Umgang mit diesen Indikatoren eröffnet.

Fazit

Nach all diesen Anpassungen im Code erhalten wir eine Menge Details. Doch bei genauerer Betrachtung der Details dieses Vorgangs funktioniert alles komplett logisch und verständlich, wenn wir mit der Analyse der einfachsten Dinge beginnen und dann den sinnvollen und überlegten nächsten Schritt von einfach zu komplex machen.