English Русский 中文 Español 日本語 Português 한국어 Français Italiano Türkçe
Anlegen eines Spektrumanalysators

Anlegen eines Spektrumanalysators

MetaTrader 5Beispiele | 14 März 2016, 13:00
567 0
Victor
Victor

Einleitung

Der hier vorliegende Beitrag möchte seine Leser mit einer möglichen Variante der Verwendung der grafischen Objekte der Programmiersprache MQL5 vertraut machen. Es wird ein Indikator analysiert, der unter Verwendung grafischer Objekte ein Bedienfeld für einen einfachen Spektrumanalysator anlegt.

Im Anhang zu diesem Beitrag befinden sich drei Dateien:

  1. SpecAnalyzer.mq5 – der in diesem Beitrag vorgestellte Indikator.
  2. SpecAnalyzer.mqh – die Include-Datei zur Einbindung von SpecAnalyzer.mq5.
  3. SAInpData.mq5 – der zur Organisation des Zugriffs auf externe Daten verwendete Indikator.

Zum Laden des Indikators SpecAnalyzer müssen üblicherweise alle drei Dateien in das Verzeichnis \MQL5\Indicators der Anwendungsinstanz auf dem Ausgabegerät (Terminal) kopiert werden. Anschließend müssen die Dateien SpecAnalyzer.mq5 und SAInpData.mq5 kompiliert werden. Der Indikator ist dazu gedacht, in das Hauptfenster des Diagramms geladen zu werden. Nach dem er geladen ist, verändert der Indikator die mit der Art der Darstellung verbundenen Einstellungen in diesem Fenster, und wenn er entfernt wird, werden alle grafischen Objekte gelöscht. Deshalb ist es besser den Indikator in ein separates, eigens für ihn angelegtes Fenster zu laden, um zu verhindern, dass die Darstellungsart in den bereits bestehenden Fenstern des Ausgabegerätes versehentlich geändert wird.

In Anbetracht des Umstandes, dass in dem Beitrag nicht der vollständige Programmcode des betrachteten Indikators aufgeführt wird, empfiehlt es sich, bei der Lektüre stets die Möglichkeit zu haben, sich den Code in den Dateien im Anhang anzusehen.

Der in diesem Beitrag vorgestellte Indikator erhebt keinesfalls den Anspruch, ein fertiges Programm zu sein. Er ist lediglich ein Beispiel für die Verwendung der grafischen Objekte der Programmiersprache. Interessierten Lesern schlage ich vor, wenn der Wunsch dazu besteht, den in diesem Beitrag vorgestellten Code eigenhändig zu aktualisieren und zu verbessern.

Die Koordinaten

In MQL5 werden bei der Platzierung grafischer Objekte zwei Verfahren zur Festlegung der entsprechenden Koordinaten verwendet. Für einige Objekte werden die Koordinaten in der Anzahl der Bildpunkte (Pixel) in Bezug auf einen ausgewählten Punkt des Fensters angegeben; für andere erfolgt die Einbindung anhand der Zeit und dem Kurswert des in dem jeweiligen Fenster abgebildeten Diagramms.

Um zum Beispiel Objekte wie Label oder Button in einem Diagramm zu platzieren, müssen ihre Koordinaten als Anzahl von Bildpunkten in Bezug auf eine der Ecken des Diagrammfensters angegeben werden. Bei dieser Art der Adresszuweisung behalten die Objekte ihre Position unabhängig von den aktuellen Fenstereigenschaften und dem eingestellten Maßstab für die Abbildung des Diagramms in diesem Fenster. Selbst bei einer Änderung der Fenstergröße ändern diese Objekte ihre Lage nicht und behalten ihre Anbindung aneinander bei. Bei einer Verschiebung des in dem Fenster abgebildeten Diagramms mithilfe der linken Maustaste behalten diese Objekte ihre Position in Bezug auf den gewählten Ankerpunkt im Fenster bei.

Die andere Gruppe von Objekten umfasst anstelle der Anbindung an Fensterkoordinaten die Einbindung in das dort abgebildete Diagramm. Dabei handelt es sich um Objekte wie Trend Line, Rectangle usw. Als Koordinaten werden bei der Erstellung und Platzierung dieser Objekte die Werte der Zeit und der Größe des in dem Fenster abgebildeten Kursdiagramms verwendet. Bei diesem Vorgehen zur Festlegung der Koordinaten ändern diese Objekte bei einer Änderung des Maßstabes der Wiedergabe des Diagramms oder im Fall seiner Verschiebung ihre Position sowohl in Bezug auf das Fenster als auch zueinander. Bei Erscheinen eines neuen Balkens in dem Diagramm ändern die Objekte ihre Lage ebenfalls, weil sich die Zeitskala des Diagramms dabei um die Größe des Zeitraums nach links verschiebt.

In dem Indikator SpecAnalyzer kommen bei der Anlage des Bedienfeldes für den Spektrumanalysator beide Arten grafischer Objekte gleichzeitig zum Einsatz. Damit die an das Diagramm angebundenen Objekte sich in Bezug auf das Fenster nicht verschieben, richten wir für die Wiedergabe des Diagramms entlang der senkrechten Skala das Vorgehen mit festen Werten ein und für die waagerechte Diagrammskala das dem kleinstmöglichen Wiedergabemaßstab entsprechende Verfahren. Außerdem setzen wir das feste Minimum der senkrechten Achse auf 0,0 und für die waagerechte Achse des Diagramms wählen wir das Verfahren, bei dem die Einrückung des Nullbalkens vom rechten Rand sowie das Verfahren der automatischen Transition zum rechten Rand des Diagramms fehlen. Auf diese Weise erscheint der dem Nullbalken und dem Wert 0,0 des Diagramms entsprechende Koordinatenpunkt in der unteren rechten Ecke und bei einem festen Wiedergabemaßstab kann er als Ankerpunkt zur Angabe der Koordinaten für Objekte der Art Trend Line und Rectangle verwendet werden. Wenn dabei als Ankerpunkt für grafische Objekte der Art Label oder Button ebenfalls die untere rechte Ecke des Diagramms angegeben wird, sind beide Arten der platzierten Objekte eindeutig aneinander gebunden.

Die Einrichtung aller erforderlichen Diagrammeigenschaften erfolgt in der Funktion SetOwnProperty() in der Datei SpecAnalyzer.mqh.

void GRaphChart::SetOwnProperty(void)
  {
  ChartSetInteger(m_chart_id,CHART_FOREGROUND,1);
  ChartSetInteger(m_chart_id,CHART_SHIFT,0);
  ChartSetInteger(m_chart_id,CHART_AUTOSCROLL,0);
  ChartSetInteger(m_chart_id,CHART_AUTOSCROLL,1);
  ChartSetInteger(m_chart_id,CHART_SCALE,0);
  ChartSetInteger(m_chart_id,CHART_SCALEFIX_11,1);
  ChartSetInteger(m_chart_id,CHART_SHOW_OHLC,0);
  ChartSetInteger(m_chart_id,CHART_SHOW_BID_LINE,0);
  ChartSetInteger(m_chart_id,CHART_SHOW_ASK_LINE,0);
  ChartSetInteger(m_chart_id,CHART_SHOW_LAST_LINE,0);
  ChartSetInteger(m_chart_id,CHART_SHOW_PERIOD_SEP,0);
  ChartSetInteger(m_chart_id,CHART_SHOW_GRID,0);
  ChartSetInteger(m_chart_id,CHART_SHOW_VOLUMES,CHART_VOLUME_HIDE);
  ChartSetInteger(m_chart_id,CHART_SHOW_OBJECT_DESCR,0);
  ChartSetInteger(m_chart_id,CHART_SHOW_TRADE_LEVELS,0);
  ChartSetInteger(m_chart_id,CHART_COLOR_BACKGROUND,Black);
  ChartSetInteger(m_chart_id,CHART_COLOR_FOREGROUND,Black);
  ChartSetDouble(m_chart_id,CHART_FIXED_MIN,0.0);
  }

Neben der Einrichtung der zur Gewährleistung der Einbindung der grafischen Objekte erforderlichen Eigenschaften werden in dieser Funktion auch die Eigenschaften eingestellt, die die Zuordnung einer Farbe sowie die Unterdrückung der Ausgabe einzelner Diagrammelemente ermöglichen.

Da sich bei der Arbeit des Indikators SpecAnalyzer die Diagrammeigenschaften ändern, muss die Speicherung der vorherigen Einstellungen beim Laden des Indikators ebenso gesichert sein wie ihre Wiederherstellung, wenn der Indikator entfernt wird. In der Standardbibliothek von MQL5 sind zu diesem Zweck die virtuellen Funktionen Save() und Load() der Klasse CChart vorgesehen. Diese Funktionen dienen zur Speicherung der Eigenschaften eines Objekte der Klasse CChart in einer Datei sowie zur Wiederherstellung dieser Eigenschaften aus der angelegten Datei. Um den Bestand der gespeicherten Einstellungen zu ändern und die Verwendung von Dateioperationen bei der Speicherung der Diagrammeigenschaften zu vermeiden, werden die virtuellen Funktionen Save() und Load() der Klasse CChart überschrieben und dabei die neue Klasse GRaphChart (s. SpecAnalyzer.mqh) angelegt. 

class GRaphChart : public CChart
  {
  protected:
    struct ChartPropertyes
      {
      double shift_size;
      double fixed_max;
      double fixed_min;
      double points_per_bar;
      long   mode;
      bool   foreground;
      bool   shift;
      bool   autoscroll;
      long   scale;
      bool   scalefix;
      bool   scalefix_11;
      bool   scale_pt_per_bar;
      bool   show_ohls;
      bool   show_bid_line;
      bool   show_ask_line;
      bool   show_last_line;
      bool   show_period_sep;
      bool   show_grid;
      long   show_volumes;
      bool   show_object_descr;
      bool   show_trade_levels;
      long   color_background;
      long   color_foreground;
      long   color_grid;
      long   color_volume;
      long   color_chart_up;
      long   color_chart_down;
      long   color_chart_line;
      long   color_candle_bull;
      long   color_candle_bear;
      long   color_bid;
      long   color_ask;
      long   color_last;
      long   color_stop_level;
      string ch_comment;
      };
      ChartPropertyes ChProp;
  
  public:
                   GRaphChart();
                  ~GRaphChart();
                   
         void      SetOwnProperty();
  virtual void      Save();
  virtual void      Load();
  };

Die Basisklasse von GRaphChart ist die Klasse CChart der Standardbibliothek von MQL5. In der Klasse GRaphChart wird einerseits das Gerüst ChartProperties definiert und andererseits das zur Speicherung der Diagrammeigenschaften im Systemspeicher statt wie in der Basisklasse in einer Datei gedachte Objekt ChProp angelegt. Die Funktion Save() füllt das Gerüst ChProp mit Daten entsprechend dem Wert der aktuellen Diagrammeigenschaften, während die Funktion Load() die zuvor gespeicherten Diagrammeigenschaften aus ihr wiederherstellt.

Die Funktion Save() wird im Konstruktor der Klasse GRaphChart aufgerufen und die Funktion Load() in ihrem Destruktor. Deshalb erfolgen das Speichern und die Wiederherstellung des vorhergehenden Zustands des Diagramms beim Anlegen bzw. Löschen eines Objektes der Klasse GRaphChart automatisch. Im Konstruktor der Klasse wird auch die oben erwähnte Funktion SetOwnProperty() aufgerufen.

//---------------------------------------------------- Constructor GRaphChart --
GRaphChart::GRaphChart()
  {
  m_chart_id=ChartID();
  Save();                                // Keep a original chart settings
  SetOwnProperty();                             // Set own chart settings
  }
//----------------------------------------------------- Destructor GRaphChart --
GRaphChart::~GRaphChart()
  {
  Load();                             // Restore a previous chart settings
  m_chart_id=-1;
  }

Wir führen die Verwendung der Klasse GRaphChart an einem einfachen Beispiel vor. Dazu legen wir in dem Verarbeitungsprogramm MetaEditor einen neuen benutzerdefinierten Indikator unter der Bezeichnung Test an. In den Code dieses Indikators fügen wir die Kopfzeilendatei SpecAnalyzer.mqh ein und legen ein Objekt der Klasse GRaphChart an, wobei wir ihm zwei Zeilen hinzufügen.

//+------------------------------------------------------------------+
//|                                                         Test.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window

#include "SpecAnalyzer.mqh"    // <----- Including header file 

GRaphChart  MainChart; // <----- Creating object of the class GRaphChart

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//---
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

Zur erfolgreichen Zusammenstellung des vorgeschlagenen Codes muss die Datei SpecAnalyzer.mqh in dem Verzeichnis \MQL5\Indicators der Anwendungsinstanz auf dem Ausgabegerät abgelegt werden.

Wenn auf dem Ausgabegerät ein neues Diagramm angelegt und unser Testmuster in sein Hauptfenster geladen wird, ändern sich die Eigenschaften des Diagramms, und wir sehen lediglich ein leeres, für die Wiedergabe grafischer Objekte vorbereitetes Fenster. Bei der Entfernung unseres Testindikators von dem Diagramm wird die ursprüngliche Diagrammart der Erfassung jeder Kursänderung wiederhergestellt.

Kommen wir jetzt zurück zu dem Indikator SpecAnalyzer. Am Anfang des Indikatorcodes (siehe die Datei SpecAnalyzer.mq5), erfolgt die Erstellung des Objekts MainChart der Klasse GRaphChart, was dazu führt, dass die Diagrammeigenschaften beim Laden des Indikators gespeichert werden.

                 button. button. button.
GRaphChart MainChart; // Create MainChart object
                 button. button. button.

Beim Entfernen des Indikators wird das Objekt MainChart automatisch vernichtet, wobei die ursprünglichen Eigenschaften des Diagramms in dem Destruktor seiner Klasse wiederhergestellt werden. 

Das Bedienfeld

Das Aussehen des Bedienfeldes in dem Indikator SpecAnalyzer wird vollständig von den im Fenster platzierten grafischen Objekten bestimmt. Die Klasse AllGrObject vereint alle Funktionen, die zu ihrer Erstellung und für die Interaktion mit ihnen erforderlich sind, siehe die Datei SpecAnalyzer.mqh.

class AllGrObject : public CChart
  {
  protected:
    long      m_chart_id;                                    // chart identifier
    
  public:
              AllGrObject();
             ~AllGrObject();
                   
    void      AddLabel(string name,int fsize,string font,
                             color col,int x,int y,string text="");
    void      AddButton(string name,int xsize,int ysize,color bgcol,
                        int fsize,string font,color col,int x,int y,
                        string text="",int state=0);
    void      AddEdit(string name,int xsize,int ysize,color bgcol,int fsize,
                      string font,color col,int x,int y,string text="");
    void      AddTrendLine(string name,color col,int style=0,int width=1);
    void      AddArrowline(string name,color col,int style=0,int width=1);
    void      AddRectangle(string name,color col,int style=0,int width=1);
    void      MoveGrObject(string name,int x1,int y1,int x2,int y2);
    void      SetButtonProp(string name,bool state,color col);
    long      GetRowNumber(string name);
    void      LabelTxt(string name,string str);
    
  };

Die Funktionen der Klasse, deren Bezeichnung mit Add beginnt, dienen zur Erstellung grafischer Objekte. AddButton() zum Beispiel erzeugt eine Objekt der Art Button.

Im Programm werden allen grafischen Objekten Koordinaten in der Form ihrer in Bildpunkten (Pixeln) ausgedrückten Entfernung von der unteren rechten Ecke des Diagramms zugeordnet. Bei den Objekten Trend Line, Arrowed Line und Rectangle müssen diese Koordinaten in Zeit- und Kurswerte umgewandelt werden. Diese Umwandlung erfolgt vor der Zuordnung der Koordinaten zu dem betreffenden Objekt in der Funktion MoveGrObject(). Einem Bildpunkt in der Waagerechten entspricht ein Balken, und einem Bildpunkt in der Senkrechten ein Punkt.

void AllGrObject::MoveGrObject(string name,int x1,int y1,int x2,int y2)
  {
  datetime t1[1],t2[1];
  long typ;
  
  typ=ObjectGetInteger(m_chart_id,name,OBJPROP_TYPE);
  if(typ==OBJ_TREND||typ==OBJ_ARROWED_LINE||typ==OBJ_RECTANGLE)
    {
    CopyTime(_Symbol,_Period,x1,1,t1);
    CopyTime(_Symbol,_Period,x2,1,t2);
    ObjectSetInteger(m_chart_id,name,OBJPROP_TIME,0,t1[0]);
    ObjectSetDouble(m_chart_id,name,OBJPROP_PRICE,0,_Point*y1);
    ObjectSetInteger(m_chart_id,name,OBJPROP_TIME,1,t2[0]);
    ObjectSetDouble(m_chart_id,name,OBJPROP_PRICE,1,_Point*y2);
    }
  }

Alle grafischen Objekte werden in dem Indikator jeweils nur einmal angelegt und zwar bei Aufruf von gr_object_create() aus der Funktion OnInit() des Indikators, siehe die Datei SpecAnalyzer.mq5. Bei allen Objekten mit Ausnahme von Trend Line, Arrowed Line und Rectangle werden die Koordinaten sofort festgelegt. Die Koordinaten für Objekt der Art Trend Line, Arrowed Line und Rectangle werden mithilfe des Aufrufs der Funktion gr_object_coordinate() festgelegt, die wiederum die oben angesprochene Funktion MoveGrObject() zur Umwandlung des Adresszuweisungsmodus verwendet. 

void gr_object_coordinate()
  {
  GObj.MoveGrObject("Trdline1",48,150,48,360);
  GObj.MoveGrObject("Trdline2",176,150,176,360);
  GObj.MoveGrObject("Trdline3",304,150,304,360);
  GObj.MoveGrObject("Trdline4",432,150,432,360);
  GObj.MoveGrObject("Trdline5",42,350,560,350);
  GObj.MoveGrObject("Trdline6",42,300,560,300);
  GObj.MoveGrObject("Trdline7",42,250,560,250);
  GObj.MoveGrObject("Trdline8",42,200,560,200);
  GObj.MoveGrObject("Arrline1",560,150,28,150);
  GObj.MoveGrObject("Arrline2",560,150,560,370);
  GObj.MoveGrObject("Rect1",0,1,208,110);
  GObj.MoveGrObject("Rect2",208,1,416,110);
  GObj.MoveGrObject("Rect3",416,1,624,110);
  GObj.MoveGrObject("Rect4",0,1,624,400);
  GObj.MoveGrObject("Rect5",20,10,195,80);
  }

Der Aufruf der Funktion gr_object_coordinate() ist in der Funktion OnCalculate() des Indikators enthalten. Das gewährleistet die korrekte Neuberechnung der Koordinaten bei Bildung eines neuen Diagrammbalkens, da die Funktion bei jeder neuen Kursänderung aufgerufen wird.

Die Schaltflächen des Bedienfeldes verteilen sich auf drei Gruppen: Die erste aus vier auf der linken Seite angeordneten Schaltflächen bestehende Gruppe ermöglicht die Auswahl der Art der Darstellung der Ergebnisse der Bewertung des Spektrums der Eingangsdatenfolge durch den Indikator. Es werden (entsprechend der Anzahl der Schaltflächen) vier Darstellungsarten unterstützt:

  1. Amplitude/Line - Anzeige des Moduls der Fourier-Transformation mit einer linearen Skala entlang der y-Achse;
  2. Amplitude/dB - Anzeige des Moduls der Fourier-Transformation mit einer logarithmischen Skala entlang der y-Achse;
  3. Power/Line - Anzeige des Quadrats der Fourier-Transformation mit einer linearen Skala entlang der y-Achse;
  4. Power/dB - Anzeige des Quadrats der Fourier-Transformation mit einer logarithmischen Skala entlang der y-Achse.

Zur Verarbeitung der Betätigung der Schaltflächen dieser Gruppe wurde das folgende Codefragment in die Funktion OnChartEvent() des Indikators aufgenommen: 

  if(id==CHARTEVENT_OBJECT_CLICK)                       // Click on the gr. object
    {
    if(sparam=="Butt1")                                   // Click on the button
      {
      GObj.SetButtonProp("Butt1",1,Chocolate);
      GObj.SetButtonProp("Butt2",0,SlateGray);
      GObj.SetButtonProp("Butt3",0,SlateGray);
      GObj.SetButtonProp("Butt4",0,SlateGray);
      YPowerFlag=0;YdBFlag=0;
      }
    if(sparam=="Butt2")                                   // Click on the button
      {
      GObj.SetButtonProp("Butt1",0,SlateGray);
      GObj.SetButtonProp("Butt2",1,Chocolate);
      GObj.SetButtonProp("Butt3",0,SlateGray);
      GObj.SetButtonProp("Butt4",0,SlateGray);
      YPowerFlag=0;YdBFlag=1;
      }
    if(sparam=="Butt3")                                   // Click on the button
      {
      GObj.SetButtonProp("Butt1",0,SlateGray);
      GObj.SetButtonProp("Butt2",0,SlateGray);
      GObj.SetButtonProp("Butt3",1,Chocolate);
      GObj.SetButtonProp("Butt4",0,SlateGray);
      YPowerFlag=1;YdBFlag=0;
      }
    if(sparam=="Butt4")                                   // Click on the button
      {
      GObj.SetButtonProp("Butt1",0,SlateGray);
      GObj.SetButtonProp("Butt2",0,SlateGray);
      GObj.SetButtonProp("Butt3",0,SlateGray);
      GObj.SetButtonProp("Butt4",1,Chocolate);
      YPowerFlag=1;YdBFlag=1;
      }

Wird die Betätigung einer Schaltfläche festgestellt, werden alle übrigen Schaltflächen der betreffenden Gruppe auf „nicht betätigt“ umgestellt, um die gleichzeitige Betätigung mehrerer Schaltflächen aus derselben Gruppe zu verhindern. Dabei erhalten die Auszeichner YPowerFlag und YdBFlag zur Bestimmung der aktuellen Art der Darstellung die dieser entsprechenden Werte.

Die zweite Gruppe aus vier Schaltflächen ermöglicht die Auswahl der Quelle für die Eingangsdaten. Dabei kann es sich um mithilfe des Aufrufs des Indikators SAInpData.mq5 bezogene externe Daten oder um drei von dem Programm selbst erzeugte Testdatenfolgen handeln. Die letzte Schaltflächengruppe beinhaltet zwei Schaltflächen zum Vor- und Zurückrollen der Liste im Ausgabefeld für Informationstexte. Die Verarbeitung der Betätigung dieser Schaltflächen erfolgt ebenso in der Funktion OnChartEvent() des Indikators wie bei den Schaltflächen der ersten Gruppe; siehe die Datei SpecAnalyzer.mq5.

Wir präsentieren ein Anwendungsbeispiel für die Funktionen der Klasse AllGrObject anhand des bereits angelegten Versuchsindikators Test.mq5. Dazu ergänzen wir seinen Quellcode um einige weitere Zeilen und fügen die ebenfalls bereits erwähnten Funktionen gr_object_create() und gr_object_coordinate() aus der Datei SpecAnalyzer.mq5 in ihn ein.

//+------------------------------------------------------------------+
//|                                                         Test.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window

#include "SpecAnalyzer.mqh" 

GRaphChart  MainChart;

AllGrObject GObj;        // <----- Creating object of the class AllGrObject

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
//---

  gr_object_create();          // <----- creating graphical objects

   return(0);
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//---

  MainChart.SetOwnProperty();    // <----- restoring current properties of the chart
  gr_object_coordinate();     // <----- setting coordinates for the graphical objects


//--- return value of prev_calculated for next call
   return(rates_total);
  }

//----------------------------------------------- Create all graphical objects --
void gr_object_create()
  {
  GObj.AddLabel("Title",10,"Arial",DarkGray,256,367,"Spectrum Analyzer");
  GObj.AddLabel("Label1",9,"Arial",LightSteelBlue,557,128,"0");
  GObj.AddLabel("Label2",9,"Arial",LightSteelBlue,422,128,"128");
  GObj.AddLabel("Label3",9,"Arial",LightSteelBlue,294,128,"256");
  GObj.AddLabel("Label4",9,"Arial",LightSteelBlue,166,128,"384");
  GObj.AddLabel("Label5",9,"Arial",LightSteelBlue,40,128,"512");
  GObj.AddLabel("Label6",9,"Arial",LightSteelBlue,28,156,"N");
  GObj.AddLabel("Label7",9,"Arial",LightSteelBlue,569,141,"0.00");
  GObj.AddLabel("Label8",9,"Arial",LightSteelBlue,569,191,"0.25");
  GObj.AddLabel("Label9",9,"Arial",LightSteelBlue,569,241,"0.50");
  GObj.AddLabel("Label10",9,"Arial",LightSteelBlue,569,291,"0.75");
  GObj.AddLabel("Label11",9,"Arial",LightSteelBlue,569,341,"1.00");
  GObj.AddLabel("Label12",9,"Arial",LightSteelBlue,569,358,"U");
  GObj.AddLabel("Label13",9,"Arial",DarkGray,490,86,"Y-axis Mode");
  GObj.AddLabel("Label14",9,"Arial",DarkGray,285,86,"Input Data");
  GObj.AddLabel("Label15",9,"Arial",DarkGray,75,86,"Level");
  GObj.AddLabel("Label16",9,"Arial",DarkGray,185,86,"N");
  GObj.AddLabel("Label17",8,"Courier",DarkOliveGreen,64,64);
  GObj.AddLabel("Label18",8,"Courier",DarkOliveGreen,64,51);
  GObj.AddLabel("Label19",8,"Courier",DarkOliveGreen,64,38);
  GObj.AddLabel("Label20",8,"Courier",DarkOliveGreen,64,25);
  GObj.AddLabel("Label21",8,"Courier",DarkOliveGreen,64,12);
  GObj.AddButton("Butt1",185,18,C'32,32,32',8,"Arial",SlateGray,612,79,"Amplitude (line)",0);
  GObj.AddButton("Butt2",185,18,C'32,32,32',8,"Arial",Chocolate,612,61,"Amplitude (log)",1);
  GObj.AddButton("Butt3",185,18,C'32,32,32',8,"Arial",SlateGray,612,43,"Power (line)",0);
  GObj.AddButton("Butt4",185,18,C'32,32,32',8,"Arial",SlateGray,612,25,"Power (log)",0);
  GObj.AddButton("Butt5",185,18,C'32,32,32',8,"Arial",SlateGray,403,79,"External Data",0);
  GObj.AddButton("Butt6",185,18,C'32,32,32',8,"Arial",SlateGray,403,61,"Test 1. SMA(3)",0);
  GObj.AddButton("Butt7",185,18,C'32,32,32',8,"Arial",Chocolate,403,43,"Test 2. SMA(32)",1);
  GObj.AddButton("Butt8",185,18,C'32,32,32',8,"Arial",SlateGray,403,25,"Test 3. LWMA(12)",0);
  GObj.AddButton("Butt9",14,34,C'32,32,32',8,"Wingdings",SlateGray,36,78,"\x0431",0);
  GObj.AddButton("Butt10",14,34,C'32,32,32',8,"Wingdings",SlateGray,36,44,"\x0432",0);
  GObj.AddEdit("Edit1",35,18,Black,9,"Arial",SlateGray,180,102);
  GObj.AddTrendLine("Trdline1",C'32,32,32');
  GObj.AddTrendLine("Trdline2",C'32,32,32');
  GObj.AddTrendLine("Trdline3",C'32,32,32');
  GObj.AddTrendLine("Trdline4",C'32,32,32');
  GObj.AddTrendLine("Trdline5",C'32,32,32');
  GObj.AddTrendLine("Trdline6",C'32,32,32');
  GObj.AddTrendLine("Trdline7",C'32,32,32');
  GObj.AddTrendLine("Trdline8",C'32,32,32');
  GObj.AddArrowline("Arrline1",LightSteelBlue);
  GObj.AddArrowline("Arrline2",LightSteelBlue);
  GObj.AddRectangle("Rect1",C'72,72,72');
  GObj.AddRectangle("Rect2",C'72,72,72');
  GObj.AddRectangle("Rect3",C'72,72,72');
  GObj.AddRectangle("Rect4",DarkGray);
  GObj.AddRectangle("Rect5",C'72,72,72');
  }
//---------- Set object coordinate for Trend Line, Arroved Line and Rectangle --
void gr_object_coordinate()
  {
  GObj.MoveGrObject("Trdline1",48,150,48,360);
  GObj.MoveGrObject("Trdline2",176,150,176,360);
  GObj.MoveGrObject("Trdline3",304,150,304,360);
  GObj.MoveGrObject("Trdline4",432,150,432,360);
  GObj.MoveGrObject("Trdline5",42,350,560,350);
  GObj.MoveGrObject("Trdline6",42,300,560,300);
  GObj.MoveGrObject("Trdline7",42,250,560,250);
  GObj.MoveGrObject("Trdline8",42,200,560,200);
  GObj.MoveGrObject("Arrline1",560,150,28,150);
  GObj.MoveGrObject("Arrline2",560,150,560,370);
  GObj.MoveGrObject("Rect1",0,1,208,110);
  GObj.MoveGrObject("Rect2",208,1,416,110);
  GObj.MoveGrObject("Rect3",416,1,624,110);
  GObj.MoveGrObject("Rect4",0,1,624,400);
  GObj.MoveGrObject("Rect5",20,10,195,80);
  }
//+------------------------------------------------------------------+

Zur Gewährung des Zugriffs auf die Funktionen der Klasse AllGrObject legen wir das Objekt GObj dieser Klasse an. In die Funktion OnInit() des Indikators fügen wir den Aufruf der Funktion gr_object_create() ein, in der alle zur Gestaltung des Aussehens und des Funktionsumfangs des erstellten Indikatorbedienfeldes benötigten grafischen Objekte angelegt werden.

Wir erweitern die Funktion OnCalculate um die Aufrufe der Funktionen MainChart.SetOwnProperty() und gr_object_coordinate() so, dass die Eigenschaften des Diagramms und die Koordinaten der in diesem platzierten grafischen Objekte bei jeder neuen Kursänderung wiederhergestellt werden. Diese Wiederherstellung ist bei der Bildung eines neuen Balkens in dem Ausgangsdiagramm, der Verschiebung eines Diagrammfeldes mit der linken Maustaste oder bei einer Änderung der Diagrammeigenschaften durch den Anwender unerlässlich. Nach der Zusammenstellung und dem laden dieses Testmusters sehen wir, wie unser Bedienfeld aussieht; s. Abb. 1.

Abb. 1. Benutzeroberfläche des Bedienfeldes 

Abb. 1. Benutzeroberfläche des Bedienfeldes

 Um die Platzierung des Bedienfeldes in Relation zu dem Diagramm zu veranschaulichen, lassen wir in dem vorliegenden Beispiel die Anzeige des Diagrammmaßstabs zu, s. Abb. 2.

 Abb. 2. Die Diagrammmaßstab

Abb. 2. Die Diagrammmaßstab 

Der Spektrumanalysator

In dem Indikator erfolgt die Bewertung eines Spektrums in 1.024 Ablesungen der Eingangsdatenfolge. Die Spektrumanalyse wird mithilfe des Algorithmus der schnellen Fourier-Transformation (FFT) ausgeführt. Die Funktion, die den FFT-Algorithmus umsetzt, stammt aus den Artikeln auf der MQL4-Webseite www.mql4.com. Für die Berechnungen wird die FFT-Funktion der Echtzeiteingangsdatenfolge verwendet; ihr Code befindet sich in der Datei SpecAnalyzer.mqh. Alle erforderlichen Schritte in Verbindung mit der Bewertung des Spektrums sind in der Funktion fft_calc() angelegt. 

void fft_calc()
  {
  int i,k;

  realfastfouriertransform(InpData,ArraySize(InpData),false);          // FFT
  for(i=1;i<ArraySize(Spectrum);i++)
    {
    k=i*2;
    Spectrum[i]=InpData[k]*InpData[k]+InpData[k+1]*InpData[k+1];    
    }
  Spectrum[0]=0.0;                             // Clear constant component level
  }

Bei Aufruf der Funktion fft_calc() muss das Datenfeld InpData[] die zu analysierende Eingangsdatenfolge enthalten. Nach Ausführung der Funktion realfastfouriertransform() wird das Ergebnis der FFT in diesem Datenfeld abgelegt. Weiterhin wird aus dem realen und dem irrealen Teil der Spektrumbewertung für jede harmonische Schwingung das Quadrat des Absolutbetrags errechnet und in dem Datenfeld Spectrum[] abgelegt. Die Kennziffer des Elementes im Datenfeld Spectrum[] entspricht der Nummer der harmonischen Schwingung. Da der berechnete Wert der konstanten Komponente in dem Indikator nicht verwendet wird, erhält das Element Spectrum[0] des Datenfeldes stets den Wert „0“.

Je nach Wert der Variablen InputSource kann das Datenfeld InpData[] entweder mit der Testdatenfolge oder mit aus einem externen Indikator bezogenen Daten gefüllt werden. Die Zusammenstellung der Eingangsdaten erfolgt in der Funktion get_input_data().

void get_input_data()
  {
  int i;
  
  ArrayInitialize(InpData,0.0);
  switch(InputSource)
    {
    case 0:                                                    // External Data
      if(ExtHandle!=INVALID_HANDLE)
        CopyBuffer(ExtHandle,0,0,ArraySize(InpData),InpData);
      break;
    case 1:                                                    // Test 1. SMA(3)
      for(i=0;i<3;i++)InpData[i]=1;
      break;
    case 2:                                                   // Test 2. SMA(32)
      for(i=0;i<32;i++)InpData[i]=1;
      break;
    case 3:                                                  // Test 3. LWMA(12)
      for(i=0;i<12;i++)InpData[i]=12-i;
      break;
    }
  }

Ist der Wert von InputSource gleich Null, werden 1.024 Werte aus dem Nullpuffer des Indikators SAInpData.mq5 in das Eingangsdatenfeld InpData[] kopiert. Die für die Analyse gedachten Daten werden sowohl in diesem Indikator selbst als auch nach Abruf aus ihm in anderen Indikatoren angelegt. Zur Gewährleistung des Zugriffs auf den Indikator SAInpData.mq5 wird die Funktion OnInit() um folgende Zeile erweitert, in ihr wird der Wert der Variablen ExtHandle festgelegt.

int OnInit() 
 {
 button. button. button.

 ExtHandle=iCustom(NULL,0,"SAInpData");  // External indicator Handle

 return(0);
 }

Der Indikator SAInpData.mq5 muss in dem Verzeichnis \MQL5\Indicators der Anwendungsinstanz auf dem Ausgabegerät abgelegt sein. Der im Anhang zu diesem Beitrag als Beispiel angeführte Indikator SAInpData.mq5 gibt eine willkürliche Datenfolge an den Spektrumanalysator weiter. Um SAInpData.mq5 zu veranlassen, eine andere Datenfolge an den Analysator weiterzugeben, muss sein Ausgangscode geändert werden.

Als Testdatenfolgen werden in der Funktion get_input_data() die Impulscharakteristiken der gleitenden Durchschnittswerte SMA(3), SMA(32) und LWMA(12) erzeugt. Berücksichtigt man, dass die Fourier-Transformation einer Impulscharakteristik eines Filters der AFC (Amplituden-Frequenz-Charakteristik) dieses Filters entspricht, können wir uns bei der Auswahl der Testdatenfolgen an der AFC dieser gleitenden Durchschnittswerte orientieren.

Zur Normalisierung der in dem Datenfeld Spectrum[] gespeicherten Ergebnisse der Spektrumbewertung und zu ihrer Aufbereitung für die Wiedergabe in der angegebenen Art der Darstellung kommt die Funktion norm_and_draw() zum Einsatz, siehe die Datei SpecAnalyzer.mq5. Je nach gewünschter Art der Darstellung ändert diese Funktion auch die Beschriftung der y-Achse.

Die Ergebnisse der Bewertung des Spektrums der Eingangsdatenfolge werden nicht nur bildlich sondern auch in Textform ausgegeben. Zur Präsentation der Ergebnisse in Textform werden fünf grafische Objekte der Art Label angelegt, sie entsprechen den fünf Textzeilen der Anzeige. Die Funktion list_levels() sorgt dafür, dass diese Zeilen mit Daten gefüllt werden. 

void list_levels()
  {
  int m;
  string str;
  
  if(YdBFlag==0) str="%3u    %.5f";                     // If Y-axis mode = Line
  else str="%3u  %6.1f dB";                               // If Y-axis mode = dB
  m=ArraySize(ListArray)-5;
  if(ListPointer<1)ListPointer=1;
  if(ListPointer>m)ListPointer=m;
  GObj.LabelTxt("Label17",StringFormat(str,ListPointer,ListArray[ListPointer]));
  GObj.LabelTxt("Label18",StringFormat(str,ListPointer+1,ListArray[ListPointer+1]));
  GObj.LabelTxt("Label19",StringFormat(str,ListPointer+2,ListArray[ListPointer+2]));
  GObj.LabelTxt("Label20",StringFormat(str,ListPointer+3,ListArray[ListPointer+3]));
  GObj.LabelTxt("Label21",StringFormat(str,ListPointer+4,ListArray[ListPointer+4]));
  }

In diesen Zeilen werden die mithilfe der Funktion StringFormat() formatierten Werte für die (Ebenen-) Grenzen aus dem Datenfeld ListArray[] ausgegeben. Entsprechend der aktuellen Art der Darstellung wird dieses Datenfeld mit den im Hauptteil der Funktion norm_and_draw(), siehe die Datei SpecAnalyzer.mq5, enthaltenen Daten gefüllt. Die Abbildung der Daten aus dem Datenfeld ListArray[] beginnt mit der Kennziffer in dem Datenfeld, die dem in der Variablen ListPointer gespeicherten Wert entspricht. Der Wert der Variablen ListPointer und damit auch die Anfangskennziffer der auszugebenden Zeilen kann durch Anklicken der Schaltflächen auf der rechten Seite des Bedienfeldes oder durch Eingabe der betreffenden Kennziffer in das Eingabefeld geändert werden. Die Verarbeitung der mit der Betätigung dieser Schaltflächen und dem Abschluss der Bearbeitung in dem Eingabefeld verbundenen Ereignisse erfolgt in der Funktion OnChartEvent() des Indikators, siehe die Datei SpecAnalyzer.mq5.

Wie der Indikator SpecAnalyzer aussieht, zeigt folgende Abbildung:

 Abb. 3. So sieht er aus der Indikator SpecAnalyzer.

Abb. 3. So sieht er aus der Indikator SpecAnalyzer.  

Fazit

Wie bereits gesagt handelt es sich bei dem Indikator SpecAnalyzer.mq5 lediglich um den Prototypen eines vollwertigen Spektrumanalysators, und er wird in diesem Beitrag nur als Beispiel für die Verwendung grafischer Objekte verwendet. Um einen echten Indikator zu programmieren, sind neben der Generalüberholung seiner äußeren Gestaltung höchstwahrscheinlich auch die Entwicklung einer funktionaleren Benutzeroberfläche sowie natürlich die Nachbesserung des Algorithmus zur Bewertung des Spektrums erforderlich.

Das Aussehen des Indikators lässt sich erheblich verbessern, indem man bei seiner Erstellung ein grafisches Objekt vom Typ Bitmap verwendet, nachdem man in einem Bildbearbeitungsprogramm eine Abbildung der Frontansicht des Indikatorbedienfeldes angelegt hat und diese Abbildung als Hintergrundebene importiert, auf der die Steuerelemente dargestellt werden. Dieser Ansatz eignet sich auch zur Erstellung von Indikatoren mit austauschbarer Gestaltung.

Literaturhinweise

  1. Yukio Sato. Introduction to Signal Management (Verarbeitung von Signalen. Einführung).
  2. L. Rabiner, B. Gold. Theory and Application of Digital Signal Processing (Theorie und Anwendung der digitalen Signalverarbeitung).
  3. S.L. Marple, Jr. Digital Spectral Analysis with Applications (Digitale Spektrumanalyse und ihre Anwendungsgebiete).

Dateien

  1. SpecAnalyzer.mq5 – der in diesem Beitrag vorgestellte Indikator.
  2. SpecAnalyzer.mqh – die Include-Datei zur Einbindung von SpecAnalyzer.mq5.
  3. SAInpData.mq5 – der zur Organisation des Zugriffs auf externe Daten verwendete Indikator.

 

Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/185

Beigefügte Dateien |
SAInpData.mq5 (3.29 KB)
SpecAnalyzer.mq5 (15.39 KB)
SpecAnalyzer.mqh (20.79 KB)
Seil-Indikator von Erik Nayman Seil-Indikator von Erik Nayman
Der Beitrag erläutert die Erstellung des Seil-Indikators nach dem Buch von Erik L. Nayman The Small Encyclopedia of Trader. Dieser Indikator zeigt die Trendrichtung anhand berechneter Werte der Bären und Bullen für einen angegebenen Zeitraum. Im Artikel sind die Prinzipien der Erstellung und Berechnung des Indikators mit Code-Beispielen dargelegt. Auf Basis des Indikators wird ein Expert Advisor erstellt und alle äußeren Parameter werden optimiert.
Bildung von Kursreihenmittelwerten für Zwischenberechnungen ohne zusätzliche Puffer Bildung von Kursreihenmittelwerten für Zwischenberechnungen ohne zusätzliche Puffer
In diesem Beitrag geht es sowohl um traditionelle als auch um neuartige Mittelwertbildungsalgorithmen verpackt in einfachste Klassen mit jeweils nur einer Datenart. Sie sind zur universellen Verwendung bei nahezu jeder Indikatorentwicklung gedacht. Ich hoffe, dass die vorgeschlagenen Klassen sich als gute Alternative zu den „unhandlichen“ Aufrufen benutzerdefinierter und technischer Indikatoren erweisen werden.
William Blaus Indikatoren und Handelssysteme in MQL5. Teil 1: Indikatoren William Blaus Indikatoren und Handelssysteme in MQL5. Teil 1: Indikatoren
In diesem Artikel werden die von William Blau in seinem Buch „Momentum, Direction, and Divergence“ (Momentum, Richtung und Divergenz) vorgestellten Indikatoren. Blaus Ansatz ermöglicht die schnelle und exakte Annäherung an die Schwankungen der Kurskurve zur Bestimmung der Richtung der Kursentwicklung und der Wendepunkte sowie zur Beseitigung des Kursrauschens. Unterdessen sind wir auch in der Lage, die Marktzustände des Überkaufs/Überverkaufs sowie die auf eine Richtung und die Umkehrung der Kursbewegung hinweisenden Signale zu ermitteln.
Marktbeobachtung mithilfe vorgefertigter Klassen Marktbeobachtung mithilfe vorgefertigter Klassen
Die neue MetaTrader 5-Anwendungsinstanz für Ausgabegeräte (Terminal) und die Programmiersprache MQL5 eröffnen neue Möglichkeiten zur Wiedergabe visueller Informationen für Börsenhändler. In dem folgenden Beitrag stellen wir eine universelle und erweiterbare Sammlung (Bibliothek) von Klassen vor, die Ihnen die Organisation der Wiedergabe beliebiger Informationstexte zu einem Diagramm abnehmen. Dazu präsentieren wir das Beispiel eines Marktbeobachtungsindikators.