English Русский 中文 Español 日本語 Português
preview
Nutzerdefinierte Symbole: Praktische Grundlagen

Nutzerdefinierte Symbole: Praktische Grundlagen

MetaTrader 5Tester | 3 Dezember 2020, 08:18
905 0
Stanislav Korotky
Stanislav Korotky

Der MetaTrader 5 bietet die Möglichkeit, benutzerdefinierte Symbole mit eigenen Kursen und Ticks zu erstellen. Auf diese kann sowohl von der Terminalschnittstelle als auch auf Programmebene über MQL API zugegriffen werden. Nutzerdefinierte Symbole werden auf Standard-Charts angezeigt, was die Anwendung von Indikatoren, die Markierung mit Objekten und sogar die Erstellung von Handelsstrategien auf der Grundlage dieser Symbole ermöglicht.

Nutzerdefinierte Symbole können reale, von Brokern oder externen Ressourcen bereitgestellte Kurse als Datenquellen verwenden. In diesem Artikel werden wir mehrere populäre Methoden zur Transformation von Arbeitssymbolen betrachten, die Händlern zusätzliche Analysewerkzeuge zur Verfügung stellen:

  • Chart mit gleichem Volumen und gleicher Spanne
  • Tick-Charts
  • Zeitverschiebung von Kursen bei der Umwandlung von Kerzenformen
  • Renko

Darüber hinaus werden wir einen Mechanismus zur Anpassung der Expert Advisors an den Handel mit einem realen Symbol entwickeln, das mit einem abgeleiteten benutzerdefinierten Symbol verbunden ist, auf dessen Chart der EA läuft.

In diesem Artikel verwenden Charts mit Quellen-(Standard-)Symbolen einen schwarzen Hintergrund und Charts mit nutzerdefinierten Symbolen einen weißen Hintergrund.

Charts mit gleichem Volumen/Spanne

Ein Chart mit gleichen Volumina ist ein Balkenchart, das auf dem Prinzip der Gleichheit des in Balken eingeschlossenen Volumens basiert. Bei einem regulären Chart wird jeder neue Balken in bestimmten Intervallen gebildet, die der Größe des Zeitrahmens entsprechen. Bei einem Chart mit gleichem Volumen gilt jeder Balken als gebildet, wenn die Summe der Ticks oder des realen Volumens einen voreingestellten Wert erreicht. Danach beginnt das Programm mit der Berechnung des nächsten Balkens. Natürlich werden bei der Berechnung der Volumina auch die Preisbewegungen gesteuert, und so erhalten Sie die üblichen vier Preiswerte im Chart: Eröffnung, Hoch, Tief und Schlusskurs.

Obwohl die horizontale Achse im Chart mit gleichen Volumina immer noch eine Chronologie bedeutet, sind die Zeitstempel der einzelnen Balken willkürlich und hängen von der Volatilität (Anzahl oder Größe der Abschlüsse) in jeder Zeitperiode ab. Viele Händler sind der Ansicht, dass diese Balkenbildungsmethode eine adäquatere Beschreibung eines sich verändernden Marktes im Vergleich zu einem konstanten Zeitrahmenwert bietet.

Leider bieten weder MetaTrader 4 noch MetaTrader 5 gleichwertige, vorkonfigurierte Volumencharts. Sie sollten auf eine besondere Art und Weise erstellt werden.

In MetaTrader 4 kann dies mit Hilfe von Offline-Charts erfolgen. Diese Methode wurde in dem Artikel Äquivolume Charting Revisited beschrieben.

Derselbe Algorithmus kann in MetaTrader 5 unter Verwendung benutzerdefinierter Symbole implementiert werden. Um die Aufgabe zu vereinfachen, verwenden wir einen nicht-handelnden Expert Advisor aus dem angegebenen Artikel und passen ihn an die MQL-API von MetaTrader 5 an.

Die ursprüngliche Datei EqualVolumeBars.mq4 wurde in EqualVolumeBars.mq5 umbenannt und leicht modifiziert. Insbesondere wurden die 'externen' Schlüsselwörter, die Eingabeparameter beschreiben, durch 'input' ersetzt. Anstelle von zwei Parametern, StartYear und StartMonth, wird ein Parameter StartDate verwendet. Der CustomPeriod, der in MetaTrader 4 verwendet wurde, um einen nicht standardmäßigen Zeitrahmen festzulegen, wird jetzt nicht mehr benötigt und wurde daher gelöscht.

Achten Sie darauf, dass die Volumina von MetaTrader 4 alle Tick-Volumina sind, d.h. sie stellen die Anzahl der Ticks (Preisänderungen) in einem Balken dar. Die ursprüngliche Idee war, M1-Balken (mit ihren Tick-Volumina) oder eine externe csv-Datei mit Ticks, die von einem anderen Broker zur Verfügung gestellt wurden, zu verarbeiten, die eingehenden Ticks pro Zeiteinheit zu zählen und einen neuen Balken mit gleichem Volumen zu bilden, sobald die angegebene Anzahl von Ticks erreicht ist. Die Balken wurden in eine hst-Datei geschrieben, die in MetaTrader 4 als Offline-Chart geöffnet werden konnte.

Der Codeteil, der sich auf das Lesen einer csv-Datei und auf das Schreiben einer hst-Datei bezieht, wird in MetaTrader 5 nicht benötigt. Stattdessen können wir mit Hilfe der nutzerdefinierten Symbol-API echte Tick-Historie und Form-Balken lesen. Darüber hinaus ermöglicht es MetaTrader 5 Brokern, reale Volumina und Ticks zu liefern (für Börseninstrumente, aber sie sind normalerweise nicht für Forex-Instrumente verfügbar). Wenn dieser Modus aktiviert ist, können Äquivolumenbalken nicht nach der Anzahl der Ticks, sondern nach realen Volumina aufgebaut werden.

Der Eingabeparameter FromM1 bestimmt, ob der EA M1-Balken ("true" als Standard) oder die Tick-Historie ("false") verarbeitet. Wählen Sie beim Start der Tick-Verarbeitung nicht eine zu weit zurückliegende Vergangenheit als Anfang, da dies viel Zeit und Plattenplatz beanspruchen kann. Wenn Sie bereits mit der Tick-Historie gearbeitet haben, dann kennen Sie Ihre PC-Fähigkeiten und die verfügbaren Ressourcen.

Balken gleicher Spanne werden auf ähnliche Weise dargestellt. Hier öffnet sich jedoch ein neuer Balken, wenn der Kurs eine bestimmte Anzahl von Punkten überschreitet. Bitte beachten Sie, dass diese Balken nur im Tick-Modus (FromM1 == falsch) verfügbar sind.

Chart type — EqualTickVolumes, EqualRealVolumes, RangeBars — wird durch den Eingabeparameter WorkMode festgelegt.

Der bequemste Weg, mit nutzerdefinierten Symbolen zu arbeiten, ist die Verwendung der Symbolbibliothek (durch fxsaber). Sie kann über die Direktive #include mit einem Expert Advisor eingebunden werden:

  #include <Symbol.mqh>

Jetzt können wir ein nutzerdefiniertes Symbol basierend auf dem aktuellen Chart-Symbol erstellen. Dies geschieht wie folgt:

  if(!SymbolSelect(symbolName, true))
  {
    const SYMBOL Symb(symbolName);
    Symb.CloneProperties(_Symbol);
    
    if(!SymbolSelect(symbolName, true))
    {
      Alert("Can't select symbol:", symbolName, " err:", GetLastError());
      return INIT_FAILED;
    }
  }

wobei symbolName eine Zeichenfolge mit dem nutzerdefinierten Symbolnamen ist.

Dieses Initialisierungsfragment und viele andere Hilfsaufgaben im Zusammenhang mit der Verwaltung benutzerdefinierter Symbole (insbesondere das Zurücksetzen einer bestehenden Historie, das Öffnen eines Charts mit einem neuen benutzerdefinierten Symbol) werden in allen Programmen auf ähnliche Weise durchgeführt. Sie können die entsprechenden Quellcodes in der Anlage unten einsehen. Ich werde sie in diesem Artikel weglassen, da sie von untergeordneter Bedeutung sind.

Wenn ein neuer äquivolumiger Balken erscheint oder der aktuelle sich ändert, wird die Funktion WriteToFile aufgerufen. Diese Funktion wird durch den Aufruf von CustomRatesUpdate in MetaTrader 5 implementiert:

  void WriteToFile(datetime t, double o, double l, double h, double c, long v, long m = 0)
  {
    MqlRates r[1];
    
    r[0].time = t;
    r[0].open = o;
    r[0].low = l;
    r[0].high = h;
    r[0].close = c;
    r[0].tick_volume = v;
    r[0].spread = 0;
    r[0].real_volume = m;
    
    int code = CustomRatesUpdate(symbolName, r);
    if(code < 1)
    {
      Print("CustomRatesUpdate failed: ", GetLastError());
    }
  }

Überraschenderweise ist die Schleife über die M1-Balken (FromM1 = true mode) fast derselbe wie in der MQL4-Version, was bedeutet, dass wir durch einfache Anpassung der Funktion WriteToFile einen funktionalen MQL5-Code für M1-Balken erhalten können. Der einzige Teil, der geändert werden muss, ist die Erzeugung von Ticks im RefreshWindow. In MetaTrader 4 geschah dies durch das Senden von Windows-Nachrichten zur Emulation von Ticks in einem Offline-Chart. In MetaTrader 5 wird die Funktion CustomTicksAdd verwendet:

  void RefreshWindow(const datetime t)
  {
    MqlTick ta[1];
    SymbolInfoTick(_Symbol, ta[0]);
    ta[0].time = t;
    ta[0].time_msc = ta[0].time * 1000;
    if(CustomTicksAdd(symbolName, ta) == -1)
    {
      Print("CustomTicksAdd failed:", GetLastError(), " ", (long) ta[0].time);
      ArrayPrint(ta);
    }
  }

Die Tick-Generierung ruft das OnTick-Ereignis auf benutzerdefinierten Symbolcharts auf, wodurch Expertenberater, die auf solchen Charts laufen, potenziell handeln können. Diese Technologie erfordert jedoch einige zusätzliche Aktionen, die wir später betrachten werden.

Der Modus, in dem äquivolumige Balken aus der Tick-Historie generiert werden (FromM1 = falsch), ist etwas komplizierter. Dies erfordert das Lesen echter Ticks unter Verwendung der Standardfunktionen CopyTicks/CopyTicksRange. All diese Funktionalität ist in der Klasse TicksBuffer implementiert.

  #define TICKS_ARRAY 10000
  
  class TicksBuffer
  {
    private:
      MqlTick array[];
      int tick;
    
    public:
      bool fill(ulong &cursor, const bool history = false)
      {
        int size = history ? CopyTicks(_Symbol, array, COPY_TICKS_ALL, cursor, TICKS_ARRAY) : CopyTicksRange(_Symbol, array, COPY_TICKS_ALL, cursor);
        if(size == -1)
        {
          Print("CopyTicks failed: ", GetLastError());
          return false;
        }
        else if(size == 0)
        {
          if(history) Print("End of CopyTicks at ", (datetime)(cursor / 1000));
          return false;
        }
        
        cursor = array[size - 1].time_msc + 1;
        tick = 0;
      
        return true;
      }
      
      bool read(MqlTick &t)
      {
        if(tick < ArraySize(array))
        {
          t = array[tick++];
          return true;
        }
        return false;
      }
  };

Ticks werden in der Methode 'fill' in Fragmenten von TICKS_ARRAY angefordert und dann zu 'array' hinzugefügt, von dem dann von der Methode 'read' eines nach dem anderen gelesen wird. Die Methode implementiert den Algorithmus für die Arbeit mit der Tick-History ähnlich dem von M1-History-Balken (der vollständige Quellcode ist im Anhang enthalten).

    TicksBuffer tb;
    
    while(tb.fill(cursor, true) && !IsStopped())
    {
      MqlTick t;
      while(tb.read(t))
      {
        ...
        // New or first bar
        if(IsNewBar() || now_volume < 1)
        {
          WriteToFile(...);
        }
      }
    }

Jedes Mal, wenn der Expert Advisor startet, löscht er mit der Funktion Reset die vorhandene Historie des angegebenen nutzerdefinierten Symbols, falls es existiert. Falls erforderlich, kann dieses Verhalten verbessert werden, indem die Historie gespeichert wird und an der Position, an der die vorherige Erzeugung endete, weiterhin Balken erzeugt werden.

Sie können den Quellcode von EqualVolumeBars.mq4 und des resultierenden EqualVolumeBars.mq5 vergleichen.

Sehen wir uns an, wie der neue Expert Advisor funktioniert. Dies ist das EURUSD H1 Chart, auf dem der EA läuft:

Der Expert Advisor EqualVolumeBars auf dem EURUSD H1 Chart in MetaTrader 5

Der Expert Advisor EqualVolumeBars auf dem EURUSD H1 Chart in MetaTrader 5

Unten sehen Sie das von der EA erstellte äquivolumige Chart, in dem jeder Balken aus 1000 Ticks besteht.

EURUSD Äquivolumen Chart mit 1000 Ticks pro Balken, erstellt von der EqualVolumeBars EA in MetaTrader 5

EURUSD Äquivolumen Chart mit 1000 Ticks pro Balken, erstellt von der EqualVolumeBars EA in MetaTrader 5

Beachten Sie, dass die Tick-Volumina aller Balken gleich sind, mit Ausnahme des letzten, der sich noch im Aufbau befindet (Tick-Zählung läuft weiter).

Sehen wir uns eine andere Betriebsart an - Chart gleicher Spannen. Unten sehen Sie ein Chart, das aus Balken mit einem Swing von 100 Punkten besteht.

EURUSD Chart mit der Spanne von 100 Punkten pro Balken, erzeugt von den EqualVolumeBars EA in MetaTrader 5

EURUSD Chart mit der Spanne von 100 Punkten pro Balken, erzeugt von den EqualVolumeBars EA in MetaTrader 5

Der EA erlaubt auch die Verwendung des Realvolumenmodus für Austauschinstrumente:

Der LKOH Original Chart mit dem realen Volumen von 10000 pro Barren, generiert kaufen die EqualVolumeBars EA in MetaTrader 5

Das LKOH Chart mit gleichen Volumina mit dem realen Volumen von 10000 pro Balken, generiert durch den EqualVolumeBars EA in MetaTrader 5

Das ursprüngliche (a) und das äquivolumige (b) LKOH Chart mit dem realen Volumen von 10000 pro Balken, erzeugt durch den EqualVolumeBars EA in MetaTrader 5

Der Zeitrahmen des Symbols, auf dem der EA läuft, ist nicht wichtig, da für die Berechnungen immer entweder M1-Balken oder Tick-Historie verwendet werden.

Der Zeitrahmen der nutzerdefinierten Symbolcharts muss gleich M1 sein (der kleinste verfügbare Zeitrahmen im Terminal). Daher entspricht die Zeit der Balken in der Regel eng mit ihrem Entstehungsmoment. Bei starken Marktbewegungen jedoch, wenn die Anzahl der Ticks oder die Größe der Volumen mehrere Balken pro Minute bildet, ist die Zeit der Balken den realen voraus. Wenn sich der Markt beruhigt, werden sich die Zeitmarken der Balken mit gleichem Volumen wieder normalisieren. Diese Plattformbegrenzung ist wahrscheinlich nicht besonders kritisch für Balken mit gleichem Volumen oder gleicher Spanne, da die eigentliche Idee solcher Charts darin besteht, sie von der absoluten Zeit loszubinden.

Tick Charts

Der Tick Chart in MetaTrader 5 ist im Market Watch Fenster verfügbar. Aus irgendeinem Grund unterscheidet sich seine Implementierung von regulären Charts. Es zeigt eine begrenzte Anzahl von Ticks an (soweit ich weiß, bis zu 2000), es ist klein und kann nicht auf den Vollbildschirm erweitert werden, und es fehlen alle Möglichkeiten, die normalerweise von Standard-Charts geboten werden, wie z.B. die Möglichkeit, Indikatoren, Objekte und Expert Advisors zu verwenden.

Tick-Chart im MetaTrader 5 Market Watch Fenster

Tick-Chart im MetaTrader 5 Market Watch Fenster

Warum werden also die Standardanalyse-Tools nicht für Ticks unterstützt, während MetaTrader 5 native Unterstützung für echte Ticks bietet und Hochfrequenzhandelsfunktionen (HFT) bietet? Einige Händler betrachten Ticks als zu kleine Einheiten oder sogar als Rauschen. Andere Händler versuchen, Gewinne mit den Ticks zu erzielen. Daher könnte es nützlich sein, Ticks in einem Standard-Chart mit der Möglichkeit zur Skalierung, zur Anwendung von Vorlagen und zur Verwendung von Expert Advisors anzuzeigen. Dies kann mit Hilfe der nutzerdefinierten Symbolfunktionalität implementiert werden.

Auch hier können wir bereits bekannte MQL-Funktionen wie CopyTicks und CustomRatesUpdate verwenden. Mit ihnen können wir leicht einen nicht handelsüblichen Expert Advisor implementieren, der ein nutzerdefiniertes Symbol auf der Grundlage des aktuellen Chart-Symbols generiert. Dabei ist jeder M1-Balken in der nutzerdefinierten Symbolhistorie ein separates Häkchen. Ein Beispiel für einen solchen Quellcode ist unten in der Datei Ticks2Bars.mq5 beigefügt. Wenn Sie beispielsweise den Expert Advisor auf dem EURUSD-Chart ausführen (beliebiger Zeitrahmen), wird das Symbol EURUSD_ticks erstellt.

Die Eingaben des EA folgen:

  • Limit — die Anzahl der nach dem Start erstellten Balken (Ticks), Standard ist 1000; wenn Sie 0 einstellen, wird die Online-Tick-Erzeugung ohne vorherige Historie gestartet.
  • Reset — die Option setzt den vorherigen Tick/Balken-Verlauf nach dem Start zurück, standardmäßig auf true gesetzt.
  • LoopBack — die Option aktiviert einen Ringpuffermodus, in dem Ticks (mit Indizes höher als Limit) aus dem internen Array der "Kurse" herausgeschoben werden, wenn neue Ticks am Anfang hinzugefügt werden, so dass der Chart immer Limit-Balken hat; standardmäßig auf true gesetzt; wenn LoopBack aktiviert ist, muss der Limit-Parameter größer als 0 sein; wenn LoopBack deaktiviert ist, wird das Array ständig erweitert und die Anzahl der Balken im Chart erhöht.
  • EmulateTicks — die Option emuliert neue Tick-Ankunftsereignisse für ein nutzerdefiniertes Symbol, was eine wichtige Funktion für Expert Advisor-Aufrufe ist; standardmäßig auf true gesetzt.
  • RenderBars — Balken/Tick-Anzeigemethode: OHLC oder HighLow; standardmäßig OHLC; in diesem Modus ist ein Tick ein voll ausgestatteter Balken mit einem Körper (high = ask, low = bid, last (falls verfügbar) = close; wenn last = 0, dann sind open und close gleich einem der entsprechenden high oder low, abhängig von der Kursbewegungsrichtung seit dem vorherigen Tick; im HighLow-Modus werden Ticks als Pin-Balken angezeigt, wobei high = ask, low = bid, open = close = (ask + bid) / 2.

Die Hauptoperationen werden von der Funktion 'apply' ausgeführt:

  bool apply(const datetime cursor, const MqlTick &t, MqlRates &r)
  {
    static MqlTick p;
    
    // eliminate strange things
    if(t.ask == 0 || t.bid == 0 || t.ask < t.bid) return false;
    
    r.high = t.ask;
    r.low = t.bid;
    
    if(t.last != 0)
    {
      if(RenderBars == OHLC)
      {
        if(t.last > p.last)
        {
          r.open = r.low;
          r.close = r.high;
        }
        else
        {
          r.open = r.high;
          r.close = r.low;
        }
      }
      else
      {
        r.open = r.close = (r.high + r.low) / 2;
      }
      
      if(t.last < t.bid) r.low = t.last;
      if(t.last > t.ask) r.high = t.last;
      r.close = t.last;
    }
    else
    {
      if(RenderBars == OHLC)
      {
        if((t.ask + t.bid) / 2 > (p.ask + p.bid) / 2)
        {
          r.open = r.low;
          r.close = r.high;
        }
        else
        {
          r.open = r.high;
          r.close = r.low;
        }
      }
      else
      {
        r.open = r.close = (r.high + r.low) / 2;
      }
    }
    
    r.time = cursor;
    r.spread = (int)((t.ask - t.bid)/_Point);
    r.tick_volume = 1;
    r.real_volume = (long)t.volume;
  
    p = t;
    return true;
  }

In dieser Funktion werden die Felder der Struktur MqlTick für den aktuellen Zeitpunkt 'Cursor' in die Felder der MqlRates-Struktur portiert, die dann in die Historie geschrieben wird.

Die untere Abbildung zeigt ein Chart aus Tick-Bars eines nutzerdefinierten Symbols (zum Vergleich ist ein Standard-Tick-Chart dargestellt):

Vollständig ausgestatteter EURUSD Tick-Chart in MetaTrader 5

Vollständig ausgestatteter EURUSD Tick-Chart in MetaTrader 5

Dies ist der Chart, in dem wir Indikatoren, Objekte oder Experten verwenden können, um die Tick-Analyse und den Handel zu automatisieren.

Achten Sie darauf, dass die Zeiten der Balken im Chart der Tick-Balken fiktiv sind. Im Modus LoopBack hat der letzte Balken immer die aktuelle Zeit auf eine Minute genau, und die vorhergehenden Balken sind mit jeweils einem 1-Minuten-Schritt entfernt (dies ist die minimale Zeitrahmengröße in MetaTrader 5). Wenn der LoopBack-Modus deaktiviert ist, werden die Balkenzeiten um 1 Minute erhöht, beginnend mit der Expertenstartzeit, so dass alle Barren über dem Anfangslimit in einem virtuellen Future liegen.

Der ganz rechte M1-Balken entspricht jedoch dem letzten Tick und dem aktuellen "Schlusskurs" (oder "last"). Dies ermöglicht den Handel mit solchen Charts unter Verwendung von Expert Advisors, sowohl online als auch im Tester. Um online arbeiten zu können, muss der EA leicht modifiziert werden, da er in der Lage sein muss, das ursprüngliche XY-Symbol aus dem Symbol-Chart "XY_ticks" zu handeln (nutzerdefinierte Symbole existieren nur im Terminal und sind auf dem Server nicht bekannt). Im obigen Beispiel sollte "EURUSD_ticks" in allen Handelsaufträgen durch "EURUSD" ersetzt werden.

Wenn ein Expert Advisor Handelssignale von Indikatoren empfängt, dann kann es ausreichen, ihre Instanzen auf dem nutzerdefinierten Symbol-Chart anstelle des aktuellen Arbeitssymbols zu erstellen und diesen EA auf diesem Arbeitssymbol-Chart laufen zu lassen. Aber diese Methode ist nicht immer anwendbar. Eine weitere Methode zur Anpassung von Expert Advisors für den Handel mit nutzerdefinierten Symbolen wird weiter unten beschrieben.

Eine gewisse Schwierigkeit bei der Arbeit mit Tick Charts hängt mit der Tatsache zusammen, dass sie sehr schnell aktualisiert werden. Aus diesem Grund ist es fast unmöglich, die Charts manuell zu analysieren und zu markieren - alles muss mit Hilfe von Indikatoren oder Skripten automatisiert werden.

Der vorgestellte Ansatz mit "Tick-Kursen" ermöglicht das Testen von Skalierungsstrategien ohne spezielle Datenakkumulation in internen Puffern und die Berechnung von Signalen auf der Grundlage der Puffer, während wir einfach gewöhnliche Indikatoren oder Objekte verwenden können.

Zeitverschiebung und Metamorphose von Kerzen

Viele Händler verwenden Kerzenmuster in ihrer Praxis als Haupt- oder Zusatzsignal. Diese Methode ist visuell informativ, hat aber einen gewichtigen Nachteil.

Kerzenmuster beschreiben eine vordefinierte Geometrie einer Folge von Balken. Alle Stäbe werden gebildet, wenn sich der Preis im Laufe der Zeit ändert. Die Zeit ist jedoch von Natur aus kontinuierlich, obwohl Charts die Zeit künstlich in Segmente unterteilt darstellen, die den Balken entsprechen und auf eine bestimmte Zeitzone (vom Broker gewählt) ausgerichtet sind. Wenn Sie beispielsweise ein H1-Balkendiagramm um einige Minuten (sagen wir 15 Minuten) verschieben, wird sich die Geometrie der Balken höchstwahrscheinlich ändern. Infolgedessen können früher bestehende Muster vollständig verschwinden, und an anderen Stellen werden sich neue Muster bilden. Die Preisaktion ist jedoch die gleiche.

Wenn Sie sich einige beliebte Kerzenmuster ansehen, können Sie leicht erkennen, dass sie durch ähnliche Preisbewegungen gebildet werden, und der Unterschied in ihrem Aussehen wird durch die Startzeit der Balkenberechnung verursacht. Wenn Sie beispielsweise die Zeitachse um einen halben Balken verschieben, dann kann sich ein "hammer" in ein "piercing" verwandeln, und ein "hangman" kann sich in eine "dark cloud cover" verwandeln. Je nach Verschiebungswert und lokalen Preisänderungen kann sich das Muster auch in ein "bearishsch" oder "bullishsch engulfing" verwandeln. Wenn Sie zu einem niedrigeren Zeitrahmen wechseln, können sich alle oben genannten Muster in einen "morning star" oder einen "evening star" verwandeln.

Mit anderen Worten, jedes Muster ist eine Funktion des Beginns der Zeit- und Skalenberechnung. Insbesondere können wir, indem wir den Startpunkt ändern, eine Figur erkennen und alle anderen, die ihr entsprechen, abfangen.

Umkehrmuster (die bei Händlern sehr beliebt sind, da sie es ermöglichen, den Beginn einer Bewegung zu bestimmen) können in der folgenden vereinfachten Form dargestellt werden:

Preisumkehrungen und äquivalente Kerzenstrukturen

Preisumkehrungen und äquivalente Kerzenstrukturen

Die Abbildung zeigt eine schematische Darstellung einer Umkehrung nach oben und einer Umkehrung nach unten, jeweils mit zwei Balkenkonfigurationsvarianten. Wir haben bereits gesehen, dass es mehr Muster mit ähnlicher Bedeutung geben kann. Es wäre nicht sinnvoll, sie alle zu verfolgen. Eine bequemere Lösung wäre die Möglichkeit, Kerzenmuster für jeden beliebigen Zeitpunkt zu Beginn zu bestimmen.

Darüber hinaus kann die Zeitumwandlung eine akzeptable Verschiebung relativ zur GMT für eine andere Zeitzone darstellen, was theoretisch bedeutet, dass diese neuen Kerzenmuster auch in diesem neuen Bereich funktionieren sollten, so wie sie sich in unserer Zeitzone gebildet haben. Da sich die Handelsserver überall auf der Welt und in allen Zeitzonen befinden, können die Händler irgendwo ganz sicher absolut unterschiedliche Signale sehen. Jedes Signal ist wertvoll.

Wir können zu dem Schluss kommen, dass die Anhänger von Kerzenmustern deren Variabilität je nach Ausgangspunkt berücksichtigen sollten. Hier bieten sich nutzerdefinierte Symbole an.

Ein funktionierendes, symbolbasiertes, nutzerdefiniertes Signal erlaubt es, Balken und Ticks mit Zeitstempeln zu bilden, die um den angegebenen Wert in die Zukunft oder in die Vergangenheit verschoben sind. Dieser Verschiebungswert kann als Teil eines Balkens eines beliebigen ausgewählten Zeitrahmens interpretiert werden. Die Preise und die Bewegung ändern sich nicht, aber dies kann dennoch ein interessantes Ergebnis liefern. Erstens ermöglicht es das Erkennen von Kerzenmustern, die ohne eine solche Verschiebung unbemerkt bleiben würden. Zweitens können wir tatsächlich einen unvollständigen Balken vor uns sehen.

Wenn Sie beispielsweise die Kurse um 5 Minuten nach vorne verschieben, werden die Balken in einem M15-Chart um ein Drittel früher geöffnet und geschlossen als der Ausgangs-Chart (an der 10., 25., 40., 55. Minute einer Stunde). Wenn die Verschiebung unbedeutend ist, dann sind die Muster im ursprünglichen und nutzerdefinierten Chart fast identisch, aber die Signale (berechnet durch Balken, einschließlich Indikatorsignale) aus dem nutzerdefinierten Chart kommen früher.

Die Erstellung eines solchen zeitverschobenen,. nutzerdefinierten Symbols ist im TimeShift.mq5 Expert Advisor implementiert.

Der Verschiebungswert wird im Quellparameter Shift (in Sekunden) angegeben. Der EA arbeitet mit Ticks, die es ermöglichen, beim Start die Historie der transformierten Kurse zu berechnen, beginnend mit dem im Startparameter angegebenen Datum. Dann werden Ticks online verarbeitet, wenn der OnTick-Ereigniserzeugungsmodus aktiviert ist, für den der Parameter EmulateTicks vorgesehen ist (standardmäßig true).

Die Zeitkonvertierung erfolgt in einem einfachen Modus, der für historische Ticks und für Online-Ticks gilt: Im letzteren Fall wird z.B. die Funktion "add" verwendet:

  ulong lastTick;
  
  void add()
  {
    MqlTick array[];
    int size = CopyTicksRange(_Symbol, array, COPY_TICKS_ALL, lastTick + 1, LONG_MAX);
    if(size > 0)
    {
      lastTick = array[size - 1].time_msc;
      for(int i = 0; i < size; i++)
      {
        array[i].time += Shift;
        array[i].time_msc += Shift * 1000;
      }
      if(CustomTicksAdd(symbolName, array) == -1)
      {
        Print("Tick error: ", GetLastError());
      }
    }
  }
  
  void OnTick(void)
  {
    ...
    if(EmulateTicks)
    {
      add();
    }
  }

Die Quelle und die modifizierten EURUSD H1 Charts sind unten dargestellt.

EURUSD H1 Chart mit dem TimeShift Expert Advisor

EURUSD H1 Chart mit dem TimeShift Expert Advisor

Nach einer Verschiebung von einem halben Balken (30 Minuten) ändert sich das Bild.

Das EURUSD H1 nutzerdefinierte Chart mit einer halbstündigen Verschiebung

Das EURUSD H1 nutzerdefinierte Chart mit einer halbstündigen Verschiebung

Diejenigen, die mit Kerzenmustern gut vertraut sind, werden mit Sicherheit viele Unterschiede bemerken, einschließlich neuer Signale, die auf dem ursprünglichen Chart nicht vorhanden waren. Wenn wir also einen gewöhnlichen Kerzen-Indikator auf ein nutzerdefiniertes Symbol-Chart anwenden, können wir doppelt so viele Warnungen und Handelsmöglichkeiten erhalten. Der Prozess könnte sogar mit Hilfe von Expert Advisors automatisiert werden, aber hier müssen wir dem EA beibringen, mit einem echten Symbol aus einem nutzerdefinierten Symbol-Chart zu handeln. Diese Aufgabe wird am Ende des Artikels behandelt. Betrachten wir nun einen weiteren nutzerdefinierten Charttyp, der wahrscheinlich der bekanntesten ist - Renko.

Renko

Der nicht-handelnde Expert Advisor RenkoTicks.mq5 wird zur Implementierung des Renko-Charts verwendet. Der EA generiert Renko als nutzerdefinierte Kurse mit Symbolen während der Verarbeitung echter Ticks (erhältlich in MetaTrader 5 bei Ihrem Broker). Wir können alle Quell-Symbol-Kurse (Balken) und den Zeitrahmen des Arbeits-Chart verwenden, auf dem RenkoTicks läuft.

Bei der Generierung von Renko könnte eine Alternative zu nutzerdefinierten Symbolen ein Indikator oder eine Zeichnung (unter Verwendung von Objekten oder auf einer Leinwand) sein, aber in beiden Fällen wäre es unmöglich, Indikatoren oder Skripte auf den resultierenden Pseudo-Charts zu verwenden.

Alle Renko-Balken werden auf dem M1-Zeitrahmen gebildet. Dies geschieht absichtlich, da Renko-Balken manchmal sehr schnell hintereinander gebildet werden können, zum Beispiel während Zeiten hoher Volatilität, und die Zeit zwischen den Balken sollte so kurz wie möglich sein. Eine Minute ist der kleinst mögliche Abstand, der in MetaTrader 5 unterstützt wird. Aus diesem Grund sollte der Renko-Chart immer den M1-Zeitrahmen haben. Es hat keinen Sinn, das Renko-Chart auf einen anderen Zeitrahmen umzustellen. Der Zeitpunkt des Beginns jedes 1-Minuten-Balkens entspricht dem Zeitpunkt, zu dem die Bildung des entsprechenden Renko-Balkens beginnt. Die Zeit des Abschlusses eines solchen Minutenbalkens ist gefälscht, und Sie sollten stattdessen den Beginn des nächsten 1-Minuten-Balkens überprüfen.

Leider müssen manchmal mehrere Renko-Balken innerhalb einer Minute gebildet werden. Da MetaTrader 5 dies nicht zulässt, erzeugt der EA Balken als Sequenzen benachbarter M1-Balken, wobei die Anzahl der Balken jede Minute künstlich erhöht wird. Infolgedessen kann es sein, dass die formale Zeit der Renko-Balken nicht mit der tatsächlichen Zeit übereinstimmt (kann ihr voraus sein). Bei einer Renko-Größe von 100 Pips hätte z.B. eine Bewegung von 300 Pips, die um 12:00:00 Uhr stattfand und 10 Sekunden dauerte, Renko-Balken um 12:00:00, 12:00:05, 12:00:10 erzeugt. Stattdessen wird der EA Balken um 12:00, 12:01, 12:02 erzeugen.

Wenn dies in der Kurshistorie geschieht, kann folgendes Problem auftreten: Solche aus der Vergangenheit übertragenen Renko-Balken überschneiden sich mit anderen Balken, die aus den späteren Balken des ursprünglichen Charts gebildet werden. Angenommen, um 12:02 Uhr findet eine weitere Bewegung von 100 Punkten statt, dann müssen wir einen Renko-Balken mit einer Eröffnungszeit von 12:02 Uhr erzeugen, aber diesmal ist bereits besetzt! Um solche Konflikte zu lösen, hat der Expert Advisor einen speziellen Modus mit einer erzwungenen Erhöhung der Zeit des nächsten gebildeten Balkens um 1 Minute, wenn die erforderliche Zählung bereits belegt ist. Dieser Modus wird durch den Parameter SkipOverflows eingestellt, der standardmäßig auf false gesetzt ist (die Balken überlappen sich nicht, sondern bewegen sich gegebenenfalls in die Zukunft). Wenn SkipOverflows wahr ist, überschreiben sich die Balken mit überlappenden Zeiten gegenseitig, und der resultierende Renko ist nicht völlig korrekt.

Bitte beachten Sie, dass eine solche Situation mit einer starken Bewegung und der Erzeugung mehrerer "vorauseilender" Balken in Echtzeit möglich ist - in diesem Fall bilden sich die Balken tatsächlich in der Zukunft! In unserem Beispiel werden Renko-Balken mit den Öffnungszeiten 12:00, 12:01, 12:02 um 12:00:10 existieren! Dies sollte bei Analyse und Handel berücksichtigt werden.

Es gibt eine Reihe von Möglichkeiten, dieses Problem zu lösen, z.B. die Vergrößerung des Renko-Balken. Es hat jedoch einen offensichtlichen Nachteil - dies wird Renkos Genauigkeit verringern, d. h. es wird rauhere Kursbewegungen registrieren und weniger Balken erzeugen. Eine andere Möglichkeit besteht darin, ältere Balken zu packen (nach links zu verschieben), aber dafür müssen die Indikatoren oder Objekte neu gezeichnet werden.

Aufgrund der spezifischen Eigenschaften der Plattform generiert der EA fiktive Ticks mit einer Zeit, die der Öffnungszeit des letzten Renko-Balkens entspricht. Ihr einziger Zweck besteht darin, den die Funktion OnTick in einem handelnden Expert Advisor zu starten. Würden Ticks ohne Änderungen vom ursprünglichen Symbol in ein nutzerdefiniertes Symbol übersetzt, würde dies Renkos Struktur selbst verderben. Auch hier können wir die starke Bewegung als Beispiel nehmen und versuchen, an einen Renko-Chart einen Tick mit der tatsächlichen Zeit um 12:00:00 Uhr zu senden. Aber die Zeit dieses Ticks wird nicht dem letzten (aktuellen) Balken 0 entsprechen, sondern dem Balken 2 mit der Eröffnungszeit 12:00 Uhr. Infolgedessen verdirbt ein solcher Tick den Renko-Balken von 12:00 (der sich in der Historie befindet) oder führt zu einem Fehler. Renko kann durch eine entgegengesetzte Situation verdorben werden, wenn der Satz zu langsam ist. Wenn Kurse für längere Zeit im Bereich eines Balkens liegen, bleibt der Renko-Balken bei der gleichen Eröffnungszeit, während neue Ticks eine Zeit von mehr als einer Minute größer als der 0. Renko-Balken haben können. Wenn solche Ticks an einen Renko-Chart gesendet werden, würde dies Phantombalken in der "Zukunft" bilden.

Beachten Sie, dass historische Renko-Ticks in einem minimalistischen Stil gebildet werden, 1 Tick pro Kasten. Wenn Sie online arbeiten, werden alle Ticks an Renko gesendet.

Ähnlich wie bei anderen nutzerdefinierten Symbolen ermöglicht uns dieser Ansatz die Verwendung beliebiger Indikatoren, Skripte und Objekte in Renko-Charts sowie den Handel durch Expert Advisors.

Hauptparameter

  • RenkoBoxSize — Renko-Balkengröße in Punkten, standardmäßig 100.
  • ShowWicks — Flag, ob der Docht gezeigt wird, standardmäßig true.
  • EmulateOnLineChart — Flag für das Senden der Ticks, standardmäßig true.
  • OutputSymbolName — nutzerdefinierter Symbolname für den generierten Renko, standardmäßig eine leere Zeichenfolge — der Name wird als "Symbol_T_Type_Size" gebildet, wobei Symbol das aktuelle Arbeitssymbol ist, T ist das Tick-Modus-Zeichen, Typ — "r" (Renko) mit Docht oder "b" (Block) ohne Docht, Größe — RenkoBoxSize; Beispiel: "EURUSD_T_r100".
  • Reset — Flag für die Neuberechnung des gesamten Renko-Chart, standardmäßig auf false gesetzt. Wenn Sie es auf true setzen, empfiehlt es sich, auf das Ergebnis zu warten und es wieder auf false zurückzusetzen, um eine Neuberechnung bei jedem Terminal-Neustart zu vermeiden. Dieser Modus ist nützlich, wenn die Erzeugung von Renko-Balken an irgendeiner Position fehlgeschlagen ist. Normalerweise ist die Option immer deaktiviert, da der EA die Berechnung ab dem letzten verfügbaren Renko-Balken fortsetzen kann.
  • StartFrom, StopAt — Beginn und Ende des Historienzeitraums; standardmäßig werden Nullen verwendet, was bedeutet, dass die gesamte verfügbare Historie verwendet wird. Bei der ersten Verwendung des EA wird empfohlen, StartFrom auf die jüngste Vergangenheit zu setzen, um die Systemgeschwindigkeit bei der Erzeugung von Renko-Balken durch echte Ticks zu bewerten.
  • SkipOverflows — Flag für die Verarbeitung von Box-Überlappungskonflikten; standardmäßig auf false gesetzt, was bedeutet, dass die neue Balkenzeit zwangsweise um 1 Minute erhöht wird, wenn die erforderliche Berechnung bereits durch die vorherige Box belegt ist.
  • CloseTimeMode — wenn true, werden die Kästchen vollständig zum Schlusszeitpunkt gebildet (ein "Tick"-Ereignis pro Kästchen); standardmäßig auf false gesetzt.

Die Renko-Klasse behandelt den Tick-Strom und erzeugt auf seiner Grundlage neue Renko-Balken. Ihre Hauptkomponenten sind im folgenden Pseudocode angegeben:

  class Renko
  {
    protected:
      bool incrementTime(const datetime time);
      void doWriteStruct(const datetime dtTime, const double dOpen, const double dHigh, const double dLow, const double dClose, const double dVol, const double dRealVol, const int spread);
      
    public:
      datetime checkEnding();
      void continueFrom(const datetime time);
      void doReset();
  
      void onTick(const MqlTick &t);
      void updateChartWindow(const double bid = 0, const double ask = 0);
  };

Die geschützten Methoden incrementTime bzw. doWriteStruct wechseln zur nächsten freien, der angegebenen Zeit am nächsten liegenden M1-Probe für die nächste Renko-Box und schreiben den Balken selbst mit dem Aufruf CustomRatesUpdate. Die ersten drei Methoden im 'public' Teil sind für die Initialisierung des Algorithmus beim Start verantwortlich. Der Expert Advisor kann das Vorhandensein früherer Renko-Kurse überprüfen (dies geschieht durch die Methode checkEnding, die das Enddatum und die Endzeit der Historie zurückgibt), und je nachdem, ob diese vorhanden sind oder nicht, fährt der EA entweder ab dem angegebenen Zeitpunkt mit der Methode continueFrom fort (Wiederherstellung der Werte interner Variablen), oder er verwendet doReset, um Ticks aus einem "leeren" Zustand zu behandeln.

Die Methode onTick wird bei jedem Tick aufgerufen (sowohl in der Historie als auch online) und bildet, falls erforderlich, einen Renko-Balken mit doWriteStruct (ich habe einen Algorithmus aus dem berühmten RenkoLiveChart.mq4 EA mit einigen Korrekturen verwendet). Wenn in den EA-Einstellungen eine Tick-Emulation angegeben ist, wird zusätzlich das updateChartWindow aufgerufen. Die vollständigen Quellcodes sind unten angehängt.

Die Klasse TickProvider ist für die "Lieferung" von Ticks an das Renko-Objekt verantwortlich:

  class TickProvider
  {
    public:
      virtual bool hasNext() = 0;
      virtual void getTick(MqlTick &t) = 0;
  
      bool read(Renko &r)
      {
        while(hasNext() && !IsStopped())
        {
          MqlTick t;
          getTick(t);
          r.onTick(t);
        }
        
        return IsStopped();
      }
  };

Es ist abstrakt, da es eine gemeinsame Schnittstelle für das Lesen/Empfangen von Ticks aus zwei verschiedenen Quellen deklariert: die Tick-Historie des Basissymbols beim EA und die OnTick Event-Warteschlange bei der Online-Arbeit. Die Methode 'read' ist eine universelle Tick-Schleife, die die virtuellen Methoden hasNext() und getTick() verwendet.

Die Tick-Historie wird in der Klasse HistoryTickProvider auf bekannte Weise gelesen: Sie verwendet CopyTicksRange und den Zwischenpuffer MqlTick array[], in dem Ticks tageweise angefordert werden:

  class HistoryTickProvider : public TickProvider
  {
    private:
      datetime start;
      datetime stop;
      ulong length;     // in seconds
      MqlTick array[];
      int size;
      int cursor;
      
      int numberOfDays;
      int daysCount;
      
    protected:
      void fillArray()
      {
        cursor = 0;
        do
        {
          size = CopyTicksRange(_Symbol, array, COPY_TICKS_ALL, start * 1000, MathMin(start + length, stop) * 1000);
          Comment("Processing: ", DoubleToString(daysCount * 100.0 / (numberOfDays + 1), 0), "% ", TTSM(start));
          if(size == -1)
          {
            Print("CopyTicksRange failed: ", GetLastError());
          }
          else
          {
            if(size > 0 && array[0].time_msc < start * 1000) // prevent older than requested data returned
            {
              start = stop;
              size = 0;
            }
            else
            {
              start = (datetime)MathMin(start + length, stop);
              if(size > 0) daysCount++;
            }
          }
        }
        while(size == 0 && start < stop);
      }
    
    public:
      HistoryTickProvider(const datetime from, const long secs, const datetime to = 0): start(from), stop(to), length(secs), cursor(0), size(0)
      {
        if(stop == 0) stop = TimeCurrent();
        numberOfDays = (int)((stop - start) / DAY_LONG);
        daysCount = 0;
        fillArray();
      }
  
      bool hasNext() override
      {
        return cursor < size;
      }
  
      void getTick(MqlTick &t) override
      {
        if(cursor < size)
        {
          t = array[cursor++];
          if(cursor == size)
          {
            fillArray();
          }
        }
      }
  };

Die Klasse CurrentTickProvider Online-Tick-Provider:

  class CurrentTickProvider : public TickProvider
  {
    private:
      bool ready;
      
    public:
      bool hasNext() override
      {
        ready = !ready;
        return ready;
      }
      
      void getTick(MqlTick &t) override
      {
        SymbolInfoTick(_Symbol, t);
      }
  };

Der Hauptteil der Tick-Verarbeitung sieht in Kurzform wie folgt aus:

  const long DAY_LONG = 60 * 60 * 24;
  bool _FirstRun = true;
  
  Renko renko;
  CurrentTickProvider online;
  
  void OnTick(void)
  {
    if(_FirstRun)
    {
      // find existing renko tail to supersede StartFrom
      const datetime trap = renko.checkEnding();
      if(trap > TimeCurrent())
      {
        Print("Symbol/Timeframe data not ready...");
        return;
      }
      if((trap == 0) || Reset) renko.doReset();
      else renko.continueFrom(trap);
  
      HistoryTickProvider htp((trap == 0 || Reset) ? StartFrom : trap, DAY_LONG, StopAt);
      
      const bool interrupted = htp.read(renko);
      _FirstRun = false;
      
      if(!interrupted)
      {
        Comment("RenkoChart (" + (string)RenkoBoxSize + "pt): open ", _SymbolName, " / ", renko.getBoxCount(), " bars");
      }
      else
      {
        Print("Interrupted. Custom symbol data is inconsistent - please, reset or delete");
      }
    }
    else if(StopAt == 0) // process online if not stopped explicitly
    {
      online.read(renko);
    }
  }

Beim ersten Start wird das Ende der Renko-Historie durchsucht, das Objekt HistoryTickProvider mit der Startzeit StartFrom oder aus der Historie (falls gefunden) erzeugt und dann alle Ticks gelesen. Alle weiteren Ticks werden online über das Objekt CurrentTickProvider verarbeitet (es wird wie das Renko-Objekt im globalen Kontext angelegt).

Lassen Sie uns ein Renko-Chart auf der Basis von EURUSD mit einer Balkengröße von 100 Pips ab 2019 erzeugen. Führen Sie dazu das EA auf dem EURUSD H1 Chart mit Standardeinstellungen außer StartFrom aus. Der Zeitrahmen spielt nur dann eine Rolle, wenn das EA mit der verfügbaren Renko-Historie neu gestartet wird - in diesem Fall beginnt die Renko-Neuberechnung mit einer Einrückung für den Zeitpunkt des Balkens, auf den der vorletzte Renko-Block fällt.

Zum Beispiel für den ursprünglichen EURUSD H1:

EURUSD H1 Chart mit dem RenkoTicks EA

EURUSD H1 Chart mit dem RenkoTicks EA

erhalten wir das folgende Chart:

EURUSD Renko Chart mit der Blockgröße von 100 Punkten

EURUSD Renko Chart mit der Blockgröße von 100 Punkten

Aus Gründen der visuellen Klarheit habe ich zwei MAs hinzugefügt.

Nachdem wir nun Kurse für das Renko-Symbol erhalten haben, ist es an der Zeit, einen Test-EA für den Handel zu entwickeln.

Ein Trading Expert Advisor auf der Grundlage einer Schnittmenge von zwei MAs

Lassen Sie uns eine der einfachsten Handelsstrategien anwenden, die die Schnittmenge zweier Gleitender Durchschnitte ist. Der vorherige Screenshot demonstriert die Idee. Wenn der schnelle MA (rot) den langsamen MA (blau) nach oben oder unten kreuzt, kaufen bzw. verkaufen wir. Dies ist ein Umkehrsystem.

Es wäre schwierig, einen Expert Advisor von Grund auf zu erstellen, aber MetaTrader 5 bietet einen MQL-Assistenten, der Expert Advisors auf der Grundlage einer Bibliothek von Standardklassen (mit dem Terminal geliefert) generieren kann. Er ist sehr praktisch für Händler, die mit der Programmierung nicht vertraut sind. Die resultierende Code-Struktur ist für eine große Anzahl von Robotern üblich, und daher ist es eine gute Idee, sie für die Hauptaufgabe zu verwenden - für die Anpassung von Robotern an den Handel mit nutzerdefinierten Symbolen. Expert Advisors, die ohne eine Standardbibliothek erstellt wurden, können ebenfalls nach der gleichen Methode angepasst werden, aber da ihre Erstellung sehr unterschiedlich sein kann, muss der Programmierer bei Bedarf die entsprechenden Änderungen vornehmen (im Allgemeinen können erfahrene Programmierer alle anderen EAs nach unserem Beispiel anpassen).

Merkwürdigerweise gibt es in der Standardbibliothek kein Signal für die Schnittmenge zweier MAs, obwohl dies eine der beliebtesten Strategien ist (zumindest ist es die beliebteste beim Erlernen des algorithmischen Handels). Und daher müssen wir das entsprechende Signalmodul schreiben. Nennen wir ihn Signal2MACross.mqh. Nachfolgend finden Sie seinen Code, der die erforderlichen Regeln für das mit dem MQL-Wizard zu verwendende Signal erfüllt.

Er beginnt mit einem "Header" — einem speziellen Kommentar mit der Signalbeschreibung im entsprechenden Format, das ihn vom MetaEditor aus zugänglich macht:

  //--- wizard description start
  //+------------------------------------------------------------------+
  //| Description of the class                                         |
  //| Title=Signals of 2 MAs crosses                                   |
  //| Type=SignalAdvanced                                              |
  //| Name=2MA Cross                                                   |
  //| ShortName=2MACross                                               |
  //| Class=Signal2MACross                                             |
  //| Page=signal_2mac                                                 |
  //| Parameter=SlowPeriod,int,11,Slow MA period                       |
  //| Parameter=FastPeriod,int,7,Fast Ma period                        |
  //| Parameter=MAMethod,ENUM_MA_METHOD,MODE_LWMA,Method of averaging  |
  //| Parameter=MAPrice,ENUM_APPLIED_PRICE,PRICE_OPEN,Price type       |
  //| Parameter=Shift,int,0,Shift                                      |
  //+------------------------------------------------------------------+
  //--- wizard description end

Der Klassenname (Zeile Class) muss mit dem Namen einer realen Klasse in weiterem MQL-Code übereinstimmen. Das Signal hat 5 Parameter, die typisch für zwei MAs sind: 2 Perioden (schnell und langsam), Mittelwertbildungsmethode, Preistyp und Verschiebung.

Die Klasse wurde von CExpertSignal abgeleitet. Sie enthält zwei Instanzen von CiMA-Indikatorobjekten, Variablen mit Arbeitsparametern, Parameter-Setter-Methoden (Methodennamen müssen mit den Namen in der Kopfzeile übereinstimmen). Außerdem hat die Klasse neu definierte virtuelle Methoden, die bei der Indikatorinitialisierung sowie bei der Überprüfung von Einstellungen und der Bestimmung von Kauf- und Verkaufssignalen aufgerufen werden.

  class Signal2MACross : public CExpertSignal
  {
    protected:
      CiMA              m_maSlow;         // object-indicator
      CiMA              m_maFast;         // object-indicator
      
      // adjustable parameters
      int               m_slow;
      int               m_fast;
      ENUM_MA_METHOD    m_method;
      ENUM_APPLIED_PRICE m_type;
      int               m_shift;
      
      // "weights" of market models (0-100)
      int               m_pattern_0;      // model 0 "fast MA crosses slow MA"
  
    public:
                        Signal2MACross(void);
                       ~Signal2MACross(void);
                       
      // parameters setters
      void              SlowPeriod(int value) { m_slow = value; }
      void              FastPeriod(int value) { m_fast = value; }
      void              MAMethod(ENUM_MA_METHOD value) { m_method = value; }
      void              MAPrice(ENUM_APPLIED_PRICE value) { m_type = value; }
      void              Shift(int value) { m_shift = value; }
      
      // adjusting "weights" of market models
      void              Pattern_0(int value) { m_pattern_0 = value; }
      
      // verification of settings
      virtual bool      ValidationSettings(void);
      
      // creating the indicator and timeseries
      virtual bool      InitIndicators(CIndicators *indicators);
      
      // checking if the market models are formed
      virtual int       LongCondition(void);
      virtual int       ShortCondition(void);
  
    protected:
      // initialization of the indicators
      bool              InitMAs(CIndicators *indicators);
      
      // getting data
      double            FastMA(int ind) { return(m_maFast.Main(ind)); }
      double            SlowMA(int ind) { return(m_maSlow.Main(ind)); }
  };

Die Klasse beschreibt die einzige Strategie (Muster oder Modell): Wenn der schnelle MA den langsamen kreuzt, wird ein Kauf (für ein Aufwärtskreuzen) oder ein Verkauf (für ein Abwärtskreuzen) initialisiert. Das Modellgewicht ist standardmäßig gleich 100.

  Signal2MACross::Signal2MACross(void) : m_slow(11), m_fast(7), m_method(MODE_LWMA), m_type(PRICE_OPEN), m_shift(0), m_pattern_0(100)
  {
  }

Die Bedingungen für die Eröffnung einer Position werden mit den beiden folgenden Methoden bestimmt (streng genommen prüft der Code nicht die Schnittmenge, sondern die Positionierung einer MA im Verhältnis zur anderen, während der Effekt für das System, das immer auf dem Markt ist, derselbe sein wird, jedoch ist der Code einfacher):

  int Signal2MACross::LongCondition(void)
  {
    const int idx = StartIndex();
    
    if(FastMA(idx) > SlowMA(idx))
    {
      return m_pattern_0;
    }
    return 0;
  }
  
  int Signal2MACross::ShortCondition(void)
  {
    const int idx = StartIndex();
  
    if(FastMA(idx) < SlowMA(idx))
    {
      return m_pattern_0;
    }
    return 0;
  }

Die Funktion StartIndex wird in der übergeordneten Klasse definiert. Wie Sie aus dem Code ersehen können, ist der Index die Nummer des Balkens, für den das Signal analysiert wird. Wenn in den EA-Einstellungen jede Tick-basierte Operation ausgewählt ist (Expert_EveryTick = true, siehe weiter unten), dann ist der Startindex gleich 0; wenn nicht (d.h. durch geschlossene Balken arbeitet), dann ist der Index 1.

Speichern Sie die Datei Signal2MACross.mqh im Ordner MQL5/Include/Expert/Signal/MySignals und starten Sie dann den MetaEditor neu (falls er läuft), um das neue Modul im MQL-Assistenten aufzunehmen.

Jetzt können wir einen Expert Advisor basierend auf unserem Signal generieren. Wählen Sie 'Datei' -> 'Neu' im Menü und öffnen Sie den Wizard-Dialog. Folgen Sie dann den untenstehenden Schritten:

  1. Wählen Sie "Expert Advisor (generieren)".
  2. EA-Name festlegen, zum Beispiel Experts\Examples\MA2Cross
  3. Signal "Signals of 2 MAs crosses" hinzufügen
  4. verwenden Sie "Trailing stop not used".
  5. verwenden Sie "Trading with fixed volume".

Als Ergebnis erhalten Sie den folgenden EA-Code:

  #include <Expert\Expert.mqh>
  #include <Expert\Signal\MySignals\Signal2MACross.mqh>
  #include <Expert\Trailing\TrailingNone.mqh>
  #include <Expert\Money\MoneyFixedLot.mqh>
  
  //+------------------------------------------------------------------+
  //| Inputs                                                           |
  //+------------------------------------------------------------------+
  // inputs for expert
  input string             Expert_Title              = "MA2Cross";  // Document name
  ulong                    Expert_MagicNumber        = 7623;
  bool                     Expert_EveryTick          = false;
  // inputs for main signal
  input int                Signal_ThresholdOpen      = 10;          // Signal threshold value to open [0...100]
  input int                Signal_ThresholdClose     = 10;          // Signal threshold value to close [0...100]
  input double             Signal_PriceLevel         = 0.0;         // Price level to execute a deal
  input double             Signal_StopLevel          = 0.0;         // Stop Loss level (in points)
  input double             Signal_TakeLevel          = 0.0;         // Take Profit level (in points)
  input int                Signal_Expiration         = 0;           // Expiration of pending orders (in bars)
  input int                Signal_2MACross_SlowPeriod = 11;         // 2MA Cross(11,7,MODE_LWMA,...) Slow MA period
  input int                Signal_2MACross_FastPeriod = 7;          // 2MA Cross(11,7,MODE_LWMA,...) Fast Ma period
  input ENUM_MA_METHOD     Signal_2MACross_MAMethod  = MODE_LWMA;   // 2MA Cross(11,7,MODE_LWMA,...) Method of averaging
  input ENUM_APPLIED_PRICE Signal_2MACross_MAPrice   = PRICE_OPEN;  // 2MA Cross(11,7,MODE_LWMA,...) Price type
  input int                Signal_2MACross_Shift     = 0;           // 2MA Cross(11,7,MODE_LWMA,...) Shift
  input double             Signal_2MACross_Weight    = 1.0;         // 2MA Cross(11,7,MODE_LWMA,...) Weight [0...1.0]
  // inputs for money
  input double             Money_FixLot_Percent      = 10.0;        // Percent
  input double             Money_FixLot_Lots         = 0.1;         // Fixed volume
  
  //+------------------------------------------------------------------+
  //| Global expert object                                             |
  //+------------------------------------------------------------------+
  CExpert ExtExpert;
  
  //+------------------------------------------------------------------+
  //| Initialization function of the expert                            |
  //+------------------------------------------------------------------+
  int OnInit()
  {
    // Initializing expert
    if(!ExtExpert.Init(Symbol(), Period(), Expert_EveryTick, Expert_MagicNumber))
    {
      printf(__FUNCTION__ + ": error initializing expert");
      ExtExpert.Deinit();
      return(INIT_FAILED);
    }
    // Creating signal
    CExpertSignal *signal = new CExpertSignal;
    if(signal == NULL)
    {
      printf(__FUNCTION__ + ": error creating signal");
      ExtExpert.Deinit();
      return(INIT_FAILED);
    }
    
    ExtExpert.InitSignal(signal);
    signal.ThresholdOpen(Signal_ThresholdOpen);
    signal.ThresholdClose(Signal_ThresholdClose);
    signal.PriceLevel(Signal_PriceLevel);
    signal.StopLevel(Signal_StopLevel);
    signal.TakeLevel(Signal_TakeLevel);
    signal.Expiration(Signal_Expiration);
    
    // Creating filter Signal2MACross
    Signal2MACross *filter0 = new Signal2MACross;
    if(filter0 == NULL)
    {
      printf(__FUNCTION__ + ": error creating filter0");
      ExtExpert.Deinit();
      return(INIT_FAILED);
    }
    signal.AddFilter(filter0);
    
    // Set filter parameters
    filter0.SlowPeriod(Signal_2MACross_SlowPeriod);
    filter0.FastPeriod(Signal_2MACross_FastPeriod);
    filter0.MAMethod(Signal_2MACross_MAMethod);
    filter0.MAPrice(Signal_2MACross_MAPrice);
    filter0.Shift(Signal_2MACross_Shift);
    filter0.Weight(Signal_2MACross_Weight);
  
    ...
    
    // Check all trading objects parameters
    if(!ExtExpert.ValidationSettings())
    {
      ExtExpert.Deinit();
      return(INIT_FAILED);
    }
    
    // Tuning of all necessary indicators
    if(!ExtExpert.InitIndicators())
    {
      printf(__FUNCTION__ + ": error initializing indicators");
      ExtExpert.Deinit();
      return(INIT_FAILED);
    }
  
    return(INIT_SUCCEEDED);
  }

Der vollständige Code von MA2Cross.mq5 ist dem Artikel beigefügt. Alles ist bereit zur Kompilierung, zum Testen im Strategietester und sogar zur Optimierung für jedes Symbol, einschließlich unseres nutzerdefinierten Renko. Da wir genau an Renko interessiert sind, müssen wir einige Momente erklären.

Jeder Renko-Block, in seiner "rechteckigen" Form, existiert erst, wenn er vollständig durch die Preisbewegung gebildet wird. Wenn der nächste Block erscheint, kennen wir nicht nur seinen Schlusskurs, sondern auch seinen Eröffnungskurs, denn es gibt zwei mögliche entgegengesetzte Richtungen: nach oben und nach unten. Wenn der Block schließlich schließt, ist der Schlusskurs entscheidend und am charakteristischsten. Aus diesem Grund wird der Standardwert des Parameters Signal_2MACross_MAPrice im EA in PRICE_CLOSE geändert - seine Änderung wird nicht empfohlen. Sie können mit anderen Preistypen experimentieren, aber es geht nicht nur darum, sich von der Zeit abzukoppeln, sondern auch darum, kleine Preisschwankungen zu beseitigen, und dies wird durch die Quantisierung durch die Bausteingröße erreicht.

Beachten Sie, dass der 0te Renko-Balken immer unvollständig ist (in den meisten Fällen handelt es sich um eine Kerze ohne Körper, nicht um ein Rechteck), weshalb wir das Signal des 1ten Balkens verwenden. Zu diesem Zweck setzen wir den Parameterwert Expert_EveryTick auf den Wert false.

Erzeugen Sie EURUSD-basiertes nutzerdefiniertes Renko mit der Blockgröße von 100 Punkten. Als Ergebnis erhalten wir das Symbol EURUSD_T_r100. Wählen Sie es im Tester aus. Stellen Sie sicher, dass Sie den M1-Zeitrahmen einstellen.

Sehen wir uns an, wie sich der Expert Advisor bei diesem Symbol für den Zeitraum 2019-2020 (erste Hälfte) verhält, z.B. mit den Standardperioden 7 und 11 (andere Kombinationen können mit Hilfe der Optimierung unabhängig geprüft werden).

Das Ergebnis der MA2CrossCustom-Strategie auf einem von EURUSD abgeleiteten 100-Punkte-Renko-Chart

Das Ergebnis der MA2CrossCustom-Strategie auf einem von EURUSD abgeleiteten 100-Punkte-Renko-Chart

Um das nutzerdefinierte Symbol mit einem realen Symbol zu vergleichen, stelle ich hier einen Bericht des MA2CrossCustom EA zur Verfügung, der MA2Cross mit einem leeren WorkSymbol-Parameter ähnelt. Im nächsten Abschnitt werden wir uns überlegen, wie wir MA2CrossCustom von MA2Cross beziehen können.

Wie aus der Deals-Tabelle ersichtlich ist, werden die Deals zu Preisen ausgeführt, die ein Vielfaches der Blockgröße sind: Verkaufspreise stimmen vollständig überein, und Kaufpreise unterscheiden sich durch den Spread (unser Renko-Generator speichert in jedem Balken den Maximalwert des Spread, der während seiner Bildung registriert wurde, wenn Sie möchten, können Sie dieses Verhalten im Quellcode ändern). Renko wird durch den im Quellcode verwendeten Preistyp im Chart gebildet. In unserem Fall ist es der Bid-Preis. Der letzte Preis (last) wird für Börseninstrumente verwendet.

Deals-Tabelle beim Handel mit EURUSD-basierten nutzerdefinierten Renko-Symbol

Deals-Tabelle beim Handel mit EURUSD-basierten nutzerdefinierten Renko-Symbol

Nun scheint das Ergebnis zu gut, um wahr zu sein. Wirklich, aber es gibt viele verdeckte Nuancen.

Der Handel mit dem Renko-Symbol im Tester beeinflusst die Genauigkeit der Ergebnisse in jedem Modus: nach Eröffnungspreisen, M1 OHLC und nach Ticks.

Der Eröffnungspreis des Balkens eines Standard-Renko wird nicht immer zu dem Zeitpunkt erreicht, zu dem der Balken markiert wird, sondern in vielen Fällen erst später (weil der Preis innerhalb der Renko-Größe eine Zeitlang auf und ab "wandert" und eventuell die Richtung ändert und einen Umkehrbalken bildet). Die Markierungszeit des Balkens ist die Beendigungszeit des vorherigen Balkens.

Der Schlusskurs entspricht nicht der Schlusszeit, da der Renko-Balken der M1-Balken ist, d.h. er hat eine feste Dauer von 1 Minute.

Es ist möglich, einen Nicht-Standard-Renko zu erzeugen, bei dem die Balken mit der Beendigungszeit und nicht mit der Anfangszeit markiert werden. Dann entsprechen sich Schlusskurs und Schlusszeit. Die Eröffnungszeit liegt jedoch 1 Minute vor der Schließzeit und entspricht somit nicht dem tatsächlichen Eröffnungskurs (es ist der Schlusskurs plus/minus die Renko-Größe).

Die Renko-Analyse soll durch die geformten Balken durchgeführt werden, aber ihr charakteristischer Preis ist der Schlusskurs, aber während der Balken-für-Balken-Operation liefert der Tester nur den Eröffnungskurs für den aktuellen (letzten) Balken (es gibt keinen Modus durch Schlusskurse). Hier sind die Eröffnungspreise der Balken per Definition Prädiktoren. Wenn wir Signale von geschlossenen Balken (normalerweise vom 1ten) verwenden, werden Geschäfte ohnehin zum aktuellen Kurs des 0ten ausgeführt. Selbst wenn wir Tick-Modi verwenden, generiert der Tester Ticks für Renko nach gemeinsamen Regeln, indem er Referenzpunkte auf der Grundlage jeder Balkenkonfiguration verwendet. Der Tester berücksichtigt nicht die spezifische Struktur und das Verhalten von Renko-Kursen (die wir versuchen, mit M1-Balken visuell zu emulieren). Wenn wir uns hypothetisch eine einmalige Bildung eines ganzen Balkens vorstellen, hat dieser immer noch einen Körper - und für solche Balken generiert der Tester Ticks ausgehend vom Eröffnungspreis. Wenn wir das Tick-Volumen des Balkens gleich eins setzen, verliert der Balken seine Konfiguration (verwandelt sich in ein Preisschild mit gleichem OHLC).

Daher werden alle Renko-Konstruktionsmethoden beim Testen eines nutzerdefinierten Renko-Symbols Orderausführungs-Artefakte aufweisen.

Mit anderen Worten, die Struktur von Renko selbst ermöglicht es, Testgrails auf Renko-Symbolen zu erhalten, indem man durch die Größe eines Renko-Balkens in die Zukunft blickt.

Deshalb ist es notwendig, das Handelssystem nicht auf einem separaten Renko-Balken zu testen, sondern in Kombination mit der Ausführung von Handelsaufträgen auf einem realen Symbol.

Renko bietet Analytik und Timing - wann man in den Markt einsteigen sollte.

Bislang haben wir nur die Fähigkeit des EA getestet, mit einem nutzerdefinierten Symbol zu handeln. Dies schränkt die Anwendung des EA nur im Tester ein. Um das EA universell zu machen, d.h. das ursprüngliche Symbol online handeln zu können, während es auf dem Chart von Renko läuft, müssen wir einige Dinge hinzufügen. Dies wird auch dazu beitragen, das Problem mit zu optimistischen Ergebnissen zu lösen.

Anpassung der Expert Advisors für den Handel mit nutzerdefinierten Symbolcharts

Ein nutzerdefiniertes Symbol ist nur dem Client-Terminal bekannt und existiert nicht auf dem Handelsserver. Es liegt auf der Hand, dass ein Fachberater, der einen nutzerdefinierten Symbol-Chart einstellt, alle Handelsaufträge für das Originalsymbol (auf dem das nutzerdefinierte Symbol basiert) generieren muss. Als einfachste Lösung für dieses Problem kann der EA auf dem ursprünglichen Symbol-Chart ausgeführt werden, aber Signale (z.B. von Indikatoren) von einem nutzerdefinierten Symbol empfangen. Viele Händler ziehen es jedoch vor, das Gesamtbild zu sehen. Darüber hinaus können selektive Code-Modifikationen Fehler erzeugen. Es ist wünschenswert, die Änderungen am Quellcode so gering wie möglich zu halten.

Leider kann der Name des ursprünglichen Symbols und von Renko, das auf seiner Grundlage erstellt wurde, nicht mit Hilfe der Plattform selbst verknüpft werden. Eine bequeme Lösung wäre ein Textfeld "origin" (Ursprung) oder "parent" (Elternteil) in nutzerdefinierten Symboleigenschaften, in das wir den Namen des realen Symbols schreiben könnten. Es wäre standardmäßig leer. Aber wenn es ausgefüllt ist, würde die Plattform das Symbol automatisch in allen Handelsaufträgen und historischen Anfragen ersetzen. Da es diesen Mechanismus in der Plattform nicht gibt, müssen wir ihn selbst implementieren. Die Namen des eigentlichen und des nutzerdefinierten Symbols werden über Parameter festgelegt. Die Eigenschaften des nutzerdefinierten Symbols haben ein Feld mit passender Bedeutung - SYMBOL_BASIS. Da wir aber nicht garantieren können, dass willkürliche Generatoren von nutzerdefinierten Symbolen (beliebige MQL-Programme) den Parameter korrekt füllen oder ihn genau zu diesem Zweck verwenden, müssen wir eine andere Lösung anbieten.

Zu diesem Zweck habe ich die Klasse CustomOrder entwickelt (siehe CustomOrder.mqh im Anhang unten). Sie enthält Wrapper-Methoden für alle MQL-API-Funktionen, die mit dem Senden von Handelsaufträgen und Historienanforderungen zusammenhängen, die einen String-Parameter mit dem Symbolnamen des Instruments enthalten. Diese Methoden ersetzen ein nutzerdefiniertes Symbol durch das aktuelle Arbeitssymbol oder umgekehrt. Alle anderen API-Funktionen erfordern kein "Abfangen". Der Code ist unten dargestellt.

  class CustomOrder
  {
    private:
      static string workSymbol;
      
      static void replaceRequest(MqlTradeRequest &request)
      {
        if(request.symbol == _Symbol && workSymbol != NULL)
        {
          request.symbol = workSymbol;
          if(request.type == ORDER_TYPE_BUY
          || request.type == ORDER_TYPE_SELL)
          {
            if(request.price == SymbolInfoDouble(_Symbol, SYMBOL_ASK)) request.price = SymbolInfoDouble(workSymbol, SYMBOL_ASK);
            if(request.price == SymbolInfoDouble(_Symbol, SYMBOL_BID)) request.price = SymbolInfoDouble(workSymbol, SYMBOL_BID);
          }
        }
      }
      
    public:
      static void setReplacementSymbol(const string replacementSymbol)
      {
        workSymbol = replacementSymbol;
      }
      
      static bool OrderSend(MqlTradeRequest &request, MqlTradeResult &result)
      {
        replaceRequest(request);
        return ::OrderSend(request, result);
      }
      
      static bool OrderCalcProfit(ENUM_ORDER_TYPE action, string symbol, double volume, double price_open, double price_close, double &profit)
      {
        if(symbol == _Symbol && workSymbol != NULL)
        {
          symbol = workSymbol;
        }
        return ::OrderCalcProfit(action, symbol, volume, price_open, price_close, profit);
      }
      
      static string PositionGetString(ENUM_POSITION_PROPERTY_STRING property_id)
      {
        const string result = ::PositionGetString(property_id);
        if(property_id == POSITION_SYMBOL && result == workSymbol) return _Symbol;
        return result;
      }
      
      static string OrderGetString(ENUM_ORDER_PROPERTY_STRING property_id)
      {
        const string result = ::OrderGetString(property_id);
        if(property_id == ORDER_SYMBOL && result == workSymbol) return _Symbol;
        return result;
      }
      
      static string HistoryOrderGetString(ulong ticket_number, ENUM_ORDER_PROPERTY_STRING property_id)
      {
        const string result = ::HistoryOrderGetString(ticket_number, property_id);
        if(property_id == ORDER_SYMBOL && result == workSymbol) return _Symbol;
        return result;
      }
      
      static string HistoryDealGetString(ulong ticket_number, ENUM_DEAL_PROPERTY_STRING property_id)
      {
        const string result = ::HistoryDealGetString(ticket_number, property_id);
        if(property_id == DEAL_SYMBOL && result == workSymbol) return _Symbol;
        return result;
      }
      
      static bool PositionSelect(string symbol)
      {
        if(symbol == _Symbol && workSymbol != NULL) return ::PositionSelect(workSymbol);
        return ::PositionSelect(symbol);
      }
      
      static string PositionGetSymbol(int index)
      {
        const string result = ::PositionGetSymbol(index);
        if(result == workSymbol) return _Symbol;
        return result;
      }
      ...
  };
  
  static string CustomOrder::workSymbol = NULL;

Um Bearbeitungen im Quellcode zu minimieren, werden (für alle Methoden) die folgenden Makros verwendet:

  bool CustomOrderSend(const MqlTradeRequest &request, MqlTradeResult &result)
  {
    return CustomOrder::OrderSend((MqlTradeRequest)request, result);
  }
  
  #define OrderSend CustomOrderSend

Sie ermöglichen die automatische Umleitung aller Standard-API-Funktionsaufrufe an Methoden der Klasse CustomOrder. Fügen Sie zu diesem Zweck CustomOrder.mqh in die EA ein und setzen Sie das Arbeitssymbol:

  #include <CustomOrder.mqh>
  #include <Expert\Expert.mqh>
  ...
  input string WorkSymbol = "";
  
  int OnInit()
  {
    if(WorkSymbol != "")
    {
      CustomOrder::setReplacementSymbol(WorkSymbol);
      
      // force a chart for the work symbol to open (in visual mode only)
      MqlRates rates[1];
      CopyRates(WorkSymbol, PERIOD_H1, 0, 1, rates);
    }
    ...
  }

Es ist wichtig, dass die Direktive #include <CustomOrder.mqh> an erster Stelle steht, vor allen anderen. Sie wirkt sich also auf alle Quellcodes aus, einschließlich der angeschlossenen Standardbibliotheken. Wenn der Platzhalter nicht angegeben wird, hat die verbundene CustomOrder.mqh keine Auswirkungen auf den EA und überträgt die Kontrolle an die Standard-API-Funktionen.

Der modifizierte MA2Cross EA wurde in MA2CrossCustom.mq5 umbenannt.

Jetzt können wir WorkSymbol auf EURUSD setzen, während alle anderen Einstellungen gleich bleiben, und mit dem Testen beginnen. Jetzt handelt der EA tatsächlich mit EURUSD, obwohl er auf dem Renko-Symbol-Chart läuft.

Das Ergebnis der Strategie des MA2CrossCustom auf dem 100-Punkte-Renko-Chart beim Handel mit dem echten EURUSD-Symbol

Das Ergebnis der Strategie des MA2CrossCustom auf dem 100-Punkte-Renko-Chart beim Handel mit dem echten EURUSD-Symbol

Dieses Mal ist das Ergebnis näher an der Realität.

Bei EURUSD-Geschäften unterscheiden sich die Preise deutlicher von den Renko-Balken-Schlusskursen. Das liegt daran, dass Renko-Balken immer durch einen Minutenanfang gekennzeichnet sind (dies ist eine Begrenzung des M1-Zeitrahmens in der Plattform), aber der Preis die Balkengrenze zu willkürlichen Zeitpunkten innerhalb der Minute überschreitet. Da der Expert Advisor auf dem Chart in einem Balken-für-Balken-Modus arbeitet (nicht zu verwechseln mit dem Tester-Modus), wird der Signalanfang auf die Eröffnung eines EURUSD-Minutenbalkens "verschoben", wenn der Preis normalerweise anders ist. Im Durchschnitt ist der Fehler die mathematische Erwartung der Bandbreite der Minutenbalken pro Handel.

EURUSD-Handel durch den Expert Advisor anhand eines von EURUSD abgeleiteten Renko-Chart

EURUSD-Handel durch den Expert Advisor anhand eines von EURUSD abgeleiteten Renko-Chart

Um Diskrepanzen zu eliminieren, müsste der EA alle Ticks verarbeiten, aber wir haben bereits herausgefunden, dass die Logik der Tickerzeugung im Tester von der der Renko-Bildung abweicht: Insbesondere hat der Eröffnungskurs von Umkehrbalken immer eine Lücke, die einem Renko-Block gegenüber dem Schlusskurs des vorherigen Balkens entspricht.

Dieses Problem gibt es im Online-Handel nicht.

Lassen Sie uns die Funktionalität von CustomOrder mit einem anderen Expert Advisor überprüfen, der ohne Verwendung der Standardbibliothek geschrieben wurde. Wir werden hierfür den EA ExprBot aus dem Artikel über Berechnung mathematischer Ausdrücke verwenden — er nutzt auch die Schnittmengenstrategie der beiden MAs aus und führt Handelsoperationen mit der MT4Orders-Bibliothek durch. Der modifizierte Expert Advisor ExprBotCustom.mq5 ist unten angehängt, zusammen mit den erforderlichen Header-Dateien (Ordner ExpresSParserS).

Unten sind die Ergebnisse im Zeitraum 2019-2020 (erste Jahreshälfte) mit den gleichen Einstellungen (Perioden 7/11, Mittelwerttyp LWMA, Schlusskurse des 1ten)

Das Ergebnis der Strategie ExprBotCustom auf 100-Punkte-Renko-Chart abgeleitet von EURUSD

Das Ergebnis der Strategie ExprBotCustom auf 100-Punkte-Renko-Chart abgeleitet von EURUSD

Das Ergebnis der ExprBotCustom-Strategie auf dem 100-Punkte-Renko-Chart beim Handel mit dem echten EURUSD-Symbol

Das Ergebnis der ExprBotCustom-Strategie auf dem 100-Punkte-Renko-Chart beim Handel mit dem echten EURUSD-Symbol

Diese Ergebnisse sind denen, die mit dem MA2CrossCustom EA erzielt wurden, sehr ähnlich.

Wir können feststellen, dass der vorgeschlagene Ansatz das Problem löst. Die derzeitige Implementierung von CustomOrder ist jedoch nur eine grundsätzliche Mindestanforderung. Abhängig von der Handelsstrategie und den Besonderheiten des Arbeitssymbols können Verbesserungen erforderlich sein.

Schlussfolgerung

Wir haben mehrere Möglichkeiten in Betracht gezogen, nutzerdefinierte Symbole unter Verwendung der vom Makler bereitgestellten Kurse für Arbeitssymbole zu generieren. Spezielle Verallgemeinerungs- und Akkumulationsalgorithmen der Daten ermöglichen es, die normalen Kurse aus einem anderen Blickwinkel zu sehen und auf ihrer Grundlage fortschrittliche Handelssysteme aufzubauen.

Natürlich bieten nutzerdefinierte Symbole viel mehr Möglichkeiten. Die potenziellen Anwendungen dieser Technologie sind viel breiter. Wir können zum Beispiel synthetische Symbole, Volumen-Delta und Datenquellen von Dritten verwenden. Die beschriebene Programmtransformation ermöglicht die Nutzung dieser Möglichkeiten auf Standard-MetaTrader 5 Charts, im Strategie-Tester und im Online-Modus.

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

Beigefügte Dateien |
MQL5CUST.zip (61.87 KB)
Der Algorithmus CatBoost von Yandex für das maschinelle Lernen, Kenntnisse von Python- oder R sind nicht erforderlich Der Algorithmus CatBoost von Yandex für das maschinelle Lernen, Kenntnisse von Python- oder R sind nicht erforderlich
Der Artikel liefert den Code und die Beschreibung der wichtigsten Phasen des maschinellen Lernprozesses anhand eines konkreten Beispiels. Um das Modell zu entwickeln, benötigen Sie keine Kenntnisse von Python- oder R. Es reichen grundlegende MQL5-Kenntnisse aus — das ist genau mein Niveau. Daher hoffe ich, dass der Artikel als gutes Tutorial für ein breites Publikum hilft, um diejenigen zu unterstützen, die daran interessiert sind, Fähigkeiten des maschinellen Lernens zu evaluieren und in ihre Programme zu implementieren.
Neuronale Netze leicht gemacht (Teil 2): Netzwerktraining und Tests Neuronale Netze leicht gemacht (Teil 2): Netzwerktraining und Tests
In diesem zweiten Artikel werden wir uns weiter mit Neuronalen Netzen befassen und ein Beispiel für die Verwendung unserer geschaffenen Klasse CNet in Expert Advisors besprechen. Wir werden mit zwei Modellen neuronaler Netze arbeiten, die ähnliche Ergebnisse sowohl hinsichtlich der Trainingszeit als auch der Vorhersagegenauigkeit zeigen.
Zeitreihen in der Bibliothek DoEasy (Teil 50): Verschieben der Standardindikatoren für mehrere Symbole und Perioden Zeitreihen in der Bibliothek DoEasy (Teil 50): Verschieben der Standardindikatoren für mehrere Symbole und Perioden
In diesem Artikel wollen wir die Bibliotheksmethoden für die korrekte Anzeige von Mehrsymbol- und Mehrperioden-Standardindikatoren verbessern, wobei die Linien auf dem aktuellen Symbol-Chart mit einer in den Einstellungen festgelegten Verschiebung angezeigt werden. Außerdem sollten wir die Methoden für die Arbeit mit Standardindikatoren in Ordnung bringen und den redundanten Code für den Bibliotheksbereich im endgültigen Indikatorprogramm entferne.
Was ist ein Trend und basiert die Marktstruktur auf einem Trend oder einer Seitwärtsbewegung? Was ist ein Trend und basiert die Marktstruktur auf einem Trend oder einer Seitwärtsbewegung?
Händler sprechen oft über Trends und Seitwärtsbewegungen (flat), aber nur sehr wenige von ihnen verstehen wirklich, was ein Trend/eine Seitwärtsbewegung wirklich ist, und noch weniger sind in der Lage, diese Konzepte klar zu erklären. Die Diskussion dieser Grundbegriffe ist oft mit einer Reihe von Vorurteilen und Missverständnissen behaftet. Wenn wir jedoch Gewinn erzielen wollen, müssen wir die mathematische und logische Bedeutung dieser Konzepte verstehen. In diesem Artikel werde ich einen genaueren Blick auf das Wesen von Trend und Seitwärtsbewegung werfen und versuchen zu definieren, ob die Marktstruktur auf Trend, Seitwärtsbewegung oder etwas anderem basiert. Ich werde auch die optimalsten Strategien zur Gewinnerzielung auf Trend- und flachen Märkten besprechen.