Ein System von Sprachbenachrichtigungen für Ereignisse und Signale im Handel

Alexander Fedosov | 16 November, 2020

Inhaltsverzeichnis

Einführung

Das Handelsterminal MetaTrader 5 verfügt über Optionen, die die Verwendung von akustischen Hinweisen ermöglichen. Das System umfasst 11 Ereignisse, denen separate akustische Hinweise zugeordnet werden können. Es gibt jedoch noch viel mehr Situationen, in denen der Nutzer einen akustischen Hinweis erhalten muss, wie z.B. das Auftauchen eines Handelssystemsignals oder Aktionen des Experten-Beraters, einschließlich der Eröffnung, Schließung oder Änderung einer Position. Heutzutage spielen Sprachassistenten eine herausragende Rolle im menschlichen Leben, da wir häufig Navigatoren, Sprachsuche und Übersetzer einsetzen. Diese Idee könnte beim Handel im MetaTrader 5 Terminal eingesetzt werden. In diesem Artikel werde ich versuchen, ein einfaches und nutzerfreundliches System von Sprachbenachrichtigungen für verschiedene Handelsereignisse, Marktzustände oder durch Handelssignale erzeugte Signale zu entwickeln.


Entwicklung eines Sprachbenachrichtigungssystems

Bevor wir mit der Schaffung des Systems beginnen, möchte ich eine Anmerkung hinzufügen. Die Ereignisse, die ich für die Implementierung von Sprachbenachrichtigungen ausgewählt habe, dienen lediglich der Demonstration des Systems. Sollte dieser Satz nicht ausreichen, können Sie Ihre eigenen Ereignisse und die entsprechenden Sprachbenachrichtigungen hinzufügen. Nachdem Sie den Artikel gelesen haben, wird die Erweiterung und Anpassung des Systems sehr einfach sein, auch wenn Sie keine umfangreichen MQL5-Kenntnisse haben.

Dieses System ist als Klasse CSoundsLib in einer einzubindenden Datei implementiert. Öffnen Sie also den Ordner MQL5/Include und erstellen Sie einen Ordner mit dem Titel SoundsLib, in dem Sie die Datei SoundsLib.mqh anlegen sollten. Bevor wir die Klasse erstellen, möchten wir Ihnen zwei Enumerationen vorstellen, die weiterhin für die Arbeit mit Sprachhinweise verwendet werden sollen. Die erste ist LANGUAGE, die für die Sprachauswahl der Hinweise verwendet wird. Das System wird zwei Sprachen unterstützen: Englisch und Russisch.

//+------------------------------------------------------------------+
//| Enumeration for switching the notification language              |
//+------------------------------------------------------------------+
enum LANGUAGE
{
   RUSSIAN,       // Russian
   ENGLISH        // English
};

Die zweite Enumeration enthält die Reihe von Ereignissen, die ich zu Demonstrationszwecken ausgewählt habe. Im weiteren Verlauf des Artikels werde ich zeigen, wie sie in verschiedene vorgefertigte Systeme eingebettet werden können, darunter Indikatoren, Expert Advisor und dem Schnellhandels-Toolkits. Die Enumeration heißt MESSAGE:

//+------------------------------------------------------------------+
//| List of voice alerts                                             |
//+------------------------------------------------------------------+
enum MESSAGE
{
   STATUS_ON,                          // Status of enabled voice alerts
   SIGNAL_BUY,                         // A Buy signal
   SIGNAL_SELL,                        // A Sell signal
   BUY_ORDER_SET,                      // A Buy order has been placed
   SELL_ORDER_SET,                     // A Sell order has been placed
   BUYLIMIT_ORDER_SET,                 // A Limit Buy order has been placed
   BUYSTOP_ORDER_SET,                  // A Stop Buy order has been placed
   SELLLIMIT_ORDER_SET,                // A Limit Sell order has been placed
   SELLSTOP_ORDER_SET,                 // A Stop Sell order has been placed
   BUYLIMIT_ORDER_DELETE,              // A Limit Buy order has been deleted
   BUYSTOP_ORDER_DELETE,               // A Stop Buy order has been deleted
   SELLLIMIT_ORDER_DELETE,             // A Limit Sell order has been deleted
   SELLSTOP_ORDER_DELETE,              // A Stop Sell order has been deleted
   BUY_ORDER_CLOSE_PROFIT,             // A Buy order has closed with a profit
   BUY_ORDER_CLOSE_LOSS,               // A Buy order has closed with a loss
   SELL_ORDER_CLOSE_PROFIT,            // A Sell order has closed with a profit
   SELL_ORDER_CLOSE_LOSS,              // A Sell order has closed with a loss
   BUY_ORDER_CLOSE_TP,                 // A Buy order has been closed by Take Profit
   BUY_ORDER_CLOSE_SL,                 // A Buy order has been closed by Stop Loss
   SELL_ORDER_CLOSE_TP,                // A Sell order has been closed by Take Profit
   SELL_ORDER_CLOSE_SL,                // A Sell order has been closed by Stop Loss
   MARKET_CLOSE,                       // Market is closed
   AUTO_TRADING_ON,                    // Automated trading is allowed
   AUTO_TRADING_OFF,                   // Automated trading is prohibited
};

Der Basissatz enthält 24 Hinweise. Die meisten von ihnen beziehen sich auf den Betrieb und den Status von offenen Positionen und Pending-Orders. Einige Hinweise werden für Benachrichtigungen im Handelsumfeld verwendet. Die letzten drei Benachrichtigungen beziehen sich auf häufige Ereignisse. Benachrichtigungen über den Status des aktivierten Sprachalarmsystems sowie Benachrichtigungen über das Auftauchen von Kauf- oder Verkaufssignalen sind praktisch, wenn man mit manuellen oder halbautomatischen Handelsexpertenberatern arbeitet oder wenn man Indikatoren verwendet, einschließlich einfacher und als Teil einer Handelsstrategie verfügbarer Indikatoren.

Nun wollen wir die Klasse CSoundsLib erstellen und die für die Arbeit erforderlichen Methoden hinzufügen.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CSoundsLib
{
private:
   LANGUAGE          m_language;
   bool              m_activity_status;
public:
                     CSoundsLib(void); 
                    ~CSoundsLib(void);
   //--- Set the notification language
   void              Language(LANGUAGE lang);
   //--- Set/get the status of the voice alerts system
   void              IsActive(bool flag);
   bool              IsActive(void);
   //--- Play the specified notification
   bool              Message(MESSAGE msg);
};

Der 'private' Teil verfügt über zwei Hinweise, m_language und m_activity_status, die für die folgenden Methoden Language() und IsActive() erforderlich sind. Sie werden also verwendet, um die Sprache der Sprachhinweise einzustellen und den Systemaktivitätsstatus zu erhalten/einzustellen. Hier ist die Implementierung der obigen Hinweise:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CSoundsLib::Language(LANGUAGE lang)
{
   m_language=lang;
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CSoundsLib::IsActive(void)
{
   return(m_activity_status);
}

Eine andere Methode ist Message(). Sie spielt eine aus der Enumeration MESSAGE ausgewählte Benachrichtigung ab. Die Implementierung dieser Methode ist ebenfalls leicht verständlich:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool  CSoundsLib::Message(MESSAGE msg)
{
   if(!m_activity_status)
      return(false);
   string name=(m_language==RUSSIAN ? EnumToString(msg)+"_RU" : EnumToString(msg)+"_EN");
   if(PlaySound("\\Files\\SoundsLib\\"+name+".wav"))
      return(true);
   else
   {
      if(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian")
         Print("Файл не найден");
      else
         Print("File not found");
      return(false);
   }
}

Bitte beachten Sie die folgenden wichtigen Punkte — sie helfen Ihnen, das System durch Hinzufügen eigener Sprachbenachrichtigungen korrekt weiter auszubauen. Der erste Punkt ist der richtige Speicherort für Audiodateien: Standardmäßig befinden sie sich im Ordner MQL5/Files/SoundsLib. Sie sollten den Ordner SoundsLib erstellen. Stellen Sie zweitens sicher, dass Sie Eigennamen und Audiodateiformate in dem erstellten Ordner festlegen. Achten Sie auf diese Code-Zeilen: das Suffix _RU oder _EN wird hier an die Enumeration vom Typ MESSAGE angehängt. Aus diesem Grund bezieht sich der Dateiname, der zum Beispiel einem Kaufsignal Hinweise SIGNAL_BUY entspricht, auf zwei Audiodateien, SIGNAL_BUY _RU und SIGNAL_BUY_EN, für russische und englische Hinweise. Vergessen Sie auch nicht, dass die Systemfunktion PlaySound() nur eine Datei im *.WAV-Format abspielen kann, und daher werden die vollständigen Dateinamen mit Erweiterungen im Ordner SoundsLib wie folgt aussehen:

Abb.1 Vollständiger Name und Erweiterung der Audiodatei.

Daher haben wir 48 Audiodateien für unsere 24 Ereignisse in der Enumeration MESSAGE: zwei Dateien in verschiedenen Sprachen für jedes Ereignis. Als Nächstes werde ich meine eigene Methode zur Erstellung von Sprachhinweisen zeigen. Sie können jedoch jede bevorzugte Methode verwenden. Für diesen Artikel habe ich einen kostenlosen Dienst zur Umwandlung von Text in Sprache verwendet.

Abb.2 Dienst zur Umwandlung von Text in Sprache.

Dieser Dienst bietet eine gute Funktionalität zur Umsetzung der geforderten Aufgabe. Er ermöglicht sowohl die Auswahl der Sprache als auch der Typen mit dem gewünschten Format. Das WAV-Format wird jedoch für die englische Sprache nicht unterstützt. Hier können wir jeden Online-Konverter oder jede andere Software verwenden, um mp3 in wav zu konvertieren. Ich habe alle erforderlichen Dateien für das System vorbereitet und im Ordner MQL5\Files\SoundsLib mit dem richtigen Format und Namen entsprechend der Enumeration MESSAGE und den Sprachsuffixen gespeichert. Hier ist meine Ergebnisliste:

Abb.3 Vollständige Liste der Audiodateien für Sprachhinweise.

Unten finden Sie eine Schritt-für-Schritt-Anleitung, wie Sie Ihre eigenen Sprachbenachrichtigungen erstellen können.

Schritt 1. Fügen Sie Ihr Sprachereignis in das System ein.

Öffnen Sie die Datei SoundsLib.mqh und suchen Sie die Enumeration MESSAGE. Fügen Sie Ihr Ereignis mit einem aussagekräftigen Namen hinzu. Beispiel der Benennung sind in Abbildung 3 und im obigen Code dargestellt.

Schritt 2. Erstellen Sie eine Audiodatei für die Hinweise.

Gehen Sie zu dem Dienst (Sie können jeden beliebigen Dienst nutzen), konfigurieren Sie die erforderlichen Parameter (siehe Abbildung 2) und speichern Sie die Datei im WAV-Format unter MQL5\Files\SoundsLib mit dem Namen "Ihr Ereignisname in der Enumeration MESSAGE "+_RU(_EN) je nach Sprache. Wenn alle Schritte korrekt abgeschlossen sind, wird die neue Sounddatei mit dem neuen Ereignis verknüpft, das in Schritt 1 hinzugefügt wurde, und ist somit einsatzbereit.


Praktische Anwendung bei Indikatoren

Nun wollen wir anhand verschiedener Beispiele sehen, wie es funktioniert. Lassen Sie uns einen zusammengesetzten Indikator erstellen, der auf zwei Indikatorsignalen basiert, die in der nachstehenden Tabelle beschrieben sind:

Parameter Beschreibung
Verwendeter Indikator ADXCloud
Verwendeter Indikator ColorZerolagRVI
Wahl des Zeitrahmens Beide
Kaufbedingungen Die Fläche von ADXCloud ist grün, die Fläche von ColorZerolagRVI verändert sich von rot nach grün.
Verkaufsbedingungen Die Fläche von ADXCloud ist rot, die Fläche von ColorZerolagRVI verändert sich von grün nach rot.

Die Beispiele für eine Eröffnung, die auf den Indikatorsignalen basieren, sind in Abbildung 4 dargestellt, sie sind recht einfach. Wir werden sie als Grundlage für die Erstellung eines zusammengesetzten Signalindikators verwenden, der Markteintrittspunkte als Pfeile im Chart anzeigt. 

Abb.4 Eröffnungsbedingungen durch Indikatorsignale.

//+------------------------------------------------------------------+
//|                                                      Example.mq5 |
//|                                                         Alex2356 |
//|                           https://www.mql5.com/en/users/alex2356 |
//+------------------------------------------------------------------+
#property copyright "Alex2356"
#property link      "https://www.mql5.com/en/users/alex2356"
#property version   "1.00"
#property indicator_chart_window
//--- two buffers are used for calculating and drawing the indicator
#property indicator_buffers 2
//--- used graphic constructions
#property indicator_plots   2
#property indicator_label1  "Buy Signal"
#property indicator_type1   DRAW_ARROW
//---
#property indicator_label2  "Sell Signal"
#property indicator_type2   DRAW_ARROW
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
input group "ADX Cloud Parameters"
input int                  ADXPeriod         =  8;
input double               Alpha1            =  0.25;
input double               Alpha2            =  0.25;
input group "RVI Color Parameters"
input uint                 Smoothing         =  15;
//----
input double               Weight1           =  0.05;
input int                  RVI_period1       =  8;
//----
input double               Weight2           = 0.10;
input int                  RVI_period2       =   21;
//----
input double               Weight3           = 0.16;
input int                  RVI_period3       =   34;
//----
input double               Weight4           = 0.26;
input int                  RVI_period4       =   55;
//----
input double               Weight5           = 0.43;
input int                  RVI_period5       =   89;
//---
double BuySignal[],SellSignal[],ADXCloud[],FastRVI[],SlowRVI[];
int ADX_Handle,RVI_Hadnle,min_rates_total;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
//---
   SetIndexBuffer(0,BuySignal,INDICATOR_DATA);
   SetIndexBuffer(1,SellSignal,INDICATOR_DATA);
//---
   PlotIndexSetInteger(0,PLOT_ARROW,233);
   PlotIndexSetInteger(1,PLOT_ARROW,234);
//---
   PlotIndexSetInteger(0,PLOT_LINE_COLOR,clrDodgerBlue);
   PlotIndexSetInteger(1,PLOT_LINE_COLOR,clrCrimson);
//---
   ArraySetAsSeries(SellSignal,true);
   ArraySetAsSeries(BuySignal,true);
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,EMPTY_VALUE);
   PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,EMPTY_VALUE);
   PlotIndexSetInteger(0,PLOT_ARROW_SHIFT,20);
   PlotIndexSetInteger(1,PLOT_ARROW_SHIFT,-20);
//---
   ADX_Handle=iCustom(Symbol(),PERIOD_CURRENT,"adxcloud",ADXPeriod,Alpha1,Alpha2);
   if(ADX_Handle==INVALID_HANDLE)
   {
      Print(" Failed to create indicator handle");
      return(INIT_FAILED);
   }
//---
   RVI_Hadnle=iCustom(Symbol(),PERIOD_CURRENT,"colorzerolagrvi",
                      Smoothing,
                      Weight1,RVI_period1,
                      Weight2,RVI_period2,
                      Weight3,RVI_period3,
                      Weight4,RVI_period4,
                      Weight5,RVI_period5
                     );
   if(RVI_Hadnle==INVALID_HANDLE)
   {
      Print(" Failed to create indicator handle");
      return(INIT_FAILED);
   }
//---
   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[])
{
//--- c
   if(BarsCalculated(ADX_Handle)<rates_total || BarsCalculated(RVI_Hadnle)<rates_total || rates_total<min_rates_total)
      return(0);
//--- 
   int limit,to_copy,i;
//--- 
   ArraySetAsSeries(ADXCloud,true);
   ArraySetAsSeries(FastRVI,true);
   ArraySetAsSeries(SlowRVI,true);
   ArraySetAsSeries(high,true);
   ArraySetAsSeries(low,true);
//--- 
   if(prev_calculated>rates_total || prev_calculated<=0) 
      limit=rates_total-2;
   else
      limit=rates_total-prev_calculated; 
   to_copy=limit+2;
//---
   if(CopyBuffer(ADX_Handle,0,0,to_copy,ADXCloud)<=0)
      return(0);
//---
   if(CopyBuffer(RVI_Hadnle,0,0,to_copy,FastRVI)<=0)
      return(0);
   if(CopyBuffer(RVI_Hadnle,1,0,to_copy,SlowRVI)<=0)
      return(0);
//--- 
   for(i=limit-1; i>=0 && !IsStopped(); i--)
   {
      if(ADXCloud[i+1]>0 && FastRVI[i+1]>SlowRVI[i+1] && FastRVI[i+2]<SlowRVI[i+2])
      {
         BuySignal[i]=low[i];
         SellSignal[i]=EMPTY_VALUE;
      }
      else if(ADXCloud[i+1]<0 && FastRVI[i+1]<SlowRVI[i+1] && FastRVI[i+2]>SlowRVI[i+2])
      {
         SellSignal[i]=high[i];
         BuySignal[i]=EMPTY_VALUE;
      }
      else
      {
         BuySignal[i]=EMPTY_VALUE;
         SellSignal[i]=EMPTY_VALUE;
      }
   }
//--- return value of prev_calculated for the next call
   return(rates_total);
}
//+------------------------------------------------------------------+

Die resultierende Implementierung ist in Abbildung 5 dargestellt. Jetzt müssen wir das System der Sprachbenachrichtigungen implementieren.

Abb.5 Ein Pfeilindikator, der auf zwei Indikatoren basiert.

Binden Sie zunächst die Datei SoundsLib.mqh mit dem Indikator ein:

#include <SoundsLib/SoundsLib.mqh>

Erstellen Sie eine Instanz der Klasse einer Sprachbenachrichtigung:

CSoundsLib Notify;

Stellen Sie in OnInit() Initialisierungsfunktion die Benachrichtigungssprache ein. Hier werde ich Englisch einstellen. Eigentlich ist Englisch standardmäßig eingestellt, so dass es nicht notwendig ist, es zusätzlich einzustellen. Hier machen wir es also zu Demonstrationszwecken.

Notify.Language(ENGLISH);

Da der Pfeilindikator nur Markteintrittspunkte oder Kauf-/Verkaufssignale anzeigt, werden wir zwei Sprachbenachrichtigungen aus der Enumeration MESSAGE verwenden: 

   SIGNAL_BUY,                         // A Buy signal
   SIGNAL_SELL,                        // A Sell signal

Wenn wir das Benachrichtigungssystem in einen Indikator einbetten, sollten Hinweise nicht in der gesamten Historie generiert werden, sondern nur auf dem aktuellen Balken. Ändern Sie also die Schleife für die Signalsuche wie folgt:

//--- 
   for(i=limit-1; i>=0 && !IsStopped(); i--)
   {
      if(ADXCloud[i+1]>0 && FastRVI[i+1]>SlowRVI[i+1] && FastRVI[i+2]<SlowRVI[i+2])
      {
         BuySignal[i]=low[i];
         SellSignal[i]=EMPTY_VALUE;
         if(i==0)
            Notify.Message(SIGNAL_BUY);
      }
      else if(ADXCloud[i+1]<0 && FastRVI[i+1]<SlowRVI[i+1] && FastRVI[i+2]>SlowRVI[i+2])
      {
         SellSignal[i]=high[i];
         BuySignal[i]=EMPTY_VALUE;
         if(i==0)
            Notify.Message(SIGNAL_SELL);
      }
      else
      {
         BuySignal[i]=EMPTY_VALUE;
         SellSignal[i]=EMPTY_VALUE;
      }
   }

Hier prüfen wir, ob ein Signal auf dem Balken Null vorhanden ist, um den Nutzer des Terminals zu benachrichtigen


Praktische Anwendung im Handel durch Expert Advisor

Im Allgemeinen reichen zwei Arten von Sprachhinweisen als Hinweise aus. Zusätzlich können Sie Hinweise implementieren, um über den in den überkauften Bereich eintretenden Wert für Oszillatoren, über Kanalausbrüche für Bollinger-Bänder usw. zu informieren. Viel mehr Hinweise können in Expert Advisors verwendet werden. Schaffen wir also einen Test-Handelsroboter, der nicht nur über ein Markteintrittssignal benachrichtigt, sondern auch weitere Aktionen kommentiert, z.B. welcher Positionstyp eröffnet wird. Lassen Sie uns zunächst die Markteintrittsstrategie für den Expert Advisor definieren. 

Parameter Beschreibung
Verwendeter Indikator ColorStDev
Verwendeter Indikator Three Tirone levels
Wahl des Zeitrahmens Beide
Kaufbedingungen Das Histogramm von ColorStdDev ist rot (starker Trend), während der aktuelle Preis über dem oberen Tirone-Level liegen sollte.
Verkaufsbedingungen Das Histogramm von ColorStdDev ist rot (starker Trend), während der aktuelle Preis unter dem unteren Tirone-Level liegen sollte.
Exit-Bedingungen   Take-Profit/Stop-Loss

Markteintrittspunkte werden visuell dargestellt, wie in Abbildung 6.

Abb. 6 Beispiele für Markteintritte nach dieser Strategie.

Lassen Sie uns nun die Strategie für MetaTrader 5 umsetzen. Sprachliche Hinweise werden für einige Ereignisse verwendet. 

//+------------------------------------------------------------------+
//|                                                  VoiceNotify.mq5 |
//|                                                         Alex2356 |
//|                           https://www.mql5.com/en/users/alex2356 |
//+------------------------------------------------------------------+
#property copyright "Alex2356"
#property link      "https://www.mql5.com/en/users/alex2356"
#property version   "1.00"
#include <SoundsLib/SoundsLib.mqh>
#include <DoEasy25/Engine.mqh>
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
input uint                 InpStopLoss          =  150;              // Stop Loss, in pips
input uint                 InpTakeProfit        =  250;              // Take Profit, in pips
input double               InpLot               =  0.1;              // Take Profit, in pips
input ulong                InpDeviation         =  10;               // Deviation
input int                  InpMagic             =  2356;             // Magic number
input LANGUAGE             NotifyLanguage       =  ENGLISH;          // Notification Language
//--- ColorStDev indicator parameters
input int                  StDevPeriod          =  12;               // Smoothing period StDev
input ENUM_MA_METHOD       MA_Method            =  MODE_EMA;         // Histogram smoothing method
input ENUM_APPLIED_PRICE   applied_price        =  PRICE_CLOSE;      // Applied price
input int                  MaxTrendLevel        =  90;               // Maximum trend level
input int                  MiddLeTrendLevel     =  50;               // Middle trend level
input int                  FlatLevel            =  20;               // Flat level
//--- Tirone Levels indicator parameters
input int                  TironePeriod         =  13;               // Tirone Period
//---
CEngine trade;
CSoundsLib notify;
int Handle1,Handle2;
double stdev[],tirone_b[],tirone_s[];
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()


{
//---
   if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
      notify.Message(AUTO_TRADING_OFF);
//---
   OnInitTrading();
//--- Get the handle of the ColorStDev indicator
   Handle1=iCustom(Symbol(),PERIOD_CURRENT,"ArticleVoiceNotify\\colorstddev",
                   StDevPeriod,
                   MA_Method,
                   applied_price,
                   MaxTrendLevel,
                   MiddLeTrendLevel,
                   FlatLevel
                  );
   if(Handle1==INVALID_HANDLE)
   {
      Print("Failed to get colorstddev handle");
      Print("Handle = ",Handle1,"  error = ",GetLastError());
      return(INIT_FAILED);
   }
//--- Getting the handle of the Tirone Levels indicator
   Handle2=iCustom(Symbol(),PERIOD_CURRENT,"ArticleVoiceNotify\\tirone_levels_x3",TironePeriod,0);
   if(Handle2==INVALID_HANDLE)
   {
      Print("Failed to get Tirone Levels handle");
      Print("Handle = ",Handle2,"  error = ",GetLastError());
      return(INIT_FAILED);
   }
//---
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//---
}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
//--- If there are no market positions
   if(ExistPositions(Symbol(),-1,InpMagic)<1)
   {
      //--- Getting data for calculations
      if(!GetIndValue())
         return;
      //--- Open an order if there is a buy signal
      if(BuySignal())
      {
         notify.Message(SIGNAL_BUY);
         if(trade.OpenBuy(InpLot,Symbol(),InpMagic,InpStopLoss,InpTakeProfit))
         {
            Sleep(1400);
            notify.Message(BUY_ORDER_SET);
         }
      }
      //--- Opening an order if there is a sell signal
      if(SellSignal())
      {
         notify.Message(SIGNAL_SELL);
         if(trade.OpenSell(InpLot,Symbol(),InpMagic,InpStopLoss,InpTakeProfit))
         {
            Sleep(1400);
            notify.Message(SELL_ORDER_SET);
         }
      }
   }
}
//+------------------------------------------------------------------+
//| Buy conditions                                                   |
//+------------------------------------------------------------------+
bool BuySignal()
{
   return(tirone_b[1]>iClose(Symbol(),PERIOD_CURRENT,1) && stdev[0]>FlatLevel)?true:false;
}
//+------------------------------------------------------------------+
//| Sell conditions                                                  |
//+------------------------------------------------------------------+
bool SellSignal()
{
   return(tirone_b[1]<iClose(Symbol(),PERIOD_CURRENT,1) && stdev[0]>FlatLevel)?true:false;
}
//+------------------------------------------------------------------+
//| Getting the current values of indicators                         |
//+------------------------------------------------------------------+
bool GetIndValue()
{
   return(CopyBuffer(Handle1,0,0,2,stdev)<=0    ||
          CopyBuffer(Handle2,0,0,2,tirone_b)<=0 ||
          CopyBuffer(Handle2,2,0,2,tirone_s)<=0
         )?false:true;
}
//+----------------------------------------------------------------------------+
//|  Returns the number of open orders                                         |
//+----------------------------------------------------------------------------+
//|  Parameters:                                                               |
//|    op - operation                  (-1   - any position)                   |
//|    mn - MagicNumber                (-1   - any magic number)               |
//+----------------------------------------------------------------------------+
int ExistPositions(string sy,int op=-1,int mn=-1)
{
   int pos=0;
   uint total=PositionsTotal();
//---
   for(uint i=0; i<total; i++)
   {
      if(SelectByIndex(i))
         if(PositionGetString(POSITION_SYMBOL)==sy)
            if(op<0 || PositionGetInteger(POSITION_TYPE)==op)
               if(mn<0 || PositionGetInteger(POSITION_MAGIC)==mn)
                  pos++;
   }
   return(pos);
}
//+------------------------------------------------------------------+
//| Select a position on the index                                   |
//+------------------------------------------------------------------+
bool SelectByIndex(const int index)
{
   ENUM_ACCOUNT_MARGIN_MODE margin_mode=(ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
//---
   if(margin_mode==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
   {
      ulong ticket=PositionGetTicket(index);
      if(ticket==0)
         return(false);
   }
   else
   {
      string name=PositionGetSymbol(index);
      if(name=="")
         return(false);
   }
//---
   return(true);
}
//+------------------------------------------------------------------+
//| Trading Environment Initialization                               |
//+------------------------------------------------------------------+
void OnInitTrading()
{
   string array_used_symbols[];
//--- Fill in the array of used symbols
   CreateUsedSymbolsArray(SYMBOLS_MODE_CURRENT,"",array_used_symbols);
//--- Set the type of the used symbol list in the symbol collection and fill in the list of symbol timeseries
   trade.SetUsedSymbols(array_used_symbols);
//--- Pass all existing collections to the trading class
   trade.TradingOnInit();
   trade.TradingSetMagic(InpMagic);
   trade.TradingSetLogLevel(LOG_LEVEL_ERROR_MSG);
//--- Set synchronous passing of orders for all used symbols
   trade.TradingSetAsyncMode(false);
//--- Set correct order expiration and filling types to all trading objects
   trade.TradingSetCorrectTypeExpiration();
   trade.TradingSetCorrectTypeFilling();
}
//+------------------------------------------------------------------+

Lassen Sie uns diesen Code im Hinblick auf die Verwendung von Sprachhinweisen genauer besprechen. Die Initialisierungsfunktion des Handelsroboters prüft, ob Handelssysteme im Terminal handeln dürfen. Wenn diese Option deaktiviert ist, wird ein entsprechender Sprachhinweis abgespielt, um den Nutzer zu benachrichtigen. Wenn in der Funktion OnTick() das gewünschte Handelssignal gefunden wird, wird der EA benachrichtigt, dass das gewünschte Kauf- oder Verkaufssignal gefunden wurde. Es wird versucht, eine Position entsprechend dem Signal zu eröffnen. Wenn dies erfolgreich ist, wird ein weiterer Sprachhinweis abgespielt, um den Nutzer zu benachrichtigen, dass eine Position platziert wurde.

Diese Hinweise sind viel effizienter, da der Nutzer möglicherweise einen Texthinweis auf der Registerkarte "Experten" im Terminal verpasst. Was Standard-Soundbenachrichtigungen betrifft, so ist die Bedeutung des Tons nicht immer klar, sie können bei Indikatoren und Expertenberatern unterschiedlich sein. Sprachbenachrichtigungen liefern die genauen Informationen und sind daher viel bequemer.


Praktische Anwendung in Quick Trading Tools

In meinen früheren Artikeln habe ich ein Toolkit für manuelle Händler entwickelt, die selbständig nach Markteinträgen suchen, Aufträge manuell erteilen, Positionen verwalten und schließen. Zu Demonstrationszwecken möchte ich diesem Toolkit ein System der Sprachbenachrichtigung hinzufügen. Dies wird auch zeigen, dass die Funktionen der Sprachbenachrichtigungen leicht zu jedem Tool hinzugefügt werden kann. Als Grundlage werde ich einen Anhang aus diesem Artikel verwenden. Lassen Sie uns zunächst eine Liste von Aktionen und Ereignissen definieren, für die wir Hinweise per Sprachbenachrichtigung hinzufügen werden.

Bevor wir mit der Integration von Sprachbenachrichtigungen beginnen, lassen Sie uns die entsprechende Bibliothek an dieses Projekt anschließen. Öffnen Sie Programm.mqh und fügen Sie ganz am Anfang Folgendes hinzu.

//+------------------------------------------------------------------+
//|                                                      Program.mqh |
//|                                                         Alex2356 |
//|                    https://www.mql5.com/en/users/alex2356/       |
//+------------------------------------------------------------------+
#include <EasyAndFastGUI\WndEvents.mqh>
#include <DoEasy25\Engine.mqh>
#include "Defines.mqh"
#include <SoundsLib/SoundsLib.mqh>

Gehen Sie in den 'private' Teil der Klasse CFastTrading und erstellen Sie eine Klassenvariable der Instanz CSoundsLib.

   //---
   CSoundsLib        m_notify;

Legen Sie außerdem zwei neue Parameter im Toolkit fest, die das Aktivieren/Deaktivieren von Benachrichtigungen und die Auswahl der Sprache ermöglichen. Öffnen Sie SimpleTrading.mq5 und fügen Sie neue Parameter im EA-Abschnitt Eingabeparameter hinzu:

//+------------------------------------------------------------------+
//| Expert Advisor input parameters                                  |
//+------------------------------------------------------------------+
input int                  Inp_BaseFont      =  10;                  // Base FontSize
input color                Caption           =  C'0,130,225';        // Caption Color
input color                Background        =  clrWhite;            // Back color
input LANG                 Language          =  ENGLISH;             // Interface language
input ulong                MagicNumber       =  1111;                // Magic Number
//---
input bool                 UseVoiceNotify    =  true;                // Use Voice Notify
input LANGUAGE             NotifyLanguage    =  ENGLISH;             // Notification Language

Um sie CSoundsLib der Klasseninstanz m_notify zu übergeben, erstellen Sie zwei Methoden im 'public' Teil der Basisklasse CFastTrading und implementieren Sie diese:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::SetNotifyLanguage(LANGUAGE lang)
{
   m_notify.Language(lang);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::UseVoiceNotify(bool state)
{
   m_notify.IsActive(state);
}
//+------------------------------------------------------------------+

Implementieren Sie diese nun in der Funktion OnInit() in SimpleTrading.mq5 und übergeben Sie die Eingabeparameter an die neu erstellten Methoden.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
//---
   tick_counter=GetTickCount();
//--- Initialize class variables
   program.FontName("Trebuchet MS");
   program.FontSize(Inp_BaseFont);
   program.BackgroundColor(Background);
   program.CaptionColor(Caption);
   program.SetLanguage(Language);
   program.SetMagicNumber(MagicNumber);
   program.UseVoiceNotify(UseVoiceNotify);
   program.SetNotifyLanguage(NotifyLanguage);
//--- Set up the trading panel
   if(!program.CreateGUI())
   {
      Print(__FUNCTION__," > Failed to create graphical interface!");
      return(INIT_FAILED);
   }
   program.OnInitEvent();
//---
   return(INIT_SUCCEEDED);
}

So haben wir die wichtigsten Eingabeparameter des Sprachbenachrichtigungssystems festgelegt. Finden Sie nun Methoden zur Festlegung der Marktpositionen für Kauf und Verkauf. Dies sind die Methoden SetBuyOrder() und SetSellOrder() in der Basisklasse CFastTrading. Öffnen Sie den Hauptteil der Methode, die Kaufaufträge platziert, und suchen Sie den Teil, in dem geprüft wird, ob die Position erfolgreich eröffnet wurde. Fügen Sie dort die entsprechenden Hinweise BUY_ORDER_SET hinzu:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::SetBuyOrder(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_buy_execute.Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_B))
   {
      //---
      double lot;
      if(m_switch_button[0].IsPressed())
         lot=LotPercent(Symbol(),ORDER_TYPE_BUY,SymbolInfoDouble(Symbol(),SYMBOL_ASK),StringToDouble(m_lot_edit[0].GetValue()));
      else
         lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[0].GetValue()));
      if(m_switch_button[1].IsPressed() && m_switch_button[2].IsPressed())
      {
         double tp=double(m_tp_edit[0].GetValue());
         double sl=double(m_sl_edit[0].GetValue());
         if(m_trade.OpenBuy(lot,Symbol(),m_magic_number,sl,tp))
         {
            m_notify.Message(BUY_ORDER_SET);
            return(true);
         }
      }
      else if(!m_switch_button[1].IsPressed() && !m_switch_button[2].IsPressed())
      {
         int tp=int(m_tp_edit[0].GetValue());
         int sl=int(m_sl_edit[0].GetValue());
         if(m_trade.OpenBuy(lot,Symbol(),m_magic_number,sl,tp))
         {
            m_notify.Message(BUY_ORDER_SET);
            return(true);
         }
      }
      else if(m_switch_button[1].IsPressed() && !m_switch_button[2].IsPressed())
      {
         double tp=double(m_tp_edit[0].GetValue());
         int sl=int(m_sl_edit[0].GetValue());
         if(m_trade.OpenBuy(lot,Symbol(),m_magic_number,sl,tp))
         {
            m_notify.Message(BUY_ORDER_SET);
            return(true);
         }
      }
      else if(!m_switch_button[1].IsPressed() && m_switch_button[2].IsPressed())
      {
         int tp=int(m_tp_edit[0].GetValue());
         double sl=double(m_sl_edit[0].GetValue());
         if(m_trade.OpenBuy(lot,Symbol(),m_magic_number,sl,tp))
         {
            m_notify.Message(BUY_ORDER_SET);
            return(true);
         }
      }
   }
   return(false);
}

Nehmen Sie die gleichen Änderungen für Methoden zur Eröffnung von Verkaufspositionen vor. Die zu verwendenden Hinweise lauten SELL_ORDER_SET:

bool CFastTrading::SetSellOrder(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_sell_execute.Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_S))
   {
      //---
      double lot;
      if(m_switch_button[3].IsPressed())
         lot=LotPercent(Symbol(),ORDER_TYPE_SELL,SymbolInfoDouble(Symbol(),SYMBOL_BID),StringToDouble(m_lot_edit[1].GetValue()));
      else
         lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[1].GetValue()));
      //---
      if(m_switch_button[4].IsPressed() && m_switch_button[5].IsPressed())
      {
         double tp=double(m_tp_edit[1].GetValue());
         double sl=double(m_sl_edit[1].GetValue());
         if(m_trade.OpenSell(lot,Symbol(),m_magic_number,sl,tp))
         {
            m_notify.Message(SELL_ORDER_SET);
            return(true);
         }
      }
      else if(!m_switch_button[4].IsPressed() && !m_switch_button[5].IsPressed())
      {
         int tp=int(m_tp_edit[1].GetValue());
         int sl=int(m_sl_edit[1].GetValue());
         if(m_trade.OpenSell(lot,Symbol(),m_magic_number,sl,tp))
         {
            m_notify.Message(SELL_ORDER_SET);
            return(true);
         }
      }
      else if(!m_switch_button[4].IsPressed() && m_switch_button[5].IsPressed())
      {
         int tp=int(m_tp_edit[1].GetValue());
         double sl=double(m_sl_edit[1].GetValue());
         if(m_trade.OpenSell(lot,Symbol(),m_magic_number,sl,tp))
         {
            m_notify.Message(SELL_ORDER_SET);
            return(true);
         }
      }
      else if(m_switch_button[4].IsPressed() && !m_switch_button[5].IsPressed())
      {
         double tp=double(m_tp_edit[1].GetValue());
         int sl=int(m_sl_edit[1].GetValue());
         if(m_trade.OpenSell(lot,Symbol(),m_magic_number,sl,tp))
         {
            m_notify.Message(SELL_ORDER_SET);
            return(true);
         }
      }
   }
   return(false);
}

Kommen wir nun zu den Pending-Orders. Das Toolkit unterstützt vier Typen, jeder hat eine eigene Methode:

   bool              SetBuyStopOrder(int id,long lparam);
   bool              SetSellStopOrder(int id,long lparam);
   bool              SetBuyLimitOrder(int id,long lparam);
   bool              SetSellLimitOrder(int id,long lparam);

Für jeden von ihnen sollte eine separate Sprachbenachrichtigung eingestellt werden. Hier ist ein Beispiel für eine BuyStop-Order, andere werden auf ähnliche Weise eingerichtet. Wie aus dem untenstehenden Code ersichtlich ist, wird die Benachrichtigung BUYSTOP_ORDER_SET verwendet.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::SetBuyStopOrder(int id,long lparam)
{
   if(!m_orders_windows[1].IsVisible())
      return(false);
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_buystop_execute.Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_1))
   {
      //---
      double lot;
      if(m_p_switch_button[0].IsPressed())
         lot=LotPercent(Symbol(),ORDER_TYPE_BUY,SymbolInfoDouble(Symbol(),SYMBOL_ASK),StringToDouble(m_lot_edit[2].GetValue()));
      else
         lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[2].GetValue()));
      //---
      double pr=double(m_pr_edit[0].GetValue());
      //---
      if(m_p_switch_button[1].IsPressed() && m_p_switch_button[2].IsPressed())
      {
         double tp=double(m_tp_edit[2].GetValue());
         double sl=double(m_sl_edit[2].GetValue());
         if(m_trade.PlaceBuyStop(lot,Symbol(),pr,sl,tp,m_magic_number))
         {
            m_notify.Message(BUYSTOP_ORDER_SET);
            return(true);
         }
      }
      else if(!m_p_switch_button[1].IsPressed() && !m_p_switch_button[2].IsPressed())
      {
         int tp=int(m_tp_edit[2].GetValue());
         int sl=int(m_sl_edit[2].GetValue());
         if(m_trade.PlaceBuyStop(lot,Symbol(),pr,sl,tp,m_magic_number))
         {
            m_notify.Message(BUYSTOP_ORDER_SET);
            return(true);
         }
      }
      else if(m_p_switch_button[1].IsPressed() && !m_p_switch_button[2].IsPressed())
      {
         double tp=double(m_tp_edit[2].GetValue());
         int sl=int(m_sl_edit[2].GetValue());
         if(m_trade.PlaceBuyStop(lot,Symbol(),pr,sl,tp,m_magic_number))
         {
            m_notify.Message(BUYSTOP_ORDER_SET);
            return(true);
         }
      }
      else if(!m_p_switch_button[1].IsPressed() && m_p_switch_button[2].IsPressed())
      {
         int tp=int(m_tp_edit[2].GetValue());
         double sl=double(m_sl_edit[2].GetValue());
         if(m_trade.PlaceBuyStop(lot,Symbol(),pr,sl,tp,m_magic_number))
         {
            m_notify.Message(BUYSTOP_ORDER_SET);
            return(true);
         }
      }
   }
   return(false);
}

Nun, wenn die Benachrichtigungen für erteilte Pending-Orders fertig sind, müssen wir Benachrichtigungen für das Löschen früher erteilter Orders hinzufügen. Die Methode RemoveOrder() bestimmt, welche der Pending-Orders in der Tabelle ausgewählt wird. Die ausgewählte Order kann dann geändert oder gelöscht werden. Dabei wird eine Order durch Klicken auf die Schaltfläche Delete gelöscht. 

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::RemoveOrder(long lparam)
{
//--- Check the element ID
   if(lparam==m_small_button[3].Id())
   {
      //--- Get index and symbol
      if(m_table_orders.SelectedItem()==WRONG_VALUE)
         return(false);
      int row=m_table_orders.SelectedItem();
      ulong ticket=(ulong)m_table_orders.GetValue(0,row);
      //---
      if(OrderSelect(ticket))
      {
         string position_symbol=OrderGetString(ORDER_SYMBOL);                          // symbol
         ulong  magic=OrderGetInteger(ORDER_MAGIC);                                    // order MagicNumber
         ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);            // order type
         if(type==ORDER_TYPE_BUY_STOP)
            m_notify.Message(BUYSTOP_ORDER_DELETE);
         else if(type==ORDER_TYPE_SELL_STOP)
            m_notify.Message(SELLSTOP_ORDER_DELETE);
         else if(type==ORDER_TYPE_BUY_LIMIT)
            m_notify.Message(BUYLIMIT_ORDER_DELETE);
         else if(type==ORDER_TYPE_SELL_LIMIT)
            m_notify.Message(SELLLIMIT_ORDER_DELETE);
         //--- declare the request and the result
         MqlTradeRequest request;
         MqlTradeResult  result;
         //--- zeroing the request and result values
         ZeroMemory(request);
         ZeroMemory(result);
         //--- set the operation parameters
         request.action=TRADE_ACTION_REMOVE;             // trading operation type
         request.order = ticket;                         // order ticket
         //--- sending a request
         bool res=true;
         for(int j=0; j<5; j++)
         {
            res=OrderSend(request,result);
            if(res && result.retcode==TRADE_RETCODE_DONE)
               return(true);
            else
               PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
         }
      }
   }
//---
   return(false);
}

Lassen Sie uns die Modifikation im Hauptteil der Methode genauer besprechen. Nachdem wir das Ticket der ausgewählten Order definiert haben, erhalten wir die erforderlichen Daten, um eine Anforderung für deren Löschung festzulegen, indem wir die Struktur MqlTradeRequest ausfüllen und die Methode OrderSend() aufrufen. Um zu verstehen, welcher Typ einer Pending-Order in der Tabelle ausgewählt wurde, verwenden wir den Wert des Variablentyps. Legen Sie auf der Grundlage des Variablenwerts die entsprechende Sprachbenachrichtigung in der Methode Message() fest.

Die letzte zu implementierende Aufgabe besteht darin, eine Sprachbenachrichtigung hinzuzufügen, wenn der automatisierte Handel im MetaTrader 5-Terminal deaktiviert ist. Bei dem Toolkit handelt es sich eigentlich um einen Expert Advisor: Obwohl Nutzer Aufträge manuell erteilen, erkennen das Terminal und der Broker sie als automatisierten Handel. Um eine Prüfung hinzuzufügen, ob automatisierter Handel stattfindet, gehen Sie zu Basisklasse, finden Sie die Ereignisbehandlung durch OnEvent() -> im Bereich ON_END_CREATE_GUI und fügen Sie eine Prüfung mit der entsprechenden Sprachbenachrichtigung hinzu:

// --- GUI creation completion
   if(id==CHARTEVENT_CUSTOM+ON_END_CREATE_GUI)
   {
      //---
      SetButtonParam(m_switch_button[0],LOT);
      SetButtonParam(m_switch_button[1],POINTS);
      SetButtonParam(m_switch_button[2],POINTS);
      SetButtonParam(m_switch_button[3],LOT);
      SetButtonParam(m_switch_button[4],POINTS);
      SetButtonParam(m_switch_button[5],POINTS);
      //---
      SetButtonParam(m_p_switch_button[0],LOT);
      SetButtonParam(m_p_switch_button[1],POINTS);
      SetButtonParam(m_p_switch_button[2],POINTS);
      SetButtonParam(m_p_switch_button[3],LOT);
      SetButtonParam(m_p_switch_button[4],POINTS);
      SetButtonParam(m_p_switch_button[5],POINTS);
      SetButtonParam(m_p_switch_button[6],LOT);
      SetButtonParam(m_p_switch_button[7],POINTS);
      SetButtonParam(m_p_switch_button[8],POINTS);
      SetButtonParam(m_p_switch_button[9],LOT);
      SetButtonParam(m_p_switch_button[10],POINTS);
      SetButtonParam(m_p_switch_button[11],POINTS);
      //---
      if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
         m_notify.Message(AUTO_TRADING_OFF);
   }

Das Video unten zeigt, wie Sprachbenachrichtigungen im Schnellhandels-Toolkit funktionieren, in dem Hinweise für Marktpositionen und Pending-Orders verwendet werden.


Schlussfolgerung

Das angehängte Archiv enthält alle besprochenen Dateien, die sich in den entsprechenden Ordnern befinden. Für ihren ordnungsgemäßen Betrieb brauchen Sie nur den MQL5-Ordner im Terminalordner zu speichern. Um das Stammverzeichnis des Terminals, in dem sich der MQL5-Ordner befindet, zu öffnen, drücken Sie die Tastenkombination Strg+Umschalt+D im Terminal des MetaTrader 5 oder verwenden Sie das Kontextmenü, wie in Abb. 7 unten dargestellt.


Abb. 7. Öffnen des Ordners MQL5 im Stammordner von MetaTrader 5