English 日本語
preview
Aufbau und Test von Keltner-Kanal-Handelssystemen

Aufbau und Test von Keltner-Kanal-Handelssystemen

MetaTrader 5Handel | 7 Mai 2024, 15:56
176 0
Mohamed Abdelmaaboud
Mohamed Abdelmaaboud

Einführung

Es ist sehr wichtig, das Konzept der Volatilität auf dem Finanzmarkt zu verstehen und es zu Ihren Gunsten zu nutzen, wenn Sie auf dem Markt handeln. Der Hauptzweck dieses Artikels ist es, Ihnen zu helfen, Zeit zu sparen, indem er Ihnen viele Testergebnisse für Handelssysteme liefert, die auf dem Konzept der Volatilität basieren und den technischen Indikator Keltner Channel verwenden, um herauszufinden, wie er als Teil Ihres kompletten Systems funktionieren kann, um die Volatilität zu messen oder unter bestimmten Bedingungen bezüglich dieses wichtigen Konzepts zu handeln.

Das Wichtigste ist, dass Sie feststellen, dass Sie Ihr System nach Ihren Vorlieben optimieren müssen, um bessere Ergebnisse zu erzielen. Sie müssen also Ihre Arbeit machen und viele verschiedene Aspekte entsprechend Ihren Handelszielen testen, um das beste endgültige Setup für Ihr System zu finden.

Der Ansatz in diesem Artikel wird sein, zu lernen, wie wir unsere nutzerdefinierten Keltner-Kanal-Indikator und Handelssysteme erstellen können, um einfache Handelsstrategien zu handeln und dann testen Sie sie auf verschiedenen finanziellen Vermögenswerten und vergleichen sie, um herauszufinden, welche eine bessere Ergebnisse nach unseren Tests und Optimierung geben kann, und wir werden versuchen, das beste Setup so viel wie möglich zu bieten. Wir werden diesen Artikel anhand der folgenden Themen behandeln:

Nach den vorangegangenen Themen werden wir in der Lage sein, den Keltner-Kanal-Indikator auf der Grundlage seines Hauptkonzepts zu verwenden. Nachdem wir den Indikator im Detail verstanden haben, werden wir in der Lage sein, Handelssysteme zu erstellen, die auf einfachen Strategien basieren. Danach können wir sie testen und anhand ihrer Ergebnisse feststellen, welche Strategie besser ist als andere. Ich hoffe, Sie finden diesen Artikel nützlich, um Ihr Trading zu verbessern und bessere Ergebnisse zu erzielen, nachdem Sie etwas Neues gelernt haben oder mehr Einblicke in eine verwandte Idee erhalten, die Ihre Trading-Ergebnisse verbessert.
Haftungsausschluss: Alle Informationen werden in der vorliegenden Form nur zu Informationszwecken bereitgestellt und sind nicht für Handelszwecke oder als Ratschläge gedacht. Die Informationen garantieren keinen Erfolg. Wenn Sie sich dafür entscheiden, diese Materialien auf einem Ihrer Handelskonten zu verwenden, tun Sie dies auf eigenes Risiko und Sie sind allein verantwortlich.


Definition der Volatilität

In diesem Teil werden wir verstehen, was Volatilität im Allgemeinen ist und welche Bedeutung sie für den Handel und den Finanzmarkt hat. Die Volatilität ist ein statistisches Konzept, mit dem die Streuung der Renditen von Finanzanlagen gemessen werden kann, und wenn die Volatilität zunimmt, steigt auch das Risiko. Wir können sagen, dass Volatilität ein Konzept ist, das sich auch auf große Schwankungen auf den Märkten in beide Richtungen bezieht, und diese Schwankungsbewegung wird durch die Schwankung um den Durchschnittspreis gemessen.

Es gibt viele Gründe, die zur Marktvolatilität führen, wie z.B. die folgenden:

  • Marktstimmung: Die Marktvolatilität kann durch die Emotionen der Händler beeinflusst werden.
  • Gewinne: Gewinnmeldungen können aufgrund ihrer Ergebnisse die Marktvolatilität beeinflussen.
  • Wirtschaftliche Ereignisse und Indikatoren: Wichtige Wirtschaftsereignisse und -indikatoren können ebenfalls zu Volatilität am Markt führen, wie z. B. Inflation, BIP und Beschäftigungsnachrichten.
  • Liquidität: Es besteht ein Zusammenhang zwischen der Liquidität und der Volatilität auf dem Markt; bei geringer Liquidität steigt die Volatilität.

Was die Messung der Volatilität angeht, so gibt es viele Möglichkeiten oder Methoden, die in diesem Zusammenhang verwendet werden können, wie z. B. der Beta-Koeffizient und die Standardabweichungen. Es gibt auch viele technische Indikatoren, die zur Messung der Volatilität auf dem Markt verwendet werden können, wie z. B. Bollinger-Bänder, Average True Range, Volatilitätsindex (VIX) und der Keltner-Kanal, um den es in diesem Artikel geht. Die Messung der Volatilität kann sehr hilfreich sein, um die Fluktuation der Finanzanlage zu bewerten.

Es gibt auch viele Arten von Volatilität, wie z. B. die implizite Volatilität und die historische Volatilität.


Definition des Keltner-Kanals

In diesem Teil werden wir mehr über den Keltner-Kanal-Indikator im Detail erfahren, wie er verwendet wird und wie wir diesen Indikator berechnen können, um zu verstehen, wie wir ihn zu unseren Gunsten nutzen können. Der Keltner-Kanal-Indikator ist ein Volatilitätsindikator, der zwei Bänder über und unter den Kursen und dazwischen einen gleitenden Durchschnitt enthält.

Der Keltner-Kanal-Indikator wurde erstmals von Chester Keltner in den 1960er Jahren in seinem Buch „How to Make Money in Commodities“ vorgestellt. Er verwendete den einfachen gleitenden Durchschnitt und die Hoch-Tief-Spanne für seine Berechnung, entwickelte sich aber zu der heute üblichen Form, die den Average True Range (ATR) verwendet. Die typische Einstellung für den gleitenden Durchschnitt ist 20 Perioden, die oberen und unteren Bänder werden als das Doppelte der ATR über und unter dem EMA berechnet, und diese Einstellungen können je nach Nutzerpräferenz und Handelsziel angepasst werden.

Es gilt als Kaufsignal, wenn der Kurs das obere Band des Keltner-Kanal erreicht, und als Verkaufssignal, wenn der Kurs das untere Band erreicht. Er kann also zur Bestimmung der Trendrichtung verwendet werden. Bänder können auch als Unterstützung und Widerstand verwendet werden, wenn sich die Preise zwischen ihnen bewegen, ohne dass ein klarer Auf- oder Abwärtstrend zu erkennen ist. Zusammenfassend lässt sich sagen, dass der Keltner-Kanal ein technischer Indikator ist, der die Volatilität von Finanzwerten anhand des exponentiellen gleitenden Durchschnitts und der durchschnittlichen wahren Spanne misst. 

Im Folgenden wird beschrieben, wie der Keltner-Kanal-Indikator berechnet werden kann:

  • Berechnung des exponentiellen gleitenden Durchschnitts, der die Mittellinie des Indikators darstellt.
  • Berechnung des Average True Range, um ihn zur Berechnung des Abstands der Bänder zu verwenden.
  • Berechnung des obere Bands, indem zum EMA der ATR multipliziert mit zwei addiert wird.
  • Berechnung des unteren Bands, indem vom EMA der ATR multipliziert mit zwei subtrahiert wird.

Also,

Die mittlere Linie des Keltner-Kanals = Exponentieller Gleitender Durchschnitt (EMA)

Das obere Band des Keltner-Kanals = Exponentieller gleitender Durchschnitt (EMA) + 2 * Average True Range (ATR)

Das untere Band des Keltner-Kanals = Exponentieller gleitender Durchschnitt (EMA) - 2 * Average True Range (ATR)


Strategien mit dem Keltner-Kanal

Nach dem, was wir darüber verstanden haben, wie wir den Keltner-Kanal-Indikator nutzen können, indem wir die Bänder als Unterstützung oder Widerstand verwenden oder als Aufwärtssignal, wenn wir uns dem oberen Band nähern, bzw. als Abwärtssignal, wenn wir uns dem unteren Band nähern, werden wir zwei einfache Strategien anwenden:

  • Rebound, Abprall, von den Bändern: Wir erteilen einen Kaufauftrag, wenn der Kurs über das untere Band steigt, und einen Verkaufsauftrag, wenn der Kurs unter das obere Band fällt.
  • Ausbruch aus dem Kanal der Bänder: Wir erteilen einen Kaufauftrag, wenn das obere Band durchbrochen wird, und einen Verkaufsauftrag, wenn das untere Band durchbrochen wird.

Strategie eins: Rebound von den Bändern

Nach dem, was wir über den Keltner-Kanal-Indikator wissen, können wir ihn verwenden, indem wir Kauf- und Verkaufssignale auf der Grundlage der Position des Schlusskurses und der Bänder der Indikatoren erkennen. Wir eröffnen einen Kaufauftrag, wenn der letzte Schlusskurs unter dem vorherigen unteren Band schließt und gleichzeitig der letzte Schlusskurs über dem unteren Band schließt. Der Verkaufsauftrag wird eröffnet, wenn der vorhergehende letzte Schlusskurs über dem oberen Band liegt und gleichzeitig der letzte Schlusskurs unter dem oberen Band schließt.

Einfach:

Vorletzter Schlusskurs < unterem Band und letzter Schlusskurs > unterem Band ==> Kaufsignal

Vorletzter Schlusskurs > oberem Band und letzter Schlusskurs < oberem Band ==> Verkaufssignal

Strategie zwei: Ausbruch aus dem Bandkanal
Gemäß dieser Strategie werden wir einen Auftrag erteilen, wenn ein Ausbruch stattfindet. Wir erteilen einen Kaufauftrag, wenn der vorletzte Schlusskurs unter dem vorletzten Preis des oberen Bandes liegt und gleichzeitig der letzte Schlusskurs über dem letzten des oberen Bandes geschlossen hat. Umgekehrt werden wir einen Verkaufsauftrag erteilen, wenn der vorletzte Schlusskurs über dem vorletzten Preis des unteren Bandes liegt und gleichzeitig der letzte Schlusskurs unter dem letzten des unteren Bandes geschlossen hat.

Einfach:

Vorletzter Schlusskurs < oberem Band und letzter Schlusskurs > oberem Band ==> Kaufsignal

Vorletzter Schlusskurs > unterem Band und letzter Schlusskurs < unterem Band ==> Verkaufssignal


Keltner.Kanal Handelssystem

In diesem Teil werden wir ein Handelssystem erstellen, das automatisch Aufträge auf der Grundlage der zuvor erwähnten Strategie erteilt. Zunächst werden wir den nutzerdefinierten Indikator erstellen, um den Keltner-Kanal-Indikator zu berechnen, der später zur Erstellung unseres Handelssystems verwendet werden soll:

Wir verwenden den Präprozessor #property, um den Ort des Indikators zu bestimmen, und wir verwenden indicator_chart_window, um den Indikator im Chart anzuzeigen

#property indicator_chart_window

Bei der Bestimmung der Pufferindikator über den Identifikator von indicator_buffers verwenden wir 3, und bei der Bestimmung der Anzahl der zu zeichnenden Linien (Plots) verwenden wir ebenfalls:

#property indicator_buffers 3
#property indicator_plots 3

Es folgt die Einstellung der Eigenschaften von Indikatoren in Bezug auf Typ, Stil, Breite, Farbe und Beschriftung für die obere, mittlere und untere Linie:

#property indicator_type1 DRAW_LINE
#property indicator_style1 STYLE_SOLID
#property indicator_width1 2
#property indicator_color1 clrRed
#property indicator_label1 "Keltner Upper Band"


#property indicator_type2 DRAW_LINE
#property indicator_style2 STYLE_SOLID
#property indicator_width2 1
#property indicator_color2 clrBlue
#property indicator_label2 "Keltner Middle Line"


#property indicator_type3 DRAW_LINE
#property indicator_style3 STYLE_SOLID
#property indicator_width3 2
#property indicator_color3 clrGreen
#property indicator_label3 "Keltner Lower Band"

Einstellung des Inputs für den Indikator in Bezug auf die Periodenlänge des gleitenden Durchschnitts, den Kanalmultiplikator, die Verwendung von ATR zur Berechnung der Bänder, die Methode oder den Modus des gleitenden Durchschnitts und den Preistyp:

input int      maPeriod            = 10;           // Moving Average Period
input double   multiInp             = 2.0;          // Channel multiplier
input bool     isAtr                 = false;        // ATR
input ENUM_MA_METHOD     maMode       = MODE_EMA;     // Moving Average Mode
input ENUM_APPLIED_PRICE priceType = PRICE_TYPICAL;// Price Type

Deklarieren als globale Variable die drei Arrays (upper, middle, and lower), zwei Handles für gleitenden Durchschnitt und ATR sowie minBars:

double upper[], middle[], lower[];
int maHandle, atrHandle;
static int minBars = maPeriod + 1;

In der Funktion OnInit() verknüpfen wir den Indikatorpuffer mit Arrays, indem wir die Funktion SetIndexBuffer verwenden, deren Parameter sind:

  • index: Um den Indexpuffer zu bestimmen, wobei 0 für die obere, 1 für die mittlere und 2 für die untere Linie steht.
  • buffer[]: für die Arrays upper, middle, and lower.
  • data_type: Legt fest, was im Indikator gespeichert werden soll. Es kann einer der ENUM_INDEXBUFFER_TYPE sein, standardmäßig ist es INDICATOR_DATA, und das werden wir auch verwenden:
   SetIndexBuffer(0,upper,     INDICATOR_DATA);
   SetIndexBuffer(1,middle,  INDICATOR_DATA);
   SetIndexBuffer(2,lower,  INDICATOR_DATA);

Setzen des AS_Series-Flags auf Arrays mit der Funktion ArraySetAsSeries mit folgenden Parameterwerten:

  • array[]: Um die Arrays upper, middle, und lower über ihre Referenz zu bestimmen.
  • Flag: Um die Richtung der Array-Indizierung zu bestimmen. Bei Erfolg wird true zurückgegeben, im Falle eines Fehlers false.
   ArraySetAsSeries(upper, true);
   ArraySetAsSeries(middle, true);
   ArraySetAsSeries(lower, true);

Der Name des Indikators wird mit Hilfe der Funktion IndicatorSetString und den folgenden Parametern festgelegt:

  • prop_id: Um den Bezeichner zu bestimmen, kann es einer der ENUM_CUSTOMIND_PROPERTY_STRING sein, wir werden den INDICATOR_SHORTNAME verwenden, um den Namen des Indikators festzulegen.
  • prop_value: Um den gewünschten Indikatornamen zu bestimmen.
IndicatorSetString(INDICATOR_SHORTNAME,"Custom Keltner Channel " + IntegerToString(maPeriod));

Die Einstellung der Dezimalstellen der Indikatorwerte mit der Funktion IndicatorSetInteger. Ihren Parametern entspricht der Funktion IndicatorSetString, mit der Ausnahme, dass der Datentyp von prop_value statt einer Zeichenkette:

IndicatorSetInteger(INDICATOR_DIGITS,_Digits);

Definition des gleitenden Durchschnitts mit Hilfe der Funktion iMA, deren Parameter sind:

  • symbol: Zur Angabe des Symbolnamens verwenden wir NULL, sodass das Symbol des Charts verwendet wird.
  • period: Zur Angabe des Zeitrahmens, wobei wir 0 verwenden, um den aktuelle Zeitrahmen des Charts zu verwenden.
  • ma_period: Zur Angabe der Periodenlänge des gleitenden Durchschnitts wird die Nutzereingabe von maPeriod verwendet.
  • ma_shift: Zur Angabe der horizontalen Verschiebung, falls erforderlich.
  • ma_method: Zur Angabe der Berechnungsmethode des gleitenden Durchschnitts, wobei wir die Nutzereingabe von maMode verwenden werden.
  • applied_price: Zur Angabe des Preistyps, wobei wir die Nutzereingabe priceType verwenden werden.
maHandle = iMA(NULL, 0, maPeriod, 0, maMode, priceType);

Die Definition der ATR entsprechend der Eingabe von ATR, ob er, je nach der Eingabe true oder false, für die Berechnung der Bänder verwendet werden soll oder nicht:

   if(isAtr)
     {
      atrHandle = iATR(NULL, 0, maPeriod);
      if(atrHandle == INVALID_HANDLE)
        {
         Print("Handle Error");
         return(INIT_FAILED);
        }
     }
   else
      atrHandle = INVALID_HANDLE;

Deklaration der Funktion indValue zur Angabe der Indikatorpuffer, des gleitenden Durchschnitts, der Werte von middle, upper, und lower:

void indValue(const double& h[], const double& l[], int shift)
  {
   double ma[1];
   if(CopyBuffer(maHandle, 0, shift, 1, ma) <= 0)
      return;
   middle[shift] = ma[0];
   double average = AVG(h, l, shift);
   upper[shift]    = middle[shift] + average * multiInp;
   lower[shift] = middle[shift] - average * multiInp;
  }

Deklaration der Funktion AVG zur Berechnung der Positionen der Grenzen auf der Grundlage des berechneten Multiplikators:

double AVG(const double& High[],const double& Low[], int shift)
  {
   double sum = 0.0;
   if(atrHandle == INVALID_HANDLE)
     {
      for(int i = shift; i < shift + maPeriod; i++)
         sum += High[i] - Low[i];
     }
   else
     {
      double t[];
      ArrayResize(t, maPeriod);
      ArrayInitialize(t, 0);
      if(CopyBuffer(atrHandle, 0, shift, maPeriod, t) <= 0)
         return sum;
      for(int i = 0; i < maPeriod; i++)
         sum += t[i];
     }
   return sum / maPeriod;
  }

In der Funktion OnCalculate berechnen wir die Indikatorwerte wie im folgenden Code:

   if(rates_total <= minBars)
      return 0;
   ArraySetAsSeries(close,true);
   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low,  true);
   int limit = rates_total - prev_calculated;
   if(limit == 0)             
     {
     }
   else
      if(limit == 1)      
        {
         indValue(high, low, 1);
         return(rates_total);
        }
      else
         if(limit > 1)       
           {
            ArrayInitialize(middle, EMPTY_VALUE);
            ArrayInitialize(upper,    EMPTY_VALUE);
            ArrayInitialize(lower, EMPTY_VALUE);
            limit = rates_total - minBars;
            for(int i = limit; i >= 1 && !IsStopped(); i--)
               indValue(high, low, i);
            return(rates_total);
           }
   indValue(high, low, 0);
   return(rates_total);

Wir können also den vollständigen Code in einem Block des nutzerdefinierten Keltner-Kanal-Indikators finden, der wie folgt aussieht:

//+------------------------------------------------------------------+
//|                                       Custom_Keltner_Channel.mq5 |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property indicator_buffers 3
#property indicator_plots 3
#property indicator_type1 DRAW_LINE
#property indicator_style1 STYLE_SOLID
#property indicator_width1 2
#property indicator_color1 clrRed
#property indicator_label1 "Keltner Upper Band"
#property indicator_type2 DRAW_LINE
#property indicator_style2 STYLE_SOLID
#property indicator_width2 1
#property indicator_color2 clrBlue
#property indicator_label2 "Keltner Middle Line"
#property indicator_type3 DRAW_LINE
#property indicator_style3 STYLE_SOLID
#property indicator_width3 2
#property indicator_color3 clrGreen
#property indicator_label3 "Keltner Lower Band"
input int      maPeriod            = 10;           // Moving Average Period
input double   multiInp             = 2.0;          // Channel multiplier
input bool     isAtr                 = false;        // ATR
input ENUM_MA_METHOD     maMode       = MODE_EMA;     // Moving Average Mode
input ENUM_APPLIED_PRICE priceType = PRICE_TYPICAL;// Price Type
double upper[], middle[], lower[];
int maHandle, atrHandle;
static int minBars = maPeriod + 1;
//+------------------------------------------------------------------+
int OnInit()
  {
   SetIndexBuffer(0,upper, INDICATOR_DATA);
   SetIndexBuffer(1,middle, INDICATOR_DATA);
   SetIndexBuffer(2,lower, INDICATOR_DATA);
   ArraySetAsSeries(upper, true);
   ArraySetAsSeries(middle, true);
   ArraySetAsSeries(lower, true);
   IndicatorSetString(INDICATOR_SHORTNAME,"Custom Keltner Channel " + IntegerToString(maPeriod));
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
   maHandle = iMA(NULL, 0, maPeriod, 0, maMode, priceType);
   if(isAtr)
     {
      atrHandle = iATR(NULL, 0, maPeriod);
      if(atrHandle == INVALID_HANDLE)
        {
         Print("Handle Error");
         return(INIT_FAILED);
        }
     }
   else
      atrHandle = INVALID_HANDLE;
   return INIT_SUCCEEDED;
  }
void indValue(const double& h[], const double& l[], int shift)
  {
   double ma[1];
   if(CopyBuffer(maHandle, 0, shift, 1, ma) <= 0)
      return;
   middle[shift] = ma[0];
   double average = AVG(h, l, shift);
   upper[shift]    = middle[shift] + average * multiInp;
   lower[shift] = middle[shift] - average * multiInp;
  }
double AVG(const double& High[],const double& Low[], int shift)
  {
   double sum = 0.0;
   if(atrHandle == INVALID_HANDLE)
     {
      for(int i = shift; i < shift + maPeriod; i++)
         sum += High[i] - Low[i];
     }
   else
     {
      double t[];
      ArrayResize(t, maPeriod);
      ArrayInitialize(t, 0);
      if(CopyBuffer(atrHandle, 0, shift, maPeriod, t) <= 0)
         return sum;
      for(int i = 0; i < maPeriod; i++)
         sum += t[i];
     }
   return sum / maPeriod;
  }
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
   if(rates_total <= minBars)
      return 0;
   ArraySetAsSeries(close,true);
   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low,  true);
   int limit = rates_total - prev_calculated;
   if(limit == 0)             
     {
     }
   else
      if(limit == 1)      
        {
         indValue(high, low, 1);
         return(rates_total);
        }
      else
         if(limit > 1)       
           {
            ArrayInitialize(middle, EMPTY_VALUE);
            ArrayInitialize(upper,    EMPTY_VALUE);
            ArrayInitialize(lower, EMPTY_VALUE);
            limit = rates_total - minBars;
            for(int i = limit; i >= 1 && !IsStopped(); i--)
               indValue(high, low, i);
            return(rates_total);
           }
   indValue(high, low, 0);
   return(rates_total);
  }
//+------------------------------------------------------------------+

Nach dem Kompilieren dieses Codes finden wir ihn im Indikatorordner, nachdem wir ihn in den Chart eingefügt haben, finden wir ihn wie im folgenden Beispiel:

KCH_insert

Wie wir im vorherigen Chart sehen können, umgibt ein Kanal den Preis, der den Keltner Channel Indikator darstellt. Jetzt müssen wir unser Handelssystem mit diesem Indikator auf der Grundlage der genannten Strategien erstellen und wir können das wie folgt tun.

Nun müssen wir für jede der genannten Strategien ein Handelssystem erstellen, um die Platzierung von Kauf- und Verkaufsaufträgen auf der Grundlage des Konzepts dieser Strategien zu automatisieren.


Strategie eins: Das Handelssystem Rebound von den Bändern:

Die erste Strategie ist, wie bereits erwähnt, die Eröffnung von Handelsgeschäften auf der Grundlage des Rebounds von den Bändern durch die Schaffung eines EA, um automatisch Aufträge zu platzieren. Im Folgenden wird beschrieben, wie wir dieses Handelssystem codieren können:

Wir werden die Handelsdatei mit Hilfe des Präprozessors include einbinden

#include <trade/trade.mqh>

Deklarieren von Eingaben für die gleitende Durchschnittsperiode, dem Kanalmultiplikator, dem ATR für die Berechnung der Bänder oder nicht, dem Berechnungsmodus des gleitenden Durchschnitts, dem Preistyp, der Lotgröße, Stop-Loss-Pips und Take-Profit-Pips:

input int      maPeriod            = 10;           // Moving Average Period
input double   multiInp             = 2.0;          // Channel multiplier
input bool     isAtr                 = false;        // ATR
input ENUM_MA_METHOD     maMode       = MODE_EMA;     // Moving Average Mode
input ENUM_APPLIED_PRICE priceType = PRICE_TYPICAL;// Price Type
input double      lotSize=1;
input double slPips = 150;
input double tpPips = 300;

Deklarieren der globalen Variablen für Keltner, barTotal und das Handelsobjekt:

int keltner;
int barsTotal;
CTrade trade;

In der Funktion OnInit() wird die Variable barsTotal definiert, die später bei der Auswertung, ob es einen neuen Balken gibt oder nicht, verwendet wird, indem die Funktion iBars und ihre Parameter verwendet werden:

  • symbol: Um das Symbol zu bestimmen, auf das der Code angewendet werden soll, und wir werden _Symbol verwenden, um auf das aktuelle Symbol angewendet zu werden.
  • timeframe: Um den Zeitraum zu bestimmen, und wir werden PERIOD_CURRENT für den aktuellen Zeitrahmen verwenden.
barsTotal=iBars(_Symbol,PERIOD_CURRENT);

Definition der Keltner-Variable, die als Indikator-Handle verwendet werden soll, mit Hilfe der Funktion iCustom und ihren Parametern:

  • symbol: Zur Bestimmung des Symbolnamens und _Symbl wird für das aktuelle Symbol verwendet.
  • period: Zur Festlegung des Zeitrahmens wird PERIOD_CURRENT verwendet.
  • Name: Um den Namen des Indikators anzugeben.
  • ...: Liste der Indikatoreingänge.
keltner = iCustom(_Symbol,PERIOD_CURRENT,"Custom_Keltner_Channel",maPeriod,multiInp, isAtr, maMode, priceType);

In der Funktion OnDeinit wird eine Meldung ausgegeben, wenn der EA entfernt wird:

Print("EA is removed");

In der Funktion OnTick() deklarieren und definieren wir eine Integer-Variable für die Balken, die mit barsTotal verglichen wird, um zu prüfen, ob es einen neuen Balken gibt:

int bars=iBars(_Symbol,PERIOD_CURRENT);

Vergleich von barsTotal mit bars, wenn es kleiner als bars ist:

if(barsTotal < bars)

Wenn barsTotal kleiner als bars ist, müssen wir den Wert von bars dem Wert von barsTotal zuweisen.

barsTotal=bars;

Dann deklarieren wir die drei Arrays upper, middle und lower

double upper[], middle[], lower[];

Abrufen von Daten aus den Indikatorpuffern mit der Funktion CopyBuffer, deren Parameter sind:

  • indicator_handle: Um den Indikatorgriff zu bestimmen, verwenden wir Keltner für upper, middle und lower.
  • buffer_num: Gibt die Puffernummer des Indikators an mit 0 für upper, 1 für middle und 2 für lower.
  • start_pos: Hier wird die Position angegeben, an der die Zählung beginnen soll; wir verwenden 0.
  • count: Zur Angabe der Menge, die kopiert werden soll; wir verwenden 3.
  • buffer[]: Um das zu kopierende Ziel-Array zu spezifizieren, wir verwenden upper, middle und lower.
      CopyBuffer(keltner,0,0,3,upper);
      CopyBuffer(keltner,1,0,3,middle);
      CopyBuffer(keltner,2,0,3,lower);

Setzen des AS_SERIES-Flags mit der Funktion ArraySetAsSeries

      ArraySetAsSeries(upper,true);
      ArraySetAsSeries(middle,true);
      ArraySetAsSeries(lower,true);

Deklarieren und Definieren der Werte prevUpper, prevMiddle und prevLower für den vorherigen bzw. letzten Wert der Indikatorwerte:

      double prevUpperValue = NormalizeDouble(upper[2], 5);
      double prevMiddleValue = NormalizeDouble(middle[2], 5);
      double prevLowerValue = NormalizeDouble(lower[2], 5);

Deklarieren und Definieren der Werte upperValue, middleValue und lowerValue für die letzten Indikatorwerte

      double upperValue = NormalizeDouble(upper[1], 5);
      double middleValue = NormalizeDouble(middle[1], 5);
      double lowerValue = NormalizeDouble(lower[1], 5);

Die Deklaration und Definition des letzten und des vorhergehenden des letzten Schlusskurses unter Verwendung der Funktion iClose mit folgenden Parametern:

  • symbol: Zur Angabe des Symbolnamens.
  • timeframe: Zur Angabe des anzuwendenden Zeitrahmens.
  • shift: Um anzugeben, ob eine Rückwärtsverschiebung erforderlich ist oder nicht, indem der Index angegeben wird.
      double lastClose=iClose(_Symbol,PERIOD_CURRENT,1);
      double prevLastClose=iClose(_Symbol,PERIOD_CURRENT,2);

Einstellung der Bedingungen für eine Kaufstrategie: Der vorletzte Schlusskurs ist niedriger als der vorletzte Wert des unteren Bandes und der letzte Schlusskurs liegt über dem letzte Wert des unteren Bandes:

      if(prevLastClose<prevLowerValue && lastClose>lowerValue)
        {
         double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
         double slVal = lowerValue - slPips*_Point;
         double tpVal = middleValue + tpPips*_Point;
         trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
        }

Einstellung der Bedingungen für eine Verkaufsstrategie: Der vorletzte Schlusskurs ist höher als der vorletzte Wert des oberen Bandes und der letzte Schlusskurs liegt unter dem letzte Wert des oberen Bandes:

      if(prevLastClose>prevUpperValue && lastClose<upperValue)
        {
         double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
         double slVal = upperValue + slPips*_Point;
         double tpVal = middleValue - tpPips*_Point;
         trade.Sell(lotSize,_Symbol,bid,upperValue,tpVal);
        }

Im Folgenden finden Sie den vollständigen Code in einem Block für die Erstellung eines Handelssystems, das auf der Strategie des Rebounds von den Bändern basiert:

//+------------------------------------------------------------------+
//|                                       Keltner_Trading_System.mq5 |
//+------------------------------------------------------------------+
#include <trade/trade.mqh>
input int      maPeriod            = 10;           // Moving Average Period
input double   multiInp             = 2.0;          // Channel multiplier
input bool     isAtr                 = false;        // ATR
input ENUM_MA_METHOD     maMode       = MODE_EMA;     // Moving Average Mode
input ENUM_APPLIED_PRICE priceType = PRICE_TYPICAL;// Price Type
input double      lotSize=1;
input double slPips = 150;
input double tpPips = 300; 
int keltner;
int barsTotal;
CTrade trade;
int OnInit()
  {
   barsTotal=iBars(_Symbol,PERIOD_CURRENT);
   keltner = iCustom(_Symbol,PERIOD_CURRENT,"Custom_Keltner_Channel",maPeriod,multiInp, isAtr, maMode, priceType);
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   Print("EA is removed");
  }
//+------------------------------------------------------------------+
void OnTick()
  {
   int bars=iBars(_Symbol,PERIOD_CURRENT);
   if(barsTotal < bars)
     {
      barsTotal=bars;
      double upper[], middle[], lower[];
      CopyBuffer(keltner,0,0,3,upper);
      CopyBuffer(keltner,1,0,3,middle);
      CopyBuffer(keltner,2,0,3,lower);
      ArraySetAsSeries(upper,true);
      ArraySetAsSeries(middle,true);
      ArraySetAsSeries(lower,true);
      double prevUpperValue = NormalizeDouble(upper[2], 5);
      double prevMiddleValue = NormalizeDouble(middle[2], 5);
      double prevLowerValue = NormalizeDouble(lower[2], 5);
      double upperValue = NormalizeDouble(upper[1], 5);
      double middleValue = NormalizeDouble(middle[1], 5);
      double lowerValue = NormalizeDouble(lower[1], 5);
      double lastClose=iClose(_Symbol,PERIOD_CURRENT,1);
      double prevLastClose=iClose(_Symbol,PERIOD_CURRENT,2);
      if(prevLastClose<prevLowerValue && lastClose>lowerValue)
        {
         double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
         double slVal = lowerValue - slPips*_Point;
         double tpVal = middleValue + tpPips*_Point;
         trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
        }
      if(prevLastClose>prevUpperValue && lastClose<upperValue)
        {
         double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
         double slVal = upperValue + slPips*_Point;
         double tpVal = middleValue - tpPips*_Point;
         trade.Sell(lotSize,_Symbol,bid,upperValue,tpVal);
        }
     }
  }
//+------------------------------------------------------------------+


Strategie zwei: Ausbruch aus dem Bandkanal:

Die erste Strategie ist, wie bereits erwähnt, die Eröffnung von Handelsgeschäften, die auf dem Über- oder Unterschreiten der Bänder basieren, indem ein EA erstellt wird, der automatisch die Aufträge erteilt. Die folgenden Punkte sind für Unterschiede bei der Codierung dieses Handelssystems:

Die Bedingungen für Kaufaufträge: Der vorletzte Schlusskurs ist niedriger als der vorletzte Wert des oberen Bandes und der letzte Schlusskurs liegt über dem letzte Wert des oberen Bandes:

      if(prevLastClose<prevUpperValue && lastClose>upperValue)
        {
         double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
         double slVal = upperValue - slPips*_Point;
         double tpVal = upperValue + tpPips*_Point;
         trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
        }

Die Bedingungen für Verkaufsaufträge: Der vorletzte Schlusskurs ist höher als der vorletzte Wert des unteren Bandes und der letzte Schlusskurs liegt unter dem letzte Wert des unteren Bandes:

      if(prevLastClose>prevLowerValue && lastClose<lowerValue)
        {
         double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
         double slVal = lowerValue + slPips*_Point;
         double tpVal = lowerValue - tpPips*_Point;
         trade.Sell(lotSize,_Symbol,bid,upperValue,tpVal);
        }

Im Folgenden finden Sie den vollständigen Code für die Erstellung eines Handelssystems, das auf der Strategie des Ausbruchs aus dem Kanal basiert:

//+------------------------------------------------------------------+
//|                                      Keltner_Trading_System2.mq5 |
//+------------------------------------------------------------------+
#include <trade/trade.mqh>
input int      maPeriod            = 10;           // Moving Average Period
input double   multiInp             = 2.0;          // Channel multiplier
input bool     isAtr                 = false;        // ATR
input ENUM_MA_METHOD     maMode       = MODE_EMA;     // Moving Average Mode
input ENUM_APPLIED_PRICE priceType = PRICE_TYPICAL;// Price Type
input double      lotSize=1;
input double slPips = 150;
input double tpPips = 500; 
int keltner;
int barsTotal;
CTrade trade;
int OnInit()
  {
   barsTotal=iBars(_Symbol,PERIOD_CURRENT);
   keltner = iCustom(_Symbol,PERIOD_CURRENT,"Custom_Keltner_Channel",maPeriod,multiInp, isAtr, maMode, priceType);
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   Print("EA is removed");
  }
//+------------------------------------------------------------------+
void OnTick()
  {
   int bars=iBars(_Symbol,PERIOD_CURRENT);
   if(barsTotal < bars)
     {
      barsTotal=bars;
      double upper[], middle[], lower[];
      CopyBuffer(keltner,0,0,3,upper);
      CopyBuffer(keltner,1,0,3,middle);
      CopyBuffer(keltner,2,0,3,lower);
      ArraySetAsSeries(upper,true);
      ArraySetAsSeries(middle,true);
      ArraySetAsSeries(lower,true);
      double prevUpperValue = NormalizeDouble(upper[2], 5);
      double prevMiddleValue = NormalizeDouble(middle[2], 5);
      double prevLowerValue = NormalizeDouble(lower[2], 5);
      double upperValue = NormalizeDouble(upper[1], 5);
      double middleValue = NormalizeDouble(middle[1], 5);
      double lowerValue = NormalizeDouble(lower[1], 5);
      double lastClose=iClose(_Symbol,PERIOD_CURRENT,1);
      double prevLastClose=iClose(_Symbol,PERIOD_CURRENT,2);
      if(prevLastClose<prevUpperValue && lastClose>upperValue)
        {
         double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
         double slVal = upperValue - slPips*_Point;
         double tpVal = upperValue + tpPips*_Point;
         trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
        }
      if(prevLastClose>prevLowerValue && lastClose<lowerValue)
        {
         double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
         double slVal = lowerValue + slPips*_Point;
         double tpVal = lowerValue - tpPips*_Point;
         trade.Sell(lotSize,_Symbol,bid,upperValue,tpVal);
        }
     }
  }
//+------------------------------------------------------------------+

Jetzt werden wir diese EAs von zwei Handelsstrategien auf dem Gold XAUUSD, EURUSD, GBPUSD unter Verwendung des 1H-Zeitrahmens mit den Standardeinstellungen der Eingänge für den Zeitraum vom 1. Januar 2023 bis 31. Dezember 2023 testen,

Wir werden uns auf die folgenden Schlüsselkennzahlen konzentrieren, um einen Vergleich zwischen den beiden durchzuführen:

  • Net Profit: Er wird berechnet, indem der Bruttoverlust vom Bruttogewinn abgezogen wird. Der höchste Wert ist der beste.
  • Balance DD relative: Dies ist der maximale Verlust, den das Konto während des Handels erleidet. Das Niedrigste ist das Beste.
  • Profit factor: Es ist das Verhältnis von Bruttogewinn zu Bruttoverlust. Der höchste Wert ist der beste.
  • Expected payoff: Es ist der durchschnittliche Gewinn oder Verlust eines Handelsgeschäfts. Der höchste Wert ist der beste.
  • Recovery factor: Er misst, wie gut sich die getestete Strategie nach Verlusten erholt. Der höchste Wert ist der beste.
  • Sharpe Ratio: Sie bestimmt das Risiko und die Stabilität des getesteten Handelssystems, indem sie die Rendite mit der risikofreien Rendite vergleicht. Die höchste Sharpe Ratio ist die beste.


Strategie eins: Rebound von den Bändern:

XAUUSD Test

Die Ergebnisse des Tests von XAUUSD:

Diagramm

Backtest

Backtest2

Wie wir aus den vorangegangenen Ergebnissen des XAUUSD-Tests ersehen können, konnten wir in dem Test folgende Zahlen ermitteln:

  • Net Profit: 11918.60 USD.
  • Balance DD relative: 3.67%.
  • Profit factor: 1.36.
  • Expected payoff: 75.91.
  • Recovery factor: 2.85.
  • Sharpe Ratio: 4.06.

EURUSD-Test

Die Ergebnisse des EURUSD-Tests:

Diagramm

Backtest

Backtest2

Wie wir aus den vorangegangenen Ergebnissen des EURUSD-Tests sehen können, konnten wir in dem Test folgende Zahlen ermitteln:

  • Net Profit: 2221.20 USD.
  • Balance DD relative: 2.86%.
  • Profit factor: 1.11.
  • Expected payoff: 13.63.
  • Recovery factor: 0.68.
  • Sharpe Ratio: 1.09.

GBPUSD-Test

Die Ergebnisse des GBPUSD-Tests sind wie folgt:

Diagramm

Backtest

Backtest2

Wie wir aus den vorangegangenen Ergebnissen des GBPUSD-Tests sehen können, konnten wir in dem Test folgende Zahlen ermitteln:

  • Net Profit: -1389.40 USD.
  • Balance DD relative: 4.56%.
  • Profit factor: 0.94.
  • Expected payoff: -8.91.
  • Recovery factor: -0.28.
  • Sharpe Ratio: -0.78.


Strategie zwei: Ausbruch aus dem Kanal der Bänder:

XAUUSD-Test

Die Ergebnisse des XAUUSD-Tests:

Diagramm

Backtest

Backtest2

Wie wir aus den vorangegangenen Ergebnissen des XAUUSD-Tests ersehen können, konnten wir in dem Test folgende Zahlen ermitteln:

  • Net Profit: -11783 USD.
  • Balance DD relative: 12.89%.
  • Profit factor: 0.56.
  • Expected payoff: -96.58.
  • Recovery factor: -0.83.
  • Sharpe Ratio: -5.00.

EURUSD-Test

Die Ergebnisse des EURUSD-Tests:

Diagramm

Backtest

Backtest2

Wie wir aus den vorangegangenen Ergebnissen des EURUSD-Tests sehen können, konnten wir in dem Test folgende Zahlen ermitteln:

  • Net Profit: -1358.30 USD.
  • Balance DD relative: 6.53%.
  • Profit factor: 0.94.
  • Expected payoff: -8.54.
  • Recovery factor: -0.20.
  • Sharpe Ratio: -0.59.

GBPUSD-Test

Die Ergebnisse des GBPUSD-Tests:

Diagramm

Backtest

Backtest2

Wie wir aus den vorangegangenen Ergebnissen des GBPUSD-Tests sehen können, konnten wir in dem Test folgende Zahlen ermitteln:

  • Net Profit: -3930.60 USD.
  • Balance DD relative: 5.06%.
  • Profit factor: 0.84.
  • Expected payoff: -25.69.
  • Recovery factor: -0.75.
  • Sharpe Ratio: -2.05.

Wie in den vorangegangenen Strategien, die für mehr als ein Instrument getestet wurden, können wir alle Ergebnisse in einer Tabelle zusammenfassen, um sie leicht vergleichen zu können (siehe unten):

alle_Ergebnisse2

Auf der Grundlage der obigen Tabelle können wir die besten Zahlen finden, die der getesteten Strategie und dem Zeitrahmen entsprechen, wie die folgenden:
  • Net Profit:  Der beste Höchstwert von 11918,60 USD wird mit der Strategie des Rebounds für das Finanzinstrument XAUUSD erzielt.
  • Balance DD Relative: Der beste Tiefstwert von 2,86 % wird mit der Strategie des Rebounds für das Finanzinstrument EURUSD erzielt.
  • Profit Factor: Der beste Höchstwert von 1,36 wird mit der Strategie des Rebounds für das Finanzinstrument XAUUSD erzielt.
  • Expected Payoff: Der beste Höchstwert von 75,91 wird mit der Strategie des Rebounds für das Finanzinstrument XAUUSD erzielt.
  • Recovery Factor: Der Höchstwert von 2,85 wird mit der Strategie des Rebounds für das Finanzinstrument XAUUSD erzielt.
  • Sharpe Ratio: Der Höchstwert von 4,06 wird mit der Strategie des Rebounds für das Finanzinstrument XAUUSD erzielt.

Wie wir sehen können, ist die beste Strategie der Bänder die Strategie des Rebounds mit XAUUSD. Sie können die Strategien nach Ihren Präferenzen optimieren und testen, um Ihre Handelsziele zu erreichen, wenn Sie bessere Ergebnisse und Einstellungen sehen wollen.


Schlussfolgerung

Als Händler ist es sehr wichtig, ein zuverlässiges Handelssystem zu haben, und wenn dieses Handelssystem alle wichtigen Aspekte wie die Volatilität und andere einschließt und misst, erhöht dies seine Zuverlässigkeit.

In diesem Artikel haben wir versucht, die Ergebnisse des Testens einfacher Strategien auf der Grundlage des Keltner-Kanal-Indikators darzustellen, um eine Vorstellung zu erhalten, wie das auf diesem wichtigen Konzept basierende Handelssystem eingebunden oder verwendet werden kann, nachdem wir den Indikator im Detail identifiziert haben, wie er auf der Grundlage seines Hauptkonzepts verwendet werden kann und wie wir ihn berechnen können.

Wir haben die folgenden Strategien erwähnt:

  • Rebound von den Bändern: Einen Kaufauftrag wird bei einem Rebound vom oberen Band erteilt, und einen Verkaufsauftrag beim Rebound vom oberen Band.
  • Ausbruch aus dem Kanal der Bänder: Wir erteilen einen Kaufauftrag, wenn das obere Band durchbrochen wird, und einen Verkaufsauftrag, wenn das untere Band durchbrochen wird.

Wir haben sie dann getestet und auf der Grundlage der Ergebnisse der einzelnen Strategien wichtige Zahlen für viele Finanzinstrumente XAUUSD, EURUSD und GBPUSD ermittelt, um zu wissen, welche Strategie die bessere ist.

Es ist von entscheidender Bedeutung zu verstehen, dass diese genannten Strategien mehr Optimierung und mehr Aufwand benötigen, um bessere Ergebnisse für unsere Handelssysteme zu finden, weil das Hauptziel dieses Artikels, wie wir bereits erwähnt haben, ist es, zu teilen, was wir tun können, indem wir einige Handelsideen teilen, die unseren Geist öffnen können, um bessere Handelssysteme zu bauen oder zu entwickeln.

Ich hoffe, Sie fanden diesen Artikel nützlich für das Erlernen von Handel und Kodierung, um bessere Ergebnisse zu erhalten. Wenn Sie diesen Artikel nützlich finden und Sie mehr über den Aufbau von Handelssystemen auf der Grundlage verschiedener Strategien und verschiedene technische Indikatoren wissen wollen, können Sie meine anderen Publikation über die beliebtesten technischen Indikatoren lesen.

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

Einführung in MQL5 (Teil 4): Strukturen, Klassen und Zeitfunktionen beherrschen Einführung in MQL5 (Teil 4): Strukturen, Klassen und Zeitfunktionen beherrschen
Enthüllen wir die Geheimnisse der MQL5-Programmierung in unserem neuesten Artikel! Vertiefen wir uns in die Grundlagen von Strukturen, Klassen und Zeitfunktionen und machen uns mit der Programmierung vertraut. Egal, ob Sie Anfänger oder erfahrener Entwickler sind, unser Leitfaden vereinfacht komplexe Konzepte und bietet wertvolle Einblicke für die Beherrschung von MQL5. Verbessern Sie Ihre Programmierkenntnisse und bleiben Sie in der Welt des algorithmischen Handels an der Spitze!
Implementierung des verallgemeinerten Hurst-Exponenten und des Varianz-Verhältnis-Tests in MQL5 Implementierung des verallgemeinerten Hurst-Exponenten und des Varianz-Verhältnis-Tests in MQL5
In diesem Artikel untersuchen wir, wie der verallgemeinerte Hurst-Exponent und der Varianzverhältnis-Test verwendet werden können, um das Verhalten von Preisreihen in MQL5 zu analysieren.
Datenwissenschaft und maschinelles Lernen (Teil 20): Algorithmische Handelseinblicke, eine Gegenüberstellung von LDA und PCA in MQL5 Datenwissenschaft und maschinelles Lernen (Teil 20): Algorithmische Handelseinblicke, eine Gegenüberstellung von LDA und PCA in MQL5
Entdecken Sie die Geheimnisse dieser leistungsstarken Dimensionsreduktionstechniken, indem wir ihre Anwendungen in der MQL5-Handelsumgebung analysieren. Vertiefen Sie sich in die Feinheiten der linearen Diskriminanzanalyse (LDA) und der Hauptkomponentenanalyse (PCA) und gewinnen Sie ein tiefes Verständnis für deren Auswirkungen auf die Strategieentwicklung und Marktanalyse,
Einführung in MQL5 (Teil 3): Beherrschung der Kernelemente von MQL5 Einführung in MQL5 (Teil 3): Beherrschung der Kernelemente von MQL5
Entdecken Sie die Grundlagen der MQL5-Programmierung in diesem einsteigerfreundlichen Artikel, in dem wir Arrays, nutzerdefinierte Funktionen, Präprozessoren und die Ereignisbehandlung entmystifizieren, wobei jede Codezeile verständlich erklärt wird. Erschließen wir die Leistungsfähigkeit von MQL5 mit einem einzigartigen Ansatz, der das Verständnis bei jedem Schritt sicherstellt. Dieser Artikel legt den Grundstein für die Beherrschung von MQL5, indem er die Erklärung jeder Codezeile hervorhebt und eine eindeutige und bereichernde Lernerfahrung bietet.