Erstellen eines Expert Advisors, der mit verschiedenen Instrumenten handelt

9 März 2016, 11:17
Nikolay Kositsin
0
415

Einleitung

Die technische Seite der Implementierung des Programmcodes, damit ein einzelner Expert Advisor, der an ein einzelnes Diagramm angehängt wird, gleichzeitig mit unterschiedlichen Vermögenswerten handeln kann, war im Allgemeinen auch in MQL4 kein großes Problem. Doch erst mit dem MetaTrader 5 Client Terminal erhielten Händler endlich die Gelegenheit, die Arbeit solcher Mechanismen mithilfe von Strategietestern vollständig zu analysieren.

Also werden Mehrwährungsmechanismen jetzt beliebter denn je und wir können steigendes Interesse an der Erstellung solcher Handelssysteme prognostizieren. Das Hauptproblem bei der Implementierung solcher Roboter ist allerdings, dass ihre Ausmaße im Programmcode sich ausdehnen, und zwar im besten Fall in einer arithmetischen Progression, was für den typischen Programmierer nicht leicht zu erfassen ist.

In diesem Beitrag schreiben wir einen einfachen Expert Advisor für mehrere Währungen, in dem die strukturellen Mängel vielleicht nicht vollständig behoben, aber dennoch reduziert werden.


1. Umsetzung eines einfachen Systems zur Trendverfolgung

Im Grunde genommen, könnten wir mit einem möglichst einfachen Handelssystem beginnen, das den Trend basierend auf dem im Terminal eingebauten Indikator Triple Exponential Moving Average verfolgt. Dabei handelt es sich um einen äußerst simplen Algorithmus, der keine besonderen Kommentare benötigt und den wir jetzt in den Programmcode einbauen werden.

Doch zuallererst möchte ich die allgemeinsten Schlussfolgerungen über den Expert Advisor erläutern. Es ist sinnvoll, mit dem Block eingehender Parameter des Expert Advisors zu beginnen, deklariert auf globaler Ebene.

Also müssen wir zuerst die Vermögenswerte auswählen, mit denen wir arbeiten werden. Dies kann mithilfe der Zeileneingabevariablen bewerkstelligt werden, in denen die Symbole der Vermögenswerte gespeichert werden können. Nun wäre es schön, über einen Handelsverbotsschalter für jeden Vermögenswert zu verfügen. Damit könnten Handelstätigkeiten nach Vermögenswert deaktiviert werden.

Natürlich sollte jeder Vermögenswert mit seinen jeweiligen Handelsparametern Stop Loss, Take Profit, dem Volumen der offenen Position und Abweichungen verbunden werden. Und aus offensichtlichen Gründen müssen die Eingabeparameter des Indikators Triple Exponential Moving Average für jeden zu handelnden Vermögenswert individuell festgelegt sein.

Hier sehen Sie einen fertigen Block von Eingabevariablen für nur einen Vermögenswert, ausgeführt in Übereinstimmung mit diesen Überlegungen. Die verbleibenden Blöcke unterscheiden sich nur hinsichtlich der Zahlen in den Namen der Eingabeparameter des Expert Advisors. Für dieses Beispiel habe ich mich auf nur zwölf Vermögenswerte eingeschränkt, obwohl es idealerweise keine softwareseitigen Begrenzungen für die Menge solcher Blöcke gibt.

Wir brauchen nur etwas, womit wir handeln können! Und das Allerwichtigste: Unser PC muss über genügend Ressourcen verfügen, um dieses Problem zu lösen.

input string                Symb0 = "EURUSD";
input  bool                Trade0 = true; 
input int                    Per0 = 15; 
input ENUM_APPLIED_PRICE ApPrice0 = PRICE_CLOSE;
input int                 StLoss0 = 1000;
input int               TkProfit0 = 2000;
input double                Lots0 = 0.1;
input int               Slippage0 = 30;

Da wir nun die Variablen auf globaler Ebene kennen, können wir mit der Konstruktion des Codes innerhalb der Funktion OnTick() fortfahren. Die vernünftigste Option wäre in diesem Fall eine Aufteilung des Algorithmus zum Erhalten von Handelssignalen und des tatsächlichen handelnden Teils des Expert Advisors in zwei benutzerdefinierte Funktionen.

Und da der Expert Advisor mit zwölf Vermögenswerten gleichzeitig arbeitet, muss es auch zwölf Aufrufe dieser Funktionen innerhalb des OnTick()-Blocks geben. 

Natürlich sollte der erste Eingabeparameter dieser Funktionen eine eindeutige Zahl sein, unter der diese zu handelnden Vermögenswerte aufgelistet werden. Der zweite Eingabeparameter ist aus offensichtlichen Gründen der Zeilenname des zu handelnden Vermögenswerts.

Die Rolle des dritten Parameters erfüllt eine logische Variable, die den Handel auflöst. Als Nächstes folgen für den Algorithmus zum Bestimmen von Handelssignalen die Eingangssignale des Indikators und für die Handelsfunktion die Distanz zu den Pending Orders, das Volumen der Position und die Abweichung (zulässige Abweichung vom Eröffnungspreis der Position).

Für die Übertragung der Handelssignale von einer Funktion zur anderen sollten statische Arrays als Parameter der Funktion festgelegt werden, die ihre Werte per Verweis beziehen. Dies ist die finale Version des empfohlenen Codes für die OnTick()-Funktion.

void OnTick()
  {
//--- declare variables arrays for trade signals 
   static bool UpSignal[12], DnSignal[12], UpStop[12], DnStop[12];
  
//--- get trade signals
   TradeSignalCounter( 0, Symb0,  Trade0,  Per0,  ApPrice0,  UpSignal, DnSignal, UpStop, DnStop);
   TradeSignalCounter( 1, Symb1,  Trade1,  Per1,  ApPrice1,  UpSignal, DnSignal, UpStop, DnStop);
   TradeSignalCounter( 2, Symb2,  Trade2,  Per2,  ApPrice2,  UpSignal, DnSignal, UpStop, DnStop);
   TradeSignalCounter( 3, Symb3,  Trade3,  Per3,  ApPrice3,  UpSignal, DnSignal, UpStop, DnStop);
   TradeSignalCounter( 4, Symb4,  Trade4,  Per4,  ApPrice4,  UpSignal, DnSignal, UpStop, DnStop);
   TradeSignalCounter( 5, Symb5,  Trade5,  Per5,  ApPrice5,  UpSignal, DnSignal, UpStop, DnStop);
   TradeSignalCounter( 6, Symb6,  Trade6,  Per6,  ApPrice6,  UpSignal, DnSignal, UpStop, DnStop);
   TradeSignalCounter( 7, Symb7,  Trade7,  Per7,  ApPrice7,  UpSignal, DnSignal, UpStop, DnStop);
   TradeSignalCounter( 8, Symb8,  Trade8,  Per8,  ApPrice8,  UpSignal, DnSignal, UpStop, DnStop);
   TradeSignalCounter( 9, Symb9,  Trade9,  Per9,  ApPrice9,  UpSignal, DnSignal, UpStop, DnStop);
   TradeSignalCounter(10, Symb10, Trade10, Per10, ApPrice10, UpSignal, DnSignal, UpStop, DnStop);
   TradeSignalCounter(11, Symb11, Trade11, Per11, ApPrice11, UpSignal, DnSignal, UpStop, DnStop);
  
//--- perform trade operations
   TradePerformer( 0, Symb0,  Trade0,  StLoss0,  TkProfit0,  Lots0,  Slippage0,  UpSignal, DnSignal, UpStop, DnStop);
   TradePerformer( 1, Symb1,  Trade1,  StLoss1,  TkProfit1,  Lots1,  Slippage1,  UpSignal, DnSignal, UpStop, DnStop);
   TradePerformer( 2, Symb2,  Trade2,  StLoss2,  TkProfit2,  Lots2,  Slippage2,  UpSignal, DnSignal, UpStop, DnStop);
   TradePerformer( 3, Symb3,  Trade3,  StLoss3,  TkProfit3,  Lots3,  Slippage3,  UpSignal, DnSignal, UpStop, DnStop);
   TradePerformer( 4, Symb4,  Trade4,  StLoss4,  TkProfit4,  Lots4,  Slippage4,  UpSignal, DnSignal, UpStop, DnStop);
   TradePerformer( 5, Symb5,  Trade5,  StLoss5,  TkProfit5,  Lots5,  Slippage5,  UpSignal, DnSignal, UpStop, DnStop);
   TradePerformer( 6, Symb6,  Trade6,  StLoss6,  TkProfit6,  Lots6,  Slippage6,  UpSignal, DnSignal, UpStop, DnStop);
   TradePerformer( 7, Symb7,  Trade7,  StLoss7,  TkProfit7,  Lots7,  Slippage7,  UpSignal, DnSignal, UpStop, DnStop);
   TradePerformer( 8, Symb8,  Trade8,  StLoss8,  TkProfit8,  Lots8,  Slippage8,  UpSignal, DnSignal, UpStop, DnStop);
   TradePerformer( 9, Symb9,  Trade9,  StLoss9,  TkProfit9,  Lots9,  Slippage9,  UpSignal, DnSignal, UpStop, DnStop);
   TradePerformer(10, Symb10, Trade10, StLoss10, TkProfit10, Lots10, Slippage10, UpSignal, DnSignal, UpStop, DnStop);
   TradePerformer(11, Symb11, Trade11, StLoss11, TkProfit11, Lots11, Slippage11, UpSignal, DnSignal, UpStop, DnStop); 
//---
  }

Innerhalb der Funktion TradeSignalCounter() muss nur das Handle des technischen Indikators Triple Exponential Moving Average einmal beim Start jedes Vermögenswerts abgerufen werden und anschließend bei jeder Änderung des Balkens, um die Handelssignale zu berechnen.

Dieses relativ simple Schema zur Umsetzung im Code bekommt immer mehr kleine Details.

bool TradeSignalCounter(int Number,
                        string Symbol_,
                        bool Trade,
                        int period,
                        ENUM_APPLIED_PRICE ApPrice,
                        bool &UpSignal[],
                        bool &DnSignal[],
                        bool &UpStop[],
                        bool &DnStop[])
  {
//--- check if trade is prohibited
   if(!Trade)return(true);

//--- declare variable to store final size of variables arrays
   static int Size_=0;

//--- declare array to store handles of indicators as static variable
   static int Handle[];

   static int Recount[],MinBars[];
   double TEMA[4],dtema1,dtema2;

//--- initialization
   if(Number+1>Size_) // Entering the initialization block only on first start
     {
      Size_=Number+1; // For this number entering the block is prohibited

      //--- change size of variables arrays
      ArrayResize(Handle,Size_);
      ArrayResize(Recount,Size_);
      ArrayResize(MinBars,Size_);

      //--- determine minimum number of bars, sufficient for calculation 
      MinBars[Number]=3*period;

      //--- setting array elements to 0
      DnSignal[Number] = false;
      UpSignal[Number] = false;
      DnStop  [Number] = false;
      UpStop  [Number] = false;

      //--- use array as timeseries
      ArraySetAsSeries(TEMA,true);

      //--- get indicator's handle
      Handle[Number]=iTEMA(Symbol_,0,period,0,ApPrice);
     }

//--- check if number of bars is sufficient for calculation 
   if(Bars(Symbol_,0)<MinBars[Number])return(true);
//--- get trade signals 
   if(IsNewBar(Number,Symbol_,0) || Recount[Number]) // Entering the block on bar change or on failed copying of data
     {
      DnSignal[Number] = false;
      UpSignal[Number] = false;
      DnStop  [Number] = false;
      UpStop  [Number] = false;

      //--- using indicator's handles, copy values of indicator's
      //--- buffers into static array, specially prepared for this purpose
      if(CopyBuffer(Handle[Number],0,0,4,TEMA)<0)
        {
         Recount[Number]=true; // As data were not received, we should return 
                               // into this block (where trade signals are received) on next tick!
         return(false);        // Exiting the TradeSignalCounter() function without receiving trade signals
        }

      //--- all copy operations from indicator buffer are successfully completed
      Recount[Number]=false; // We may not return to this block until next change of bar

      int Digits_ = int(SymbolInfoInteger(Symbol_,SYMBOL_DIGITS)+4);
      dtema2 = NormalizeDouble(TEMA[2] - TEMA[3], Digits_);
      dtema1 = NormalizeDouble(TEMA[1] - TEMA[2], Digits_);

      //---- determining the input signals
      if(dtema2 > 0 && dtema1 < 0) DnSignal[Number] = true;
      if(dtema2 < 0 && dtema1 > 0) UpSignal[Number] = true;

      //---- determining the output signals
      if(dtema1 > 0) DnStop[Number] = true;
      if(dtema1 < 0) UpStop[Number] = true;
     }
//----+
   return(true);
  }

Diesbezüglich erweist sich der Code der Funktion TradePerformer() als ziemlich einfach:

bool TradePerformer(int    Number,
                    string Symbol_,
                    bool   Trade,
                    int    StLoss,
                    int    TkProfit,
                    double Lots,
                    int    Slippage,
                    bool  &UpSignal[],
                    bool  &DnSignal[],
                    bool  &UpStop[],
                    bool  &DnStop[])
  {
//--- check if trade is prohibited
   if(!Trade)return(true);

//--- close opened positions 
   if(UpStop[Number])BuyPositionClose(Symbol_,Slippage);
   if(DnStop[Number])SellPositionClose(Symbol_,Slippage);

//--- open new positions
   if(UpSignal[Number])
      if(BuyPositionOpen(Symbol_,Slippage,Lots,StLoss,TkProfit))
         UpSignal[Number]=false; //This trade signal will be no more on this bar!
//---
   if(DnSignal[Number])
      if(SellPositionOpen(Symbol_,Slippage,Lots,StLoss,TkProfit))
         DnSignal[Number]=false; //This trade signal will be no more on this bar!
//---
   return(true);
  }
Doch das liegt nur daran, dass die tatsächlichen Befehle für die Durchführung von Handelstätigkeiten in vier zusätzlichen Funktionen verpackt sind:
BuyPositionClose();
SellPositionClose();
BuyPositionOpen();
SellPositionOpen();

Alle vier Funktionen arbeiten absolut analog, also können wir uns auf nur eine davon beschränken:

bool BuyPositionClose(const string symbol,ulong deviation)
  {
//--- declare structures of trade request and result of trade request
   MqlTradeRequest request;
   MqlTradeResult result;
   ZeroMemory(request);
   ZeroMemory(result);

//--- check if there is BUY position
   if(PositionSelect(symbol))
     {
      if(PositionGetInteger(POSITION_TYPE)!=POSITION_TYPE_BUY) return(false);
     }
   else  return(false);

//--- initializing structure of the MqlTradeRequest to close BUY position
   request.type   = ORDER_TYPE_SELL;
   request.price  = SymbolInfoDouble(symbol, SYMBOL_BID);
   request.action = TRADE_ACTION_DEAL;
   request.symbol = symbol;
   request.volume = PositionGetDouble(POSITION_VOLUME);
   request.sl = 0.0;
   request.tp = 0.0;
   request.deviation=(deviation==ULONG_MAX) ? deviation : deviation;
   request.type_filling=ORDER_FILLING_FOK;
//---
   string word="";
   StringConcatenate(word,
                     "<<< ============ BuyPositionClose():   Close Buy position at ",
                     symbol," ============ >>>");
   Print(word);

//--- send order to close position to trade server
   if(!OrderSend(request,result))
     {
      Print(ResultRetcodeDescription(result.retcode));
      return(false);
     }
//----+
   return(true);
  }

Alles in allem ist das der komplette Expert Advisor für mehrere Währungen (Exp_TEMA.mq5)!

Abgesehen von den bereits betrachteten Funktionen beinhaltet er zwei weitere benutzerdefinierte Funktionen:

bool IsNewBar(int Number, string symbol, ENUM_TIMEFRAMES timeframe);
string ResultRetcodeDescription(int retcode);

Die erste dieser Funktionen gibt zum Zeitpunkt der Balkenänderung den Wert true basierend auf dem ausgewählten Symbol und Timeframe aus, die zweite gibt die Zeile nach dem Code des Ergebnisses der Handelstransaktion aus, abgeleitet aus dem Feld retcode der Handelsanfragestruktur MqlTradeResult

Der Expert Advisor ist fertig und es ist an der Zeit, ihn zu testen! Zwischen dem Testing des Expert Advisors für mehrere Währungen und des Expert Advisors für eine Währung bestehen keine ernst zu nehmenden Unterschiede.

Bestimmen Sie die Konfigurationen unter der Registerkarte "Settings" des Strategietesters:

Abbildung 1. Registerkarte "Settings" des Strategietesters

Abbildung 1. Registerkarte "Settings" des Strategietesters

Passen Sie die Werte der Eingabeparameter unter der Registerkarte "Inputs" an, falls erforderlich:

Abbildung 2. Registerkarte "Inputs" des Strategietesters

Abbildung 2. Registerkarte "Inputs" des Strategietesters

und klicken Sie dann in der Registerkarte "Settings" des Strategietesters auf die Schaltfläche "Start":

Abbildung 3. Durchführen des Expert-Advisor-Tests

Abbildung 3. Durchführen des Expert-Advisor-Tests

Da die Historie für alle zwölf Symbole geladen wird, kann der erste Test des Expert Advisors eine beträchtliche Durchlaufzeit haben. Öffnen Sie nach dem Abschluss des Tests im Strategietester die Registerkarte "Results":

Abbildung 4. Testergebnisse

Abbildung 4. Testergebnisse

und führen Sie mithilfe der Optionen unter der Registerkarte "Graph" eine Analyse der Daten durch:

Abbildung 5. Diagramm der Saldendynamik und des Eigenkapitals

Abbildung 5. Diagramm der Saldendynamik und des Eigenkapitals

und das "Journal" (Logbuch):

Abbildung 6. Logbuch des Strategietesters

Abbildung 6. Logbuch des Strategietesters

Der eigentliche Sinn hinter dem Algorithmus der Marktein- und -austritte dieses Expert Advisors ist natürlich zu einfach und es wäre naiv, bedeutende Ergebnisse zu erwarten, wenn man die ersten, beliebig gewählten Parameter nutzt. Doch im Moment ist es unser Ziel, die grundlegende Idee hinter der möglichst einfachen Erstellung eines Expert Advisors für mehrere Währungen vorzuführen.

Mit der Optimierung dieses Expert Advisors können aufgrund von zu vielen Eingabeparametern Unannehmlichkeiten entstehen. Der erbliche Algorithmus der Optimierung benötigt eine viel kleinere Menge dieser Parameter, also sollte der Expert Advisor für jeden Vermögenswert individuell optimiert werden, wobei die verbleibenden Vermögenswerte durch die Eingabeparameter TradeN deaktiviert werden.

Nun, da der eigentliche Sinn dieser Herangehensweise erläutert wurde, können Sie anfangen, mit dem interessanteren Algorithmus der Entscheidungsfindung für den Roboter für den Handel mit mehreren Währungen zu arbeiten.


2. Resonanzen auf den Finanzmärkten und ihre Anwendung in Handelssystemen

Die Idee, die Zusammenhänge zwischen den verschiedenen Vermögenswerten zu berücksichtigen, ist im Allgemeinen nicht neu. Es wäre interessant, einen Algorithmus einzusetzen, der genau auf der Analyse solcher Trends basiert. In diesem Beitrag implementiere ich einen Mehrwährungsmechanismus auf Basis des Artikels "Resonanzen – eine neue Klasse von technischen Indikatoren" von Vasilij Jakimkin, veröffentlicht in der Fachzeitschrift "Valutny Spekulant" (Russisch) 04, 05, 2001.

Die Essenz dieser Herangehensweise sieht kurz gefasst wie folgt aus. Zum Beispiel nutzen wir für die Untersuchung des Marktes für EUR/USD nicht nur die Ergebnisse von einigen Indikatoren für diesen Vermögenswert, sondern auch die Ergebnisse des gleichen Indikators zu Vermögenswerten, die mit EUR/USD zusammenhängen, nämlich EUR/JPY und USD/JPY. Am besten sollte ein Indikator genutzt werden, dessen Werte im gleichen Änderungsbereich normalisiert sind, um die Messungen und Berechnungen zu vereinfachen.

Unter Berücksichtigung dieser Anforderungen ist der klassische stochastische Indikator gut für unsere Zwecke geeignet, auch wenn es in der Realität keinen Unterschied im Gebrauch anderer Indikatoren gibt. Als Trendrichtung betrachten wir das Vorzeichen der Differenz zwischen dem Wert des stochastischen Stoh und dessen Signallinie Sign.

Abbildung 7. Bestimmung der Trendrichtung

Abbildung 7. Bestimmung der Trendrichtung

Für das Symbol der Variable dStoh existiert eine ganze Tabelle von möglichen Kombinationen und deren Interpretationsmöglichkeiten für die Richtung des aktuellen Trends:

Abbildung 8. Kombinationen des Symbols der Variable dStoh und Richtung des Trends

Abbildung 8. Kombinationen des Symbols der Variable dStoh und Richtung des Trends

In Fällen, in denen zwei Signale der Vermögenswerte EUR/JPY und USD/JPY entgegengesetzte Werte haben, sollten wir deren Summe berechnen. Ist die Summe größer als Null, können beide Signale als positiv betrachtet werden, andernfalls als negativ.

Nutzen Sie also für das Eröffnen von Longs Situationen, in denen der Trend steigt und für den Austritt einen Abwärtstrend oder einen Trend, bei dem die Signale des Indikators der Hauptvermögenswerts EUR/USD negativ sind. Verlassen Sie die Long außerdem, wenn der Hauptvermögenswert keine Signale hat und wenn die Summe der Variable dStoh für die verbleibenden Vermögenswerte weniger als Null beträgt. Für Shorts ist alles analog, nur die Situation ist genau umgekehrt.

Es wäre am sinnvollsten, den gesamten analytischen Teil des Expert Advisors im Mehrwährungsindikator zu platzieren und für den Expert Advisor aus den Indikatorpuffern nur die fertigen Signale für die Steuerung des Handels zu nutzen. Eine Variante dieses Indikatortypen wird im Indikator MultiStochastic.mq5 bereitgestellt und bietet visuelle Analysen der Marktbedingungen.

Abbildung 9. Indikator MultiStochastic

Abbildung 9. Indikator MultiStochastic

Die grünen Balken signalisieren das Eröffnen und Halten von Longs und die roten das von Shorts. Rosafarbene und hellblaue Punkte am oberen Rand des Diagramms stehen für Signale für das Verlassen der Long und Short Positions.

Dieser Indikator kann direkt für das Abrufen von Signalen im Expert Advisor genutzt werden, doch es wäre trotzdem besser, seine Arbeit zu erleichtern und sämtliche unnötigen Puffer und Visualisierungselemente zu entfernen, sodass nur übrig bleibt, was direkt an der Versorgung mit Handelssignalen beteiligt ist. Genau das wurde im Indikator MultiStochastic_Exp.mq5 getan.

In diesem Expert Advisor handelte ich mit nur drei Vermögenswerten, sodass der Code der OnTick()-Funktion nahezu primitiv wurde:

void OnTick()
  {
//--- declare variables arrays for trade signals
  static bool UpSignal[], DnSignal[], UpStop[], DnStop[];
  
//--- get trade signals
  TradeSignalCounter(0, Trade0, Kperiod0, Dperiod0, slowing0, ma_method0, price_0, SymbolA0, SymbolB0, SymbolC0, UpSignal, DnSignal, UpStop, DnStop);
  TradeSignalCounter(1, Trade1, Kperiod1, Dperiod1, slowing1, ma_method1, price_1, SymbolA1, SymbolB1, SymbolC1, UpSignal, DnSignal, UpStop, DnStop);
  TradeSignalCounter(2, Trade2, Kperiod2, Dperiod2, slowing2, ma_method2, price_2, SymbolA2, SymbolB2, SymbolC2, UpSignal, DnSignal, UpStop, DnStop);
                             
//--- perform trade operations
   TradePerformer( 0, SymbolA0,  Trade0,  StopLoss0,  0,  Lots0,  Slippage0,  UpSignal, DnSignal, UpStop, DnStop);
   TradePerformer( 1, SymbolA1,  Trade1,  StopLoss1,  0,  Lots1,  Slippage1,  UpSignal, DnSignal, UpStop, DnStop);
   TradePerformer( 2, SymbolA2,  Trade2,  StopLoss2,  0,  Lots2,  Slippage2,  UpSignal, DnSignal, UpStop, DnStop);
//---
  }

Der Code der Funktion TradeSignalCounter() ist allerdings etwas komplexer: Das liegt daran, dass ein Mehrwährungsindikator direkt mit drei Zeitreihen von verschiedenen Vermögenswerten arbeitet. Deshalb nutzen wir eine subtilere Überprüfung der Balken auf die Hinlänglichkeit ihrer Mindestmenge in einer der drei Zeitreihen mithilfe der Funktion Rates_Total().

Zudem wird eine zusätzliche Überprüfung der Synchronisation der Zeitreihen mithilfe der Funktion SynchroCheck() durchgeführt, um die Genauigkeit der Bestimmung des Zeitpunkts, zu dem in allen Zeitreihen gleichzeitig eine Balkenänderung stattfindet, zu gewährleisten.

bool TradeSignalCounter(int Number,
                        bool Trade,
                        int Kperiod,
                        int Dperiod,
                        int slowing,
                        ENUM_MA_METHOD ma_method,
                        ENUM_STO_PRICE price_,
                        string SymbolA,
                        string SymbolB,
                        string SymbolC,
                        bool &UpSignal[],
                        bool &DnSignal[],
                        bool &UpStop[],
                        bool &DnStop[])
  {
//--- check if trade is prohibited
   if(!Trade)return(true);
//--- declare variable to store sizes of variables arrays
   static int Size_=0;
//--- declare arrays to store handles of indicators as static variables
   static int Handle[];
   static int Recount[],MinBars[];
//---
   double dUpSignal_[1],dDnSignal_[1],dUpStop_[1],dDnStop_[1];
//--- change size of variables arrays
   if(Number+1>Size_)
     {
      uint size=Number+1;
      //----
      if(ArrayResize(Handle,size)==-1
         || ArrayResize(Recount,size)==-1
         || ArrayResize(UpSignal, size) == -1
         || ArrayResize(DnSignal, size) == -1
         || ArrayResize(UpStop, size) == -1
         || ArrayResize(DnStop, size) == -1
         || ArrayResize(MinBars,size) == -1)
        {
         string word="";
         StringConcatenate(word,"TradeSignalCounter( ",Number,
                           " ): Error!!! Unable to change sizes of variables arrays!!!");
         int error=GetLastError();
         ResetLastError();
         //---
         if(error>4000)
           {
            StringConcatenate(word,"TradeSignalCounter( ",Number," ): Error code ",error);
            Print(word);
           }
         Size_=-2;
         return(false);
        }

      Size_=int(size);
      Recount[Number] = false;
      MinBars[Number] = Kperiod + Dperiod + slowing;

      //--- get indicator's handle
      Handle[Number]=iCustom(SymbolA,0,"MultiStochastic_Exp",
                             Kperiod,Dperiod,slowing,ma_method,price_,
                             SymbolA,SymbolB,SymbolC);
     }
//--- check if number of bars is sufficient for calculation 
   if(Rates_Total(SymbolA,SymbolB,SymbolC)<MinBars[Number])return(true);
//--- check timeseries synchronization
   if(!SynchroCheck(SymbolA,SymbolB,SymbolC))return(true);
//--- get trade signals 
   if(IsNewBar(Number,SymbolA,0) || Recount[Number])
     {
      DnSignal[Number] = false;
      UpSignal[Number] = false;
      DnStop  [Number] = false;
      UpStop  [Number] = false;

      //--- using indicators' handles, copy values of indicator's 
      //--- buffers into static arrays, specially prepared for this purpose
      if(CopyBuffer(Handle[Number], 1, 1, 1, dDnSignal_) < 0){Recount[Number] = true; return(false);}
      if(CopyBuffer(Handle[Number], 2, 1, 1, dUpSignal_) < 0){Recount[Number] = true; return(false);}
      if(CopyBuffer(Handle[Number], 3, 1, 1, dDnStop_  ) < 0){Recount[Number] = true; return(false);}
      if(CopyBuffer(Handle[Number], 4, 1, 1, dUpStop_  ) < 0){Recount[Number] = true; return(false);}

      //--- convert obtained values into values of logic variables of trade commands
      if(dDnSignal_[0] == 300)DnSignal[Number] = true;
      if(dUpSignal_[0] == 300)UpSignal[Number] = true;
      if(dDnStop_  [0] == 300)DnStop  [Number] = true;
      if(dUpStop_  [0] == 300)UpStop  [Number] = true;

      //--- all copy operations from indicator's buffers completed successfully
      //--- unnecessary to return into this block until next bar change
      Recount[Number]=false;
     }
//----+
   return(true);
  }

Es gibt keine weiteren bedeutenden ideologischen Unterschiede im Code dieses Expert Advisors (Exp_ResonanceHunter.mq5), da er auf Basis derselben funktionalen Komponenten kompiliert wird. Deshalb glaube ich nicht, dass es nötig ist, noch mehr Zeit mit seiner internen Struktur zu verbringen.


Fazit

Meiner Meinung nach entspricht der Code des Expert Advisors für mehrere Währungen in MQL5 vollständig dem Code eines herkömmlichen Expert Advisors.


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

Beigefügte Dateien |
multistochastic.mq5 (17.47 KB)
exp_tema.mq5 (25.17 KB)
Analyse von Kerzenmustern Analyse von Kerzenmustern

Die Konstruktion japanischer Kerzendiagramme und die Analyse von Kerzenmustern sind ein erstaunlicher Bereich der technischen Analyse. Der Vorteil von Kerzen ist, dass sie Daten auf eine Art darstellen, dank der Sie die Dynamiken innerhalb der Daten verfolgen können. In diesem Beitrag analysieren wir Arten von Kerzen, klassifizieren Kerzenmuster und stellen einen Indikator vor, der Kerzenmuster bestimmen kann.

Kontrolle der Saldo-Gefällekurve während der Arbeit eines Expert Advisors Kontrolle der Saldo-Gefällekurve während der Arbeit eines Expert Advisors

Regeln für ein Handelssystem zu finden und sie in einen Expert Advisor zu programmieren, ist nur die Hälfte der Arbeit. Irgendwie muss man ja auch die Abläufe des Expert Adivsors kontrollieren, während er die Ergebnisse des Handels anhäuft. Dieser Beitrag beschreibt einen Ansatz, der die Leistung eines Expert Advisors durch Erzeugung eines Feedbacks steigert, das die Saldo-Gefällekurve misst.

Test-Performance der Berechnung von gleitenden Mittelwerten in MQL5 Test-Performance der Berechnung von gleitenden Mittelwerten in MQL5

Seit der Erstellung des ersten Moving-Average-Indikators ist eine Vielzahl von Indikatoren erschienen. Viele von ihnen nutzen ähnliche Glättungsmethoden, doch die Glättung der unterschiedlichen Algorithmen von gleitenden Mittelwerten wurde nie eingehend studiert. In diesem Beitrag betrachten wir Möglichkeiten zur Nutzung der gleitenden Mittelwerte in MQL5 und vergleichen ihre Performance.

Der Einsatz von MQL5 Standard Library Handelsklassen beim Schreiben eines Expert Advisors Der Einsatz von MQL5 Standard Library Handelsklassen beim Schreiben eines Expert Advisors

Dieser Beitrag beschreibt die Verwendung der Hauptfunktionalitäten der MQL5 Standard Library Handelsklassen beim Schreiben des Expert Advisors, die das Schließen und die Änderung von Positions, Platzierung und Löschung von pending Orders sowie die Prüfung nach Margen vor dem Platzieren eines Handels implementieren. Es wird auch gezeigt, wie man mit Hilfe von Handelsklassen Details zu Orders und Abschlüssen bekommen kann.