English 日本語
preview
Erstellen von selbstoptimierenden Expert Advisors in MQL5 (Teil 3): Dynamische Trendfolge- und Mean-Reversion-Strategien

Erstellen von selbstoptimierenden Expert Advisors in MQL5 (Teil 3): Dynamische Trendfolge- und Mean-Reversion-Strategien

MetaTrader 5Beispiele | 23 April 2025, 11:44
32 0
Gamuchirai Zororo Ndawana
Gamuchirai Zororo Ndawana

Algorithmische Handelsstrategien, die auf gleitenden Durchschnitten basieren, unterscheiden sich von den meisten Handelsstrategien durch ihre Fähigkeit, unsere Handelsanwendungen am langfristigen Markttrend auszurichten. Leider schaden unsere einst zuverlässigen Trendfolgestrategien mehr, als sie nützen, wenn die Märkte schwanken und nicht einem echten langfristigen Trend folgen. Wenn wir verstehen, wie die Märkte zwischen Trend- und Bandbreitenregimen wechseln, können wir unsere auf gleitenden Durchschnitten basierenden Strategien effektiver einsetzen.

In der Regel neigen Händler dazu, die Märkte entweder als derzeit in einer Handelsspanne oder in einem Trend befindlich einzustufen, bevor sie entscheiden, welche Strategien für den jeweiligen Handelstag geeignet sind. Es gibt nur wenig Literatur, die sich mit der Frage beschäftigt, wie die Märkte zwischen diesen beiden Regimen wechseln. Anstatt Märkte als ein statisches Umfeld zu betrachten, das in einem von 2 Zuständen existiert. Wir wollen die Finanzmärkte als ein dynamisches Umfeld betrachten, das ständig zwischen zwei möglichen Zuständen wechselt, ohne sich wirklich in einem von beiden einzurichten. Unser Ziel ist es heute, eine einzige dynamische Handelsstrategie zu entwickeln, die selbständig erkennen kann, wenn das zugrunde liegende Marktregime von einem Trend zu einer Schwankungsbreite wechselt oder umgekehrt.

Diese einheitliche dynamische Strategie wird hoffentlich die traditionellen Paradigmen ersetzen, bei denen es für jede Marktbedingung eine Strategie gibt. Die vorgeschlagene Strategie beruht auf der Berechnung eines Handelskanals auf Bewegungsbasis nach vorne. Der Grundgedanke dieser Strategie ist die Überzeugung, dass es eine Grenze gibt, die Trendbewegungen von den Vorwärtsbewegungen innerhalb eines Bereiches trennt. Und indem wir darauf achten, wo sich die Preisniveaus im Verhältnis zu unserer Grenze befinden, können wir besser informierte Handelsgeschäfte machen. Die obere und untere Grenze unseres Kanals wird berechnet, indem ein Vielfaches des ATR-Wertes vom Durchschnittswert des gleitenden Durchschnittsindikators addiert (obere Grenze) und subtrahiert (untere Grenze) wird. Wir werden diese Strategie in den folgenden Abschnitten des Artikels ausführlich erörtern.

Der Leser sollte jedoch wissen, dass der Kanal täglich dynamisch mit Hilfe von technischen Standardindikatoren berechnet wird, die in jeder Installation von MetaTrader 5 enthalten sind. Unsere ursprüngliche Strategie war eine einfache Trendfolgestrategie, bei der wir Kaufpositionen einnahmen, wenn die Kurse über dem gleitenden Durchschnitt mit einer Periodenlänge von 100 schlossen, und Verkaufspositionen, wenn dies nicht der Fall war. Nachdem die Handelsgeschäfte eröffnet wurden, wurden sie anschließend mit einem festen Stop-Loss und Take-Profit verwaltet. In einem 4-Jahres-Backtest mit M1-Daten für den EURUSD waren nur 52 % der von unserer ursprünglichen Handelsstrategie getätigten Abschlüsse profitabel. Die von uns vorgeschlagenen dynamischen, kanalbasierten Regeln steigerten den Anteil der Handelsgeschäfte mit Gewinn im gleichen 4-Jahres-Zeitraum auf 86 % auf dem M1-Zeitrahmen, ohne dass eine Kurvenanpassung oder KI-Techniken eingesetzt wurden.

Die Ergebnisse deuten darauf hin, dass sich der Aufwand für das Erlernen einer besseren Abschätzung der Grenzen lohnt. Indem wir unser Bedürfnis nach einer kategorischen Einordnung der Märkte in genau definierte Boxen aufgaben und stattdessen versuchten, dem natürlichen Rhythmus des Marktes zu folgen, konnten wir feststellen, dass unsere Handelsanwendung einen beeindruckenden Anteil an Handelsgeschäfte mit Gewinn verzeichnen konnte. Darüber hinaus wird es dem Leser aufgrund des einfachen Layouts, das wir für unseren Kodierungsstil gewählt haben, leicht fallen, die ihm zur Verfügung gestellte Vorlage mit seinem eigenen Verständnis des Marktes zu erweitern. 


Überblick über die Handelsstrategie

Wie wir in der Einleitung des Artikels erläutert haben, sind Handelsstrategien, die auf gleitenden Durchschnitten basieren, bei Händlern besonders beliebt, da sie uns am langfristigen Markttrend orientieren. Ein eher außergewöhnliches Beispiel für die Anwendung dieses Prinzips ist in Abbildung 1 dargestellt. Der Screenshot ist dem täglichen EURUSD-Wechselkurs entnommen und zeigt einen Aufwärtstrend, der Ende November 2016 begann und bis April 2018 andauerte. Ein in jeder Hinsicht beeindruckender Lauf. Der Indikator für den gleitenden Durchschnitt hat ebenfalls bestätigt, dass sich die Kurse in einem lang anhaltenden Aufwärtstrend befinden. 

Generell können wir feststellen, dass unsere Kaufpositionen immer dann aufgebaut werden konnten, wenn die Kurse über unserem gleitenden Durchschnitt schlossen, und unsere Verkaufspositionen immer dann, wenn die Kurse unter unserem gleitenden Durchschnitt schlossen. 

Trend

Abb. 1: Ein Beispiel für unser Trendfolgesystem mit gleitendem Durchschnitt in der Praxis

Das in Abbildung 1 dargestellte Beispiel zeigt die Hauptstärke von Strategien mit gleitendem Durchschnitt, wenn sie auf Märkten eingesetzt werden, die sich im Trend befinden. Wenn der Markt jedoch keinen langfristigen Trend aufweist, wie die in Abbildung 2 dargestellten Marktbedingungen, sind diese einfachen Trendfolgestrategien nicht wirksam. 

Markt mit Bandbreitenbegrenzung

Abb. 2: Ein Beispiel eines längeren unrentablen Zeitraums für die Verwendung von Trendfolgesystemen

Die in diesem Artikel vorgeschlagene Strategie kann beide Marktbedingungen außerordentlich gut handhaben, ohne dass zusätzliche Indikatoren oder eine komplexe Handelslogik erforderlich sind, um zu erkennen, wann die Strategie gewechselt werden muss und welche Strategie dafür am besten geeignet ist. Lassen Sie uns beginnen zu lernen, was nötig ist, um unsere einfache Handelsstrategie mit gleitendem Durchschnitt dynamisch und selbstanpassend zu machen.


Erste Schritte in MQL5

Unser Handelssystem setzt sich aus verschiedenen Teilen zusammen:

Systemteil Verwendungszweck
Systemkonstanten Dies sind Konstanten, die dem Endnutzer verborgen bleiben und dazu dienen, das Verhalten unseres Systems in beiden Backtests konsistent zu halten, um Verzerrungen oder unbeabsichtigte Änderungen zu vermeiden, die die Handelslogik zerstören würden.
Bibliotheken In unserer Anwendung haben wir die Handelsbibliothek nur importiert, um die Eröffnung und Verwaltung unserer Positionen zu unterstützen.
Globale Variablen Diese Variablen sind dazu gedacht, Indikatorwerte, Preisniveaus und andere Daten im System zu speichern, die von verschiedenen Teilen des Systems gemeinsam genutzt werden.
System der Ereignisbehandlung Funktionen wie OnTick() reagieren systemgesteuert auf Ereignisse, die uns helfen, unsere Aufgaben auf organisierte Weise zu erledigen.
Nutzerdefinierte Funktionen Es handelt sich um Funktionen, die auf unsere spezifischen Bedürfnisse zugeschnitten sind, um dem durch den gleitenden Durchschnitt definierten Trend erfolgreich zu folgen.

Zu Beginn werden wir zunächst unsere Systemkonstanten definieren. Beachten Sie, dass wir diese Werte in keiner der nachfolgenden Versionen, die wir von unserer Handelsanwendung erstellen werden, ändern werden.

//+------------------------------------------------------------------+
//|                              Dynamic Moving Average Strategy.mq5 |
//|                                               Gamuchirai Ndawana |
//|                    https://www.mql5.com/en/users/gamuchiraindawa |
//+------------------------------------------------------------------+
#property copyright "Gamuchirai Ndawana"
#property link      "https://www.mql5.com/en/users/gamuchiraindawa"
#property version   "1.00"

//+------------------------------------------------------------------+
//| System constants                                                 |
//+------------------------------------------------------------------+
#define MA_PRICE    PRICE_CLOSE           //Moving average applied price
#define MA_MODE    MODE_EMA               //Moving average type
#define MA_SHIFT   0                      //Moving average shift
#define ATR_PERIOD 14                     //Period for our ATR
#define TF_1       PERIOD_D1              //Hihger order timeframe
#define TRADING_MA_PERIOD  100            //Moving average period
#define SL_WIDTH 1.5                      //How wide should the stop loss be?
#define CURRENT_VOL 0.1                   //Trading volume

Als Nächstes binden wir eine der am häufigsten verwendeten Bibliotheken in der MQL5-API ein, die Handelsbibliothek. Sie ist für die einfache Verwaltung unserer Positionen unerlässlich.

//+------------------------------------------------------------------+
//| Libraries                                                        |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
CTrade Trade;

Außerdem müssen wir globale Variablen für die Speicherung von Marktpreisen und technischen Indikatorwerten erstellen.

//+------------------------------------------------------------------+
//| Global variables                                                 |
//+------------------------------------------------------------------+
int    ma_handler,trading_ma_handler,atr_handler;
double ma[],atr[],trading_ma[];
double ask,bid;

Der Hauptteil unserer MQL5-Anwendung besteht aus Ereignisbehandlungen. Diese werden immer dann aufgerufen, wenn in unserem Terminal ein neues Ereignis eintritt, z. B. wenn neue Preise angeboten werden oder die Anwendung aus dem Chart entfernt wird. Einige Ereignisse werden von unserem Endnutzer ausgelöst, andere vom Handelsserver. OnInit() wird ausgelöst, wenn der Endnutzer unsere Handelsanwendung startet. Sie ruft eine spezielle Funktion auf, die wir für die Initialisierung unserer globalen Variablen entwickelt haben.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   setup();
//---
   return(INIT_SUCCEEDED);
  }

Wenn unser Endnutzer die Handelsanwendung aus dem Chart entfernt, wird OnDeinit() aufgerufen. Wir werden diese Ereignisbehandlung verwenden, um die nicht mehr benötigten System-Speicherressourcen freizugeben.

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
IndicatorRelease(atr_handler);
IndicatorRelease(ma_handler);
IndicatorRelease(trading_ma_handler);
  }

OnTick() wird vom Handelsserver ausgelöst, nicht vom Endnutzer. Es wird immer dann aufgerufen, wenn wir aktualisierte Preisinformationen erhalten. Wir rufen spezielle Funktionen auf, um unsere technischen Indikatoren zu aktualisieren und die neuen Kursinformationen zu speichern, und wenn wir keine offenen Positionen haben, werden wir anschließend versuchen, eine Position zu eröffnen.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   update();
   if(PositionsTotal() == 0)
      find_setup();
  }
//+------------------------------------------------------------------+

Unsere Regeln für die Eröffnung von Positionen im Rahmen der Trendfolgestrategie sind einfach zu verstehen. Wenn die Kurse über dem gleitenden 100-Perioden-Durchschnitt liegen, eröffnen wir Kaufpositionen mit festen Stop-Loss. Andernfalls, wenn die Kurse unter dem gleitenden Durchschnitt liegen, werden wir Verkaufspositionen eröffnen.

//+------------------------------------------------------------------+
//| Find setup                                                       |
//+------------------------------------------------------------------+
void find_setup(void)
  {
//Buy on rallies
   if(iClose(Symbol(),PERIOD_CURRENT,0) > trading_ma[0])
      Trade.Buy(CURRENT_VOL,Symbol(),ask,(bid - (atr[0] * SL_WIDTH)),(bid + (atr[0] * SL_WIDTH)),"");

   if(iClose(Symbol(),PERIOD_CURRENT,0) < trading_ma[0])
      Trade.Sell(CURRENT_VOL,Symbol(),bid,(ask + (atr[0] * SL_WIDTH)),(ask - (atr[0] * SL_WIDTH)),"");
  }

Die Funktion, die vom OnInit() aufgerufen wird, um unsere technischen Indikatoren einzurichten.

//+------------------------------------------------------------------+
//| Setup our global variables                                       |
//+------------------------------------------------------------------+
void setup(void)
  {
   atr_handler =  iATR(Symbol(),TF_1,ATR_PERIOD);
   trading_ma_handler  =  iMA(Symbol(),PERIOD_CURRENT,TRADING_MA_PERIOD,MA_SHIFT,MA_MODE,MA_PRICE);
  }

Unsere Funktion Update() wird vom OnTick() aufgerufen, um unsere globalen Variablen zu aktualisieren.

//+------------------------------------------------------------------+
//| Update                                                           |
//+------------------------------------------------------------------+
void update(void)
  {
   static datetime time_stamp;
   datetime current_time = iTime(Symbol(),PERIOD_CURRENT,0);
   ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK);
   bid = SymbolInfoDouble(Symbol(),SYMBOL_BID);

   if(time_stamp != current_time)
     {
      time_stamp = current_time;
      CopyBuffer(atr_handler,0,0,1,atr);
      CopyBuffer(trading_ma_handler,0,0,1,trading_ma);
     }
  }
//+------------------------------------------------------------------+

Dies ist der aktuelle Stand unserer Handelsanwendung in ihrer jetzigen Form.

//+------------------------------------------------------------------+
//|                              Dynamic Moving Average Strategy.mq5 |
//|                                               Gamuchirai Ndawana |
//|                    https://www.mql5.com/en/users/gamuchiraindawa |
//+------------------------------------------------------------------+
#property copyright "Gamuchirai Ndawana"
#property link      "https://www.mql5.com/en/users/gamuchiraindawa"
#property version   "1.00"

//+------------------------------------------------------------------+
//| System constants                                                 |
//+------------------------------------------------------------------+
#define MA_PRICE    PRICE_CLOSE           //Moving average applied price
#define MA_MODE    MODE_EMA               //Moving average type
#define MA_SHIFT   0                      //Moving average shift
#define ATR_PERIOD 14                     //Period for our ATR
#define TF_1       PERIOD_D1              //Hihger order timeframe
#define TRADING_MA_PERIOD  100            //Moving average period
#define SL_WIDTH 1.5                      //How wide should the stop loss be?
#define CURRENT_VOL 0.1                   //Trading volume

//+------------------------------------------------------------------+
//| Libraries                                                        |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
CTrade Trade;

//+------------------------------------------------------------------+
//| Global variables                                                 |
//+------------------------------------------------------------------+
int    ma_handler,trading_ma_handler,atr_handler;
double ma[],atr[],trading_ma[];
double ask,bid;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   setup();
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
IndicatorRelease(atr_handler);
IndicatorRelease(ma_handler);
IndicatorRelease(trading_ma_handler);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   update();
   if(PositionsTotal() == 0)
      find_setup();
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Find setup                                                       |
//+------------------------------------------------------------------+
void find_setup(void)
  {
//Buy on rallies
   if(iClose(Symbol(),PERIOD_CURRENT,0) > trading_ma[0])
      Trade.Buy(CURRENT_VOL,Symbol(),ask,(bid - (atr[0] * SL_WIDTH)),(bid + (atr[0] * SL_WIDTH)),"");

   if(iClose(Symbol(),PERIOD_CURRENT,0) < trading_ma[0])
      Trade.Sell(CURRENT_VOL,Symbol(),bid,(ask + (atr[0] * SL_WIDTH)),(ask - (atr[0] * SL_WIDTH)));
  }

//+------------------------------------------------------------------+
//| Setup our global variables                                       |
//+------------------------------------------------------------------+
void setup(void)
  {
   atr_handler =  iATR(Symbol(),TF_1,ATR_PERIOD);
   trading_ma_handler  =  iMA(Symbol(),PERIOD_CURRENT,TRADING_MA_PERIOD,MA_SHIFT,MA_MODE,MA_PRICE);
  }

//+------------------------------------------------------------------+
//| Update                                                           |
//+------------------------------------------------------------------+
void update(void)
  {
   static datetime time_stamp;
   datetime current_time = iTime(Symbol(),PERIOD_CURRENT,0);
   ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK);
   bid = SymbolInfoDouble(Symbol(),SYMBOL_BID);

   if(time_stamp != current_time)
     {
      time_stamp = current_time;
      CopyBuffer(atr_handler,0,0,1,atr);
      CopyBuffer(trading_ma_handler,0,0,1,trading_ma);
     }
  }
//+------------------------------------------------------------------+


Festlegen einer Benchmark

Beobachten wir, wie gut unsere Trendfolgeanwendung anhand der historischen Daten M1 vom Mittwoch, dem 1. Januar 2020, bis Montag, dem 6. Januar 2025, funktioniert. Wir werden das Paar EURUSD für unseren Backtest verwenden. Beachten Sie, dass wir keine Parameter der Strategie einstellen, daher ist die Einstellung „Vorwärtstest“ des Backtests auf „No“ gesetzt. 

Abb. 3: Die Daten unseres Backtests

Wir werden unsere Modellierung zusätzlich auf reale Ticks einstellen, um die tatsächlichen Handelsbedingungen in unserem Testgerät bestmöglich nachzubilden. Außerdem gibt uns die Verwendung der Zufallsverzögerung eine Vorstellung davon, wie unser System unter Stress arbeiten wird. Es sei daran erinnert, dass die Latenzzeit beim Handel in Echtzeit variieren kann. Daher wollen wir uns natürlich an die Bedingungen halten, die wir für unser System in der Produktion erwarten.

Abb. 4: Die Bedingungen für unseren Backtest

Dies ist die resultierende Saldenkurve, die durch unsere Handelsstrategie über unseren 5-Jahres-Backtest erhalten wurde. Achten Sie auf die Differenz zwischen dem Höchstsaldo und dem Endsaldo des Kontos. Die Ergebnisse zeigen deutlich, dass unsere Strategie nicht stabil ist und dass sie dazu neigt, fast genauso viel Geld zu verlieren wie zu gewinnen. Unsere Gewinnsträhnen, die wir mit dieser Strategie erzielten, waren nur von kurzer Dauer und wurden von Drawdown-Perioden gefolgt, die genauso lange andauerten wie unsere Gewinnperioden. Diese lang anhaltenden Drawdown-Perioden sind wahrscheinlich mit den Zeiten verbunden, in denen der Markt keinem langfristigen Trend folgte, und unsere naive Handelsstrategie kann mit solchen Marktbedingungen nicht angemessen umgehen.

Abb. 5: Unsere Saldenkurve, die sich aus der ersten Version unserer Strategie ergibt

Wenn wir uns die detaillierten Ergebnisse unseres Backtests ansehen, können wir feststellen, dass unsere Strategie über den 5-Jahres-Zeitraum profitabel war, was uns ermutigt, zu versuchen, die Strategie zu verbessern, allerdings ist das Verhältnis von Gewinn und Verlust fast 50/50. Dies ist unerwünscht. Wir wollen die Verluste herausfiltern, damit wir einen höheren Anteil an Gewinnen haben. Unsere durchschnittlichen aufeinanderfolgenden Gewinne sind 2 und unsere durchschnittlichen aufeinanderfolgenden Verluste sind ebenfalls 2, was unsere Bemerkung unterstützt, dass unser System genauso wahrscheinlich Gewinne für uns macht, wie es unser Kapital verliert. Einem solchen System kann man nicht zutrauen, dass es ohne Aufsicht handelt.

Abb. 6: Eine ausführliche Zusammenfassung unseres Backtests


Überblick über die vorgeschlagene Lösung

Dieser erste Backtest bringt uns nun dazu, das vorgeschlagene System im Detail zu diskutieren. Wir wissen, dass unser System profitiert, wenn die Märkte einem Trend folgen, aber es wird Geld verlieren, wenn die Märkte keinen Trend haben. Wie können wir also unser System so gestalten, dass es unabhängig entscheiden kann, ob ein Markt wahrscheinlich einem Trend folgt oder nicht?

Wenn wir wissen, wie die Märkte zwischen diesen 2 Modi wechseln, kann unser System den gleitenden Durchschnittsindikator besser nutzen. An Tagen, an denen unser System davon ausgeht, dass sich der Markt in einer bestimmten Spanne bewegt, platziert es seine Handelsgeschäfte gegen den Trend, der durch den gleitenden Durchschnitt impliziert wird. Andernfalls platziert er seine Handelsgeschäfte im Einklang mit dem impliziten Trend.

Mit anderen Worten: Wenn unser Algorithmus feststellt, dass sich das von uns gehandelte Symbol wahrscheinlich in einer Handelsspanne befindet, werden wir verkaufen, sobald sich die Kurse über dem gleitenden Durchschnitt schließen, anstatt zu kaufen. Wenn unser Algorithmus jedoch feststellt, dass sich das Symbol in einem Trend befindet, werden wir kaufen, wenn der Kurs über dem gleitenden Durchschnitt schließt. Im Wesentlichen wird ein und dasselbe Marktereignis unterschiedlich interpretiert und behandelt, je nachdem, in welchem Modus wir den Markt erkennen.

Es bleibt also nur noch die Frage: „Wie können wir erkennen, in welchem Modus sich der Markt befindet?“. Die von uns vorgeschlagene Strategie besteht darin, die Preisniveaus in 4 diskrete Zonen zu unterteilen. Der Grundgedanke hinter unserer Strategie ist intuitiv:

  1. Trend-Modus: Der Markt kann sich nur entweder in Zone 1 oder in Zone 4 wirklich entwickeln.
  2. Bereichs-Modus: Der Markt kann nur in Zone 2 oder Zone 3 wirklich begrenzt sein.
Eine Visualisierung der 4 Zonen ist in Abb. 7 zu sehen.

Abb. 7: Ein einfaches illustriertes Beispiel für unser zonenbasiertes Handelssystem

Schauen wir uns die einzelnen Zonen nacheinander an, beginnend mit Zone 1. Wenn sich die Kursniveaus in Zone 1 befinden, werten wir dies als steigenden Marktstimmung und suchen nach Kaufgelegenheiten, sobald die Kursniveaus über dem gleitenden 100-Perioden-Durchschnitt schließen. Der Abstand von Take-Profit und Stop-Loss wird in unserem ersten Backtest beibehalten. Solange wir uns in Zone 1 befinden, werden wir nur nach Kaufgelegenheiten suchen, um den Aufwärtstrends zu folgen. Wir werden keine Verkaufspositionen eingehen, solange sich die Kurse in Zone 1 befinden!

Abb. 8: Erläuterung der Bedeutung von Zone 1

Wenn das Preisniveau von Zone 1 in Zone 2 fällt, ändert sich unsere Marktstimmung. Wir glauben nicht mehr, dass echte Trends zu beobachten sind, solange das Preisniveau in Zone 2 bleibt. Vielmehr glauben wir, dass die Preisniveaus in Zone 2 dazu neigen, um das Mittelband zwischen Zone 2 und Zone 3 zu oszillieren. Wir werden ausschließlich nach Verkaufsmöglichkeiten Ausschau halten, wenn wir uns in der Zone 2 befinden, da wir davon ausgehen, dass die Preisniveaus eine Tendenz zum Rückfall in das mittlere Band aufweisen werden.

Wenn die Kurse über den gleitenden 100-Perioden-Durchschnitt steigen, während wir uns in Zone 2 befinden, werden wir verkaufen, wobei die Platzierung unseres Stop-Losses aus unserer ursprünglichen Handelsstrategie beibehalten wird. Die Positionierung unseres Take-Profits muss jedoch geändert werden. Wir werden unseren Take-Profit auf dem mittleren Band zwischen Zone 2 und Zone 3 platzieren, weil wir vermuten, dass sich die Kursniveaus dort einpendeln werden, solange wir uns innerhalb der Zone 2 befinden. Beachten Sie, dass wir keine Kaufposition eingehen, solange wir uns in Zone 2 befinden. Jede Zone erlaubt nur 1 Positionstyp.

Abb. 9: Wie sich unsere Handelsstrategie beim Durchlaufen der 4 von uns definierten Marktzonen entwickelt

Ich hoffe, dass sich an diesem Punkt ein Muster im Kopf des Lesers bildet und die restlichen Regeln intuitiv zu verstehen sind. Lassen Sie uns ein lustiges Quiz spielen, um sicherzustellen, dass wir beide auf der gleichen Seite sind. Wenn das Preisniveau in die Zone 3 fällt, welche Arten von Positionen sollten wir dann Ihrer Meinung nach eingehen? Ich hoffe, Sie haben im Geiste schon von Kaufpositionen gesprochen.

Und wo werden wir unseren Take-Profit platzieren? Ich möchte glauben, dass der Leser nun intuitiv versteht, dass, wenn wir uns in Zone 2 oder Zone 3 befinden, unseren Take-Profit auf dem mittleren Band platziert wird, das Zone 2 und Zone 3 trennt.

Mit anderen Worten: Wenn wir uns in Zone 3 befinden, werden wir nur dann Kaufpositionen eingehen, wenn die Kurse unter dem gleitenden 100-Perioden-Durchschnitt schließen. Unser Take-Profit wird auf dem mittleren Band platziert, und unser Stop-Loss wird die gleiche Größe haben wie in der ursprünglichen Strategie. Solange wir uns in Zone 3 befinden, gehen wir keine Verkaufspositionen ein.

Abb. 10: Zone 2 und Zone 3 gelten als Gebiete, in denen sich der Mittelwert umkehrt.

Wenn sich die Kursniveaus in der Zone 4 befinden, werden wir nur nach Gelegenheiten suchen, um Verkaufspositionen einzunehmen. Wir werden unsere Verkaufspositionen immer dann eingehen, wenn die Kurse unter unserem gleitenden 100-Perioden-Durchschnitt liegen. Der Abstand von Take-Profit und Stop-Loss ist identisch mit der Breite unserer Benchmark-Strategie. Wir werden keine Kaufpositionen eingehen, solange sich die Preise in der Zone 4 befinden.

Abb. 11: In Zone 4 werden sich unserer Meinung nach Abwärtstrends bilden. Daher werden wir in Zone 4 keine Kaufpositionen eingehen.

Um die 4-Zonen-Strategie zu implementieren, müssen wir Änderungen an der ursprünglichen Trendfolgestrategie vornehmen:

Vorgeschlagene Änderung Verwendungszweck
Neue Systemvariablen Wir benötigen neue Systemvariablen, die uns die Kontrolle über den Kanal geben, den wir erstellen werden.
Erstellung von Nutzereingaben Obwohl der Nutzer nicht alle Aspekte des Kanals kontrollieren kann, werden einige Aspekte wie die Breite des Kanals und die Anzahl der Balken, die zur Berechnung des Kanals verwendet werden, vom Endnutzer kontrolliert.
Neue globale Variablen Es werden neue globale Variablen in Bezug auf den Kanal erstellt, um die Handelslogik unserer Anwendung zu verbessern.
Änderungen an unseren nutzerdefinierten Funktionen Wir werden neue Funktionen in unserer Handelsanwendung erstellen und einige der vorhandenen Funktionen ändern, um die von uns skizzierte Handelslogik zu implementieren.

Auf die Einzelheiten der Berechnung des Kanals wird im weiteren Verlauf noch eingegangen.


Implementierung unserer Lösung in MQL5

Wir beginnen mit der Definition von Systemkonstanten, die uns helfen, den Kanal zu berechnen. Für die Berechnung des Kanals müssen wir zunächst einen gleitenden Durchschnittsindikator auf einen Zeitrahmen anwenden, der höher ist als der vorgesehene Zeitrahmen. In unserem Beispiel wollen wir also die M1 handeln. Wir werden einen gleitenden 20-Perioden-Durchschnitt auf den täglichen Zeitrahmen anwenden und ihn zur Berechnung unseres Kanals verwenden. Wenn wir den Kanal auf demselben Zeitrahmen berechnen, auf dem wir handeln wollen, können wir feststellen, dass sich unser Kanal zu sehr bewegt, als dass wir eine stabile Handelsstrategie entwickeln könnten.

#define MA_PERIOD  20                     //Moving Average period for calculating the mid point of our range zone

Als Nächstes müssen wir globale Variablen definieren, die den Kanal für uns im Auge behalten. Insbesondere wollen wir wissen, wo der Rand von Zone 2 (obere Grenze) und Zone 3 (untere Grenze) liegt. Darüber hinaus möchten wir herausfinden, wo die Grenze zwischen Zone 2 und Zone 3 liegt (mittlere Grenze). Die Zone 1 wird von der oberen Grenze aus definiert und hat keine Obergrenze. Ebenso beginnt Zone 4 dort, wo Zone 3 endet, und hat keine untere Begrenzung.

double range_zone_mid,range_zone_ub,range_zone_lb;
int    active_zone;

Nun werden wir die Parameter des Kanals festlegen, die der Endnutzer ändern kann. So z. B. den Zeitraum der Berechnung aus historischen Daten und die vom Nutzer gewünschte Breite des Kanals. Es ist wichtig, dem Nutzer die Möglichkeit zu geben, die Breite des Kanals zu kontrollieren, da die Kanalbreite mit der Höhe des Risikos auf dem Konto verbunden ist. Für unsere Demonstration werden wir die Kanalbreite einfach auf 2 ATR-Werte einstellen. Die meisten Märkte neigen dazu, sich an einem Tag um 1 ATR zu bewegen, und wir möchten nicht, dass sich unser Kanal zu sehr bewegt.

//+------------------------------------------------------------------+
//| User inputs                                                      |
//+------------------------------------------------------------------+
input group "Technical Analysis"
input double atr_multiple = 
2 ;            //ATR Multiple
input int    bars_used = 30;              //How Many Bars should we use to calculate the channel?

Wir brauchen eine Funktion, die feststellt, in welcher Zone wir uns gerade befinden. Die Logik zur Bestimmung der Zonen wurde bereits ausführlich erläutert.

//+------------------------------------------------------------------+
//| Get our current active zone                                      |
//+------------------------------------------------------------------+
void get_active_zone(void)
  {
   if(iClose(Symbol(),PERIOD_CURRENT,0) > range_zone_ub)
     {
      active_zone = 1;
      return;
     }

   if((iClose(Symbol(),PERIOD_CURRENT,0) < range_zone_ub) && (iClose(Symbol(),PERIOD_CURRENT,0) > range_zone_mid))
     {
      active_zone = 2;
      return;
     }

   if((iClose(Symbol(),PERIOD_CURRENT,0) < range_zone_mid) && (iClose(Symbol(),PERIOD_CURRENT,0) > range_zone_lb))
     {
      active_zone = 3;
      return;
     }

   if(iClose(Symbol(),PERIOD_CURRENT,0) < range_zone_lb)
     {
      active_zone = 4;
      return;
     }
  }
//+------------------------------------------------------------------+

Wir müssen auch unsere Setup-Funktion aktualisieren. Ignorieren wir die Teile der Funktion, die sich nicht geändert haben. Wir führen nur 1 neuen technischen Indikator ein. Der erste technische Indikator, den wir verwendeten, war ein gleitender Durchschnitt über 100 Perioden auf dem Zeitrahmen M1. Unser neuer Indikator ist ein gleitender Durchschnitt über 20 Perioden, der auf den täglichen Zeitrahmen angewendet wird.

//+------------------------------------------------------------------+
//| Setup our global variables                                       |
//+------------------------------------------------------------------+
void setup(void)
  {
   //We have omitted parts of the code that have not changed
   ma_handler  =  iMA(Symbol(),TF_1,MA_PERIOD,MA_SHIFT,MA_MODE,MA_PRICE);
  }

Als Nächstes müssen wir unsere Aktualisierungsfunktion anpassen. Wir haben absichtlich Teile der Funktion, die unverändert geblieben sind, weggelassen, damit wir uns auf die neuen Teile der Funktion konzentrieren können. Wir beginnen mit der Initialisierung eines Vektors mit Nullen. Dann kopieren wir mit unserem neuen Vektor die Anzahl der Balken, die wir nach den Anweisungen des Nutzers für unsere Berechnungen verwenden sollen, aus dem neuen gleitenden Durchschnitt, den wir in unserem vorherigen Schritt auf den täglichen Zeitrahmen angewendet haben.

Wir nehmen dann den Durchschnittswert des gleitenden 20-Perioden-Tagesdurchschnitts, der das mittlere Band zwischen Zone 2 und Zone 3 darstellt. Die Grenzen von Zone 2 und Zone 3 werden durch Addition (Zone 2) und Subtraktion (Zone 3) eines Vielfachen des ATR-Wertes vom Mittelpunkt berechnet, den wir anhand des gleitenden 20-Perioden-Durchschnitts ermittelt haben.

//+------------------------------------------------------------------+
//| Update                                                           |
//+------------------------------------------------------------------+
void update(void)
  {
   static datetime time_stamp;
   datetime current_time = iTime(Symbol(),PERIOD_CURRENT,0);
   ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK);
   bid = SymbolInfoDouble(Symbol(),SYMBOL_BID);

   if(time_stamp != current_time)
     {
      //Omitted parts of the function that remained unchanged
      vector ma_average = vector::Zeros(1);
      ma_average.CopyIndicatorBuffer(ma_handler,0,1,bars_used);
      range_zone_mid = ma_average.Mean();
      range_zone_ub = (range_zone_mid + (atr[0] * atr_multiple));
      range_zone_lb = (range_zone_mid - (atr[0] * atr_multiple));
      get_active_zone();
      Comment("Zone: ",active_zone);
      ObjectDelete(0,"RANGE HIGH");
      ObjectDelete(0,"RANGE LOW");
      ObjectDelete(0,"RANGE MID");
      ObjectCreate(0,"RANGE MID",OBJ_HLINE,0,0,range_zone_mid);
      ObjectCreate(0,"RANGE LOW",OBJ_HLINE,0,0,range_zone_lb);
      ObjectCreate(0,"RANGE HIGH",OBJ_HLINE,0,0,range_zone_ub);
     }
  }

Die letzte Änderung, die wir vornehmen müssen, betrifft die Art und Weise, wie wir unsere Setups finden. Die Handelslogik, die unserer Positionsplatzierung zugrunde liegt, wurde bereits ausführlich erörtert, sodass dieses Codesegment für den Leser einfach zu verstehen sein sollte. Um den Grundgedanken zusammenzufassen, werden wir nur die Entwicklung in den Zonen 1 und 4 verfolgen. Das heißt, wenn der Kurs über dem gleitenden 100-Perioden-Durchschnitt in Zone 1 schließt, werden wir kaufen. Wenn wir uns in den Zonen 2 oder 3 befinden, werden wir gegen den Trend handeln, d. h. wenn die Kurse über dem gleitenden 100-Perioden-Durchschnitt in Zone 2 schließen, werden wir verkaufen, anstatt zu kaufen, wie wir es in Zone 1 getan haben.

//+------------------------------------------------------------------+
//| Find setup                                                       |
//+------------------------------------------------------------------+
void find_setup(void)
  {

// Follow the trend
   if(active_zone == 1)
     {
      //Buy on rallies
      if(iClose(Symbol(),PERIOD_CURRENT,0) > trading_ma[0])
         Trade.Buy(CURRENT_VOL,Symbol(),ask,(bid - (atr[0] * 1.5)),(bid + (atr[0] * SL_WIDTH)),"");
     }

// Go against the trend
   if(active_zone == 2)
     {
      //Sell on rallies
      if(iClose(Symbol(),PERIOD_CURRENT,0) > trading_ma[0])
         Trade.Sell(CURRENT_VOL,Symbol(),bid,(ask + (atr[0] * 1.5)),range_zone_mid);
     }

// Go against the trend
   if(active_zone == 3)
     {
      //Buy the dip
      if(iClose(Symbol(),PERIOD_CURRENT,0) < trading_ma[0])
         Trade.Buy(CURRENT_VOL,Symbol(),ask,(bid - (atr[0] * 1.5)),range_zone_mid,"");
     }

// Follow the trend
   if(active_zone == 4)
     {
      //Sell the dip
      if(iClose(Symbol(),PERIOD_CURRENT,0) < trading_ma[0])
         Trade.Sell(CURRENT_VOL,Symbol(),bid,(ask + (atr[0] * atr_multiple)),(ask - (atr[0] * SL_WIDTH)));
     }
  }

Zusammengefasst sieht die überarbeitete Version unserer Handelsstrategie folgendermaßen aus.

//+------------------------------------------------------------------+
//|                              Dynamic Moving Average Strategy.mq5 |
//|                                               Gamuchirai Ndawana |
//|                    https://www.mql5.com/en/users/gamuchiraindawa |
//+------------------------------------------------------------------+
#property copyright "Gamuchirai Ndawana"
#property link      "https://www.mql5.com/en/users/gamuchiraindawa"
#property version   "1.00"

//+------------------------------------------------------------------+
//| System constants                                                 |
//+------------------------------------------------------------------+
#define MA_PRICE    PRICE_CLOSE           //Moving average shift
#define MA_MODE    MODE_EMA               //Moving average shift
#define MA_SHIFT   0                      //Moving average shift
#define ATR_PERIOD 14                     //Period for our ATR
#define TF_1       PERIOD_D1              //Hihger order timeframe
#define MA_PERIOD  20                     //Moving Average period for calculating the mid point of our range zone
#define TRADING_MA_PERIOD  100            //Moving average period
#define SL_WIDTH   1.5                    //How wide should the stop loss be?
#define CURRENT_VOL 0.1                   //Trading volume

//+------------------------------------------------------------------+
//| Libraries                                                        |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
CTrade Trade;

//+------------------------------------------------------------------+
//| Global variables                                                 |
//+------------------------------------------------------------------+
int    ma_handler,trading_ma_handler,atr_handler;
double ma[],atr[],trading_ma[];
double range_zone_mid,range_zone_ub,range_zone_lb;
double ask,bid;
int    active_zone;

//+------------------------------------------------------------------+
//| User inputs                                                      |
//+------------------------------------------------------------------+
input group "Technical Analysis"
input double atr_multiple = 1;            //ATR Multiple
input int    bars_used = 30;              //How Many Bars should we use to calculate the channel?

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   setup();
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   IndicatorRelease(ma_handler);
   IndicatorRelease(atr_handler);
   IndicatorRelease(trading_ma_handler);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   update();
   if(PositionsTotal() == 0)
      find_setup();
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Find setup                                                       |
//+------------------------------------------------------------------+
void find_setup(void)
  {

// Follow the trend
   if(active_zone == 1)
     {
      //Buy on rallies
      if(iClose(Symbol(),PERIOD_CURRENT,0) > trading_ma[0])
         Trade.Buy(CURRENT_VOL,Symbol(),ask,(bid - (atr[0] * 1.5)),(bid + (atr[0] * SL_WIDTH)),"");
     }

// Go against the trend
   if(active_zone == 2)
     {
      //Sell on rallies
      if(iClose(Symbol(),PERIOD_CURRENT,0) > trading_ma[0])
         Trade.Sell(CURRENT_VOL,Symbol(),bid,(ask + (atr[0] * 1.5)),range_zone_mid);
     }

// Go against the trend
   if(active_zone == 3)
     {
      //Buy the dip
      if(iClose(Symbol(),PERIOD_CURRENT,0) < trading_ma[0])
         Trade.Buy(CURRENT_VOL,Symbol(),ask,(bid - (atr[0] * 1.5)),range_zone_mid,"");
     }

// Follow the trend
   if(active_zone == 4)
     {
      //Sell the dip
      if(iClose(Symbol(),PERIOD_CURRENT,0) < trading_ma[0])
         Trade.Sell(CURRENT_VOL,Symbol(),bid,(ask + (atr[0] * atr_multiple)),(ask - (atr[0] * SL_WIDTH)));
     }
  }

//+------------------------------------------------------------------+
//| Setup our global variables                                       |
//+------------------------------------------------------------------+
void setup(void)
  {
   atr_handler =  iATR(Symbol(),TF_1,ATR_PERIOD);
   trading_ma_handler  =  iMA(Symbol(),PERIOD_CURRENT,TRADING_MA_PERIOD,MA_SHIFT,MA_MODE,MA_PRICE);
   ma_handler  =  iMA(Symbol(),TF_1,MA_PERIOD,MA_SHIFT,MA_MODE,MA_PRICE);
  }

//+------------------------------------------------------------------+
//| Update                                                           |
//+------------------------------------------------------------------+
void update(void)
  {
   static datetime time_stamp;
   datetime current_time = iTime(Symbol(),PERIOD_CURRENT,0);
   ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK);
   bid = SymbolInfoDouble(Symbol(),SYMBOL_BID);

   if(time_stamp != current_time)
     {
      time_stamp = current_time;
      CopyBuffer(ma_handler,0,0,1,ma);
      CopyBuffer(atr_handler,0,0,1,atr);
      CopyBuffer(trading_ma_handler,0,0,1,trading_ma);
      vector ma_average = vector::Zeros(1);
      ma_average.CopyIndicatorBuffer(ma_handler,0,1,bars_used);
      range_zone_mid = ma_average.Mean();
      range_zone_ub = (range_zone_mid + (atr[0] * atr_multiple));
      range_zone_lb = (range_zone_mid - (atr[0] * atr_multiple));
      get_active_zone();
      Comment("Zone: ",active_zone);
      ObjectDelete(0,"RANGE HIGH");
      ObjectDelete(0,"RANGE LOW");
      ObjectDelete(0,"RANGE MID");
      ObjectCreate(0,"RANGE MID",OBJ_HLINE,0,0,range_zone_mid);
      ObjectCreate(0,"RANGE LOW",OBJ_HLINE,0,0,range_zone_lb);
      ObjectCreate(0,"RANGE HIGH",OBJ_HLINE,0,0,range_zone_ub);
     }
  }

//+------------------------------------------------------------------+
//| Get our current active zone                                      |
//+------------------------------------------------------------------+
void get_active_zone(void)
  {
   if(iClose(Symbol(),PERIOD_CURRENT,0) > range_zone_ub)
     {
      active_zone = 1;
      return;
     }

   if((iClose(Symbol(),PERIOD_CURRENT,0) < range_zone_ub) && (iClose(Symbol(),PERIOD_CURRENT,0) > range_zone_mid))
     {
      active_zone = 2;
      return;
     }

   if((iClose(Symbol(),PERIOD_CURRENT,0) < range_zone_mid) && (iClose(Symbol(),PERIOD_CURRENT,0) > range_zone_lb))
     {
      active_zone = 3;
      return;
     }

   if(iClose(Symbol(),PERIOD_CURRENT,0) < range_zone_lb)
     {
      active_zone = 4;
      return;
     }
  }
//+------------------------------------------------------------------+

Wir werden sehen, ob diese Änderungen uns die gewünschte Kontrolle geben werden. Wir werden den Zeitraum festlegen, in dem der Backtest durchgeführt wird.

Abb. 12: Der Zeitraum unseres Backtests entspricht dem ursprünglichen Zeitraum, den wir verwendet haben

Ebenso werden wir die Bedingungen des Backtests so ändern, dass sie mit unserem vorherigen Test übereinstimmen.

Abb. 13: Sicherstellen, dass beide Strategien unter den gleichen Bedingungen getestet werden

Unsere neue Strategie hat 2 Inputs. Die erste Einstellung steuert die Breite des Kanals, die zweite, wie viele Balken für die Berechnung des Kanals verwendet werden. 

Abb. 14: Die Kontrollen, die der Endnutzer einstellen kann

Die neue Saldenkurve, die durch unsere überarbeitete Strategie entstanden ist, hat wie jede andere Strategie auch Verlustperioden, aber beachten Sie, wie schnell sie sich von einem Verlust erholt. Sie bleibt nicht stecken, wie es bei unserer alten Handelsstrategie der Fall war. Er verliert einen Handelsgeschäft, richtet sich aber schnell wieder am Markt aus. Dies zeigt sich daran, dass auf unsere Verlustphasen eine Phase des profitablen Handels folgt.

Abb. 15: Die Kapitalkurve, die durch unsere neue Handelsstrategie entsteht

Wenn wir die detaillierten Ergebnisse unserer neuen Handelsstrategie analysieren, können wir feststellen, dass der Anteil unserer Handelsgeschäfte mit Gewinn von 52% auf 86% gestiegen ist und unsere Handelsgeschäfte mit Verlust von 47% auf 13% gesunken sind. Unser durchschnittlicher Gewinn ist kleiner als unser durchschnittlicher Verlust, aber bedenken Sie, dass wir Stop-Loss und Take-Profit fest vereinbart haben. Dieses Problem könnte dadurch gelöst werden, dass wir den Stop-Loss festhalten, wenn wir verlieren, und ihn nachziehen lassen, wenn wir gewinnen. Darüber hinaus stieg unsere durchschnittliche Anzahl von Gewinnern in Folge von 2 auf 9, während unsere durchschnittliche Anzahl von Verlierern in Folge von 2 auf 1 fiel. Unsere ursprüngliche Strategie platzierte 300 Handelsgeschäfte. Während unsere neue Strategie insgesamt 1301 Handelsgeschäfte platzierte. Unsere neue Strategie besteht also darin, mehr Handelsgeschäfte zu tätigen und häufiger zu gewinnen. 

Abb. 16: Eine detaillierte Zusammenfassung der Ergebnisse unserer neuen Strategie


Schlussfolgerung

In der heutigen Diskussion begannen wir mit einer naiven Trendfolgestrategie, die wir mit historischer Daten, die in unserem MetaTrader 5-Terminal verfügbar waren, zu besseren Entscheidungen entwickelten. Theoretisch können wir diese Strategie mit Stift und Papier umsetzen, aber zum Glück für uns rationalisiert die MQL5-API diesen gesamten Prozess für uns. Von der schnellen Berechnung statistischer Kennzahlen mit Hilfe von Vektorfunktionen bis hin zur optimalen Handelsausführung wird ein Großteil der schweren Arbeit im Hintergrund von unserer MetaTrader 5-Anwendung erledigt. In zukünftigen Artikeln werden wir darüber nachdenken, wie wir den Anteil der Handelsgeschäfte mit Gewinn noch weiter erhöhen können und wie wir die Größe der mit Verlust besser kontrollieren können. 

Angehängte Datei Beschreibung
Benchmark Moving Average Strategy Die erste Umsetzung unserer Trendfolgestrategie
Dynamic Moving Average Strategy Unser neuer Vorschlag für eine dynamische Strategie.

Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/16856

Wie man ein volumenbasiertes Handelssystem aufbaut und optimiert (Chaikin Money Flow - CMF) Wie man ein volumenbasiertes Handelssystem aufbaut und optimiert (Chaikin Money Flow - CMF)
In diesem Artikel werden wir einen volumenbasierten Indikator, den Chaikin Money Flow (CMF), vorstellen, nachdem wir erläutert haben, wie er konstruiert, berechnet und verwendet werden kann. Wir werden verstehen, wie man einen nutzerdefinierten Indikator erstellt. Wir werden einige einfache Strategien vorstellen, die verwendet werden können, und sie dann testen, um zu verstehen, welche davon besser ist.
Einführung in MQL5 (Teil 10): Eine Anleitung für Anfänger zur Arbeit mit den integrierten Indikatoren in MQL5 Einführung in MQL5 (Teil 10): Eine Anleitung für Anfänger zur Arbeit mit den integrierten Indikatoren in MQL5
Dieser Artikel führt in die Arbeit mit integrierten Indikatoren in MQL5 ein und konzentriert sich auf die Erstellung eines RSI-basierten Expert Advisors (EA) mit einem projektbasierten Ansatz. Sie werden lernen, RSI-Werte abzurufen und zu nutzen, Liquiditätsdurchbrüche zu handhaben und die Handelsvisualisierung mit Chart-Objekten zu verbessern. Darüber hinaus wird in dem Artikel ein wirksames Risikomanagement hervorgehoben, einschließlich der Festlegung eines prozentualen Risikos, der Umsetzung von Risiko-Ertrags-Verhältnissen und der Anwendung von Risikomodifikationen zur Sicherung von Gewinnen.
Nachrichtenhandel leicht gemacht (Teil 6): Ausführen des Handels (III) Nachrichtenhandel leicht gemacht (Teil 6): Ausführen des Handels (III)
In diesem Artikel wird die Nachrichtenfilterung für einzelne Nachrichtenereignisse auf der Grundlage ihrer IDs implementiert. Darüber hinaus werden frühere SQL-Abfragen verbessert, um zusätzliche Informationen zu liefern oder die Laufzeit der Abfrage zu verkürzen. Außerdem wird der in den vorangegangenen Artikeln erstellte Code funktionsfähig gemacht.
Entwicklung eines Toolkit zur Analyse von Preisaktionen (Teil 5): Volatilitätsnavigator EA Entwicklung eines Toolkit zur Analyse von Preisaktionen (Teil 5): Volatilitätsnavigator EA
Die Marktrichtung zu bestimmen kann einfach sein, aber zu wissen, wann man einsteigen sollte, kann eine Herausforderung sein. Im Rahmen der Serie „Entwicklung eines Toolkit zur Analyse von Preisaktionen" freue ich mich, ein weiteres Tool vorzustellen, das Einstiegspunkte, Take-Profit-Levels und Stop-Loss-Platzierungen bietet. Um dies zu erreichen, haben wir die Programmiersprache MQL5 verwendet. In diesem Artikel wollen wir die einzelnen Schritte näher erläutern.