English Русский 中文 Español 日本語 Português
Entwicklung eines Symbolauswahl- und Navigationsprogramms in MQL5 und MQL4

Entwicklung eines Symbolauswahl- und Navigationsprogramms in MQL5 und MQL4

MetaTrader 5Statistik und Analyse | 25 Januar 2019, 08:20
933 1
Roman Klymenko
Roman Klymenko

Einführung

Erfahrene Händler sind sich der Tatsache bewusst, dass die meisten zeitaufwendigen Dinge im Handel nicht das Öffnen und Verfolgen von Positionen sind, sondern das Auswählen von Symbolen und das Suchen von Einstiegspunkten.

Natürlich sind das keine großen Probleme, wenn man nur mit 1-2 Symbolen arbeitet. Aber wenn Ihr Handelsansatz Hunderte von Aktien und Dutzenden von Forex-Symbolen umfasst, kann es mehrere Stunden dauern, nur um geeignete Einstiegspunkte zu finden.

In diesem Artikel werden wir ein EA entwickeln, das die Suche nach Aktien vereinfacht. Der EA soll den Händler auf drei Arten unterstützen:

  • Er soll die Aktien filtern, um eine Liste derjenigen zu liefern, die unseren Bedingungen entsprechen.
  • Er soll die Navigation durch die erstellte Liste der Aktien erleichtern.
  • Er soll zusätzliche Daten anzuzeigen, die für eine Entscheidung notwendig sind.

Erste EA-Vorlage

Zunächst werden wir den EA auf MQL5 entwickeln. Da viele Broker jedoch immer noch keine MetaTrader 5-Konten anbieten, werden wir den EA neu entwickeln, so dass er auch auf dem MetaTrader 4 am Ende des Artikels läuft.

Bereiten wir also die Vorlage vor, die sich fast nicht von derjenigen unterscheidet, die mit dem MQL5-Assistenten erstellt werden kann:

//+------------------------------------------------------------------+
//|                                                     _finder.mq5  |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Klymenko Roman (needtome@icloud.com)"
#property link      "https://logmy.net"
#property version   "1.00"
#property strict

//+------------------------------------------------------------------+
//| Initialisierungsfunktion des Experten                            |
//+------------------------------------------------------------------+
int OnInit()
  {

//--- Timer erstellen
   EventSetTimer(1);
      
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Deinitialisierungsfunktion des Experten                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   EventKillTimer();
  }
//+------------------------------------------------------------------+
//| Timer Funktion                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
  }
//+------------------------------------------------------------------+

void OnChartEvent(const int id,         // Ereignis-ID   
                  const long& lparam,   // Ereignisparameter vom Typ long
                  const double& dparam, // Ereignisparameter vom Typ double
                  const string& sparam) // Ereignisparameter vom Typ string
  { 

}

In dieser Vorlage installieren wir den Timer beim Erstellen eines EAs. Unser Timer wird jede Sekunde aktiviert. Das bedeutet, dass die Standardfunktion OnTimer einmal pro Sekunde aufgerufen werden wird.

Die einzige Zeile, die sich von einer typischen vom MQL5-Assistenten generierten Vorlage unterscheidet, ist #property strict. Diese Zeile ist notwendig, damit der EA in MetaTrader 4 korrekt funktioniert. Da sie keinen signifikanten Einfluss auf MetaTrader 5 hat, fügen wir es am Anfang unserer Vorlage hinzu.

Wir werden die folgenden Standardfunktionen verwenden:

  • OnInit: Anzeige der Schaltflächen von Handelsinstrumenten, die unseren Bedingungen entsprechen, auf dem Chart;
  • OnDeinit: Entfernt den Timer und alle vom EA erstellten grafischen Objekte;
  • OnTimer: Der Timer soll verwendet werden, um Klicks auf grafische Objekte in dem vom EA erstellten Chart zu bestimmen;
  • OnChartEvent: Reaktion auf das Anklicken von grafischen Objekten, die im Chart erstellt wurden, auf dem der EA gestartet wurde.

Die Liste der Symbole, die unseren Bedingungen entsprechen, wird im Objekt vom Typ CArrayString gespeichert. Wir binden daher die MQH-Datei mit der Objektbeschreibung in unserem EA ein:

#include <Arrays\ArrayString.mqh>

Wir werden auch das Objekt vom Typ CChart benötigen, um mit dem Chart zu arbeiten. Auch das binden wir ein:

#include <Charts\Chart.mqh>

All das steht zu Beginn unserer Vorlage, außerhalb von jeder Funktion und nach dem Block der Zeilen mit #property.

Als Nächstes müssen wir uns für die Breite und Höhe aller vom EA erstellten Buttons entscheiden. Definieren wir diese Werte in Form von Makros, die wir nach dem Block mit den Zeilen #include eintragen:

#define BTN_HEIGHT                        (20)
#define BTN_WIDTH                         (100)

Eingaben

Der EA wird über die Eingabeparameter geregelt. Werfen wir einen Blick darauf, damit wir sofort erkennen können, welche Funktionen, weiter unten im Artikel, implementiert werden:

sinput string        delimeter_01="";        // --- Filtereinstellungen ---
input bool           noSYMBmarketWath=true;  // Ausblenden, wenn nicht im Market Watch
input bool           noSYMBwithPOS=true;     // Ausblenden, wenn es Positionen gibt
input ValueOfSpread  hide_SPREAD=spread_b1;  // Ausblenden, wenn der Spread zu hoch ist
input uint           hide_PRICE_HIGH=0;      // Ausblenden, wenn der Preis zu hoch ist
input uint           hide_PRICE_LOW=0;       // Ausblenden, wenn der Preis zu niedrig ist
input bool           hideProhibites=true;    // Ausblenden, wenn der Handel unmöglich ist
input bool           hideClosed=true;        // Ausblenden, wenn der Markt geschlossen ist
input StartHour      hide_HOURS=hour_any;    // Anzeigen, wenn Handelszeit ist
input double         hideATRcents=0.00;      // Ausblenden, wenn der ATR kleiner als der Dollarwert ist
sinput string        delimeter_02="";        // --- Charteinstellungen ---
input bool           addInfoWatch=false;     // Chart zum Market Watch hinzufügen
input bool           viewCandle=true;        // Öffnen eines Kerzencharts
input bool           viewVolumes=true;       // Anzeigen des Tick-Volumens
input bool           showInfoSymbol=true;    // Anzeigen der Bewegungsrichtung
input bool           showNameSymbol=true;    // Anzeigen des Symbolnamens

Wir können sofort feststellen, dass zwei Eingabeparameter einen benutzerdefinierten Typ haben. Deshalb fügen wir die Definition dieser Typen vor den Eingabeparameter hinzu. Beide benutzerdefinierten Typen sind Enumerationen.

Die Enumeration ValueOfSpread definiert mögliche Bedingungen für einen Wert des Spread der Symbole, die vom EA angezeigt werden sollen:

enum ValueOfSpread
  {
   spread_no,//No
   spread_b05,// > 0.05%
   spread_b1,// > 0.1% 
   spread_b15,// > 0.15% 
   spread_l15,// < 0.15% 
   spread_l1,// < 0.1% 
  }; 

Ein Spread, der 0,1% des Preises übersteigt, gilt als erhöht. Daher werden wir standardmäßig nur Symbole mit einem Spread von weniger als 0,1% anzeigen. Der Wert des Parameters Hide bei einem Spread ist also > 0,1%. Wenn die Liste der von Ihrem Broker bereitgestellten Symbole jedoch zu klein ist, können Sie einen anderen Wert für diesen Parameter wählen.

Die Enumeration StartHour enthält die Liste der Hauptperioden, in denen sich einige der Märkte öffnen:

enum StartHour
  {
   hour_any, //Any time
   hour_9am, // 9 am
   hour_10am,// 10 am 
   hour_4pm, // 4 pm 
   hour_0am, // Midnight
  }; 

9 am (oder ein anderer Wert) bedeutet nicht, dass nur Symbole angezeigt werden sollen, die sich genau zu dem angegebenen Zeitpunkt öffnen. Stattdessen bedeutet dies, dass die Symbole, die sich innerhalb dieser Stunde öffnen (z.B. um 9:05 Uhr), angezeigt werden sollen.

Dementsprechend bedeutet 4 pm, dass nur US-Aktien angezeigt werden sollen, die um 16:30 Uhr eröffnet wurden.

10 am bezieht sich hauptsächlich auf russische und europäische Börsenmärkte.

9 am ist die Öffnungszeit einiger Indizes.

Schließlich ist midnight eine offene Zeit am Devisenmarkt, da er rund um die Uhr arbeitet.

Globale Variablen

Bevor wir mit dem Inhalt von Standardfunktionen arbeiten, müssen wir noch eine Reihe von Variablen deklarieren, die überall im EA sichtbar sein müssen. Fügen wir sie nach den Eingabeparametern hinzu:

// Präfix, das den Namen aller vom EA erstellten grafischen Objekte hinzugefügt werden soll:
string exprefix="finder";
// Array der Symbole, die unsere Bedingungen erfüllen:
CArrayString arrPanel1;
// Index des aktuellen Symbols im Array arrPanel1:
int panel1val;
// Array, das die vomn EA erstellten charts speichert (es gibt in diesem Moment nur einen Chart):
CChart charts[];
// Array, der die Pointer zuu den vom EA erstellten Charts speichert (es gibt in diesem Moment nur einen Pointer):
long curChartID[];

Die Kommentare sollten klären, warum wir diese Variablen brauchen.

Alle Vorbereitungen sind abgeschlossen. Jetzt können wir mit der Entwicklung des EAs beginnen. Werfen wir zuerst einen Blick auf das Ergebnis:

//+------------------------------------------------------------------+
//|                                                     _finder.mq5  |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Klymenko Roman (needtome@icloud.com)"
#property link      "https://logmy.net"
#property version   "1.00"
#property strict

#include <Arrays\ArrayString.mqh>
#include <Charts\Chart.mqh>

#define BTN_HEIGHT                        (20)
#define BTN_WIDTH                         (100)

enum ValueOfSpread
  {
   spread_no,//No
   spread_b05,// > 0.05 %
   spread_b1,// > 0.1 %
   spread_b15,// > 0.15 %
   spread_l15,// < 0.15 %
   spread_l1,// < 0.1 %
  }; 
enum StartHour
  {
   hour_any,//Any time
   hour_9am,// 9 am
   hour_10am,// 10 am 
   hour_4pm,// 4 pm 
   hour_0am,// Midnight
  }; 

input bool           noSYMBmarketWath=true; // Ausblenden, wenn das Symbol nicht im Panel des Market Watch ist
input bool           noSYMBwithPOS=true;    // Symbole mit offenen Positionen ausblenden
input ValueOfSpread  hide_SPREAD=spread_b1; // Symbole mit zu großen Spread ausblenden
input uint           hide_PRICE_HIGH=0;     // Symbole mit einem zu hohem Preis ausblenden (0 - nichts ausblenden)
input uint           hide_PRICE_LOW=0;      // Symbole mit einem zu niedrigem Preis ausblenden (0 - nichts ausblenden)
input bool           hideProhibites=true;   // nicht handelbare Symbole ausblenden
input StartHour      hide_HOURS=hour_any;   // nur eröffnete Symbole zeigen
input bool           viewCandle=true;       // Öffnen eines Kerzencharts

// Präfix, das den Namen aller vom EA erstellten grafischen Objekte hinzugefügt werden soll:
string exprefix="finder";
// Array der Symbole, die unsere Bedingungen erfüllen:
CArrayString arrPanel1;
// Index des aktuellen Symbols im Array arrPanel1:
int panel1val;
// Array, das die vomn EA erstellten charts speichert (es gibt in diesem Moment nur einen Chart):
CChart charts[];
// Array, der die Pointer zuu den vom EA erstellten Charts speichert (es gibt in diesem Moment nur einen Pointer):
long curChartID[];

//+------------------------------------------------------------------+
//| Initialisierungsfunktion des Experten                            |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Timer erstellen
   EventSetTimer(1);
      
//---
   return(INIT_SUCCEEDED);
  }
  
//+------------------------------------------------------------------+
//| Deinitialisierungsfunktion des Experten                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(reason!=REASON_CHARTCHANGE){
      ObjectsDeleteAll(0, exprefix);
   }
   EventKillTimer();
  }

//+------------------------------------------------------------------+
//| Timer Funktion                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
  }

void OnChartEvent(const int id,         // Ereignis-ID   
                  const long& lparam,   // Ereignisparameter vom Typ long
                  const double& dparam, // Ereignisparameter vom Typ double
                  const string& sparam) // Ereignisparameter vom Typ string
  { 

}

Dir Funktion zum Filtern der Symbole

Wir beginnen mit der Funktion, die die Schaltflächen der Symbole erstellt, die unseren Bedingungen auf dem Chart entsprechen. Benennen wir diese Funktion start_symbols. Die Funktion wird innerhalb der Funktion OnInit aufgerufen. Dadurch nimmt die Funktion OnInit ihre endgültige Form an:

//+------------------------------------------------------------------+
//| Initialisierungsfunktion des Experten                            |
//+------------------------------------------------------------------+
int OnInit()
  {
   start_symbols();

//--- Timer erstellen
   EventSetTimer(1);
      
//---
   return(INIT_SUCCEEDED);
  }

Warum brauchen wir eine separate Funktion, wenn alles innerhalb von OnInit implementiert werden könnte? Ganz einfach. Wir werden diese Funktion nicht nur beim Start des EA aufrufen, sondern auch beim Drücken der Taste R. Auf diese Weise können wir die Liste der Zeichen leicht aktualisieren, ohne den EA vom Chart entfernen und wieder starten zu müssen.

Da sich die Spreads ständig ändern, müssen wir die Symbolliste sehr oft aktualisieren. Außerdem ändert sich auch das Vorhandensein von offenen Positionen auf bestimmten Symbolen. Vergessen Sie daher nicht, die Symbolliste zu aktualisieren (durch Drücken von R), bevor Sie den zuvor gestarteten EA wieder verwenden, um die aktuellen Daten anzuzeigen.

Werfen wir einen Blick auf die Funktion start_symbols. Sie dient auch als Wrapper zum Starten anderer Funktionen:

void start_symbols(){
   // setzen des Index des aktuellen Symbols in der Liste auf Null (es ist das erste Symbol der Liste):
   panel1val=0;
   // Vorbereitung der Symbolliste:
   prepare_symbols();
   // Entfernen vorher erstellter Symboltasten vom Chart:
   ObjectsDeleteAll(0, exprefix);
   // Anzeigen der Symbolliste:
   show_symbols();
   // Aktualisierung des Charts, um die Änderungen zu sehen:
   ChartRedraw(0);
}

Wir haben zwei weitere benutzerdefinierte Funktionen kennengelernt: prepare_symbols und show_symbols. Die erste erstellt den Array mit den von Symbolen, die unseren Bedingungen erfüllen. Die zweite zeigt die Schaltflächen dieser Symbole auf dem Chart, auf dem der EA läuft.

Die Darstellung der Schaltflächen auf dem Chart ist einfach. Zuerst finden wir die X- und Y-Koordinaten, mit denen eine Schaltfläche angezeigt wird, so dass sie sich nicht mit anderen Tasten überschneidet. Dann sollten wir sie anzeigen:

void show_symbols(){
   
   // Initialisieren der Variablen für die X- und Y-Koordinaten
   int btn_left=0;
   int btn_line=1;
   int btn_right=(int) ChartGetInteger(0, CHART_WIDTH_IN_PIXELS)-77;
   
   // Tasten auf dem Chart für jedes Symbol im Array anzeigen
   // Schreioben des Symbolnamens auf die Taste
   for( int i=0; i<arrPanel1.Total(); i++ ){
      if( btn_left>btn_right-BTN_WIDTH ){
         btn_line++;
         btn_left=0;
      }
      
      ObjectCreate(0, exprefix+"btn"+(string) i, OBJ_BUTTON, 0, 0, 0);
      ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_XDISTANCE,btn_left); 
      ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_YDISTANCE,BTN_HEIGHT*btn_line); 
      ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_XSIZE,BTN_WIDTH); 
      ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_YSIZE,BTN_HEIGHT); 
      ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_FONTSIZE,8); 
      ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_COLOR,clrBlack); 
      ObjectSetString(0,exprefix+"btn"+(string) i,OBJPROP_TEXT,arrPanel1.At(i));    
      ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_SELECTABLE,false);
      
      
      btn_left+=BTN_WIDTH;
   }
   
}

Damit werden die Symbole, die unsere Bedingungen erfüllen, auf dem Chart dargestellt:

Dargestellte Tasten der Symbole auf dem Chart

Konzentrieren wir uns jetzt auf die Bedingungen für die Symbolauswahl (die Funktion prepare_symbols). Zuerst tragen wir alle Symbole in die Liste ein:

void prepare_symbols(){
   // Variable für das Zwischenspeichern der Symbolnamen
   string name;
   // Variable für das Speichern der letzten Kurse der Symbole
   MqlTick lastme;
   
   // Rücksetzen des Symbolarray, falls es Werte enthält
   arrPanel1.Resize(0);
   
   // Bilden des Zwischenarrays tmpSymbols
   // es soll alle verfügbaren Symbole aufnehmen
   CArrayString tmpSymbols;
   for( int i=0; i<SymbolsTotal(noSYMBmarketWath); i++ ){
      tmpSymbols.Add(SymbolName(i, noSYMBmarketWath));
   }
   
   // Bedingungen werden hier überprüft
   // und ein Symbol soll in die Liste eingetragen werden
   // wenn es die Bedingungen erfüllt
   for( int i=0; i<tmpSymbols.Total(); i++ ){
      name=tmpSymbols[i];
      
      // Entfernen des überzähligen Leerzeichen im Symbolnamen,
      // da wir nicht genau wissen, von wo es ist
      StringTrimLeft(name);
      StringTrimRight(name);
      if( !StringLen(name) ){
         continue;
      }
      
      // Hauptfilter der Symbole wird im Weiteren durchgeführt
      // ...

      
      // wenn ein Symbol alle Bedingungen erfüllt, wird es in die Liste eingetragen
      arrPanel1.Add(name);
   }
}

Zuerst tragen wir alle Symbole in einem temporären Array ein. Die anfängliche Filterung erfolgt bereits an dieser Stelle durch den Eingabeparameter Hide symbols absent in the Market Watch panel.

Das Platzieren von Symbolen in den temporären Arrays ist nicht erforderlich. Stattdessen können wir die benötigten Symbole in die Hauptliste aufnehmen. Aber in diesem Fall müssten wir den Code neu schreiben, zum Beispiel, wenn wir ein Eingabeparameter hinzufügen müssen, die nur die Symbole hinzufügt, die in der Liste angezeigt werden sollen. Mit anderen Worten, wir müssten benutzerdefinierte Symbole in der erforderlichen Reihenfolge verwenden, anstatt alle vom Broker angebotenen Symbole zu nehmen.

Aus den gleichen Gründen wird zunächst ein Symbolname aus Leerzeichen in der Schleife gelöscht, die alle Symbole aus dem temporären Array aufzählt. Wenn Sie die oben beschriebene Eingabeparameter implementieren möchten, können Sie nicht auf die Filterung der benutzerdefinierten Eingabe verzichten.

Nun, lassen Sie uns die erhaltenen Symbole auf Grund der Eingabeparameter sortieren. Wir fügen die folgenden Codeblöcke unterhalb der Kommentarzeile hinzu: Als Nächstes erfolgt die Hauptfilterung der Symbole der Funktion prepare_symbols (in der Schleife zum Hinzufügen von Symbolen zur Liste).

Symbole mit Positionen ausblenden:

      // Symbole mit offenen Positionen ausblenden:
      bool isskip=false;
      if( noSYMBwithPOS ){
         // Anzeigen der Liste aller offenen Positionen
         int cntMyPos=PositionsTotal();
         for(int ti=cntMyPos-1; ti>=0; ti--){
            // auslassen, wenn es eine Position gibt für das aktuelle Symbol
            if(PositionGetSymbol(ti) == name ){
               isskip=true;
               break;
            }
         }
         if(!isskip){
            int cntMyPosO=OrdersTotal();
            if(cntMyPosO>0){
               for(int ti=cntMyPosO-1; ti>=0; ti--){
                  ulong orderTicket=OrderGetTicket(ti);
                  if( OrderGetString(ORDER_SYMBOL) == name ){
                     isskip=true;
                     break;
                  }
               }
            }
         }
      }

Überprüfen wir zunächst, ob es eine Position mit einem Symbol gibt. Wenn es keine Position gibt, überprüfen wir, ob es eine Limit-Order gibt. Wenn eine offene Position oder eine Limit-Order vorhanden ist, überspringen wir das Symbol.

Symbole mit einem Spread ausblenden:

      // wenn die Eingabeparameter bezüglich der Symbolpreise aktiviert sind,
      // Versuch des Abrufs der aktuellen Werte
      if(hide_PRICE_HIGH>0 || hide_PRICE_LOW>0 || hide_SPREAD>0 ){
         SymbolInfoTick(name, lastme);
         if( lastme.bid==0 ){
            Alert("Failed to get BID value. Some filtration functions may not work.");
         }
      }
      if(hide_SPREAD>0 && lastme.bid>0){
         switch(hide_SPREAD){
            // wenn der aktuelle Spread den Wert von 0.05% des Preises überschreitet, auslassen des Symbols
            case spread_b05:
               if( ((SymbolInfoInteger(name, SYMBOL_SPREAD)*SymbolInfoDouble(name, SYMBOL_POINT))/lastme.bid)*100 > 0.05 ){
                  isskip=true;
               }
               break;
            // wenn der aktuelle Spread den Wert von 0.1% des Preises überschreitet, auslassen des Symbols
            case spread_b1:
               if( ((SymbolInfoInteger(name, SYMBOL_SPREAD)*SymbolInfoDouble(name, SYMBOL_POINT))/lastme.bid)*100 > 0.1 ){
                  isskip=true;
               }
               break;
            // wenn der aktuelle Spread den Wert von 0.15% des Preises überschreitet, auslassen des Symbols
            case spread_b15:
               if( ((SymbolInfoInteger(name, SYMBOL_SPREAD)*SymbolInfoDouble(name, SYMBOL_POINT))/lastme.bid)*100 > 0.15 ){
                  isskip=true;
               }
               break;
            // wenn der aktuelle Spread den Wert von 0.15% des Preises unterschreitet, auslassen des Symbols
            case spread_l15:
               if( ((SymbolInfoInteger(name, SYMBOL_SPREAD)*SymbolInfoDouble(name, SYMBOL_POINT))/lastme.bid)*100 < 0.15 ){
                  isskip=true;
               }
               break;
            // wenn der aktuelle Spread den Wert von 0.1% des Preises unterschreitet, auslassen des Symbols
            case spread_l1:
               if( ((SymbolInfoInteger(name, SYMBOL_SPREAD)*SymbolInfoDouble(name, SYMBOL_POINT))/lastme.bid)*100 < 0.1 ){
                  isskip=true;
               }
               break;
         }
      }
      if(isskip){
         continue;
      }

Je kleiner der Spread, desto besser. Aus dieser Sicht ist es am besten, mit Symbolen zu arbeiten, deren Spread kleiner als 0,05% des Preises ist. Nicht alle Broker bieten solche guten Bedingungen, insbesondere beim Handel an der Börse.

Symbole mit dem höheren Preis ausblenden (0 - nicht ausblenden):

      // Symbole mit dem höheren Preis ausblenden (0 - nicht ausblenden)
      if(hide_PRICE_HIGH>0 && lastme.bid>0 && lastme.bid>hide_PRICE_HIGH){
         continue;
      }

Symbole mit dem niedrigeren Preis ausblenden (0 - nicht ausblenden):

      if(hide_PRICE_LOW>0 && lastme.bid>0 && lastme.bid<hide_PRICE_LOW){
         continue;
      }

nicht handelbare Symbole ausblenden:

      if(hideProhibites){
         // Auslassen, wenn das Mindestvolumen einer Position eines Symbols 0 ist
         if( SymbolInfoDouble(name, SYMBOL_VOLUME_MIN)==0 ) continue;
         // Auslassen, wenn das Eröffnen von Position eines Symbols deaktiviert ist
         if(SymbolInfoInteger(name, SYMBOL_TRADE_MODE)==SYMBOL_TRADE_MODE_DISABLED || SymbolInfoInteger(name, SYMBOL_TRADE_MODE)==SYMBOL_TRADE_MODE_CLOSEONLY ){
            continue;
         }
      }

Natürlich ist es möglich, die Symbole auszublenden, für die der Handel ohne Bedingungen von den Eingaben deaktiviert ist. Aber man könnte trotzdem solche Symbole in der Liste haben wollen. Aus diesem Grund fügen wir diesen Eingabeparameter hinzu.

Zeige Symbole, die nur zu einer bestimmten Stunde geöffnet werden:

      // liefert den aktuellen Tag in der Variable curDay.
      MqlDateTime curDay;
      TimeCurrent(curDay);
      MqlDateTime curDayFrom;
      datetime dfrom;
      datetime dto;
      // falls es eine Einschränkung bei der Eröffnungszeit des Marktes gibt 
      // und wir die Eröffnungszeit der aktuellen Aktie für den aktuellen Tag regeln können, dann ...
      if( hide_HOURS!=hour_any && SymbolInfoSessionTrade(name, (ENUM_DAY_OF_WEEK) curDay.day_of_week, 0, dfrom, dto)){
         TimeToStruct(dfrom, curDayFrom);
         if(hide_HOURS==hour_9am && curDayFrom.hour != 9){
            continue;
         }
         if(hide_HOURS==hour_10am && curDayFrom.hour != 10){
            continue;
         }
         if(hide_HOURS==hour_4pm && curDayFrom.hour != 16){
            continue;
         }
         if(hide_HOURS==hour_0am && curDayFrom.hour != 0){
            continue;
         }
      }

Ausblenden, wenn der Markt geschlossen ist. Wenn man den EA am Sonntag startet, würde man wohl kaum den Aktienmarkt analysieren wollen. Aber vielleicht möchten man sich mit den am Sonntag verfügbaren Symbole wie TA25-Index oder Kryptowährungen beschäftigen? Dieser Eingabeparameter erlaubt es uns, dies zu tun.

Natürlich wäre es möglich, nur die heute gehandelten Symbole anzuzeigen, anstatt eine separate Eingabe vorzunehmen. Aber was ist, wenn es Sonntag ist und wir uns trotzdem auf den nächsten Handelstag vorbereiten wollen, indem wir geeignete Aktien auswählen, etc. Wir werden diese Option als Eingabeparameter implementieren.

Um zu definieren, ob der Markt heute geöffnet sein wird, benötigen wir die Funktion SymbolInfoSessionTrade. Wenn sie false zurückgibt, dann ist das Symbol anscheinend heute nicht für den Handel verfügbar. Um zu vermeiden, dass die Funktion zweimal aufgerufen wird, müssen wir den Code neu schreiben, so dass nur Symbole anzeigt werden, die geöffnet sind:

      MqlDateTime curDay;
      TimeCurrent(curDay);
      MqlDateTime curDayFrom;
      datetime dfrom;
      datetime dto;
      
      bool sessionData=SymbolInfoSessionTrade(name, (ENUM_DAY_OF_WEEK) curDay.day_of_week, 0, dfrom, dto);

      // Ausblenden eines Symbol , wenn der Markt heute geschlossen bleibt
      if( hideClosed && !sessionData ){
         continue;
      }
      
      // Anzeigen nur der geöffneten Symbole
      // Zuweisen des aktuellen Tages der Variablen curDay
      // falls es eine Einschränkung bei der Eröffnungszeit des Marktes gibt, 
      // und wir die Eröffnungszeit der aktuellen Aktie für den aktuellen Tag regeln können, dann ...
      if( hide_HOURS!=hour_any && sessionData){
         TimeToStruct(dfrom, curDayFrom);
         if(hide_HOURS==hour_9am && curDayFrom.hour != 9){
            continue;
         }
         if(hide_HOURS==hour_10am && curDayFrom.hour != 10){
            continue;
         }
         if(hide_HOURS==hour_4pm && curDayFrom.hour != 16){
            continue;
         }
         if(hide_HOURS==hour_0am && curDayFrom.hour != 0){
            continue;
         }
      }

Ausblenden, wenn der ATR kleiner als der Dollarwert ist. Wenn man während eines Tages handelt und darauf wartet, dass sich der Preis für mindestens 50-90 Cent bewegt, benötigen man wahrscheinlich keine Symbole, die sich statistisch für nicht mehr als 30 Cent pro Tag bewegen. Dieser Parameter ermöglicht es uns, solche Symbole zu sortieren, indem wir die notwendige Mindestgröße der täglichen Kursbewegung festlegen:

      // Ausblenden, wenn der ATR kleiner als der eingestellte Wert in Dollar ist.
      if(hideATRcents>0){
         MqlRates rates[];
         ArraySetAsSeries(rates, true);
         double atr;
         if(CopyRates(name, PERIOD_D1, 1, 5, rates)==5){
            atr=0;
            for(int j=0; j<5; j++){
               atr+=rates[j].high-rates[j].low;
            }
            atr/=5;
            if( atr>0 && atr<hideATRcents ){
               continue;
            }
         }
      }

Ich denke, das ist genug für ein vollständiges Filtern nach den meisten Parametern. Wenn Sie jedoch andere Filterbedingungen benötigen, können Sie diese jederzeit innerhalb der Funktionsschleife prepare_symbols hinzufügen.

Chart öffnen

Wir haben bereits gelernt, wie die Symboltasten entstehen, die unseren Bedingungen entsprechen. Wir könnten an dieser Stelle anhalten und die Charts der erhaltenen Symbole manuell öffnen. Aber das ist umständlich. Glücklicherweise können wir den Prozess vereinfachen und ein benötigtes Chart öffnen, wenn wir auf die Schaltfläche klicken.

Damit die Aktion beim Klicken auf die Schaltfläche ausgeführt wird, sollte diese Aktion in der Standardfunktion OnChartEvent MQL-Sprache beschrieben werden. Die Funktion fängt jedes Ereignis auf dem Chart ab. Mit anderen Worten, sie wird bei jedem Ereignis, das auf dem Chart passiert, aufgerufen.

Die Funktion OnChartEvent verfügt über vier Argumente. Der allererste Parameter (id) enthält die ID eines Ereignisses, das gerade von der Funktion OnChartEvent abgefangen wurde. Um zu verstehen, dass die Funktion OnChartEvent genau nach dem Anklicken einer Diagrammschaltfläche aufgerufen wurde, vergleichen wir den Parameterwert mit dem benötigten.

Das Ereignis eines Klicks auf die Taste hat die ID CHARTEVENT_OBJECT_CLICK. Fügen wir daher den folgenden Code zur Funktion OnChartEvent hinzu, um Tastenklicks auf dem Chart zu verarbeiten:

void OnChartEvent(const int id,         // Ereignis-ID   
                  const long& lparam,   // Ereignisparameter vom Typ long
                  const double& dparam, // Ereignisparameter vom Typ double
                  const string& sparam) // Ereignisparameter vom Typ string
  { 
   switch(id){
      case CHARTEVENT_OBJECT_CLICK:
         // ausgeführter Code nach einem Tastenklick
         break;
   }

}

Wie können wir genau erkennen, welche Taste auf dem Chart gedrückt wurde? Der zweite Parameter der Funktion OnChartEvent (sparam) kann uns dabei helfen. Für das Ereignis CHARTEVENT_OBJECT_CLICK enthält er den Namen der Taste, die von einem Benutzer angeklickt wurde. Wir müssen diesen Namen nur mit dem Namen vergleichen, der von unserem EA generiert wurde. Wenn dies die EA-Taste ist, öffnet sich die entsprechende Symboltabelle. Der Name eines geöffneten Symbols wird aus dem Text auf der Schaltfläche übernommen. Als Ergebnis haben wir den folgenden Code, der in die Bedingung case CHARTEVENT_OBJECT_CLICK eingefügt werden sollte:

         // wenn der Name der Taste eine Zeile enthält, die in allen grafischen Objekten vorhanden ist.
         // erstellt von Ihrem EA, dann...
         if( StringFind(sparam, exprefix+"btn")>=0 ){
            // zuweisen des Index der aktuellen Taste
            // (Position eines aktuellen Symbols in der Symbolliste) der Variablen panel1val
            string tmpme=sparam;
            StringReplace(tmpme, exprefix+"btn", "");
            panel1val=(int) tmpme;
            
            // öffnen des Charts des aktuellen Symbols
            showcharts(ObjectGetString(0,sparam,OBJPROP_TEXT));
         }

Der Chart eines ausgewählten Symbols wird mit der nutzerdefinierten Funktion showcharts geöffnet. Die Funktion öffnet nicht nur den notwendige Chart, sondern auch zusätzlich:

  • Schließt Charts, die zuvor vom EA geöffnet wurden;
  • Fügt dem Panel Market Watch ein Symbol hinzu, wenn es nicht vorhanden ist;
  • Schaltet den Chart bei Bedarf in den Modus Kerzendarstellung um.
  • Ändert den Maßstab des Charts (ich habe diese Funktion einfach deshalb hinzugefügt, weil ich einen nutzerdefinierten Maßstab anstelle des Standardmaßstabs verwende).
void showcharts(string name){
   // wenn die Charts bereits geöffnet sind, schließe sie
   closecharts();
   
   // Hinzufügen eines Symbols zum Panel "Market Watch", falls es noch nicht existiert
   // und falls der Eingabeparameter "Add chart to Market Watch" 'true' ist
   if( addInfoWatch ){
      SymbolSelect(name, true);
   }
   
   // Öffnen des Charts und platzieren der Chart-ID im Array curChartID
   curChartID[ArrayResize(curChartID,ArraySize(curChartID)+1)-1]=charts[(uchar) ArrayResize(charts,ArraySize(charts)+1)-1].Open( name, PERIOD_D1 );
   
   // wenn der Eingabeparameter "Open candlestick charts" 'true' ist,
   // wechsle den Chart in den Kerzenmodus
   if(viewCandle){
      ChartSetInteger( curChartID[ArraySize(curChartID)-1], CHART_MODE, CHART_CANDLES);
   }
   // wenn der Eingabeparameter "Show tick volumes" 'true' ist,
   // zeige das Tick-Volumen
   if(viewVolumes){
      ChartSetInteger( curChartID[ArraySize(curChartID)-1], CHART_SHOW_VOLUMES, CHART_VOLUME_TICK);
   }
   // ändern der Maßstäbe des Charts
   ChartSetInteger( curChartID[ArraySize(curChartID)-1], CHART_SCALE, 2);
      
   // 0,3 Sekunden auf die Implementierung warten
   Sleep(333);
   // aktualisieren des geöffneten Charts mit allen Änderungen
   ChartRedraw(curChartID[ArraySize(curChartID)-1]);
   
}
Die Funktion closecharts schließt alle vorher vom EA geöffneten Charts. Der Code ist ganz einfach:
void closecharts(){
   // wenn das Array der von dem EA geöffneten Charts nicht leer ist, dann...
   if(ArraySize(charts)){
      // schließen aller Charts der Reihe nach
      for( int i=0; i<ArraySize(charts); i++ ){
         charts[i].Close();
      }
      // Löschen des Arrays der Charts
      ArrayFree(charts);
   }
   // wenn das Array der IDs der vom EA geöffneten Charts nicht leer ist, wird es gelöscht
   if(ArraySize(curChartID)){
      ArrayFree(curChartID);
   }
}

Anzeigen zusätzlicher Symbolinformationen

Es wäre gut, nicht nur ein Symbolchart zu öffnen, sondern auch zusätzliche Daten wie die Beschreibung (einige Broker machen Symbolnamen so unverständlich, dass sie eine Art Chiffre zu sein scheinen) und die Symbolbewegungsrichtung des letzten Tages und der letzten Stunde anzuzeigen.

Diese Informationen können über grafische Objekte angezeigt werden. Wir werden jedoch einen einfacheren Ansatz verfolgen. Wir werden die Informationen als Kommentar auf dem Charts anzeigen.

Wir fügen dazu den folgenden Code zur Funktion showcharts hinzu, bevor wir die Funktion Sleep aufrufen:

   //Zusatzinformationen zum Chart anzeigen
   string msg="";
   if(showNameSymbol){
      StringAdd(msg, getmename_symbol(name)+"\r\n");
   }
   if(showInfoSymbol){
      StringAdd(msg, getmeinfo_symbol(name, false)+"\r\n");
   }
   if( StringLen(msg)>0 ){
      ChartSetString(curChartID[ArraySize(curChartID)-1], CHART_COMMENT, msg);
   }

Wenn der Eingabeparameter showNameSymbol true ist, wird die Funktion getmename_symbol aufgerufen, die die Zeile mit dem Symbolnamen zurückgibt. Wenn der Eingabeparameter showInfoSymbol true ist, wird die Funktion getmeinfo_symbol aufgerufen, die die Zeile mit der Symbolinformation zurückgibt:

string getmename_symbol(string symname){
   return SymbolInfoString(symname, SYMBOL_DESCRIPTION);
}
string getmeinfo_symbol(string symname, bool show=true){
   MqlRates rates2[];
   ArraySetAsSeries(rates2, true);
   string msg="";

   if(CopyRates(symname, PERIOD_D1, 0, 1, rates2)>0){
      if(show){
         StringAdd(msg, (string) symname+": ");
      }
      StringAdd(msg, "D1 ");
      if( rates2[0].close > rates2[0].open ){
         StringAdd(msg, "+"+DoubleToString(((rates2[0].close-rates2[0].open)/rates2[0].close)*100, 2) +"%");
      }else{
         if( rates2[0].close < rates2[0].open ){
            StringAdd(msg, "-"+DoubleToString(((rates2[0].open-rates2[0].close)/rates2[0].close)*100, 2) +"%");
         }else{
            StringAdd(msg, "0%");
         }
      }
   }
   if(CopyRates(symname, PERIOD_H1, 0, 1, rates2)>0){
      StringAdd(msg, ", H1 ");
      if( rates2[0].close > rates2[0].open ){
         StringAdd(msg, "+"+DoubleToString(((rates2[0].close-rates2[0].open)/rates2[0].close)*100, 2)+"% (+"+DoubleToString(rates2[0].close-rates2[0].open, (int) SymbolInfoInteger(symname, SYMBOL_DIGITS))+" "+SymbolInfoString(symname, SYMBOL_CURRENCY_PROFIT)+")");
      }else{
         if( rates2[0].close < rates2[0].open ){
            StringAdd(msg, "-"+DoubleToString(((rates2[0].open-rates2[0].close)/rates2[0].close)*100, 2)+"% (-"+DoubleToString(rates2[0].open-rates2[0].close, (int) SymbolInfoInteger(symname, SYMBOL_DIGITS))+" "+SymbolInfoString(symname, SYMBOL_CURRENCY_PROFIT)+")");
         }else{
            StringAdd(msg, "0%");
         }
      }
   }
   
   return msg;
}

Im Ergebnis sehen wir folgende Informationen auf den neueröffneten Chart:

Anzeigen zusätzlicher Symbolinformationen

Steuerung des EAs über die Tastatur

Während wir uns noch in der Funktion OnChartEvent befinden, lassen Sie uns eine Antwort auf das Drücken einiger Tasten dort hinzufügen:

  • Wenn Sie R drücken, aktualisiert sich die Liste der Symbole, die unseren Bedingungen entsprechen:
  • Wenn Sie X drücken, wird der EA vom Chart entfernt.

Das Ereignis mit der ID CHARTEVENT_KEYDOWN ermöglicht das Abfangen des Tastendrucks. Der Code einer gedrückten Taste wird im bereits erwähnten Parameter sparam übergeben. Daher müssen wir dem Operator switch einfach die folgende Bedingung hinzufügen:

      case CHARTEVENT_KEYDOWN:
         switch((int) sparam){
            case 45: //x
               ExpertRemove();
               break;
            case 19: //r
               start_symbols();
               break;
         }
         break;

Wie man sieht, rufen wir beim Drücken von R einfach die zuvor erstellte Funktion start_symbols auf.

Hinzufügen einer Navigation auf dem Chart

Wir haben bereits gelernt, wie man nicht nur Symboltasten anzeigt, sondern auch, wie man beim Anklicken dieser Tasten die Charts der benötigten Symbole öffnet. Aber wir sind hier noch nicht fertig. Unser Hilfsmittel ist immer noch umständlich zu verwenden. Nachdem wir eine Symbolchart geöffnet haben, müssen wir es manuell schließen und auf die Taste für den nächsten Chart klicken. Dies sollte jedes Mal geschehen, wenn wir zum nächsten Symbol wechseln müssen, was die Arbeit sehr mühsam macht. Fügen wir nun Navigationstasten für die Symbolleiste hinzu, um Charts zu öffnen.

Wir werden nur drei weitere Tasten hinzufügen: um zum nächsten Diagramm zu gelangen, um zum vorherigen Chart zu gelangen und um das Chart zu schließen.

Es bleibt nur noch zu entscheiden, wie das umgesetzt werden soll. Wir können die Tasten direkt in der Funktion showcharts hinzufügen, wenn wir ein neues Chart erstellen. Die Anzahl der Tasten kann jedoch in Zukunft zunehmen. Das Erstellen der Tasten und anderer grafischer Objekte kann das Öffnen des Diagramms verlangsamen, was unerwünscht ist.

Deshalb werden wir die Tasten in der standardmäßigen Funktion OnTimer erstellen. Wir werden regelmäßig überprüfen, ob ein Chart durch den EA geöffnet wurde, und, wenn der Chart geöffnet ist, ob es Tasten hat. Wenn es keine Tasten gibt, erstellen wir sie:

void OnTimer()
  {
   // wenn das Array der Chart-IDs Werte enthält, dann...
   uchar tmpCIDcnt=(uchar) ArraySize(curChartID);
   if(tmpCIDcnt>0 ){
      // wenn die letzte ID im Array gültig ist, dann ...
      if(curChartID[tmpCIDcnt-1]>0){
         // wenn der Chart mit der ID keine Tasten hat, erstelle dieselben
         if(ObjectFind(curChartID[tmpCIDcnt-1], exprefix+"_p_btn_next")<0){
            createBTNS(curChartID[tmpCIDcnt-1]);
         }
      }
   }
   
  }

Die Tasten auf dem Chart werden in der Nutzerfunktion createBTNS erstellt. Der Code ist ganz einfach:

void createBTNS(long CID){
   ObjectCreate(CID, exprefix+"_p_btn_prev", OBJ_BUTTON, 0, 0, 0);
   ObjectSetInteger(CID,exprefix+"_p_btn_prev",OBJPROP_XDISTANCE,110); 
   ObjectSetInteger(CID,exprefix+"_p_btn_prev",OBJPROP_YDISTANCE,90); 
   ObjectSetInteger(CID,exprefix+"_p_btn_prev",OBJPROP_XSIZE,BTN_WIDTH); 
   ObjectSetInteger(CID,exprefix+"_p_btn_prev",OBJPROP_YSIZE,BTN_HEIGHT); 
   ObjectSetInteger(CID,exprefix+"_p_btn_prev",OBJPROP_CORNER,CORNER_LEFT_LOWER); 
   ObjectSetString(CID,exprefix+"_p_btn_prev",OBJPROP_TEXT,"Prev chart");
   ObjectSetInteger(CID,exprefix+"_p_btn_prev",OBJPROP_SELECTABLE,false); 
      
   ObjectCreate(CID, exprefix+"_p_btn_next", OBJ_BUTTON, 0, 0, 0);
   ObjectSetInteger(CID,exprefix+"_p_btn_next",OBJPROP_XDISTANCE,110); 
   ObjectSetInteger(CID,exprefix+"_p_btn_next",OBJPROP_YDISTANCE,65); 
   ObjectSetInteger(CID,exprefix+"_p_btn_next",OBJPROP_XSIZE,BTN_WIDTH); 
   ObjectSetInteger(CID,exprefix+"_p_btn_next",OBJPROP_YSIZE,BTN_HEIGHT); 
   ObjectSetInteger(CID,exprefix+"_p_btn_next",OBJPROP_CORNER,CORNER_LEFT_LOWER); 
   ObjectSetString(CID,exprefix+"_p_btn_next",OBJPROP_TEXT,"Next chart");
   ObjectSetInteger(CID,exprefix+"_p_btn_next",OBJPROP_SELECTABLE,false); 
      
   ObjectCreate(CID, exprefix+"_p_btn_close", OBJ_BUTTON, 0, 0, 0);
   ObjectSetInteger(CID,exprefix+"_p_btn_close",OBJPROP_XDISTANCE,110); 
   ObjectSetInteger(CID,exprefix+"_p_btn_close",OBJPROP_YDISTANCE,40); 
   ObjectSetInteger(CID,exprefix+"_p_btn_close",OBJPROP_XSIZE,BTN_WIDTH); 
   ObjectSetInteger(CID,exprefix+"_p_btn_close",OBJPROP_YSIZE,BTN_HEIGHT); 
   ObjectSetInteger(CID,exprefix+"_p_btn_close",OBJPROP_CORNER,CORNER_LEFT_LOWER); 
   ObjectSetString(CID,exprefix+"_p_btn_close",OBJPROP_TEXT,"Close chart");
   ObjectSetInteger(CID,exprefix+"_p_btn_close",OBJPROP_SELECTABLE,false); 
   
   // Aktualisierung des Charts, um die Änderungen zu sehen
   ChartRedraw(CID);
}

Im Ergebnis schaut ein neuer Chart dann so aus:

Die Tasten für die Navigation

Hinzufügen einer Reaktion auf einen Tastendruck

Bisher sind die dem Chart hinzugefügten Tasten nur eine Dekoration. Beim Drücken passiert nichts. Teilen wir ihnen mit, wie sie auf das Drücken reagieren sollen.

Leider hilft uns die Standardfunktion OnChartEvent hier nicht weiter, da sie nur auf die Ereignisse reagiert, die auf einem Chart passiert sind, auf dem den EA gestartet wird, während die Buttons einem neuen Chart hinzugefügt werden.

Vielleicht gibt es einige bequemere Möglichkeiten. Ich habe nur einen Weg gefunden, um auf Änderungen zu reagieren, die in einem anderen Chart aufgetreten sind. Es handelt sich um die Standardfunktion OnTimer. Wenn der Chart die Tasten enthält, werden wir prüfen, ob welche davon gedrückt sind. Wenn ja, wird eine notwendige Aktion durchgeführt. Infolgedessen wird die Bedingung:

         if(ObjectFind(curChartID[tmpCIDcnt-1], exprefix+"_p_btn_next")<0){
            createBTNS(curChartID[tmpCIDcnt-1]);
         }

... wird neugeschrieben wie folgt:

         if(ObjectFind(curChartID[tmpCIDcnt-1], exprefix+"_p_btn_next")<0){
            createBTNS(curChartID[tmpCIDcnt-1]);
         }else{
            if(ObjectGetInteger(curChartID[tmpCIDcnt-1],exprefix+"_p_btn_prev",OBJPROP_STATE)==true ){
               prevchart();
               return;
            }
            if(ObjectGetInteger(curChartID[tmpCIDcnt-1],exprefix+"_p_btn_next",OBJPROP_STATE)==true ){
               nextchart();
               return;
            }
            if(ObjectGetInteger(curChartID[tmpCIDcnt-1],exprefix+"_p_btn_close",OBJPROP_STATE)==true ){
               closecharts();
               return;
            }
         }

Wenn die Taste Prev chart gedrückt wird, wird die Funktion prevchart aufgerufen. Wenn die Taste Next chart gedrückt wird, wird die Funktion nextchart aufgerufen. Wenn die Taste Close chart gedrückt wird, wird die Funktion closecharts aufgerufen. Die Funktionen prevchart und nextchart sind ähnlich:

void nextchart(){
   // wenn die Symbolliste das nächste Symbol anbietet, öffne dessen Chart
   // andernfalls schließe den Chart
   if(arrPanel1.Total()>(panel1val+1)){
      panel1val++;
      showcharts(arrPanel1[panel1val]);
   }else{
      closecharts();
   }
}
void prevchart(){
   // wenn die Symbolliste das vorherige Symbol anbietet, öffne dessen Chart
   // andernfalls schließe den Chart
   if(arrPanel1.Total()>(panel1val-1) && (panel1val-1)>=0){
      panel1val--;
      showcharts(arrPanel1[panel1val]);
   }else{
      closecharts();
   }
}

Schlussfolgerung

Das war's. Wie Sie sehen können, ist der Umfang des gesamten Codes nicht überwältigend, während der Vorteil offensichtlich ist. Wir müssen die Charts nicht mehr selber öffnen und immer wieder schließen. Stattdessen können wir auf die entsprechende Taste klicken, und alles ist für uns erledigt.

Natürlich kann es mehr Möglichkeiten geben, unseren EA zu verbessern. Aber in seiner jetzigen Form ist es bereits ein vollwertiges Produkt, das die Auswahl der Aktien deutlich vereinfacht.

Konvertieren des Hilfsprogramms nach MQL4

Nun versuchen wir, unser Hilfsprogramm nach MQL4 zu konvertieren. Überraschenderweise müssen wir nur einen einzigen Codeblock neu schreiben. Dies dauert etwa fünf Minuten.

Wir erstellen zunächst einen neuen EA in MetaEditor 4. Danach kopieren wir den Quellcode des MQL5 EA in ihn.

Dann kompilieren wir den EA. Der Versuch endet natürlich mit einem Fehler. Aber als Ergebnis erhalten wir eine Liste von Fehlern, die wir beheben müssen. Es sind nur drei von ihnen:

  • 'PositionsTotal' - Funktion nicht definiert
  • 'PositionGetSymbol' - Funktion nicht definiert
  • 'OrderGetTicket' - Funktion nicht definiert

Mit einem Doppelklick auf den ersten Fehler, gelangt man zur entsprechenden Zeile.

'PositionsTotal' - Funktion nicht definiert. Der Fehler wird im folgenden Block des Funktionscodes prepare_symbols erkannt:

         int cntMyPos=PositionsTotal();
         for(int ti=cntMyPos-1; ti>=0; ti--){
            // auslassen, wenn es bereits eine Position mit dem aktuellen Symbol gibt
            if(PositionGetSymbol(ti) == name ){
               isskip=true;
               break;
            }
         }
         if(!isskip){
            int cntMyPosO=OrdersTotal();
            if(cntMyPosO>0){
               for(int ti=cntMyPosO-1; ti>=0; ti--){
                  ulong orderTicket=OrderGetTicket(ti);
                  if( OrderGetString(ORDER_SYMBOL) == name ){
                     isskip=true;
                     break;
                  }
               }
            }
         }

Einer der wesentlichen Unterschiede zwischen den Sprachen MQL4 und MQL5 ist die Handhabung von Positionen und Aufträgen. Daher sollten wir den Codeblock wie folgt umschreiben, damit der EA im MetaTrader 4 korrekt funktioniert:

         int cntMyPos=OrdersTotal();
         for(int ti=cntMyPos-1; ti>=0; ti--){
            if(OrderSelect(ti,SELECT_BY_POS,MODE_TRADES)==false) continue;
            if(OrderSymbol() == name ){
               isskip=true;
               break;
            }
         }

Da MQL4 keine Unterscheidung zwischen Positionen und Aufträgen vornimmt, ist der resultierende Code viel kleiner.

Die restlichen Fehler werden automatisch behoben, da sie in dem von uns bearbeiteten Codeblock aufgetreten sind.


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

Beigefügte Dateien |
_finder.mq5 (40.88 KB)
_finder4.mq4 (40.09 KB)
_finder.ex5 (56.21 KB)
_finder4.ex4 (30.97 KB)
Letzte Kommentare | Zur Diskussion im Händlerforum (1)
Otto Pauser
Otto Pauser | 26 Jan. 2019 in 00:45

Und wofür gibt es precompiler direktiven ???

#ifdef __MQL5__
   // Code für MQL5
#else
   // Code für MQL4
#endif

Aber egal, man muss es ja so oder so entweder .mq4 oder .mq5 nennen.

Die Wahrscheinlichkeitstheorie für den Handel von Kurslücken verwenden Die Wahrscheinlichkeitstheorie für den Handel von Kurslücken verwenden
In diesem Artikel werden wir die Wahrscheinlichkeitstheorie und die mathematischen Methoden der Statistik für das Erstellen und Testen von Handelsstrategien anwenden. Wir werden auch nach einem optimalen Handelsrisiko suchen, indem wir die Unterschiede zwischen dem Preis und dem Random Walk nutzen. Es ist bewiesen, dass, wenn sich die Preise wie ein Random Walk mit Null-Drift verhalten (ohne Richtungswechsel), ein profitabler Handel unmöglich ist.
Verwenden von OpenCL, um Kerzenmuster zu testen Verwenden von OpenCL, um Kerzenmuster zu testen
Der Artikel beschreibt den Algorithmus, um die Kerzenmuster von OpenCL für den Tester im Modus "1 Minute OHLC" zu implementieren. Wir werden auch die Geschwindigkeiten des integrierten Strategietesters, gestartet in schnellen und langsamen Modi, vergleichen.
Wie man nutzerdefinierte MOEX-Symbole in MetaTrader 5 erstellt und testet Wie man nutzerdefinierte MOEX-Symbole in MetaTrader 5 erstellt und testet
Der Artikel beschreibt die Erstellung eines nutzerdefinierten Symbols einer Börse mit der Sprache MQL5. Insbesondere wird die Verwendung von Börsenkursen von der beliebten Finam-Website in Betracht gezogen. Eine weitere in diesem Artikel betrachtete Option ist die Möglichkeit, mit einem beliebigen Format von Textdateien zu arbeiten, die bei der Erstellung des nutzerdefinierten Symbols verwendet werden. Dies ermöglicht die Arbeit mit beliebigen Finanzsymbolen und Datenquellen. Nachdem wir ein benutzerdefiniertes Symbol erstellt haben, können wir alle Funktionen des Strategy Tester des MetaTrader 5 nutzen, um Handelsalgorithmen für Börseninstrumente zu testen.
Die eigene, multi-threaded, asynchrone Web-Anfrage in MQL5 Die eigene, multi-threaded, asynchrone Web-Anfrage in MQL5
Der Artikel beschreibt die Bibliothek, mit der Sie die Effizienz von HTTP-Anfragen mit WebRequest in MQL5 erhöhen können. Die Ausführung von WebRequest im nicht-blockierenden Modus verwendet in zusätzliche Threads, die Hilfscharts und Expert Advisors verwendet, um nutzerdefinierte Ereignisse austauschen und gemeinsame Ressourcen lesen. Die Quellcodes sind ebenfalls besprochen und beigefügt.