English Русский 中文 Español 日本語 Português
Wie schnell ein Bedienfeld zu einem Indikator und Expert Advisor hinzugefügt werden kann

Wie schnell ein Bedienfeld zu einem Indikator und Expert Advisor hinzugefügt werden kann

MetaTrader 5Beispiele | 27 Mai 2016, 09:44
1 640 0
Vladimir Karputov
Vladimir Karputov

Wieso braucht man ein grafisches Feld!?

Ihr MQL4 / MQL5-Programm - ein Indikator oder EA - kann einer der besten sein und vollständig seine Aufgaben erfüllen. Aber Sie können es immer etwas verbessern. In der Regel muss der Benutzer in 99% für Änderungen der Eingangsparameter des Programms die Einstellungen zugreifen. Wollen Sie es umgehen?

Sie können dies tun, indem Sie Ihr eigenes Bedienfeld auf der Basis der Klassen von Standardbibliothek erstellen. Dadurch kann man die Einstellungen ändern, ohne das Programm neu zu starten. Außerdem wird eine solche Vorgehensweise das Programm attraktiver machen und macht einen Unterschied unter anderen. Die Beispiele von grafischen Feldern können Sie im Markt ansehen.

In diesem Artikel werde ich Ihnen zeigen, wie man ein einfaches Feld zu Ihrem MQL4 / MQL5-Programm hinzufügen kann. Sie erfahren, wie man das Programm gestalten kann, dass er Eingangsparameter lesen wird und auf Änderungen in ihren Werten reagieren wird.

 

1. Wir verbinden einen Indikator mit einem Bedienfeld


1.1. Indikator

Der Indikator "NewBar.mq5" führt eine Aktion durch: Wenn ein neuer Bar im Terminal entsteht, schreibt eine Nachricht im Terminal. Der Code des Indikators wird unten dargestellt:

//+------------------------------------------------------------------+
//|                                                       NewBar.mq5 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property description "The indicator identifies a new bar"
#property indicator_chart_window
#property indicator_plots 0
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| 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[])
  {
   static datetime prev_time;
//--- revert access to array time[] - do it like in timeseries 
   ArraySetAsSeries(time,true);
//--- first calculation or number of bars was changed
   if(prev_calculated==0)// first calculation
     {
      prev_time=time[0];
      return(rates_total);
     }
//---
   if(time[0]>prev_time)
      Print("New bar!");
//---
   prev_time=time[0];
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

Ich erzähle Ihnen etwas, wie der Indikator "NewBar.mq5" funktioniert.

In der Funktion OnCalculate() wird eine statische Variable "prev_time" erklärt — in dieser Variable wird die Öffnungszeit "time[0]" gespeichert. Das nächste Mal werden die Öffnungszeit"time[0]" mit der Variable "prev_time" verglichen — das heißt, es wird die Öffnungszeit "time[0]" am aktuellen Tick mit der Öffnungszeit "time[0]" vom vorherigen Tick verglichen. Wenn die Bedingung erfüllt wird

if(time[0]>prev_time)

Nehmen wir an, dass ein neuer Bar gefunden wurde.

Im folgenden Beispiel analysieren wir im Detail, wie der Indikator "NewBar.mq5" einen neuen Bar findet:

New bar

Abb. 1. Der Prozess, in dem ein neuer Bar im Indikator gefunden wird

Betrachten wir 10 Ticks auf einem sehr ruhigen Markt.

Die Ticks von 1 bis einschließlich 3: die Öffnungszeit des Bars mit dem Index "0" (time[0]) ist der Zeit gleich, die in der statischen Variable prev_time gespeichert ist, und das heißt, es gibt keinen neuen Bar.

Das Tick №4: dieses Tick ist am neuen Bar entstanden. Beim Eintritt der Funktion OnCalculate() in time[0] wird die Öffnungszeit des Bars (2015.12.01 00:02:00), und die Variable prev_time speichert noch die Zeit vom vorherigen Tick (2015.12.01 00:01:00). Deshalb finden wir bei der Überprüfung der Bedingung time[0]>prev_time einen neuen Bar. Bevor wir OnCalculate() verlassen, wird in der Variable prev_time die Zeit aus time[0] (2015.12.01 00:02:00) geschrieben.

Die Ticks von 5 bis einschließlich 8: die Öffnungszeit des Bars mit dem Index "0" (time[0]) ist der Zeit gleich, die in der statischen Variable prev_time gespeichert ist, und das heißt, es gibt keinen neuen Bar.

Das Tick №9: dieses Tick ist am neuen Bar entstanden. Beim Eintritt der Funktion OnCalculate() in time[0] wird die Öffnungszeit des Bars (2015.12.01 00:02:00), und die Variable prev_time speichert noch die Zeit vom vorherigen Tick (2015.12.01 00:02:00). Deshalb finden wir bei der Überprüfung der Bedingung time[0]>prev_time einen neuen Bar. Bevor wir OnCalculate() verlassen, wird in der Variable prev_time die Zeit aus time[0] (2015.12.01 00:03:00) geschrieben.

Das Tick 10: die Öffnungszeit des Bars mit dem Index "0" (time[0]) ist der Zeit gleich, die in der statischen Variable prev_time gespeichert ist, und das heißt, es gibt keinen neuen Bar.


1.2. Bedienfeld

Alle Parameter der Erstellung des Panels: die Anzahl, Größe und Koordinaten der Kontrolle-Elemente werden in einer Include-Datei "PanelDialog.mqh" konzentriert. Die Datei "PanelDialog.mqh" ist die Klasse der Realisierung dieses Feldes.

Das Feld hat die folgende Form:

Panel

in Abb. 2. Bedienfeld

Der Code der hinzufügbaren Datei "PanelDialog.mqh" wird unten dargestellt:

//+------------------------------------------------------------------+
//|                                                  PanelDialog.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include <Controls\Dialog.mqh>
#include <Controls\CheckGroup.mqh>
//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+
//--- indents and gaps
#define INDENT_LEFT                         (11)      // indent from left (with allowance for border width)
#define INDENT_TOP                          (11)      // indent from top (with allowance for border width)
#define INDENT_BOTTOM                       (11)      // indent from bottom (with allowance for border width)
//--- for buttons
#define BUTTON_WIDTH                        (100)     // size by X coordinate
//+------------------------------------------------------------------+
//| Class CControlsDialog                                            |
//| Usage: main dialog of the Controls application                   |
//+------------------------------------------------------------------+
class CControlsDialog : public CAppDialog
  {
private:
   CCheckGroup       m_check_group;                   // CCheckGroup object

public:
                     CControlsDialog(void);
                    ~CControlsDialog(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);

protected:
   //--- create dependent controls
   bool              CreateCheckGroup(void);
   //--- handlers of the dependent controls events
   void              OnChangeCheckGroup(void);
  };
//+------------------------------------------------------------------+
//| Event Handling                                                   |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CControlsDialog)
ON_EVENT(ON_CHANGE,m_check_group,OnChangeCheckGroup)
EVENT_MAP_END(CAppDialog)
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CControlsDialog::CControlsDialog(void)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CControlsDialog::~CControlsDialog(void)
  {
  }
//+------------------------------------------------------------------+
//| Create                                                           |
//+------------------------------------------------------------------+
bool CControlsDialog::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
   if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- create dependent controls
   if(!CreateCheckGroup())
      return(false);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Create the "CheckGroup" element                                  |
//+------------------------------------------------------------------+
bool CControlsDialog::CreateCheckGroup(void)
  {
//--- coordinates
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP;
   int x2=x1+BUTTON_WIDTH;
   int y2=ClientAreaHeight()-INDENT_BOTTOM;
//--- create
   if(!m_check_group.Create(m_chart_id,m_name+"CheckGroup",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!Add(m_check_group))
      return(false);
   m_check_group.Alignment(WND_ALIGN_HEIGHT,0,y1,0,INDENT_BOTTOM);
//--- fill out with strings
   if(!m_check_group.AddItem("Mail",1<<0))
      return(false);
   if(!m_check_group.AddItem("Push",1<<1))
      return(false);
   if(!m_check_group.AddItem("Alert",1<<2))
      return(false);
   Comment(__FUNCTION__+" : Value="+IntegerToString(m_check_group.Value()));
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CControlsDialog::OnChangeCheckGroup(void)
  {
   Comment(__FUNCTION__+" : Value="+IntegerToString(m_check_group.Value()));
  }
//+------------------------------------------------------------------+

Wie Sie in der Klasse unseres Feldes sehen können, es gibt da keine Installationsmethoden und Methoden für das Lesen der Schalter-Eigenschaften mit unabhängiger Fixierung.

Unser Ziel: den Indikator "NewBar.mq5" zur Hauptdatei zu machen und die Eingabeparameter hinzuzufügen — zum Beispiel, mit welcher der Methoden ("Mail", "Push" oder "Alert") den Benutzer zu informieren, dass ein neuer Bar gefunden wurde. Darüber hinaus müssen wir zur hinzufügbaren Datei "PanelDialog.mqh" die Installationsmethoden und Methoden für das Lesen der Schalter-Eigenschaften mit unabhängiger Fixierung "Mail", "Push" и "Alert" hinzufügen.


1.3. Ändern wir den Indikator

Hinweis: Alle Änderungen werden farblich markiert.

Zuerst muss man die hinzufügbare Datei "PanelDialog.mqh"hinzufügen:

#property indicator_chart_window
#property indicator_plots 0
#include "PanelDialog.mqh"
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()

Dann fügen wir die Eingabeparameter hinzu:

#property indicator_chart_window
#property indicator_plots 0
#include "PanelDialog.mqh"
//--- input parameters
input bool     bln_mail=false;      // Notify by email
input bool     bln_push=false;      // Notify by push
input bool     bln_alert=true;      // Notify by alert
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()

Kompilieren wir den Indikator(F7 in MetaEditor) und überprüfen wir die Darstellung der Eingangsparameter im Terminal:

Input parameters

Abb. 3. Eingabeparameter des Indikators


1.4. Ändern wir das Bedienfeld

Zum Bedienfeld muss man die Installationsmethoden und Methoden für das Lesen der Schalter-Eigenschaften mit unabhängiger Fixierung "Mail", "Push" и "Alert" hinzufügen.

Fügen Wir in der Klasse des Feldes neue Methoden:

class CControlsDialog : public CAppDialog
  {
private:
   CCheckGroup       m_check_group;                   // CCheckGroup object

public:
                     CControlsDialog(void);
                    ~CControlsDialog(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- set check for element
   virtual bool      SetCheck(const int idx,const int value);
   //--- get check for element
   virtual int       GetCheck(const int idx) const;

protected:
   //--- create dependent controls
   bool              CreateCheckGroup(void);

Die Realisierung dieser Methoden:

//+------------------------------------------------------------------+
//| Set check for element                                            |
//+------------------------------------------------------------------+
bool CControlsDialog::SetCheck(const int idx,const bool check)
  {
   return(m_check_group.Check(idx,check));
  }
//+------------------------------------------------------------------+
//| Get check for element                                            |
//+------------------------------------------------------------------+
int CControlsDialog::GetCheck(const int idx)
  {
   return(m_check_group.Check(idx));
  }


1.5. Die letzte Stufe der Verbindung des Indikators mit dem Bedienfeld

Im Indikator "NewBar.mq5", deklarieren wir eine globale Variable der Klasse unseres Feldes:

#property indicator_chart_window
#property indicator_plots 0
#include "PanelDialog.mqh"
//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
CControlsDialog ExtDialog;
//--- input parameters
input bool     bln_mail=false;      // Notify by email
input bool     bln_push=false;      // Notify by push
input bool     bln_alert=true;      // Notify by alert

und fügen wir am Ende des Indikators der Funktion OnChartEvent ():

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   ExtDialog.ChartEvent(id,lparam,dparam,sparam);
  }

In der Funktion OnInit () des Indikators "NewBar.mq5" erstellen wir das Feld und klicken wir programmatisch auf die Checkbox nach den Eingangsparametern:

int OnInit()
  {
//--- indicator buffers mapping
//--- create application dialog
   if(!ExtDialog.Create(0,"Notification",0,50,50,180,160))
      return(INIT_FAILED);
//--- run application
   if(!ExtDialog.Run())
      return(INIT_FAILED);
//---
   ExtDialog.SetCheck(0,bln_mail);
   ExtDialog.SetCheck(1,bln_push);
   ExtDialog.SetCheck(2,bln_alert);
//---
   return(INIT_SUCCEEDED);
  }

Damit ist die Verbindung eines Indikators mit einem Bedienfeld abgeschlossen. In der Klasse des Bedienfelds realisierten wir die Installationsmethode des Status für Checkbox (SetCheck) Drücken / Loslassen und die Methode, den Status für Checkbox zu erhalten (GetCheck).

 

2. Wir verbinden einen EA mit einem Bedienfeld


2.1. EA

Zur Grundlage wird der Expert Advisor aus Standard-Distribution genommen ...\MQL5\Experts\Examples\MACD\MACD Sample.mq5.


2.2. Bedienfeld

Die Ansicht des Bedienfelds "PanelDialog2.mqh" nach den letzten Änderungen:

Panel number two

Abb. 4. Das Bedienfeld №2

Was bekommen wir nach der Verbindung des EAs "MACD Sample.mq5" mit dem Bedienfeld "PanelDialog2.mqh"? Auf dem aktuellen Timeframe, auf dem der EA arbeiten wird, können Sie schnell die EA-Parameter ändern ("Lots", "Trailing Stop Level (in pips)" und andere), sowie Benachrichtigungsparameter über die Handelsereignisse des EAs ("Mail", "Push", "Alert").

Die geänderten Parameter des EAs("Lots", "Trailing Stop Level (in pips)" und andere) werden nach dem Klicken auf "Änderungen übernehmen" verwendet. Ändern Sie die Benachrichtigungsparameter über Handelsereignisse des EAs ("Mail", "Push", "Alert") werden automatisch ohne Klicken auf "Änderungen übernehmen" verwendet.


2.3. Der EA und das Bedienfeld sollten nicht aufgerufen werden

Communication between the EA and the panel

Abb. 5. Die Kommunikation des EAs mit dem Bedienfeld

Beim Ausführen sollte der EA ihre Parameter ins Bedienfelds übergeben. Das Bedienfeld, nachdem dem Klicken "Änderungen übernehmen" und wenn die Daten geändert wurden, muss auch die geänderten Einstellungen in EA zurücksenden, damit der sich mit den neuen Einstellungen initialisiert.


2.4. Schritt 1. Fügen wir die Änderungen in EA hinzu

Aktionsplan: Nehmen Sie den Expert Advisor aus Standard-Distribution ...\MQL5\Experts\Examples\MACD\MACD Sample.mq5 und kopieren Sie ihn in Ihrem Ordner. Zum Beispiel können Sie einen Ordner "Notification" erstellen und darin den EA kopieren:

Create a new folder

Abb. 6. Die Erstellung des neuen Ordners


Im Bereich der globalen Variablen des EAs (nicht verwechseln mit der globalen Variablen des Terminals) erklären wir neue Variablen, die für die Art der Benachrichtigungssendung über die Handelsaktionen zuständig sein wird. Beachten Sie, dass diese Variablen sowie andere externe Variablen den "Inp" haben:

#include <Trade\PositionInfo.mqh>
#include <Trade\AccountInfo.mqh>
//--- input parameters
input bool     InpMail=false;          // Notify by email
input bool     InpPush=false;          // Notify by push
input bool     InpAlert=true;          // Notify by alert
//---
input double InpLots          =0.1; // Lots
input int    InpTakeProfit    =50;  // Take Profit (in pips)

Etwas unten fügen wir Duplikate aller externen Variable des EAs hinzu. Duplikate haben den "Ext":

input int    InpMACDCloseLevel=2;   // MACD close level (in pips)
input int    InpMATrendPeriod =26;  // MA trend period
//--- ext variables
bool           ExtMail;
bool           ExtPush;
bool           ExtAlert;

double         ExtLots;
int            ExtTakeProfit;
int            ExtTrailingStop;
int            ExtMACDOpenLevel;
int            ExtMACDCloseLevel;
int            ExtMATrendPeriod;
//---
int ExtTimeOut=10; // time out in seconds between trade operations
//+------------------------------------------------------------------+
//| MACD Sample expert class                                         |
//+------------------------------------------------------------------+

In OnInit() schreiben wir das Kopieren der Werte von externen Variablen des EAs in Werten von Variablen-Duplikaten:

//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(void)
  {
   ExtMail=InpMail;
   ExtPush=InpPush;
   ExtAlert=InpAlert;

   ExtLots=InpLots;
   ExtTakeProfit=InpTakeProfit;
   ExtTrailingStop=InpTrailingStop;
   ExtMACDOpenLevel=InpMACDOpenLevel;
   ExtMACDCloseLevel=InpMACDCloseLevel;
   ExtMATrendPeriod=InpMATrendPeriod;
//--- create all necessary objects
   if(!ExtExpert.Init())

Zu diesem Zeitpunkt in den Funktionen EAs CSampleExpert::InitIndicators, CSampleExpert:InitCheckParameters und CSampleExpert::Init werden externe Variablen des EAs mit dem Präfix "Inp" verwendet. Wir müssen ihre externe Variablen mit diesen Duplikaten ersetzen (Duplikate haben wir mit dem Präfix "Ext"). Ich schlage vor, das mit einer originellen Art zu machen:


Nach dem Austausch ist es notwendig, die Richtigkeit der Maßnahmen zu überprüfen - Dateikompilierung ausführen. Es sollten keine Fehler auftreten.


2.5. Schritt 2. Fügen wir die Änderungen ins Bedienfeld hinzu

Das Bedienfeld, das in Abb.4 dargestellt ist — ist ein Vorwurf. Sie hat noch keine Funktion für den "Dialog" mit dem EA oder der Verarbeitungsfunktion der Eingangsdaten. Die Datei für einen Vorwurf des Bedienfeldes "PanelDialog2Original.mqh" kopieren Sie auch in"Notification".

Fügen Sie in der Klasse des Bedienfeldes die internen Variablen hinzu, in den dann der Status aller Einträge gespeichert wird. Geben Sie Acht auf die Variable "mModification" — darüber wird im Abschnitt 2.7 diskutiert.

private:
   //--- get check for element
   virtual int       GetCheck(const int idx);
   //---
   bool              mMail;
   bool              mPush;
   bool              mAlert_;
   double            mLots;               // Lots
   int               mTakeProfit;         // Take Profit (in pips)
   int               mTrailingStop;       // Trailing Stop Level (in pips)
   int               mMACDOpenLevel;      // MACD open level (in pips)
   int               mMACDCloseLevel;     // MACD close level (in pips)
   int               mMATrendPeriod;      // MA trend period
   //---
   bool              mModification;       // Values have changed
  };
//+------------------------------------------------------------------+
//| Event Handling                                                   |

Direkt danach initialisieren wir die internen Variablen im Konstruktor der Bedienfeld-Klasse:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CControlsDialog::CControlsDialog(void) : mMail(false),
                                         mPush(false),
                                         mAlert_(true),
                                         mLots(0.1),
                                         mTakeProfit(50),
                                         mTrailingStop(30),
                                         mMACDOpenLevel(3),
                                         mMACDCloseLevel(2),
                                         mMATrendPeriod(26),
                                         mModification(false)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |

In der Funktion CControlsDialog::Create fügen wir die Installation von Elementen der Schaltergruppe in Übereinstimmung mit den internen Variablen hinzu:

if(!CreateButtonOK())
      return(false);

//---
   SetCheck(0,mMail);
   SetCheck(1,mPush);
   SetCheck(2,mAlert_);

//--- succeed
   return(true);
  }

 

2.6. Schritt 3. Fügen wir die Änderungen in EA hinzu

Bis jetzt der EA und das Bedienfeld waren getrennt, mit unabhängigen voneinander Dateien. Wir verbinden sie und erklären eine Klassenvariable unseres Feldes "ExtDialog":

#include <Trade\PositionInfo.mqh>
#include <Trade\AccountInfo.mqh>
#include "PanelDialog2Original.mqh"
//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
CControlsDialog ExtDialog;
//--- input parameters
input bool     InpMail=false;          // Notify by email
input bool     InpPush=false;          // Notify by push

Damit das Bedienfeld funktioniert und das sichtbar wäre, muss das erstellt und zur Ausführung gegeben werden. Sie müssen auch die Funktionen OnChartEvent () und OnDeinit() hinzufügen, die das Ereignis ChartEvent verarbeitet. OnInit() im EA bekommt diese Ansicht:

int OnInit(void)
  {
   ExtMail=InpMail;
   ExtPush=InpPush;
   ExtAlert=InpAlert;

   ExtLots=InpLots;
   ExtTakeProfit=InpTakeProfit;
   ExtTrailingStop=InpTrailingStop;
   ExtMACDOpenLevel=InpMACDOpenLevel;
   ExtMACDCloseLevel=InpMACDCloseLevel;
   ExtMATrendPeriod=InpMATrendPeriod;
//--- create all necessary objects
   if(!ExtExpert.Init())
      return(INIT_FAILED);
//--- create application dialog
   if(!ExtDialog.Create(0,"Notification",0,100,100,360,380))
      return(INIT_FAILED);
//--- run application
   if(!ExtDialog.Run())
      return(INIT_FAILED);
//--- succeed
   return(INIT_SUCCEEDED);
  }

In OnDeinit() löschen wir unser Bedienfeld, und die Funktion OnDeinit() schreiben wir direkt nach OnInit():

//--- succeed
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- 
   Comment("");
//--- destroy dialog
   ExtDialog.Destroy(reason);
  }
//+------------------------------------------------------------------+
//| Expert new tick handling function                                |
//+------------------------------------------------------------------+
void OnTick(void)

Die Funktion OnChartEvent() hinzufügen wir an Ende des EAs (nach der Funktion OnTick):

//--- change limit time by timeout in seconds if processed
         if(ExtExpert.Processing())
            limit_time=TimeCurrent()+ExtTimeOut;
        }
     }
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   ExtDialog.ChartEvent(id,lparam,dparam,sparam);
  }
//+------------------------------------------------------------------+

Jetzt kann Expert Advisor kompiliert werden und auf dem Chart überprüft. Der EA wird zusammen mit dem Bedienfeld gestartet werden:

EA and panel

Abb. 7. Der EA zusammen mit dem Bedienfeld


2.7. Schritt 4. Fügen wir die Änderungen ins Bedienfeld hinzu. Die große Integration

Zunächst wird der Expert Advisor gestartet, dann kann der Benutzer seine Eingangsparameter einstellen, und erst danach beginnt das Bedienfeld zu funktionieren. Aus diesem Grund muss im Bedienfeld die Funktion für den Datenaustausch zwischen ihr und EA vorhergesehen werden.

Fügen wir die Methode Initialization() hinzu — sie über nimmt die Parameter und initialisiert mit diesen Parametern die interne Variable des Bedienfelds. Ankündigung:

virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
      //--- initialization
   virtual bool      Initialization(const bool Mail,const bool Push,const bool Alert_,
                                    const double Lots,const int TakeProfit,
                                    const int  TrailingStop,const int MACDOpenLevel,
                                    const int  MACDCloseLevel,const int MATrendPeriod);

protected:
   //--- create dependent controls
   bool              CreateCheckGroup(void);

Der Körper dieser Methode (fügen wir ihn in CControlsDialog::GetCheck ein):

//+------------------------------------------------------------------+
//| Initialization                                                   |
//+------------------------------------------------------------------+
bool CControlsDialog::Initialization(const bool Mail,const bool Push,const bool Alert_,
                                     const double Lots,const int TakeProfit,
                                     const int  TrailingStop,const int MACDOpenLevel,
                                     const int  MACDCloseLevel,const int MATrendPeriod)
  {
   mMail=Mail;
   mPush=Push;
   mAlert_=Alert_;

   mLots=Lots;
   mTakeProfit=TakeProfit;
   mTrailingStop=TrailingStop;
   mMACDOpenLevel=MACDOpenLevel;
   mMACDCloseLevel=MACDCloseLevel;
   mMATrendPeriod=MATrendPeriod;
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| Get check for element                                            |
//+------------------------------------------------------------------+
int CControlsDialog::GetCheck(const int idx)

Da die internen Variablen des Bedienfelds von Daten initialisiert wurden, müssen die Bedienelemente jetzt richtig ausgefüllt werden - das Eingabefeld. Da die Eingabefelder sechs sind, werde ich ein Beispiel auf der Basis von m_edit1 geben. Eine Zeile, in der der Text zugewiesen wird, sah folgendermaßen aus:

...
   if(!m_edit1.Text("Edit1"))
...

und jetzt sieht es so aus:

...
   if(!m_edit1.Text(DoubleToString(mLots,2)))
...

Somit jedem Eingabefeld entspricht seine eigene interne Variable.

Die folgende Methode GetValues() ist für die Rückgabe der Werte von internen Variablen verantwortlich:

virtual bool      Initialization(const bool Mail,const bool Push,const bool Alert_,
                                    const double Lots,const int TakeProfit,
                                    const int  TrailingStop,const int MACDOpenLevel,
                                    const int  MACDCloseLevel,const int MATrendPeriod);
   //--- get values
   virtual void      GetValues(bool &Mail,bool &Push,bool &Alert_,
                               double &Lots,int &TakeProfit,
                               int &TrailingStop,int &MACDOpenLevel,
                               int &MACDCloseLevel,int &MATrendPeriod);

protected:
   //--- create dependent controls
   bool              CreateCheckGroup(void);

Sein Körper fügen wir nach CControlsDialog::Initialization()) ein:

//+------------------------------------------------------------------+
//| Get values                                                       |
//+------------------------------------------------------------------+
void CControlsDialog::GetValues(bool &Mail,bool &Push,bool &Alert_,
                                double &Lots,int &TakeProfit,
                                int &TrailingStop,int &MACDOpenLevel,
                                int &MACDCloseLevel,int &MATrendPeriod)
  {
   Mail=mMail;
   Push=mPush;
   Alert_=mAlert_;

   Lots=mLots;
   TakeProfit=mTakeProfit;
   TrailingStop=mTrailingStop;
   MACDOpenLevel=mMACDOpenLevel;
   MACDCloseLevel=mMACDCloseLevel;
   MATrendPeriod=mMATrendPeriod;
  }
//+------------------------------------------------------------------+
//| Get check for element                                            |
//+------------------------------------------------------------------+
int CControlsDialog::GetCheck(const int idx)

Da das Bedienfeld für die Sendung von Nachrichten als Reaktion auf jede Handelsaktivität des EAs verantwortlich ist, heißt es, dass er drin eine spezielle Methode braucht, die dafür verantwortlich wäre. Erklären wie sie:

virtual void      GetValues(bool &Mail,bool &Push,bool &Alert_,
                               double &Lots,int &TakeProfit,
                               int &TrailingStop,int &MACDOpenLevel,
                               int &MACDCloseLevel,int &MATrendPeriod);   //--- send notifications
   virtual void      Notifications(const string text);

protected:
   //--- create dependent controls
   bool              CreateCheckGroup(void);

Sein Körper fügen wir nach CControlsDialog::GetValues()) ein:

//+------------------------------------------------------------------+
//|  Send notifications                                              |
//+------------------------------------------------------------------+
void CControlsDialog::Notifications(const string text)
  {
   int i=m_check_group.ControlsTotal();
   if(GetCheck(0))
      SendMail(" ",text);
   if(GetCheck(1))
      SendNotification(text);
   if(GetCheck(2))
      Alert(text);
  }
//+------------------------------------------------------------------+
//| Get check for element                                            |
//+------------------------------------------------------------------+
int CControlsDialog::GetCheck(const int idx)

Als Erinnerung, ob die Änderungen der Parameter im Bedienfeld gemacht wurden, wurde die interne Variable - die Flagge "mModification" eingefügt (Sie wurde bereits früher in 2.5. erwähnt).

virtual void      Notifications(const string text);
   //---
   virtual bool      Modification(void) const { return(mModification);          }
   virtual void      Modification(bool value) { mModification=value;            }

protected:
   //--- create dependent controls
   bool              CreateCheckGroup(void);

Die Kontrolle über Änderungen machen wir in "CControlsDialog::OnClickButtonOK" — die die Ereignisse verarbeitet, wenn "Änderungen übernehmen" gedruckt wird:

//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CControlsDialog::OnClickButtonOK(void)
  {
//--- verifying changes
   if(m_check_group.Check(0)!=mMail)
      mModification=true;
   if(m_check_group.Check(1)!=mPush)
      mModification=true;
   if(m_check_group.Check(2)!=mAlert_)
      mModification=true;

   if(StringToDouble(m_edit1.Text())!=mLots)
     {
      mLots=StringToDouble(m_edit1.Text());
      mModification=true;
     }
   if(StringToInteger(m_edit2.Text())!=mTakeProfit)
     {
      mTakeProfit=(int)StringToDouble(m_edit2.Text());
      mModification=true;
     }
   if(StringToInteger(m_edit3.Text())!=mTrailingStop)
     {
      mTrailingStop=(int)StringToDouble(m_edit3.Text());
      mModification=true;
     }
   if(StringToInteger(m_edit4.Text())!=mMACDOpenLevel)
     {
      mMACDOpenLevel=(int)StringToDouble(m_edit4.Text());
      mModification=true;
     }
   if(StringToInteger(m_edit5.Text())!=mMACDCloseLevel)
     {
      mMACDCloseLevel=(int)StringToDouble(m_edit5.Text());
      mModification=true;
     }
   if(StringToInteger(m_edit6.Text())!=mMATrendPeriod)
     {
      mMATrendPeriod=(int)StringToDouble(m_edit6.Text());
      mModification=true;
     }
  }

Außerdem prüft das Bedienfeld die Eingangsdaten in Handler:

void              OnChangeCheckGroup(void);
   void              OnChangeEdit1(void);
   void              OnChangeEdit2(void);
   void              OnChangeEdit3(void);
   void              OnChangeEdit4(void);
   void              OnChangeEdit5(void);
   void              OnChangeEdit6(void);
   void              OnClickButtonOK(void);

Ich werde ihre Beschreibung überspringen.

2.8. Schritt 5. Fügen wir die Änderungen in EA hinzu. Die letzte Korrektur

Im Moment arbeitet das Feld nicht im Strategie-Tester, deshalb müssen wir den Schutz implementieren und die interne Variable einführen - eben Flagge "bool_tester".

//---
int ExtTimeOut=10; // time out in seconds between trade operations
bool           bool_tester=false;      // true - mode tester
//+------------------------------------------------------------------+
//| MACD Sample expert class                                         |
//+------------------------------------------------------------------+
class CSampleExpert

Fügen wir die Änderungen in OnInit() ein - das ist der Schutz vor einem Start im Strategie-Tester, und bevor wir das Bedienfeld initialisieren, initialisieren wir seine Parameter:

//--- create all necessary objects
   if(!ExtExpert.Init())
      return(INIT_FAILED);
//--- 
   if(!MQLInfoInteger(MQL_TESTER))
     {
      bool_tester=false;
      //---
      ExtDialog.Initialization(ExtMail,ExtPush,ExtAlert,
                               ExtLots,ExtTakeProfit,ExtTrailingStop,
                               ExtMACDOpenLevel,ExtMACDCloseLevel,ExtMATrendPeriod);
      //--- create application dialog
      if(!ExtDialog.Create(0,"Notification",0,100,100,360,380))
         return(INIT_FAILED);
      //--- run application
      if(!ExtDialog.Run())
         return(INIT_FAILED);
     }
   else
      bool_tester=true;
//--- secceed
   return(INIT_SUCCEEDED);
  }

In OnChartEvent () überprüfen wir, ob die Einstellungen im Bedienfeld eingetragen wurden. Wenn sie sich ändern, dann müssen Sie die Initialisierung des EAs mit den neuen Einstellungen starten:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   ExtDialog.ChartEvent(id,lparam,dparam,sparam);
// Weiter überprüfen wir die Variable bool im Bedienfeld: ob die Parameter sich geändert haben
// Wenn die Parameter sich ändern - dann überprüfen wir die Parameter des Bedienfelds und rufen
// CSampleExpert::Init(void) aus
   if(ExtDialog.Modification())
     {
      ExtDialog.GetValues(ExtMail,ExtPush,ExtAlert,
                          ExtLots,ExtTakeProfit,ExtTrailingStop,
                          ExtMACDOpenLevel,ExtMACDCloseLevel,ExtMATrendPeriod);
      if(ExtExpert.Init())
        {
         ExtDialog.Modification(false);
         Print("Параметры изменены, ",ExtLots,", ",ExtTakeProfit,", ",ExtTrailingStop,", ",
               ExtMACDOpenLevel,", ",ExtMACDCloseLevel,", ",ExtMATrendPeriod);
        }
      else
        {
         ExtDialog.Modification(false);
         Print("Fehler bei Änderung der Parameter");
        }
     }
  }
//+------------------------------------------------------------------+

 

Fazit

Die Verbindung des Bedienfelds mit dem Indikator war nicht schwer. Dazu muss man in der Klasse des Bedienfelds alle Funktionale (Abmessungen und die Anordnung der Bedienelemente Reaktion auf das Ereignis) realisieren und eine Variable unserer Klasse im Indikator erklären, und die Funktion OnChartEvent() hinzufügen.

Die Verbindung des EAs mit einem komplexeren Bedienfeld war eine ernster Aufgabe, vor allem wegen der Notwendigkeit, "Dialog" zwischen dem EA und dem Bedienfelds zu haben. Die Komplexität des Problems hängt zu einem großen Teil davon ab, wie das Bedienfeld für den Anschluss bereit ist. Mit anderen Worten, je mehr die Funktionen im Bedienfeld realisiert sind und die Integration mit anderen Software-Funktionen, desto leichter wird das mit einem anderen Programm (Indikator oder Berater) zu verbinden.

Im Anhang finden Sie:

  • NewBarOriginal.mq5 — die anfangs Datei des Indikators.
  • PanelDialogOriginal.mqh — die anfangs Datei des Bedienfelds.
  • NewBar.mq5 — die geänderte Datei des Indikators.
  • PanelDialog.mqh — die geänderte Datei des Bedienfelds.
  • PanelDialog2Original.mqh — die anfangs Datei des zweiten Bedienfelds.
  • PanelDialog2.mqh — die geänderte Datei des zweiten Bedienfelds.
  • MACD Sample.mq5 — die geänderte Datei des EAs.

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

Beigefügte Dateien |
newbar.mq5 (3.24 KB)
paneldialog.mqh (6.39 KB)
newbaroriginal.mq5 (2.05 KB)
macd_sample.mq5 (24.12 KB)
paneldialog2.mqh (28.48 KB)
Graphical Interfaces I: Formular mit Steuerelementen (Kapitel 2) Graphical Interfaces I: Formular mit Steuerelementen (Kapitel 2)
In diesem Artikel erstellen wir das erste und wichtigste Element für das graphische Interface - ein Formular (Form) mit Steuerelementen (Controls) Es können eine Vielzahl von Controls zu jeder Zeit und in jeder Kombination zu dieser Form hinzugefügt werden.
Die Fehlerverarbeitung und Protokollierung in MQL5 Die Fehlerverarbeitung und Protokollierung in MQL5
In diesem Artikel werden die meisten Fragen bezüglich der Fehlerverarbeitung im Programm betrachtet. Außerdem betrachten wir Protokollierung und werden ein Beispiel der Log-Realisierung mit MQL5 darstellen.
Graphical Interfaces I: Animation des graphischen Interfaces (Kapitel 3) Graphical Interfaces I: Animation des graphischen Interfaces (Kapitel 3)
In dem vorherigen Artikel haben wir eine Formular-Klasse (Form class) mit Steuerelementen (Controls) entwickelt. In diesem Artikel werden wir die Klasse mit Methoden auffüllen, welche es möglich machen, das Formular über den Chartbereich zu bewegen. Wir werden anschließend diese Komponente in den Kern der Bibliothek mit aufnehmen. Zudem werden wir sicherstellen, dass sich die Farbe der Form verändert, sobald sich die Maus darüber befindet.
Grafische Interfaces I: Vorbereitung der Bibliotheksstruktur (Kapitel 1) Grafische Interfaces I: Vorbereitung der Bibliotheksstruktur (Kapitel 1)
Dieser Artikel ist der Anfang einer weiteren Serie über die Entwicklung von grafischen Interfaces. Zur Zeit gibt es keine einzige Bibliothek, die es einem ermöglichen würde, einfach und schnell qualitative graphische Interfaces innerhalb einer MQL- Anwendung zu erzeugen. Damit meine ich graphische Interfaces, wie wir sie auch von anderen Betriebssystemen her kennen.