MetaTrader 5 herunterladen

MQL5 Cookbook: Handelsbedingungen mit Hilfe von Indikatoren in Experts Advisors einrichten

27 Juni 2016, 15:31
Anatoli Kazharski
0
280

Einleitung

In diesem Beitrag wird der Expert Advisor durch Indikatoren verbessert, mit Hilfe deren Werte nach Bedingungen zur Eröffnung von Positions gesucht werden kann. Um dem noch eins draufzusetzen, legen wir eine Dropdown-Liste in den externen Parametern an, um einen der drei Handels-Indikatoren auswählen zu können.

Bitte denken Sie daran: Wir verändern weiterhin den Expert Advisor, den wir schon in allen vorangegangenen Beiträgen der MQL5 Cookbook Reihe bearbeitet haben. Die letzte Version des Expert Advisors kann aus dem Beitrag "MQL5 Cookbook: Die History der Abschlüsse und Funktions-Library zum Erhalt von Position-Eigenschaften" heruntergeladen werden.

Darüber hinaus präsentiert dieser Beitrag eine Funktion, die wir zur Prüfung erzeugen, ob Handelsoperationen durchgeführt werden können oder nicht. Die Funktion zur Eröffnung von Positions wird dahingehend verändert, dass sie es dem Expert Advisor ermöglicht, den Handelsmodus festzustellen (Sofortige Ausführung und Markt Ausführung).

Da der Code des Expert Advisors, infolge aller Verbesserungen und Erweiterungen aus den vorangegangenen Beiträgen bereits mehr als 1.500 Zeilen beträgt, wird er mit jedem neuen Feature, das hinzukommt, immer unbequemer und aufwendiger. Daher liegt es logischerweise nahe, ihn in mehrere Kategorien als separate Library-Dateien aufzuspalten. Unsere Ziele sind jetzt erklärt, also können wir anfangen.

 

Entwicklung des Expert Advisors

Den Quellcode des Expert Advisors (*.mq5) aus dem vorangegangenen Beitrag legen wir in einem extra Ordner ab, TestIndicatorConditions, in dem wir einen Unterordner 'Mit Einschließen' anlegen müssen. In diesem Ordner werden wir alle 'Mit Einschließen'-Dateien (*.mqh) anlegen. Sie können mit Hilfe des MQL5-Assistenten (Strg+N) oder manuell als Standard-Textdateien (*.txt) im erforderlichen Directory angelegt und später in *.mqh umbenannt werden.

Unten finden Sie alle Namen und Anmerkungen für alle erzeugten 'Mit Einschließen'-Dateien:

  • Enums.mqh enthält die Aufzählungen;
  • InfoPanel.mqh präsentiert Features zum Einrichten des Info-Panels und der Erzeugung und dem Löschen grafischer Objekte;
  • Errors.mqh umfasst alle Funktionen zur Lieferung von Fehlercodes und Gründen für eine De-Initialisierung;
  • TradeSignals.mqh präsentiert Funktionen, die Arrays mit Kursen und Indikatorwerten füllen, sowie einen Signalblock;
  • TradeFunctions.mqh umfasst Handelsfunktionen;
  • ToString.mqh bietet Funktionen zur Umwandlung von numerischen Werten in String-Werte;
  • Auxiliary.mqh dient für andere Hilfsfunktionen.

Um diese Libraries in die Hauptdatei mit einzuschließen, verwenden wir die #include Direktive. Da sich die Hauptdatei des Expert Advisors und der Ordner mit den 'einzuschließenden' Dateien (Mit einschließen) im selben Ordner befinden, lautet der Code für die 'einzuschließenden' Datei wie folgt:

//--- Include custom libraries
#include "Include\Enums.mqh"
#include "Include\InfoPanel.mqh"
#include "Include\Errors.mqh"
#include "Include\TradeSignals.mqh"
#include "Include\TradeFunctions.mqh"
#include "Include\ToString.mqh"
#include "Include\Auxiliary.mqh"

Danach können wir sie öffnen und verändern und einen Teil des Quellcodes aus der Hauptdatei des Expert Advisor verschieben.

Zur korrekten Navigation durch den Code, wird jede Header-Datei durch Verweise auf die daneben liegenden Header-Dateien und auch auf die Hauptdatei des Expert Advisors ergänzt. Für unsere Library an Handelsfunktionen, TradeFunctions.mqh, sieht das dann beispielsweise so aus:

//--- Connection with the main file of the Expert Advisor
#include "..\TestIndicatorConditions.mq5"
//--- Include custom libraries
#include "Enums.mqh"
#include "InfoPanel.mqh"
#include "Errors.mqh"
#include "TradeSignals.mqh"
#include "ToString.mqh"
#include "Auxiliary.mqh"

Für Dateien in derselben Verschachtelungsebene reicht es aus, einfach den Namen anzugeben. Um eine Ebene höher zu gelangen, müssen Sie nur zwei Punkte vor dem Backslash im Pfad machen.

In der Enums.mqh Datei fügen wird eine Aufzählung für Indikatoren hinzu. Zur besseren Veranschaulichung werden wir in diesem Expert Advisor mit zwei Standard-Indikatoren (Gleitender Mittelwert und Commodity Channel Index) und einem individuell angepassten Indikator (MultiRange_PCH) arbeiten. Die Aufzählung ist folgende:

//--- Indicators
enum ENUM_INDICATORS
  {
   MA       = 0, // Moving Average
   CCI      = 1, // CCI
   PCH      = 2  // Price Channel
  };

Die externen Parameter werden so verändert:

//--- External parameters of the Expert Advisor
sinput   long              MagicNumber=777;        // Magic number
sinput   int               Deviation=10;           // Slippage
input    ENUM_INDICATORS   Indicator=MA;           // Indicator
input    int               IndicatorPeriod=5;      // Indicator period
input    int               IndicatorSegments=2;    // Number of one direction indicator segments
input    double            Lot=0.1;                // Lot
input    double            VolumeIncrease=0.1;     // Position volume increase
input    double            VolumeIncreaseStep=10;  // Step for volume increase
input    double            StopLoss=50;            // Stop Loss
input    double            TakeProfit=100;         // Take Profit
input    double            TrailingStop=10;        // Trailing Stop
input    bool              Reverse=true;           // Position reversal
sinput   bool              ShowInfoPanel=true;     // Display of the info panel

Wie oben bereits angesprochen, können Sie aus der Dropdown-Liste des Indikator-Parameters einen von drei Indikatoren auswählen.

Es gibt nur einen Parameter, der auf alle Indikatoren anwendbar ist, indem der Indikator-Zeitraum eingerichtet werden kann - IndicatorPeriod. Der Parameter NumberOfBars aus der vorangegangenen Version des Expert Advisors ist in IndicatorSegments umbenannt worden und gibt nun die Anzahl der Bars an, während der ein gegebener Indikator nach oben/nach unten gehen muss, um die Bedingung zur Eröffnung eienr Position zu erfüllen

Des weiteren haben wir einen weiteren externen Parameter hinzugefügt: VolumeIncreaseStep. Mit seiner Hilfe kann man die Schritte zur Erhöhung des Volumens in Punkten setzen.

Der Wert der Variable AllowedNumberOfBars variable (heißt jetzt AllowedNumberOfSegments) ist in der individuell angepassten Funktion GetBarsData() angepasst worden. Er wird jetzt in eine separate Funktion platziert und nur bei ihrer Initialisierung aufgerufen.

Da die Bedingung zur Eröffnung einer Position nun mit Hilfe von Indikator-Werten geprüft wird, ist der Wert, der zugewiesen werden muss, immer um 2 größer. Mit anderen Worten: Wird der externen Variable IndicatorSegments der Wert "1" zugewiesen, wird der Variable AllowedNumberOfSegments der Wert "3" zugewiesen, da der Indikatorwert auf dem abgeschlossenen Bar immer größer sein muss als der auf dem vorigen Bar, da sonst die Bedingung (z.B. für KAUFEN) nicht erfüllt wird. Aus diesem Grund müssen wir die letzten drei Indikatorwerte bekommen.

Unten steht der CorrectInputParameters() Funktionscode:

//+------------------------------------------------------------------+
//| Adjusting input parameters                                  |
//+------------------------------------------------------------------+
void CorrectInputParameters()
  {
//--- Adjust the number of bars for the position opening condition
   if(AllowedNumberOfSegments<=0)
     {
      if(IndicatorSegments<=1)
         AllowedNumberOfSegments=3;                     // At least three bars are required
      if(IndicatorSegments>=5)
         AllowedNumberOfSegments=5;                     // but no more than 7
      else
         AllowedNumberOfSegments=IndicatorSegments+1;   // and always greater by two
     }
  }

Bevor wir uns mit den Indikatoren beschäftigen, wollen wir eine Funktion erzeugen, die nachprüft, ob ein Handel überhaupt zugelassen ist. Sie heißt CheckTradingPermission(). Ist aus einem der in der Funktion aufgeführten Gründe kein Handel zugelassen, wird als Wert "0" geliefert. Das heißt, der nächste Versuch muss auf dem nächsten Bar erfolgen.

//+------------------------------------------------------------------+
//| Checking if trading is allowed                                   |
//+------------------------------------------------------------------+
bool CheckTradingPermission()
  {
//--- For real-time mode
   if(IsRealtime())
     {
      //--- Checking server connection
      if(!TerminalInfoInteger(TERMINAL_CONNECTED))
         return(1);
      //--- Permission to trade at the running program level
      if(!MQL5InfoInteger(MQL5_TRADE_ALLOWED))
         return(2);
      //--- Permission to trade at the terminal level
      if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
         return(3);
      //--- Permission to trade for the current account
      if(!AccountInfoInteger(ACCOUNT_TRADE_ALLOWED))
         return(4);
      //--- Permission to trade automatically for the current account
      if(!AccountInfoInteger(ACCOUNT_TRADE_EXPERT))
         return(5);
     }
//---
   return(0);
  }

Doch kommen wir jetzt zum Hauptanliegen dieses Beitrags. Um auf die Werte des Indikators zugreifen zu können, müssen wir zunächst seinen Handle haben. Dies gelingt uns mit Hilfe spezieller Funktionen, der Namen aus dem Kürzel des Indikators und dem Symbol 'i' davor bestehen.

So lautet die entsprechende Funktion für den Indikator 'Gleitender Mittelwert' iMA(). Mit Hilfe dieser Funktionen erhält man alle Handles der Standard-Indikatoren im MetaTrader 5 Terminal. Eine vollständige Liste steht im Abschnitt der MQL5-Referenzhinweise mit der Überschrift Technische Indikatoren zur Verfügung. Wenn Sie also einen Handle eines individuell angepassten Indikators haben müssen, verwenden Sie die iCustom() Funktion.

Wir implementieren die GetIndicatorHandle() Funktion, wo, je nach dem im Indikator-Parameter ausgewählten Indikator, der Handle-Wert des entsprechenden Indikators der globalen Variable indicator_handle zugewiesen wird. Der Code der Funktion findet sich in unserer Library an Handelssignal-Funktionen (die \Include\TradeSignals.mqh Datei); die Variable mit dem Indikator-Handle befindet sich in der Hauptdatei des Expert Advisors.

//+------------------------------------------------------------------+
//| Getting the indicator handle                                     |
//+------------------------------------------------------------------+
void GetIndicatorHandle()
  {
//--- If the Moving Average indicator is selected
   if(Indicator==MA)
      indicator_handle=iMA(_Symbol,Period(),IndicatorPeriod,0,MODE_SMA,PRICE_CLOSE);
//--- If the CCI indicator is selected
   if(Indicator==CCI)
      indicator_handle=iCCI(_Symbol,Period(),IndicatorPeriod,PRICE_CLOSE);
//--- If the MultiRange_PCH indicator is selected
   if(Indicator==PCH)
      indicator_handle=iCustom(_Symbol,Period(),"MultiRange_PCH",IndicatorPeriod);
//--- If the indicator handle could not be obtained
   if(indicator_handle==INVALID_HANDLE)
      Print("Failed to get the indicator handle!");
  }

Des weiteren erzeugen wir die GetDataIndicators() Funktion, wo wir mit Hilfe der erhaltenen Indiaktor-Handles, ihre Werte bekommen können. Dies geschieht mit Hilfe der CopyBuffer() Funktion auf ganz ähnliche Weise, als wir mit Hilfe der CopyTime(), CopyClose(), CopyOpen(), CopyHigh() und CopyLow() Funktionen, die im Beitrag "MQL5 Cookbook: Position-Eigenschaften im MetaTrader 5 Strategietester analysieren" betrachtet wurden, unsere Bar-Werte erhalten haben.

Da der Indikator mehrere Puffer (Werte-Reihen) haben kann, wird der Puffer-Index an die CopyBuffer() Funktion als zweiter Parameter übertragen. Puffer-Indices für Standard-Indikatoren finden Sie in den MQL5-Referenzhinweisen. Puffer-Indices für individuell angepasste Indikatoren finden Sie im Code, vorausgesetzt der Quellcode ist vorhanden. Gibt es keinen Code, müssen Sie den Index durch Herumprobieren finden, indem Sie im Visualisierungsmodus des Strategietesters beobachten, wie Bedingungen erfüllt werden.

Doch zuvor müssen wir dynamische Arrays für Indikator-Pufferwerte in der Hauptdatei des Expert Advisors anlegen:

//--- Arrays for indicator values
double indicator_buffer1[];
double indicator_buffer2[];

Der Code von GetIndicatorsData() steht unten:

//+------------------------------------------------------------------+
//| Getting indicator values                                         |
//+------------------------------------------------------------------+
bool GetIndicatorsData()
  {
//--- If the indicator handle has been obtained
   if(indicator_handle!=INVALID_HANDLE)
     {
      //--- For the Moving Average or CCI indicator
      if(Indicator==MA || Indicator==CCI)
        {
         //--- Reverse the indexing order (... 3 2 1 0)
         ArraySetAsSeries(indicator_buffer1,true);
         //--- Get indicator values
         if(CopyBuffer(indicator_handle,0,0,AllowedNumberOfSegments,indicator_buffer1)<AllowedNumberOfSegments)
           {
            Print("Failed to copy the values ("+
                  _Symbol+"; "+TimeframeToString(Period())+") to the indicator_buffer1 array! Error ("+
                  IntegerToString(GetLastError())+"): "+ErrorDescription(GetLastError()));
            return(false);
           }
        }
      //--- For the MultiRange_PCH indicator
      if(Indicator==PCH)
        {
         //--- Reverse the indexing order (... 3 2 1 0)
         ArraySetAsSeries(indicator_buffer1,true);
         ArraySetAsSeries(indicator_buffer2,true);
         //--- Get indicator values
         if(CopyBuffer(indicator_handle,0,0,AllowedNumberOfSegments,indicator_buffer1)<AllowedNumberOfSegments || 
            CopyBuffer(indicator_handle,1,0,AllowedNumberOfSegments,indicator_buffer2)<AllowedNumberOfSegments)
           {
            Print("Failed to copy the values ("+
                  _Symbol+"; "+TimeframeToString(Period())+") to the indicator_buffer1 or indicator_buffer2 array! Error ("+
                  IntegerToString(GetLastError())+"): "+ErrorDescription(GetLastError()));
            return(false);
           }
        }
      //---
      return(true);
     }
//--- If the indicator handle has not been obtained, retry
   else
      GetIndicatorHandle();
//---
   return(false);
  }

Die GetTradingSignal() Funktion wurde wesentlich verändert. Die Bedingungen unterscheiden sich wenn die Position nicht vorhanden ist und wenn die Position besteht. Für die Indikatoren Gleitender Mittelwert und CCI sind die Bedingungen dieselben. Für MultiRange_PCH sind sie in einem separaten Block angeordnet. Damit der Code besser lesbar wird und um Wiederholungen zu vermeiden, erzeugen wir eine Hilfsfunktion GetSignal(), die ein Signal zur Eröffnung oder Umkehrung eine Position liefert, vorausgesetzt so eine Position besteht und die entsprechende Handlung vom externen Parameter zugelassen wird.

Unten steht der Code der GetSignal() Funktion:

//+------------------------------------------------------------------+
//| Checking the condition and returning a signal                    |
//+------------------------------------------------------------------+
ENUM_ORDER_TYPE GetSignal()
  {
//--- Check conditions for the Moving Average and CCI indicators
   if(Indicator==MA || Indicator==CCI)
     {
      //--- A Sell signal
      if(AllowedNumberOfSegments==3 && 
         indicator_buffer1[1]<indicator_buffer1[2])
         return(ORDER_TYPE_SELL);
      //---
      if(AllowedNumberOfSegments==4 && 
         indicator_buffer1[1]<indicator_buffer1[2] && 
         indicator_buffer1[2]<indicator_buffer1[3])
         return(ORDER_TYPE_SELL);
      //---
      if(AllowedNumberOfSegments==5 && 
         indicator_buffer1[1]<indicator_buffer1[2] && 
         indicator_buffer1[2]<indicator_buffer1[3] && 
         indicator_buffer1[3]<indicator_buffer1[4])
         return(ORDER_TYPE_SELL);
      //---
      if(AllowedNumberOfSegments==6 && 
         indicator_buffer1[1]<indicator_buffer1[2] && 
         indicator_buffer1[2]<indicator_buffer1[3] && 
         indicator_buffer1[3]<indicator_buffer1[4] && 
         indicator_buffer1[4]<indicator_buffer1[5])
         return(ORDER_TYPE_SELL);
      //---
      if(AllowedNumberOfSegments>=7 && 
         indicator_buffer1[1]<indicator_buffer1[2] && 
         indicator_buffer1[2]<indicator_buffer1[3] && 
         indicator_buffer1[3]<indicator_buffer1[4] && 
         indicator_buffer1[4]<indicator_buffer1[5] && 
         indicator_buffer1[5]<indicator_buffer1[6])
         return(ORDER_TYPE_SELL);

      //--- A Buy signal
      if(AllowedNumberOfSegments==3 && 
         indicator_buffer1[1]>indicator_buffer1[2])
         return(ORDER_TYPE_BUY);
      //---
      if(AllowedNumberOfSegments==4 && 
         indicator_buffer1[1]>indicator_buffer1[2] && 
         indicator_buffer1[2]>indicator_buffer1[3])
         return(ORDER_TYPE_BUY);
      //---
      if(AllowedNumberOfSegments==5 && 
         indicator_buffer1[1]>indicator_buffer1[2] && 
         indicator_buffer1[2]>indicator_buffer1[3] && 
         indicator_buffer1[3]>indicator_buffer1[4])
         return(ORDER_TYPE_BUY);
      //---
      if(AllowedNumberOfSegments==6 && 
         indicator_buffer1[1]>indicator_buffer1[2] && 
         indicator_buffer1[2]>indicator_buffer1[3] && 
         indicator_buffer1[3]>indicator_buffer1[4] && 
         indicator_buffer1[4]>indicator_buffer1[5])
         return(ORDER_TYPE_BUY);
      //---
      if(AllowedNumberOfSegments>=7 && 
         indicator_buffer1[1]>indicator_buffer1[2] && 
         indicator_buffer1[2]>indicator_buffer1[3] && 
         indicator_buffer1[3]>indicator_buffer1[4] && 
         indicator_buffer1[4]>indicator_buffer1[5] && 
         indicator_buffer1[5]>indicator_buffer1[6])
         return(ORDER_TYPE_BUY);
     }
//--- Block that checks conditions for the MultiRange_PCH indicator
   if(Indicator==PCH)
     {
      //--- A Sell signal
      if(close_price[1]<indicator_buffer2[1] && 
         open_price[1]>indicator_buffer2[1])
         return(ORDER_TYPE_SELL);
      //--- A Buy signal
      if(close_price[1]>indicator_buffer1[1] && 
         open_price[1]<indicator_buffer1[1])
         return(ORDER_TYPE_BUY);
     }
//--- No signal
   return(WRONG_VALUE);
  }

Der Code der GetTradingSignal() Funktion sieht nun so aus:

//+------------------------------------------------------------------+
//| Determining trading signals                                      |
//+------------------------------------------------------------------+
ENUM_ORDER_TYPE GetTradingSignal()
  {
//--- If there is no position
   if(!pos.exists)
     {
      //--- A Sell signal
      if(GetSignal()==ORDER_TYPE_SELL)
         return(ORDER_TYPE_SELL);
      //--- A Buy signal
      if(GetSignal()==ORDER_TYPE_BUY)
         return(ORDER_TYPE_BUY);
     }
//--- If the position exists
   if(pos.exists)
     {
      //--- Get the position type
      GetPositionProperties(P_TYPE);
      //--- Get the last deal price
      GetPositionProperties(P_PRICE_LAST_DEAL);
      //--- Block that checks conditions for the Moving Average and CCI indicators
      if(Indicator==MA || Indicator==CCI)
        {
         //--- A Sell signal
         if(pos.type==POSITION_TYPE_BUY && 
            GetSignal()==ORDER_TYPE_SELL)
            return(ORDER_TYPE_SELL);
         //---
         if(pos.type==POSITION_TYPE_SELL && 
            GetSignal()==ORDER_TYPE_SELL && 
            close_price[1]<pos.last_deal_price-CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point))
            return(ORDER_TYPE_SELL);
         //--- A Buy signal
         if(pos.type==POSITION_TYPE_SELL && 
            GetSignal()==ORDER_TYPE_BUY)
            return(ORDER_TYPE_BUY);
         //---
         if(pos.type==POSITION_TYPE_BUY && 
            GetSignal()==ORDER_TYPE_BUY && 
            close_price[1]>pos.last_deal_price+CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point))
            return(ORDER_TYPE_BUY);
        }
      //--- Block that checks conditions for the MultiRange_PCH indicator
      if(Indicator==PCH)
        {
         //--- A Sell signal
         if(pos.type==POSITION_TYPE_BUY && 
            close_price[1]<indicator_buffer2[1] && 
            open_price[1]>indicator_buffer2[1])
            return(ORDER_TYPE_SELL);
         //---
         if(pos.type==POSITION_TYPE_SELL && 

            close_price[1]<pos.last_deal_price-CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point))
            return(ORDER_TYPE_SELL);
         //--- A Buy signal
         if(pos.type==POSITION_TYPE_SELL && 
            close_price[1]>indicator_buffer1[1] && 
            open_price[1]<indicator_buffer1[1])
            return(ORDER_TYPE_BUY);
         //---
         if(pos.type==POSITION_TYPE_BUY && 
            close_price[1]>pos.last_deal_price+CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point))
            return(ORDER_TYPE_BUY);
        }
     }
//--- No signal
   return(WRONG_VALUE);
  }

Jetzt müssen wir uns nur noch mit den Modi 'Sofortige Ausführung' und 'Markt-Ausführung' beschäftigen, die Teil der Symboleigenschaften sind und den Code der Funktion OpenPosition() zur Eröffnung der Position entsprechend verändern. Die Modi mit ihren selbst erklärenden Namen finden Sie ebenfalls in den MQL5-Referenzhinweisen:

  • Sofortige Ausführung
  • Markt-Ausführung

Ich möchte Sie hier daran erinnern, dass Sie, wenn Sie im Modus Markt-Ausführung sind, keine Position mit den eingerichteten Stop Loss- und Take Profit-Stufen eröffnen können. Sie müssen zunächst eine Position eröffnen und sie dann durch Setzen der Stufen entsprechend verändern.

Erst ab Bauart 803 kann Stop Loss und Take Profit bei der Eröffnung einer Position im Modus Markt-Ausführung oder Sofortige Ausführung eingerichtet werden.

Fügen wir den Ausführungs-Modus der Struktur der Symboleigenschaften hinzu:

//--- Symbol properties
struct symbol_properties
  {
   int               digits;           // Number of decimal places in the price
   int               spread;           // Spread in points
   int               stops_level;      // Stops level
   double            point;            // Point value
   double            ask;              // Ask price
   double            bid;              // Bid price
   double            volume_min;       // Minimum volume for a deal
   double            volume_max;       // Maximum volume for a deal
   double            volume_limit;     // Maximum permissible volume for a position and orders in one direction
   double            volume_step;      // Minimum volume change step for a deal
   double            offset;           // Offset from the maximum possible price for a transaction
   double            up_level;         // Upper Stop level price
   double            down_level;       // Lower Stop level price
   ENUM_SYMBOL_TRADE_EXECUTION execution_mode; // Execution mode
  };

Analog müssen wir die ENUM_SYMBOL_PROPERTIES Aufzählung

//--- Enumeration of position properties
enum ENUM_SYMBOL_PROPERTIES
  {
   S_DIGITS          = 0,
   S_SPREAD          = 1,
   S_STOPSLEVEL      = 2,
   S_POINT           = 3,
   S_ASK             = 4,
   S_BID             = 5,
   S_VOLUME_MIN      = 6,
   S_VOLUME_MAX      = 7,
   S_VOLUME_LIMIT    = 8,
   S_VOLUME_STEP     = 9,
   S_FILTER          = 10,
   S_UP_LEVEL        = 11,
   S_DOWN_LEVEL      = 12,
   S_EXECUTION_MODE  = 13,
   S_ALL             = 14
  };

und die GetSymbolProperties() Funktion verändern:

case S_EXECUTION_MODE: symb.execution_mode=(ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_EXEMODE);   break;
      //---
      case S_ALL           :
         symb.digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
         symb.spread=(int)SymbolInfoInteger(_Symbol,SYMBOL_SPREAD);
         symb.stops_level=(int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL);
         symb.point=SymbolInfoDouble(_Symbol,SYMBOL_POINT);
         symb.ask=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),symb.digits);
         symb.bid=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),symb.digits);
         symb.volume_min=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
         symb.volume_max=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX);
         symb.volume_limit=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_LIMIT);
         symb.volume_step=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);
         symb.offset=NormalizeDouble(CorrectValueBySymbolDigits(lot_offset*symb.point),symb.digits);
         symb.up_level=NormalizeDouble(symb.ask+symb.stops_level*symb.point,symb.digits);
         symb.down_level=NormalizeDouble(symb.bid-symb.stops_level*symb.point,symb.digits);
         symb.execution_mode=(ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_EXEMODE);                       break;
         //---

Als Ergebnis sieht der Code der OpenPosition() Funktion so aus:

//+------------------------------------------------------------------+
//| Opening a position                                               |
//+------------------------------------------------------------------+
void OpenPosition(double lot,
                  ENUM_ORDER_TYPE order_type,
                  double price,
                  double sl,
                  double tp,
                  string comment)
  {
//--- Set the magic number in the trading structure
   trade.SetExpertMagicNumber(MagicNumber);
//--- Set the slippage in points
   trade.SetDeviationInPoints(CorrectValueBySymbolDigits(Deviation));
//--- The Instant Execution mode
//    A position can be opened with the Stop Loss and Take Profit levels set
   if(symb.execution_mode==SYMBOL_TRADE_EXECUTION_INSTANT)
     {
      //--- If the position failed to open, print the relevant message
      if(!trade.PositionOpen(_Symbol,order_type,lot,price,sl,tp,comment))
         Print("Error opening the position: ",GetLastError()," - ",ErrorDescription(GetLastError()));
     }
//--- The Market Execution mode 
//    First open a position and only then set the Stop Loss and Take Profit levels
//    *** Starting with build 803, Stop Loss and Take Profit can be set upon position opening ***
   if(symb.execution_mode==SYMBOL_TRADE_EXECUTION_MARKET)
     {
      //--- If there is no position, first open a position and then set Stop Loss and Take Profit
      if(!pos.exists)
        {
         //--- If the position failed to open, print the relevant message
         if(!trade.PositionOpen(_Symbol,order_type,lot,price,0,0,comment))
            Print("Error opening the position: ",GetLastError()," - ",ErrorDescription(GetLastError()));
         //--- Get the flag of presence/absence of the position
         pos.exists=PositionSelect(_Symbol);
         //--- If the position exists
         if(pos.exists)
           {
            //--- Set Stop Loss and Take Profit
            if(!trade.PositionModify(_Symbol,sl,tp))
               Print("Error modifying the position: ",GetLastError()," - ",ErrorDescription(GetLastError()));
           }
        }
      //--- If the position exists, increase its volume and leave the Stop Loss and Take Profit levels unchanged
      else
        {
         //--- If the position failed to open, print the relevant message
         if(!trade.PositionOpen(_Symbol,order_type,lot,price,sl,tp,comment))
            Print("Error opening the position: ",GetLastError()," - ",ErrorDescription(GetLastError()));
        }
     }
  }

Den Funktionen zum Umgang mit den Ereignissen: OnInit

  • OnDeinit
    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- Adjust the input parameters
       CorrectInputParameters();
    //--- Get indicator handles
       GetIndicatorHandle();
    //--- Initialize the new bar
       CheckNewBar();
    //--- Get the properties
       GetPositionProperties(P_ALL);
    //--- Set the info panel
       SetInfoPanel();
    //---
       return(0);
      }
  • OnTick
    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
      {
    //--- Print the deinitialization reason to the journal
       Print(GetDeinitReasonText(reason));
    //--- When deleting from the chart
       if(reason==REASON_REMOVE)
         {
          //--- Delete all objects relating to the info panel from the chart
          DeleteInfoPanel();
          //--- Delete the indicator handle
          IndicatorRelease(indicator_handle);
         }
      }
  • müssen wir immer noch den letzten, sehr wichtigen Schliff verpassen.
    //+------------------------------------------------------------------+
    //| Expert tick function                                             |
    //+------------------------------------------------------------------+
    void OnTick()
      {
    //--- If the bar is not new, exit
       if(!CheckNewBar())
         {
          if(IsVisualMode() || IsRealtime())
            {
             //--- Get the properties and update the values on the panel
             GetPositionProperties(P_ALL);
             //--- Set/update the info panel
             SetInfoPanel();
            }
          return;
         }
    
    //--- If there is a new bar
       else
         {
          //--- If trading is allowed
          if(CheckTradingPermission()==0)
            {
             if(!GetIndicatorsData())
                return;
             GetBarsData();          // Get bar data
             TradingBlock();         // Check the conditions and trade
             ModifyTrailingStop();   // Modify the Trailing Stop level
            }
         }
    //--- Get the properties
       GetPositionProperties(P_ALL);
    //--- Update the info panel
       SetInfoPanel();
      }

Jetzt da alle Funktionen fertig sind, können wir die Parameter optimieren. Vergessen Sie nicht, dass Sie den Code aus der Haupt-Programmdatei erstellen müssen.

 

Optimierung von Parametern und Testen des Expert Advisors

Der Strategietester muss so, wie unten abgebildet, eingerichtet werden:

Abb. 1 Einstellungen des Strategietesters.

Abb. 1 Einstellungen des Strategietesters.

Des Weiteren reichten wir die Parameter des Expert Advisors für eine Optimierung ein (s. auch die angehängte *.set-Datei mit ihren Einstellungen):

Abb. 2 Einstellungen des Expert Advisors.

Abb. 2 Einstellungen des Expert Advisors.

Die Optimierung hat auf einem Dual-Core Prozessor ca. 40 Minuten gedauert. Mit Hilfe des Optimierungs-Charts können Sie teilweise die Qualität des Handelssystems auf Basis der Ergebnisse im Gewinnbereich abschätzen:

Abb. 3 Optimierungs-Chart.

Abb. 3 Optimierungs-Chart.

Die Testergebnisse des maximalen Rückflussfaktors sind wie folgt:

Abb. 4 Testergebnisse des maximalen Rückflussfaktors.

Abb. 4 Testergebnisse des maximalen Rückflussfaktors.

 

Fazit

Die Download-Version des Archivs mit den Quellcodes des Expert Advisors ist an diesen Artikel angehängt. Sobald Sie es extrahiert haben, müssen Sie den Dateiordner \TestIndicatorConditions in <Metatrader 5 terminal>\MQL5\Experts platzieren. Zur Sicherstellung der korrekten Funktionsweise des Expert Advisors sollte der IndikatorMultiRange_PCH heruntergeladen und in <Metatrader 5 terminal>\MQL5\Indicators platziert werden.

Übersetzt aus dem Russischen von MetaQuotes Software Corp.
Originalartikel: https://www.mql5.com/ru/articles/645

Der ZigZag-Indikator: Frischer Ansatz und Neue Lösungen Der ZigZag-Indikator: Frischer Ansatz und Neue Lösungen

Dieser Beitrag beschäftigt sich mit der Möglichkeit, einen fortgeschrittenen ZigZag-Indikator zu erzeugen. Das Konzept der Identifikation von Knoten beruht auf der Verwendung des Envelopes-Indikators. Wir gehen davon aus, dass wir eine bestimmte Kombination von Eingabe-Parametern für eine Reihe von Envelopes finden können, bei denen alle ZigZag-Knoten innerhalb der Grenzen der Envelopes-Bänder liegen. Als Konsequenz können wir daher versuchen, die Koordinaten des neuen Knoten vorherzusagen.

Zur Fehlerbehebung von MQL5-Programmen (Debugging) Zur Fehlerbehebung von MQL5-Programmen (Debugging)

Dieser Artikel richtet sich primär an Programmierer, die die Sprache zwar bereits gelernt haben, die allerdings noch keine Meister ihres Fachs sind. Er wird auf verschiedene Debugging-Techniken eingehen, die der gebündelten Erfahrung des Autors sowie vieler anderer Programmierer entspringen.

MQL5 Cookbook: Die History der Abschlüsse und Funktions-Library zum Erhalt von Position-Eigenschaften MQL5 Cookbook: Die History der Abschlüsse und Funktions-Library zum Erhalt von Position-Eigenschaften

Eine gute Gelegenheit, die in den vorangegangenen Beiträgen zu Position-Eigenschaften beschriebenen Informationen nochmals kurz zusammenzufassen. In diesem Beitrag werden wir einige zusätzliche Funktionen erzeugen, um die Eigenschaften zu erhalten, die man nur nach Zugriff auf die History der Abschlüsse abrufen kann. Darüber hinaus lernen wir Datenstrukturen kennen, mit deren Hilfe wir auf Position- und Symboleigenschaften auf weitaus bequemere Weise zugreifen können.

MQL5 Cookbook: Wie man bei der Einrichtung/Änderung von Handelsstufen Fehler vermeidet MQL5 Cookbook: Wie man bei der Einrichtung/Änderung von Handelsstufen Fehler vermeidet

Als Fortsetzung unserer Arbeit am Expert Advisor aus dem vorangegangenen Beitrag der MQL5 Cookbook-Reihe mit dem Titel : "Position-Eigenschaften im MetaTrader 5 Strategietester analysieren", wollen wir diesmal den EA durch viele nützliche Funktionen erweitern und die bereits bestehenden verbessern und optimieren. Diesmal wird der Expert Advisor externe Parameter haben, die im MetaTrader 5 Strategietester optimiert werden können, sodass er auf gewisse Weise einem einfachen Handelssystem gleicht.