English Русский 中文 Español 日本語 Português 한국어 Français Italiano Türkçe
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

MetaTrader 5Beispiele | 27 Juni 2016, 15:36
899 0
Anatoli Kazharski
Anatoli Kazharski

Einleitung

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.

Handelssysteme, bei denen die Position-Volumen während ihres ganzen Bestehens unverändert bleiben, verlangen nicht wirklich die Arbeit mit den Funktionen, die in diesem Beitrag beschrieben werden. Doch sollten Sie in Ihrer Handelsstrategie später ein Geldverwaltungssystem implementieren und die Größe eines Position-Postens kontrollieren wollen, dann sind diese Funktionen unerlässlich.

Bevor wir beginnen, möchte ich all denjenigen Lesern, die zum ersten Mal diese Website besuchen und via eines Links auf diesen Beitrag gestoßen sind oder gerade mit dem Erlernen der MQL5-Sprache begonnen haben, folgenden Vorschlag machen: Lesen Sie bitte zuerst die vorangegangenen Beiträge dieser "MQL5 Cookbook" Beitragsreihe.


Entwicklung des Expert Advisors

Um zu sehen, wie die neuen Funktionen im Expert Advisor funktionieren, der im vorangegangenen Beitrag mit dem Titel "MQL5 Cookbook: Wie man bei der Einrichtung/Veränderung von Handelsstufen Fehler vermeidet" verändert wurde, fügen wir die Möglichkeit hinzu, das Position-Volumen erhöhen zu können, sobald ein Signal zur Eröffnung auftritt und die Position bereits vorhanden ist.

In der Position-History können mehrere Abschlüsse vorhanden sein. Falls es im Position-Volumen im Verlauf des Handels also zu Änderungen gekommen ist, dann müssen auch im aktuellen Position-Kurs Änderungen gewesen sein. Um den Kurs am ersten Eintrittspunkt herauszufinden, müssen wir daher auf die History der Abschlüsse hinsichtlich dieser bestimmten Position zugreifen können. Die folgende Abbildung veranschaulicht einen Fall, in dem eine Position nur einen Abschluss hat (Eintrittspunkt):

Abb. 1 Erster Abschluss in der Position

Abb. 1 Erster Abschluss in der Position.

Die nächste Abbildung zweigt eine Änderung im Position-Kurs nach dem zweiten Abschluss:

Abb. 2 Zweiter Abschluss in der Position

Abb. 2 Zweiter Abschluss in der Position.

Wie in den vorangegangenen Beiträgen gezeigt, können Sie mit Standard-Identifikatoren nur den aktuellen Position-Kurs (POSITION_PRICE_OPEN) sowie den aktuellen Kurs eines Symbols (POSITION_PRICE_CURRENT) für die die Position eröffnet ist, erhalten.

Doch in einigen Handelssystemen müssen wir auch den Abstand kennen, der vom Kurs beim ersten Eintrittspunkt abgedeckt wird, sowie auch den Kurs des letzten Abschlusses. Diese Informationen sind alle in der History der Abschlüsse/Order im Konto vorhanden. Unten steht eine Liste der Abschlüsse im Zusammenhang mit der obigen Abbildung:

Abb. 3 Die History der Abschlüsse im Konto

Abb. 3 Die History der Abschlüsse im Konto.

Meiner Meinung nach ist die Situation nun klar und alle Ziele sind gesetzt. Kommen wir also nun zur Veränderung des in den vorangegangenen Beiträgen erwähnten Expert Advisors. Als Erstes fügen wir der Aufzählung der Position-Eigenschaften neue Identifikatoren hinzu: 0, 6, 9, 12 und 16:

//--- Enumeration of position properties
enum ENUM_POSITION_PROPERTIES
  {
   P_TOTAL_DEALS     = 0,
   P_SYMBOL          = 1,
   P_MAGIC           = 2,
   P_COMMENT         = 3,
   P_SWAP            = 4,
   P_COMMISSION      = 5,
   P_PRICE_FIRST_DEAL= 6,
   P_PRICE_OPEN      = 7,
   P_PRICE_CURRENT   = 8,
   P_PRICE_LAST_DEAL = 9,
   P_PROFIT          = 10,
   P_VOLUME          = 11,
   P_INITIAL_VOLUME  = 12,
   P_SL              = 13,
   P_TP              = 14,
   P_TIME            = 15,
   P_DURATION        = 16,
   P_ID              = 17,
   P_TYPE            = 18,
   P_ALL             = 19
  };

Die Anmerkungen für jede Eigenschaft erfolgen in einer Struktur, die wir uns etwas später hier ansehen werden.

Erhöhen wir auch die Anzahl der externen Parameter. Jetzt können wir nämlich angeben:

  • MagicNumber - eine eindeutige ID des Expert Advisors (magische Zahl);
  • Deviation - Modulation;
  • VolumeIncrease - Wert um den das Position-Volumen erhöht wird;
  • InfoPanel - Parameter mit dem Sie die Anzeige des Info-Panels aktivieren/deaktivieren können.

Und so wird das implementiert:

//--- External parameters of the Expert Advisor
sinput   long        MagicNumber=777;     // Magic number
sinput   int         Deviation=10;        // Slippage
input    int         NumberOfBars=2;      // Number of Bullish/Bearish bars for a Buy/Sell
input    double      Lot=0.1;             // Lot
input    double      VolumeIncrease=0.1;  // Position 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

Bitte beachten Sie die Parameter, deren Sinput-Modifikator eingerichtet wurde. Mit Hilfe dieses Modifikators können Sie die Optimierung im Strategietester deaktivieren. Denn wenn Sie ein Programm zur eigenen Verwendung entwickeln, wissen Sie in der Tat ganz genau, welche Parameter sich auf das Endergebnis auswirken werden, also können Sie sie einfach aus der Optimierung entfernen (entsprechendes Häkchen entfernen). Doch bei einer sehr großen Menge von Parametern können Sie mit dieser Methode diese Parameter visuell von anderen abtrennen, da sie grau unterlegt sind:

Abb. 4 Für die Optimierung deaktivierte Parameter sind grau unterlegt

Abb. 4 Für die Optimierung deaktivierte Parameter sind grau unterlegt.

Ersetzen wir nun die globalen Variablen, die die Werte für Position- und Symboleigenschaften gespeichert haben durch Datenstrukturen ersetzen (struct):

//--- Position properties
struct position_properties
  {
   uint              total_deals;      // Number of deals
   bool              exists;           // Flag of presence/absence of an open position
   string            symbol;           // Symbol
   long              magic;            // Magic number
   string            comment;          // Comment
   double            swap;             // Swap
   double            commission;       // Commission   
   double            first_deal_price; // Price of the first deal in the position
   double            price;            // Current position price
   double            current_price;    // Current price of the position symbol      
   double            last_deal_price;  // Price of the last deal in the position
   double            profit;           // Profit/Loss of the position
   double            volume;           // Current position volume
   double            initial_volume;   // Initial position volume
   double            sl;               // Stop Loss of the position
   double            tp;               // Take Profit of the position
   datetime          time;             // Position opening time
   ulong             duration;         // Position duration in seconds
   long              id;               // Position identifier
   ENUM_POSITION_TYPE type;            // Position type
  };
//--- 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
  }

Um auf ein bestimmtes Element der Struktur zugreifen zu können, müssen wir nun eine Variable dieses Strukturtyps erzeugen. Dies erfolgt ganz ähnlich wie die Erzeugung eines Objekts für eine Handelsklasse, was im Beitrag mit dem Titel "MQL5 Cookbook: Position-Eigenschaften m MetaTrader 5 Strategietester analysieren" beschrieben wurde.

//--- variables for position and symbol properties
position_properties  pos;
symbol_properties    symb;

Auf diese Elemente können Sie auf die gleiche Art und Weise zugreifen, wie Sie mit Klassenmethoden umgehen. Mit anderen Worten: Es genügt nach dem Namen einer Strukturvariablen einen Punkt zu setzen, damit die Liste der in dieser angegebenen Struktur enthaltenen Elemente angezeigt wird. Das ist ist ziemlich bequem. Sollten für die Felder der Struktur (wo wie in unserm Beispiel) einzeilige Anmerkungen vorhanden sein, werden diese in einem Tooltip auf der rechten Seite angezeigt.

Abb. 5a Liste der Strukturfelder für Position-Eigenschaften Abb. 5b Liste der Strukturfelder für Symbol-Eigenschaften

Abb. 5 Liste der Strukturfelder.

Noch ein wichtiger Punkt. Bei der Veränderung des Expert Advisors haben wir buchstäblich all seine globalen Variablen, die in vielen Funktionen verwendet werden, geändert, dher müssen wir sie jetzt durch die entsprechenden Strukturfeldern für Symbol- und Position-Eigenschaften ersetzen. So wurde z.B. die globale Variable pos_open, die zur Speicherung des Markers für geöffnete Position vorhanden/nicht vorhanden verwendet wurde, durch das Feld exists imposition_properties Strukturtyp ersetzt. Daher muss überall wo die Variable pos_open verwendet wurde, diese nun durch pos.exists ersetzt werden.

Wenn Sie dies per Hand machen müssen, dann dauert das extrem lange und ist sehr mühsam. Daher ist es besser diese Aufgabe mit Hilfe der MetaEditor Features: Suchen und Ersetzen -> Ersetzen im Menü Bearbeiten- oder der Tastenkombination Strg+H zu automatisieren:


Abb. 6 Den Text suchen und ersetzen

Abb. 6 Den Text suchen und ersetzen.

Wir müssen alle globalen Variablen für Position- und Symboleigenschaften suchen und ersetzen, um später, nachdem wir die Datei erstellt haben, einen Test laufen lassen zu können. Werden keine Fehler gemeldet, haben wir alles richtig gemacht. Damit dieser Beitrag nicht zu lang wird, stelle ich den Code hier nicht zur Verfügung. Denn ein einsatzbereiter Quellcode ist am Ende dieses Beitrags sowieso für eine Download angehängt.

Da wir jetzt das Problem mit den Variablen geregelt haben, gehen wir zur Änderung der bestehenden Funktionen und zur Erzeugung neuer weiter.

In den externen Parametern können Sie jetzt die magische Zahl und die Modulation in Punkten einrichten. Daher müssen wir auch die entsprechenden Veränderungen im Code des Expert Advisors vornehmen. Wir erzeugen eine benutzerdefinierte Hilfsfunktion OpenPosition(), in der diese Eigenschaften mit Hilfe der Funktionen der CTrade Klasse vor dem Abschicken einer Order für eine Position-Eröffnung eingerichtet werden.

//+------------------------------------------------------------------+
//| Opening a position                                               |
//+------------------------------------------------------------------+
void OpenPosition(double lot,
                  ENUM_ORDER_TYPE order_type,
                  double price,
                  double sl,
                  double tp,
                  string comment)
  {
   trade.SetExpertMagicNumber(MagicNumber); // Set the magic number in the trading structure
   trade.SetDeviationInPoints(CorrectValueBySymbolDigits(Deviation)); // Set the slippage in points
//--- 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())); }
  }

Im Code der Haupt-Handelsfunktion des Expert Advisors - TradingBlock() - müssen wir nur einige kleine Änderungen vornehmen. Unten steht der Teil des Codes der Funktion, der geändert wurde:

//--- If there is no position
   if(!pos.exists)
     {
      //--- Adjust the volume
      lot=CalculateLot(Lot);
      //--- Open a position
      OpenPosition(lot,order_type,position_open_price,sl,tp,comment);
     }
//--- If there is a position
   else
     {
      //--- Get the position type
      GetPositionProperties(P_TYPE);
      //--- If the position is opposite to the signal and the position reversal is enabled
      if(pos.type==opposite_position_type && Reverse)
        {
         //--- Get the position volume
         GetPositionProperties(P_VOLUME);
         //--- Adjust the volume
         lot=pos.volume+CalculateLot(Lot);
         //--- Reverse the position
         OpenPosition(lot,order_type,position_open_price,sl,tp,comment);
         return;
        }
      //--- If the signal is in the direction of the position and the volume increase is enabled, increase the position volume
      if(!(pos.type==opposite_position_type) && VolumeIncrease>0)
        {
         //--- Get the Stop Loss of the current position
         GetPositionProperties(P_SL);
         //--- Get the Take Profit of the current position
         GetPositionProperties(P_TP);
         //--- Adjust the volume
         lot=CalculateLot(Increase);
         //--- Increase the position volume
         OpenPosition(lot,order_type,position_open_price,pos.sl,pos.tp,comment);
         return;
        }

Der obige Code ist um den Block erweitert worden, in dem die Richtung der aktuellen Position vor dem Hintergrund der Richtung des Signals geprüft wird. Stimmen beide Richtungen überein und ist in den externen Parametern eine Erhöhung des Position-Volumens aktiviert (der Wert des VolumeIncrease Parameters ist > 0), prüfen und passen wir einen gegebenen Posten an und schicken die entsprechende Order ab. Um eine Order zur Eröffnung oder Umkehrung einer Position oder zur Erhöhung des Position-Volumens abzuschicken, müssen sie nur eine Codezeile schreiben.

Erzeugen wir nun Funktionen, um Position-Eigenschaften aus History der Abschlüsse zu erhalten. Wir beginnen dabei mit der CurrentPositionTotalDeals() Funktion, die die Anzahl der Abschlüsse in der aktuellen Position liefert:

//+------------------------------------------------------------------+
//| Returning the number of deals in the current position            |
//+------------------------------------------------------------------+
uint CurrentPositionTotalDeals()
  {
   int    total       =0;  // Total deals in the selected history list
   int    count       =0;  // Counter of deals by the position symbol
   string deal_symbol =""; // symbol of the deal
//--- If the position history is obtained
   if(HistorySelect(pos.time,TimeCurrent()))
     {
      //--- Get the number of deals in the obtained list
      total=HistoryDealsTotal();
      //--- Iterate over all the deals in the obtained list
      for(int i=0; i<total; i++)
        {
            //--- Get the symbol of the deal
            deal_symbol=HistoryDealGetString(HistoryDealGetTicket(i),DEAL_SYMBOL);
            //--- If the symbol of the deal and the current symbol are the same, increase the counter
            if(deal_symbol==_Symbol)
               count++;
        }
     }
//---
   return(count);
  }

Der obige Code steht mit reichlich detaillierten Anmerkungen zur Verfügung. Doch zuvor sollten wir kurz darauf eingehen, wie die History ausgewählt wird. In unserem Fall haben wir mit Hilfe der HistorySelect() Funktion die Liste vom Punkt der Eröffnung der aktuellen Position bekommen, bestimmt durch die Eröffnungszeit bis zum aktuellen Zeitpunkt Nachdem die History ausgewählt worden ist, können wir mit Hilfe der HistoryDealsTotal() Funktion die Anzahl der Abschlüsse in der Liste finden. Der Rest sollte anhand der Anmerkungen klar sein.

Mit Hilfe der HistorySelectByPosition() Funktion kann die History einer bestimmten Position auch via ihres Identifikators ausgewählt werden. Hier müssen Sie allerdings berücksichtigen, dass der Position-Identifikator derselbe bleibt, wenn die Position umgekehrt wird, da dies im Expert Advisor manchmal vorkommt. Bei einer solchen Umkehr verändert sich die Zeit der Eröffnung hingegen nicht, und daher ist diese Variante leichter zu implementieren. Wenn Sie allerdings mit der History der Abschlüsse arbeiten müssen, die nicht nur für die aktuell offene Position gilt, dann müssen Sie mit Identifikatoren arbeiten. Zur History der Abschlüsse kommen wir in späteren Beiträgen erneut.

Machen wir weiter, und erzeugen die Funktion CurrentPositionFirstDealPrice(), die den Kurs des ersten Abschlusses in der Position liefert, d.h. den Kurs des Abschlusses zu dem die Position eröffnet wurde.

//+------------------------------------------------------------------+
//| Returning the price of the first deal in the current position    |
//+------------------------------------------------------------------+
double CurrentPositionFirstDealPrice()
  {
   int      total       =0;    // Total deals in the selected history list
   string   deal_symbol ="";   // symbol of the deal
   double   deal_price  =0.0;  // Price of the deal
   datetime deal_time   =NULL; // Time of the deal
//--- If the position history is obtained
   if(HistorySelect(pos.time,TimeCurrent()))
     {
      //--- Get the number of deals in the obtained list
      total=HistoryDealsTotal();
      //--- Iterate over all the deals in the obtained list
      for(int i=0; i<total; i++)
        {
         //--- Get the price of the deal
         deal_price=HistoryDealGetDouble(HistoryDealGetTicket(i),DEAL_PRICE);
         //--- Get the symbol of the deal
         deal_symbol=HistoryDealGetString(HistoryDealGetTicket(i),DEAL_SYMBOL);
         //--- Get the time of the deal
         deal_time=(datetime)HistoryDealGetInteger(HistoryDealGetTicket(i),DEAL_TIME);
         //--- If the time of the deal equals the position opening time, 
         //    and if the symbol of the deal and the current symbol are the same, exit the loop
         if(deal_time==pos.time && deal_symbol==_Symbol)
            break;
        }
     }
//---
   return(deal_price);
  }

Das Prinzip hier ist das gleiche wie in der vorangegangenen Funktion. Wir bekommen die History vom Punkt der Position-Eröffnung und prüfen dann bei jeder Wiederholung die Uhrzeit des Abschlusses und die Eröffnungszeit der Position. Zusammen mit dem Kurs des Abschlusses, erhalten wir auch noch den Symbolnamen und den Zeitpunkt des Abschlusses. Der allererste Abschluss wird dort ermittelt, wo der Zeitpunkt des Abschlusses mit der Eröffnungszeit der Position zusammenfällt. Da ja sein Kurs bereits der entsprechenden Variable zugewiesen wurde, müssen wir den Wert nur noch liefern.

Machen wir also weiter. Manchmal müssen Sie vielleicht den Kurs des letzten Abschlusses in der aktuellen Position haben. Und zu diesem Zweck erzeugen wir eine CurrentPositionLastDealPrice() Funktion:

//+------------------------------------------------------------------+
//| Returning the price of the last deal in the current position     |
//+------------------------------------------------------------------+
double CurrentPositionLastDealPrice()
  {
   int    total       =0;   // Total deals in the selected history list
   string deal_symbol ="";  // Symbol of the deal 
   double deal_price  =0.0; // Price
//--- If the position history is obtained
   if(HistorySelect(pos.time,TimeCurrent()))
     {
      //--- Get the number of deals in the obtained list
      total=HistoryDealsTotal();
      //--- Iterate over all the deals in the obtained list from the last deal in the list to the first deal
      for(int i=total-1; i>=0; i--)
        {
         //--- Get the price of the deal
         deal_price=HistoryDealGetDouble(HistoryDealGetTicket(i),DEAL_PRICE);
         //--- Get the symbol of the deal
         deal_symbol=HistoryDealGetString(HistoryDealGetTicket(i),DEAL_SYMBOL);
         //--- If the symbol of the deal and the current symbol are the same, exit the loop
         if(deal_symbol==_Symbol)
            break;
        }
     }
//---
   return(deal_price);
  }

Diesmal hat die Zeitschleife beim letzten Abschluss in der Liste begonnen. Es kommt auch oft vor, dass der erforderliche Abschluss bei der ersten Wiederholung der Schleife identifiziert wird. Bevor Sie also auf mehreren Symbolen handeln, fährt die Schleife fort, bis das Symbol des Abschlusses mit dem aktuellen Symbol übereinstimmt.

Das aktuelle Position-Volumen erhält man mit Hilfe des POSITION_VOLUME Standard-Identifikators. Um das ursprüngliche Position-Volumen herauszufinden (das Volumen des ersten Abschlusses), erzeugen wir dazu eine CurrentPositionInitialVolume() Funktion:

//+------------------------------------------------------------------+
//| Returning the initial volume of the current position             |
//+------------------------------------------------------------------+
double CurrentPositionInitialVolume()
  {
   int             total       =0;           // Total deals in the selected history list
   ulong           ticket      =0;           // Ticket of the deal
   ENUM_DEAL_ENTRY deal_entry  =WRONG_VALUE; // Position modification method
   bool            inout       =false;       // Flag of position reversal
   double          sum_volume  =0.0;         // Counter of the aggregate volume of all deals, except for the first one
   double          deal_volume =0.0;         // Volume of the deal
   string          deal_symbol ="";          // Symbol of the deal 
   datetime        deal_time   =NULL;        // Deal execution time
//--- If the position history is obtained
   if(HistorySelect(pos.time,TimeCurrent()))
     {
      //--- Get the number of deals in the obtained list
      total=HistoryDealsTotal();
      //--- Iterate over all the deals in the obtained list from the last deal in the list to the first deal
      for(int i=total-1; i>=0; i--)
        {
         //--- If the order ticket by its position is obtained, then...
         if((ticket=HistoryDealGetTicket(i))>0)
           {
            //--- Get the volume of the deal
            deal_volume=HistoryDealGetDouble(ticket,DEAL_VOLUME);
            //--- Get the position modification method
            deal_entry=(ENUM_DEAL_ENTRY)HistoryDealGetInteger(ticket,DEAL_ENTRY);
            //--- Get the deal execution time
            deal_time=(datetime)HistoryDealGetInteger(ticket,DEAL_TIME);
            //--- Get the symbol of the deal
            deal_symbol=HistoryDealGetString(ticket,DEAL_SYMBOL);
            //--- When the deal execution time is less than or equal to the position opening time, exit the loop
            if(deal_time<=pos.time)
               break;
            //--- otherwise calculate the aggregate volume of deals by the position symbol, except for the first one
            if(deal_symbol==_Symbol)
               sum_volume+=deal_volume;
           }
        }
     }
//--- If the position modification method is a reversal
   if(deal_entry==DEAL_ENTRY_INOUT)
     {
      //--- If the position volume has been increased/decreased
      //    I.e. the number of deals is more than one
      if(fabs(sum_volume)>0)
        {
         //--- Current volume minus the volume of all deals except for the first one
         double result=pos.volume-sum_volume;
         //--- If the resulting value is greater than zero, return the result, otherwise return the current position volume         
         deal_volume=result>0 ? result : pos.volume;
        }
      //--- If there are no more deals, other than the entry,
      if(sum_volume==0)
         deal_volume=pos.volume; // return the current position volume
     }
//--- Return the initial position volume
   return(NormalizeDouble(deal_volume,2));
  }

Diese Funktion erwies sich leider als komplexer als die vorangegangenen. Ich habe nämlich versucht, alle möglichen Situationen zu berücksichtigen, die zu einem falschen wert führen könnten. Doch nach einem sorgfältigen Test konnte keine Probleme festgestellt werden. Die detaillierten Anmerkungen des Codes sollten Ihnen begreiflich machen, was ich meine.

Es empfiehlt sich auch, eine Funktion zu haben, die die Dauer der Position liefert. Diese ordnen wir so an, dass der Anwender das passende Format des gelieferten Wertes selbst auswählen kann: Sekunden, Minuten, Stunden oder Tage. Deswegen erzeugen wir nun eine weitere Aufzählung:

//--- Position duration
enum ENUM_POSITION_DURATION
  {
   DAYS     = 0, // Days
   HOURS    = 1, // Hours
   MINUTES  = 2, // Minutes
   SECONDS  = 3  // Seconds
  };

Unten steht der Code für die CurrentPositionDuration() Funktion, die für alle relevanten Berechnungen verantwortlich ist:

//+------------------------------------------------------------------+
//| Returning the duration of the current position                   |
//+------------------------------------------------------------------+
ulong CurrentPositionDuration(ENUM_POSITION_DURATION mode)
  {
   ulong     result=0;   // End result
   ulong     seconds=0;  // Number of seconds
//--- Calculate the position duration in seconds
   seconds=TimeCurrent()-pos.time;
//---
   switch(mode)
     {
      case DAYS      : result=seconds/(60*60*24);   break; // Calculate the number of days
      case HOURS     : result=seconds/(60*60);      break; // Calculate the number of hours
      case MINUTES   : result=seconds/60;           break; // Calculate the number of minutes
      case SECONDS   : result=seconds;              break; // No calculations (number of seconds)
      //---
      default        :
         Print(__FUNCTION__,"(): Unknown duration mode passed!");
         return(0);
     }
//--- Return result
   return(result);
  }

Erzeugen wir nun eine CurrentPositionDurationToString() Funktion für das Info-Panel, wo die Position-Eigenschaften angezeigt werden. Diese Funktion wandelt die Dauer der Position in Sekunden in ein Format um, das vom Anwender leicht verstanden werden kann. Die Anzahl der Sekunden wird an die Funktion übertragen und die Funktion ihrerseits liefert dann einen String mit der Dauer der Position in Tagen, Stunden, Minuten und Sekunden:

//+------------------------------------------------------------------+
//| Converting the position duration to a string                     |
//+------------------------------------------------------------------+
string CurrentPositionDurationToString(ulong time)
  {
//--- A dash if there is no position
   string result="-";
//--- If the position exists
   if(pos.exists)
     {
      //--- Variables for calculation results
      ulong days=0;
      ulong hours=0;
      ulong minutes=0;
      ulong seconds=0;
      //--- 
      seconds=time%60;
      time/=60;
      //---
      minutes=time%60;
      time/=60;
      //---
      hours=time%24;
      time/=24;
      //---
      days=time;
      //--- Generate a string in the specified format DD:HH:MM:SS
      result=StringFormat("%02u d: %02u h : %02u m : %02u s",days,hours,minutes,seconds);
     }
//--- Return result
   return(result);
  }

Jetzt ist alles eingerichtet und fertig. Die Codes der GetPositionProperties() und GetPropertyValue() Funktion, die in Übereinstimmung mit allen zuvor vorgenommenen Änderungen verändert werden müssen, biete ich hier nicht an. Wenn Sie alle vorangegangenen Beiträge dieser Reihe lesen, sollten Sie diese Änderungen ohne große Probleme selbst vornehmen können. Für alle Fälle ist die Datei mit dem Quellcode jedoch am Ende des Beitrags angehängt.

Als Ergebnis sollte das Info-Panel jetzt, wie unten gezeigt, erscheinen:

Abb. 7 Veranschaulichung aller Position-Eigenschaften auf dem Info-Panel

Abb. 7 Veranschaulichung aller Position-Eigenschaften auf dem Info-Panel.

Jetzt haben wir also die Funktions-Library, um die Position-Eigenschaften zu bekommen. Wahrscheinlich arbeiten wir in den folgenden Beiträgen, wann und wie erforderlich, weiter an ihnen.


Optimierung von Parametern und Testen des Expert Advisors

Versuchen wir mal, quasi als Experiment, die Parameter des Expert Advisors zu optimieren. Zwar ist das, was wir derzeit vor uns haben noch weit von einem voll funktionsfähigen Handelssystem entfernt, doch die Ergebnisse, die wir erhalten werden, öffnen uns die Augen in Bezug auf einige Punkte und bringen uns mehr Erfahrung als Entwickler von Handelssystemen.

Die Einstellungen des Strategietesters machen wir, wie unten gezeigt:

Abb. 8 Einstellungen des Strategietesters zur Optimierung von Parametern

Abb. 8 Einstellungen des Strategietesters zur Optimierung von Parametern.

Die Einstellungen der externen Parameter des Expert Advisors sollten folgendermaßen sein:

Abb. 9 Einstellungen der Expert Advisor-Parameter zur Optimierung

Abb. 9 Einstellungen der Expert Advisor-Parameter zur Optimierung.

Im Anschluss an die Optimierung, sortieren wir die erhaltenen Ergebnisse nach dem maximalen Rückflussfaktor

Abb. 10 Ergebnisse sortiert nach dem maximalen Rückflussfaktor

Abb. 10 Ergebnisse sortiert nach dem maximalen Rückflussfaktor.

Testen wir nun das alleroberste Parameter-Set, mit einem Rückflussfaktor-Wert von = 4,07. Selbst angesichts der Tatsache, dass die Optimierung für EURUSD durchgeführt worden ist, erkennen wir klar die positiven Ergebnisse für viele Symbole:

Ergebnisse für EURUSD:

Abb. 11 Ergebnisse für

Abb. 11 Ergebnisse für.

Ergebnisse für AUDUSD:

Abb. 12 Ergebnisse für AUDUSD

Abb. 12 Ergebnisse für AUDUSD.

Ergebnisse für NZDUSD:

Abb. 13 Ergebnisse für NZDUSD

Abb. 13 Ergebnisse für NZDUSD.


Fazit

Buchstäblich jede Idee kann entwickelt und verbessert werden. Jedes Handelssystem sollte erst sorgfältig getestet werden, bevor man es als fehlerhaft zurückweist. In den folgenden Beiträgen werden wir uns mit verschiedenen Mechanismen und Schemata beschäftigen, die bei der individuellen Konfiguration und Anpassung von fast jedem Handelssystem eine sehr positive Rolle spielen.

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

Beigefügte Dateien |
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.
MQL5 Cookbook: Handelsbedingungen mit Hilfe von Indikatoren in Experts Advisors einrichten MQL5 Cookbook: Handelsbedingungen mit Hilfe von Indikatoren in Experts Advisors einrichten
Auch in diesem Beitrag werden wir den Expert Advisor, den wir in allen vorangegangenen Beiträgen der MQL5 Cookbook Reihe bearbeitet haben, weiter verändern. Diesmal soll er durch Indikatoren verbessert werden 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.
MQL5 Cookbook: Position-Eigenschaften im MetaTrader 5 Strategietester analysieren MQL5 Cookbook: Position-Eigenschaften im MetaTrader 5 Strategietester analysieren
Wir präsentieren hier eine veränderte Version des Expert Advisors aus dem vorangegangenen Beitrag "MQL5 Cookbook: Position-Eigenschaften auf dem Angepassten Info-Panel". Einige der Themen, die wir ansprechen werden, sind: Daten von Bars bekommen, nach neuen Bar-Ereignissen auf dem aktuellen Symbol suchen, eine Handelsklasse der Standard-Library in eine Datei aufnehmen, eine Suchfunktion für Handelssignale und eine Funktion zur Ausführung von Handelsoperationen erzeugen sowie Handelsereignisse in der OnTrade() Funktion festlegen.
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.