English 日本語
preview
Wie man ein zyklusbasiertes Handelssystem aufbaut und optimiert (Detrended Price Oscillator – DPO)

Wie man ein zyklusbasiertes Handelssystem aufbaut und optimiert (Detrended Price Oscillator – DPO)

MetaTrader 5Handelssysteme |
63 2
Mohamed Abdelmaaboud
Mohamed Abdelmaaboud

In diesem Artikel stellen wir ein neues Konzept vor, indem wir einen technischen Indikator untersuchen und seine potenzielle Nützlichkeit für Handelssysteme bewerten. Wir werden uns auf den Detrended Price Oscillator (DPO) konzentrieren.

Ziel dieser Artikelserie ist es, praktische Instrumente vorzustellen, die entweder als eigenständige Techniken oder als Komponenten eines umfassenderen Handelssystems verwendet werden können. Unser Ziel ist es, Sie bei der kontinuierlichen Verbesserung Ihrer Handelsleistung zu unterstützen, indem wir diese Tools entwickeln, testen und optimieren. Sie müssen jedoch jede Methode gründlich testen, um festzustellen, ob sie für Ihren Handelsstil geeignet ist und einen echten Mehrwert bietet. Ich möchte auch einige Ratschläge geben. Wenn Sie Ihre Programmierkenntnisse verbessern wollen, müssen Sie das Gelernte unbedingt anwenden. Versuchen Sie, selbständig zu programmieren und das Gelernte so oft wie möglich zu testen, damit Sie den größten Nutzen aus dem Thema ziehen können, das Sie studieren.

Wir werden den Detrended Price Oscillator (DPO) in den folgenden Themen behandeln:

  1. Definition des Detrended Price Oscillator (DPO): Wenn wir so viel wie möglich über die Definition und die Berechnungsmethode dieses technischen Instruments lernen, werden wir es besser verstehen.
  2. Nutzerdefinierter Indikator Detrended Price Oscillator (DPO): Der Lernprozess für die Kodierung eines nutzerdefinierten Indikators beinhaltet die Änderung des Indikators oder die Anwendung der eigenen Präferenzen.
  3. Detrended Price Oscillator (DPO) Strategien: Wir werden einfache Detrended Price Oscillator (DPO)-Strategien untersuchen, die in unser Handelssystem integriert werden können, um unser Verständnis für ihre Anwendung zu verbessern.
  4. Detrended Price Oscillator (DPO) Handelssystem: Der Aufbau, das Backtests und die Optimierung dieser einfachen Handelssysteme werden durchgeführt.
  5. Schlussfolgerung

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 des Detrendierten Preisindikators (DPO)

Der Detrended Price Oscillator (DPO) ist ein Instrument, mit dem sich Kurszyklen aufzeigen lassen. Dies geschieht durch Herausfiltern langfristiger Trends. Sie konzentriert sich auf kurzfristige Preiszyklen. Mit diesem Indikator lassen sich überkaufte oder überverkaufte Bedingungen und Wendepunkte erkennen.

Der Indikator (DPO) vergleicht den aktuellen Kurs mit einem gleitenden Durchschnitt. Dieser gleitende Durchschnitt wird in der Zeit zurückversetzt. Damit sollen kürzere Zyklen isoliert werden. Die Periode des gleitenden Durchschnitts wird in der Regel auf der Grundlage der Zykluslänge gewählt. Der DPO ist ein oszillierender Indikator, da er über und unter Null schwankt, was einen guten Einblick gewährt, da das Ablesen positiver Werte anzeigt, dass der Preis über seinem verschobenen Durchschnitt handelt, während negative Werte darauf hinweisen, dass der Preis darunter liegt.

Dieser Indikator kann verwendet werden, um zyklische Hochs und Tiefs zu erkennen und um Ein- und Ausstiege zu timen. Es kann auch in Verbindung mit anderen Tools verwendet werden, um die erzeugten Signale zu verbessern.

Wenn Sie an der Berechnungsmethode eines solchen Indikators interessiert sind, können Sie die folgenden Schritte befolgen:

  • Wählen Sie den DPO-Zeitraum (N).
  • Berechnen Sie den SMA (einfachen gleitenden Durchschnitt) für den in Schritt 1 ermittelten Zeitraum.

SMA = (Preis1 + Preis 2 + ....) / N

  • Verschieben Sie den SMA (Simple Moving Average) um die Hälfte der Periode plus eins nach links.

Shift = (N/2)+1

  • Ziehen Sie den verschobenen SMA vom Kurs ab.

Heutiger DPO = Heutiger Preis – Heutiger SMA (um Shift nach hinten verschoben)



Nutzerdefinierter Detrended Price Indicator (DPO)

In diesem Abschnitt werden wir den Code für diesen DPO-Indikator Schritt für Schritt bereitstellen. Wir werden auch unsere eigene, einfache, nutzerdefinierte Version erstellen, um die Funktionen und Einstellungen zu integrieren, die wir für unser Handelssystem benötigen. So erhalten Sie einen Eindruck von den Funktionen und Einstellungen, die Sie Ihrem eigenen System hinzufügen können.

In den folgenden Schritten wird die Methodik erläutert, mit der wir diese maßgeschneiderte DPO-Zeile Zeile für Zeile kodieren können:

Zunächst geben wir die Beschreibung des Indikators mit dem Präprozessor #property an, dann den Wert des Bezeichners.

#property description "Custom Detrended Price Oscillator"

Verwenden Sie den Präprozessor #include, um die MQH-Datei der gleitenden Durchschnitte einzubinden, und geben Sie den Dateinamen so an, wie er in der Include-Datei erscheint. So können wir sie später in unserem Code verwenden.

#include <MovingAverages.mqh>

Geben Sie den gewünschten Ort (indicator_separate_window) für die Anzeige des Indikators als Teil der Einstellungen mit der Eigenschaft #property an.

#property indicator_separate_window
Die Anzahl der Puffer für die Berechnung der Indikatoren mit Hilfe der Konstante (indicator_buffers), die wir auf 2 setzen.
#property indicator_buffers 2

Die Anzahl der grafischen Reihen im Indikator mit Hilfe der Konstante (indicator_plots), wir setzen sie auf 1.

#property indicator_plots   1

Die Bezeichnung des Indikators wird mit indicator_labelN gesetzt, um die Bezeichnung „DPO“ festzulegen.

#property indicator_label1 "DPO"

Der Indikatortyp wird mit dem indicator_typeN festgelegt, um die Art der grafischen Darstellung zu bestimmen, die wir als DRAW_HISTOGRAM verwenden werden

#property indicator_type1   DRAW_HISTOGRAM

Die Farbe des angezeigten Graphen unter Verwendung von indicator_colorN, die clrRoyalBlue sein wird

#property indicator_color1  clrRoyalBlue

Die Breite oder Dicke des gezeichneten Charts wird mit indicator_widthN bestimmt, die 2 beträgt

#property indicator_width1  2

Hinzufügen der anzuzeigenden Nulllinie mit Hilfe des Indikators indicator_levelN

#property indicator_level1 0

Mit Hilfe der Eingabefunktion deklarieren wir eine Integer-Variable für den DPO-Indikator, der vom Nutzer nach seinen Wünschen festgelegt wird, und setzen 20 als Standardwert

input int detrendPeriodInp=20; // Period

Deklaration von zwei Double-Arrays dpoBuffer und maBuffer und einer weiteren Ganzzahl maPeriod

double    dpoBuffer[];
double    maBuffer[];
int       maPeriod;

Im Abschnitt OnInit() wird die Initialisierungsfunktion für das DPO definiert. Wenn der Indikator in den Chart geladen wird, wird diese Funktion automatisch einmal ausgeführt.

void OnInit()
{

}

 In der Funktion OnInit() wird die Variable maPeriod definiert, die die Zykluslänge festlegt, die für die Glättung im DPO verwendet werden soll.

maPeriod=detrendPeriodInp/2+1;

Speichern und Anzeigen der DPO- und MA-Werte als Puffer.

   SetIndexBuffer(0,dpoBuffer,INDICATOR_DATA);
   SetIndexBuffer(1,maBuffer,INDICATOR_CALCULATIONS);

Wir verwenden die Funktion IndicatorSetInteger, um die Anzahl der Dezimalstellen oder Ziffern für den DPO und seine Parameter festzulegen:

  • prop_id: zur Festlegung eines ganzzahligen Werts als Bezeichner der Indikatoreigenschaft, d. h. (INDICATOR_DIGITS).
  • prop_value: um den Wert der Eigenschaft zu definieren, der (_Digits) ist, um die Ziffern des Symbols plus 1 zu setzen.
IndicatorSetInteger(INDICATOR_DIGITS,_Digits+1);

Wir verwenden die Funktion PlotIndexSetInteger, um den ersten zu zeichnenden Balken und seine Parameter festzulegen:

  • plot_index: Definiert den Plot-Index, der 0 ist.
  • prop_id: Definiert die Eigenschaftskennung, die (PLOT_DRAW_BEGIN) lautet.
  • prop_value: Festlegung des zu setzenden Wertes, der (maPeriod-1) ist.
PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,maPeriod-1);

Wir deklarieren eine String-Variable (indShortName), um den kurzen Indikatornamen zu definieren, der auf dem Indikator angezeigt wird, indem wir StringFormat mit zwei Parametern (der Name des Indikators) und (die Periodenlänge des Indikators gemäß der Nutzereingabe) verwenden.

string indShortName=StringFormat("Custom DPO(%d)",detrendPeriodInp);
Wir legen den definierten Namen fest, indem wir IndicatorSetString mit zwei Parametern verwenden (dem Eigenschaftsbezeichner, INDICATOR_SHORTNAME, und dem String-Eigenschaftswert, der der definierte Name ist).
IndicatorSetString(INDICATOR_SHORTNAME,indShortName);

Die Funktion OnCalculate zur Berechnung des Indikators:

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {

  }
In der Funktion OnCalculate deklarieren wir eine Integer-Variable namens start. Einen weiteren Index mit dem Namen index1 deklarieren und definieren, der gleich (begin+maPeriod-1) ist:
   int start;
   int index1=begin+maPeriod-1;

Handhabung der Initialisierung und Einstellung, wo die Berechnungen beginnen sollen, wenn die erste Berechnung korrekt ist:

  • Wir verwenden (ArrayInitialize), um das Array mit Null zu initialisieren.
  • Aktualisieren der Startvariable auf den Wert index1.
  • Wenn begin größer als 0 ist, setzen wir den Wert der entsprechenden Eigenschaft der entsprechenden Indikatorzeile mit (PlotIndexSetInteger).

Andernfalls wird die Startvariable auf den Wert (prev_calculated-1) gesetzt.

   if(prev_calculated<index1)
     {
      ArrayInitialize(dpoBuffer,0.0);
      start=index1;
      if(begin>0)
         PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,index1);
     }
   else
      start=prev_calculated-1;

Wir berechnen den einfachen gleitenden Durchschnitt mit Hilfe der definierten Funktion (SimpleMAOnBuffer), die in der Datei MovingAverages.mqh enthalten ist.

SimpleMAOnBuffer(rates_total,prev_calculated,begin,maPeriod,price,maBuffer);

Wir erstellen eine Schleife für die Berechnung, die jeden Balken von Start bis rates_total durchläuft, um den dpoBuffer[i] zu definieren, bis der Indikator entfernt wird oder das Terminal herunterfährt.

for(int i=start; i<rates_total && !IsStopped(); i++)
   dpoBuffer[i]=price[i]-maBuffer[i];

Rückgabe des neuen Wertes, nachdem OnCalculate abgeschlossen ist.

return(rates_total);

Dies ist der vollständige Code für den nutzerdefinierten DPO-Indikator, den wir wie folgt in einen einzigen Codeblock packen können.

//+------------------------------------------------------------------+
//|                                                    customDPO.mq5 |
//+------------------------------------------------------------------+
#property description "Custom Detrended Price Oscillator"
#include <MovingAverages.mqh>
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   1
#property indicator_label1 "DPO"
#property indicator_type1   DRAW_HISTOGRAM
#property indicator_color1  clrRoyalBlue
#property indicator_width1  2
#property indicator_level1 0
input int detrendPeriodInp=20; // Period
double    dpoBuffer[];
double    maBuffer[];
int       maPeriod;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
void OnInit()
  {
   maPeriod=detrendPeriodInp/2+1;
   SetIndexBuffer(0,dpoBuffer,INDICATOR_DATA);
   SetIndexBuffer(1,maBuffer,INDICATOR_CALCULATIONS);
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits+1);
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,maPeriod-1);
   string indShortName=StringFormat("Custom DPO(%d)",detrendPeriodInp);
   IndicatorSetString(INDICATOR_SHORTNAME,indShortName);
  }
//+------------------------------------------------------------------+
//| Detrended Price Oscillator                                       |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
   int start;
   int index1=begin+maPeriod-1;
   if(prev_calculated<index1)
     {
      ArrayInitialize(dpoBuffer,0.0);
      start=index1;
      if(begin>0)
         PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,index1);
     }
   else
      start=prev_calculated-1;
   SimpleMAOnBuffer(rates_total,prev_calculated,begin,maPeriod,price,maBuffer);
   for(int i=start; i<rates_total && !IsStopped(); i++)
      dpoBuffer[i]=price[i]-maBuffer[i];
   return(rates_total);
  }
//+------------------------------------------------------------------+

Nachdem wir den Code ohne Fehler kompiliert haben, sehen wir Folgendes beim Start auf dem Chart:

DPOIndikator



Strategien für den Detrended Price Indicator (DPO)

In diesem Abschnitt werden wir einfache Strategien für den Aufbau dieses Handelssystems unter Verwendung von DPO bereitstellen, und sie sind die gleichen wie die unten aufgeführten:

  • DPO Strategie der Kreuzens der Nulllinie.
  • Strategie der Trendvalidierung des DPO.

DPO-Strategie der Kreuzens der Nulllinie:

Bei dieser Strategie werden Kauf- und Verkaufspositionen auf der Grundlage der Kreuzens zwischen dem DPO-Wert und dem Nullniveau platziert. Die Kaufposition wird platziert, wenn der vorherige DPO-Wert unter Null und der aktuelle Wert über Null liegt, und die Verkaufsposition wird platziert, wenn der vorherige DPO-Wert über Null und der aktuelle Wert unter Null liegt.

Einfach:

Der vorherige DPO-Wert < 0 und der aktuelle DPO-Wert > 0 --> Kaufen

Der vorherige DPO-Wert > 0 und der aktuelle DPO-Wert < 0 --> Verkaufen

DPO-Strategie zur Trendvalidierung:

Diese Strategie platziert Kauf- und Verkaufspositionen auf der Grundlage des Kreuzens der Preise mit dem gleitenden Durchschnitt und dem Kreuzen der DPO-Werte und der Nulllinie, um das Kreuzen der Nulllinie insbesondere mit dem kurzfristigen Signal zu Optimierungszwecken zu validieren. Die Kaufposition wird also platziert, wenn der Schlusskurs unter dem vorherigen gleitenden Durchschnittswert liegt, der aktuelle Briefkurs (Ask) größer ist als der aktuelle gleitende Durchschnittswert und der DPO-Wert über 0 liegt. Eine Verkaufsposition wird eingegangen, wenn der Schlusskurs über dem vorherigen gleitenden Durchschnittswert liegt, der aktuelle Geldkurs (Bid) unter dem aktuellen gleitenden Durchschnittswert liegt und der DPO-Wert unter 0 liegt.

Einfach:

Close < vorheriger MA-Wert und Briefkurs (Ask) > MA-Wert und DPO-Wert > 0 --> Kaufen
Close > vorheriger MA-Wert und Bid < MA-Wert und DPO-Wert < 0 --> Verkaufen

Wir werden jede dieser Strategien programmieren, testen und optimieren und verschiedene Konzepte ausprobieren, um die bestmöglichen Ergebnisse zu erzielen. Wir werden dies im nächsten Abschnitt näher erläutern.



Detrended Price Indicator (DPO) Handelssystem

In diesem Abschnitt zeigen wir Ihnen, wie Sie auf der Grundlage der genannten Strategien automatisierte DPO-basierte Handelssysteme erstellen können. Wir werden sehen, dass Schritt-für-Schritt zu verstehen, wie dieses System funktionieren kann, und Sie können dies als Grundlage für neue Erkenntnisse und Konzepte zu optimieren mehr, und das ist das Hauptziel dieser Art von Artikel. Zunächst werden wir einen einfachen EA programmieren, der uns die DPO-Werte auf dem Chart anzeigt, um ihn als Basis für alle EAs des Handelssystems zu verwenden, das wir erstellen werden.

Als Nächstes konfigurieren wir die Eingabe des Expert Advisors (EA), um den gewünschten Zeitraum festzulegen, der beim Aufruf des Indikators verwendet werden soll.

input int                 period=20; // Period

Deklarieren wir zunächst eine Integer-Variable für DPO

int dpo;

Im OnInit() wird unser nutzerdefinierter DPO-Indikator durch den Aufruf der Funktion iCustom initialisiert. Die Parameter sind wie folgt:

  • Symbol: Der Symbolname als Zeichenkette. Hier verwenden wir _Symbol, um es auf das aktuelle Chartsymbol anzuwenden.
  • Period: Der Zeitrahmen als ENUM_TIMEFRAMES. Wir verwenden PERIOD_CURRENT, um sie auf den aktuellen Chart-Zeitrahmen anzuwenden.
  • Name: Der Name des nutzerdefinierten Indikators, einschließlich des korrekten Pfades auf dem lokalen Rechner (geschrieben als folder/custom_indicator_name), in unserem Fall der Name des Indikators (customDPO).
  • Eingaben: Eine Liste der Eingabeparameter des Indikators, falls zutreffend. Hier ist es der (Punkt).

Anschließend verwenden wir den Rückgabewert INIT_SUCCEEDED, um die erfolgreiche Initialisierung zu bestätigen und mit dem nächsten Teil des Codes fortzufahren.

int OnInit()
  {
   dpo = iCustom(_Symbol,PERIOD_CURRENT,"customDPO",period);
   return(INIT_SUCCEEDED);
  }

In OnDeinit() wird festgelegt, dass die Nachricht von EA entfernt wird, wenn das Deinitialisierungsereignis eintritt.

void OnDeinit(const int reason)
  {
   Print("EA is removed");
  }

Im OnTick()-Ereignis, das in EAs aufgerufen wird, wenn ein neues Tick-Ereignis auftritt

void OnTick()
  {

  }

Wir werden ein Array für dpoInd[] mit den Datentyp double deklarieren

double dpoInd[];

Abrufen von Daten aus dem angegebenen DPO-Indikatorpuffer unter Verwendung der Funktion CopyBuffer. Die Parameter sind:

  • indicator_handle: Das Handle des DPO-Indikators.
  • buffer_num: Die Nummer des Puffers, aus dem kopiert werden soll; 0 für den DPO-Indikator.
  • start_pos: Die Ausgangsposition, hier auf 0 gesetzt.
  • count: Die Anzahl der zu kopierenden Werte; in diesem Fall 3.
  • buffer[]: Das Ziel-Array, in dem die kopierten Werte gespeichert werden sollen (hier dpoInd[]).
CopyBuffer(dpo,0,0,3,dpoInd);

Setzen Sie das AS_SERIES-Flag für das Array dpoInd[ ], um seine Elemente als Zeitreihe zu indizieren. Verwenden Sie den Array-Namen und den Flag-Wert (true) als Parameter; die Funktion gibt bei Erfolg true zurück.

ArraySetAsSeries(dpoInd,true);

Deklarieren und definieren Sie eine Double-Variable für den dpoVal t, die den aktuellen dpo-Index zurückgibt und ihn auf 3 Ziffern normalisiert.

double dpoVal = NormalizeDouble(dpoInd[0], 3);

Wir kommentieren den Wert des aktuellen DPO in der Tabelle

omment("DPO value = ",dpoVal);

Der vollständige Code in einem Block kann wie folgt aussehen

//+------------------------------------------------------------------+
//|                                                      DPO_Val.mq5 |
//+------------------------------------------------------------------+
input int                 period=20; // Period
int dpo;
int OnInit()
  {
   dpo = iCustom(_Symbol,PERIOD_CURRENT,"customDPO",period);
   return(INIT_SUCCEEDED);
  }
void OnDeinit(const int reason)
  {
   Print("EA is removed");
  }
void OnTick()
  {
    double dpoInd[];
    CopyBuffer(dpo,0,0,3,dpoInd);
    ArraySetAsSeries(dpoInd,true);
    double dpoVal = NormalizeDouble(dpoInd[0], 3);
    Comment("DPO value = ",dpoVal);
  }

Nach dem fehlerfreien Kompilieren und Anhängen des EA können wir den DPO-Wert als dynamischen Kommentar im Chart sehen, wie unten dargestellt:

DPOVal

Wie Sie sehen können, ist der DPO-Wert auf dem Chart derselbe wie der auf dem eingefügten Indikator (1794,363) angezeigte Wert. Wir können dies als Grundlage für andere Expert Advisors (EAs) oder Strategien verwenden.

DPO-Strategie der Kreuzens der Nulllinie:

Wie bereits erwähnt, besteht die Logik hinter dieser Strategie darin, dass wir einen EA programmieren müssen, der automatisch Orders platzieren kann, was der Nulldurchgang des DPO-Indikators ist. Wir müssen programmieren, dass bei jedem Tick geprüft wird, wo die aktuellen und vorherigen DPO-Werte liegen, und sie mit dem Null vergleichen. Wenn der vorherige Wert kleiner als Null ist und das aktuelle Kreuz über Null liegt, muss das Programm einen Kaufauftrag erteilen. Der Verkaufsauftrag wird erteilt, wenn der vorherige DPO-Wert über Null und der aktuelle Wert darunter liegt.

Im Folgenden finden Sie den vollständigen Code dieser Art von Programm, das auf dem MetaTrader 5 ausgeführt werden kann:

//+------------------------------------------------------------------+
//|                                           DPO_Zero_Crossover.mq5 |
//+------------------------------------------------------------------+
#include <trade/trade.mqh>
input int                 period=20; // Periods
input double      lotSize=1;
input double      slLvl=300;
input double      tpLvl=900;
int dpo;
CTrade trade;
int barsTotal;
int OnInit()
  {
   barsTotal=iBars(_Symbol,PERIOD_CURRENT);
   dpo = iCustom(_Symbol,PERIOD_CURRENT,"customDPO",period);
   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 dpoInd[];
      CopyBuffer(dpo,0,0,3,dpoInd);
      ArraySetAsSeries(dpoInd,true);
      double dpoVal = NormalizeDouble(dpoInd[1], 6);
      double dpoPreVal = NormalizeDouble(dpoInd[2], 6);
      double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
      double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
      if(dpoPreVal<0 && dpoVal>0)
        {
         double slVal=ask - slLvl*_Point;
         double tpVal=ask + tpLvl*_Point;
         trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
        }
      if(dpoPreVal>0 && dpoVal<0)
        {
         double slVal=bid + slLvl*_Point;
         double tpVal=bid - tpLvl*_Point;
         trade.Sell(lotSize,_Symbol,bid,slVal,tpVal);
        }
     }
  }
//+------------------------------------------------------------------+

Wie Sie im obigen Codeblock sehen können, gibt es Unterschiede zum Basiscode von DPO Values. Diese sind dieselben wie unten.

Die Datei trade.mqh einbinden, um Aufträge zu erteilen

#include <trade/trade.mqh>

Fügen Sie weitere Eingaben mit Standardwerten für Losgröße, Stop-Loss-Niveau und Take-Profit-Niveau hinzu, die vom Nutzer festgelegt werden können.

input int         period=20; // Periods
input double      lotSize=1;
input double      slLvl=300;
input double      tpLvl=900;

Wir deklarieren das zu verwendende Handelsobjekt und die Integer-Variable barsTotal.

CTrade trade;
int barsTotal;

In OnInit () definieren wir barsTotal als Funktion von iBars, um die Anzahl der Balken des Symbols und der Periode aus der Historie zurückzugeben.

barsTotal=iBars(_Symbol,PERIOD_CURRENT);

In OnTick() deklarieren wir Bars als Integer-Variable und definieren sie mit der Funktion iBars.

int bars=iBars(_Symbol,PERIOD_CURRENT);

Wir werden eine Bedingung festlegen, um zu prüfen, ob es einen neuen Balken gibt, um die Platzierung von Aufträgen zu begrenzen, indem wir festlegen, ob die Variable barsTotoal ungleich bars ist, was bedeutet, dass ein neuer Balken gezeichnet wird, dann werden wir die Codeausführung fortsetzen.

if(barsTotal != bars)

Aktualisieren Sie in einem neuen Balken den Wert barsTotal um den Wert bars.

barsTotal=bars;

Wir aktualisieren die letzte geschlossene Variable mit dem Index 1 und deklarieren eine zusätzliche Doppelvariable für den vorherigen DPO-Wert mit dem Index 2.

double dpoVal = NormalizeDouble(dpoInd[1], 6);
double dpoPreVal = NormalizeDouble(dpoInd[2], 6);

Deklarieren und definieren wir die Geld- und Briefkurse (Ask & Bid).

double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);

Bedingung der Strategie, Angabe und Definition von Stop-Loss, Take-Profit und Platzierung von Aufträgen.

if(dpoPreVal<0 && dpoVal>0)
  {
   double slVal=ask - slLvl*_Point;
   double tpVal=ask + tpLvl*_Point;
   trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
  }
if(dpoPreVal>0 && dpoVal<0)
   {
    double slVal=bid + slLvl*_Point;
    double tpVal=bid - tpLvl*_Point;
    trade.Sell(lotSize,_Symbol,bid,slVal,tpVal);
   }

Nachdem wir den Code fehlerfrei kompiliert und den EA an den Chart angehängt haben, können wir feststellen, dass die Positionen auf die gleiche Weise wie in den folgenden Beispielen platziert werden können.

Kaufposition:

DPO_Kreuz_kaufen

Position verkaufen:

DPO_Kreuz_Verkauf

Wir müssen nun alle Strategien für das Paar EUR/USD auf den Zeitrahmen 5 Minuten und 15 Minuten über den Zeitraum vom 1. Januar bis 31. Dezember 2023 testen. Wir werden einen Stop-Loss von 300 Punkten und einen Take-Profit von 900 Punkten verwenden. Auf der Grundlage der Ergebnisse wird unser Hauptansatz zur Optimierung darin bestehen, zusätzliche Konzepte oder Instrumente zu testen, wie etwa die Integration eines gleitenden Durchschnitts. Es lohnt sich auch, verschiedene Zeitrahmen zu testen, um zu sehen, welcher die beste Leistung bringt.

Was die Ergebnisse der Strategietests zum Vergleich der einzelnen Tests betrifft, so werden wir uns auf die folgenden wichtigen Schlüsselkennzahlen konzentrieren:

  • Net profit: Er wird berechnet, indem der Bruttoverlust vom Bruttogewinn abgezogen wird, und der höchste Wert ist der beste.
  • DD des Saldos relative: Es ist der maximale Verlust, den das Konto während des Handels erfährt, und der niedrigste ist der beste.
  • Profit factor: Es ist das Verhältnis von Bruttogewinn zu Bruttoverlust, und der höchste Wert ist der beste.
  • Expected Payoff: Es ist der durchschnittliche Gewinn oder Verlust eines Handels, und der höchste Wert ist der beste.
  • Recovery factor: Er misst, wie gut sich die getestete Strategie nach Verlusten erholt, und 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, und die höchste Sharpe Ratio ist die beste.

Die Testergebnisse des 15-Minuten-Zeitfensters sind die gleichen wie unten:

Ergebnisse_0CO_15m-700

Ergebnisse2_0CO_15m-700

Ergebnisse3_0CO_15m-700

Auf der Grundlage der obigen Ergebnisse des Tests eines 15-minütigen Zeitrahmens können wir die folgenden wichtigen Werte für die Testnummern ermitteln:

  • Net Profit: 91520.53 USD.
  • DD des Saldos relative: 35.81%.
  • Profit factor: 1.09.
  • Expected payoff: 21.17.
  • Recovery factor: 1.48.
  • Sharpe Ratio: 1.07.

Die Testergebnisse für den 5-Minuten-Zeitrahmen sind die gleichen wie unten:

Ergebnisse_0CO_5m-700

Ergebnisse2_0CO_5m-700

Ergebnisse3_0CO_5m-700

Ausgehend von den obigen Ergebnissen des Tests eines 5-Minuten-Zeitfensters sind die folgenden Werte für diesen Test wichtig:

  • Net Profit: 62258.14.
  • DD des Saldos relative: 97.34%.
  • Profit factor: 1.02.
  • Expected payoff: 4.78.
  • Recovery factor: 0.30.
  • Sharpe Ratio: 0.17.

Wie wir sehen können, sind die Testergebnisse dieser Strategie auf dem 15-Minuten-Chart besser als auf dem 5-Minuten-Chart. Es gibt jedoch immer noch einen hohen Drawdown, sodass wir in Erwägung ziehen werden, ein weiteres technisches Instrument – den gleitenden Durchschnitt – als Optimierung hinzuzufügen, um zu sehen, ob es die Ergebnisse verbessert.

DPO-Strategie zur Trendvalidierung:

Wie bereits erwähnt, beinhaltet diese Strategie die Programmierung eines EA, der automatisch Aufträge auf der Grundlage der Position des DPO-Werts und der Beziehung zwischen dem gleitenden Durchschnitt und den Preisen platzieren kann. Dies bietet eine zusätzliche Bestätigung in Form des gleitenden Durchschnitts. Das Programm muss bei jedem Tick die aktuellen und vorherigen DPO-Werte, den gleitenden Durchschnitt, den Schlusskurs und die Geldkurse überprüfen.

Einen Kaufauftrag erteilen, wenn:

  • Der vorherige Schlusskurs unter dem vorherigen Wert des gleitenden Durchschnitts liegt,
  • der Briefkurs (Ask) größer ist als der aktuelle gleitende Durchschnittswert und
  • der aktuelle DPO-Wert größer ist als Null. 

Einen Verkaufsauftrag erteilen, wenn:

  • Der vorherige Schlusskurs ist größer als der vorherige Wert des gleitenden Durchschnitts.
  • Der Geldkurs (Bid) ist niedriger als der aktuelle gleitende Durchschnittswert
  • Der aktuelle DPO-Wert ist kleiner als Null. 

Nachfolgend finden Sie den vollständigen Code dieser Art von Programm, das auf dem MT5 ausgeführt werden kann:

//+------------------------------------------------------------------+
//|                                          DPO_trendValidation.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include <trade/trade.mqh>
input int         period=20; // Periods
input int         maPeriodInp=20; //MA Period
input double      lotSize=1;
input double      slLvl=300;
input double      tpLvl=900;
int dpo;
int ma;
CTrade trade;
int barsTotal;
int OnInit()
  {
   barsTotal=iBars(_Symbol,PERIOD_CURRENT);
   dpo = iCustom(_Symbol,PERIOD_CURRENT,"customDPO",period);
   ma = iMA(_Symbol,PERIOD_CURRENT,maPeriodInp,0,MODE_SMA,PRICE_CLOSE);
   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 dpoInd[];
      double maInd[];
      CopyBuffer(dpo,0,0,3,dpoInd);
      CopyBuffer(ma,0,0,3,maInd);
      ArraySetAsSeries(dpoInd,true);
      ArraySetAsSeries(maInd,true);
      double dpoVal = NormalizeDouble(dpoInd[0], 6);
      double maVal= NormalizeDouble(maInd[0],5);
      double dpoPreVal = NormalizeDouble(dpoInd[1], 5);
      double maPreVal = NormalizeDouble(maInd[1],5);;
      double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
      double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
      double prevClose = iClose(_Symbol,PERIOD_CURRENT,1);
      if(prevClose<maPreVal && ask>maVal)
        {
         if(dpoVal>0)
           {
            double slVal=ask - slLvl*_Point;
            double tpVal=ask + tpLvl*_Point;
            trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
           }
        }
      if(prevClose>maPreVal && bid<maVal)
        {
         if(dpoVal<0)
           {
            double slVal=bid + slLvl*_Point;
            double tpVal=bid - tpLvl*_Point;
            trade.Sell(lotSize,_Symbol,bid,slVal,tpVal);
           }
        }
     }
  }
//+------------------------------------------------------------------+

Die Unterschiede im obigen Code sind die gleichen wie im folgenden.

Eine weitere Eingabe fügen wir für den Zeitraum des gleitenden Durchschnitts hinzu, der vom Nutzer festgelegt werden kann:

input int         maPeriodInp=20; //MA Period

Wir deklarieren eine Integer-Variable für ma.

int ma;

Definition der Variablen ma mit Hilfe der Funktion iMA, um den Handle des Indikators des gleitenden Durchschnitts zurückzugeben.

ma = iMA(_Symbol,PERIOD_CURRENT,maPeriodInp,0,MODE_SMA,PRICE_CLOSE);

Wir deklarieren ein Array für die maInd[] als Double-Datentyp.

double maInd[];

Abrufen von Daten aus dem angegebenen MA-Indikatorpuffer unter Verwendung der Funktion CopyBuffer.

CopyBuffer(ma,0,0,3,maInd);

Wir setzen das Flag AS_SERIES für das Array maInd[ ], um seine Elemente als Zeitreihe zu indizieren.

ArraySetAsSeries(maInd,true);

Deklarieren und definieren und normalisieren von maVal.

double maVal= NormalizeDouble(maInd[0],5);

 Wir deklarieren und definieren den vorherigen Schlusskurs mit Hilfe der Funktion iClose.

double prevClose = iClose(_Symbol,PERIOD_CURRENT,1);

Bedingungen der Strategie im Falle von Kauf und Verkauf.

 if(prevClose<maPreVal && ask>maVal)
   {
    if(dpoVal>0)
      {
       double slVal=ask - slLvl*_Point;
       double tpVal=ask + tpLvl*_Point;
       trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
      }
   }
if(prevClose>maPreVal && bid<maVal)
   {
    if(dpoVal<0)
      {
       double slVal=bid + slLvl*_Point;
       double tpVal=bid - tpLvl*_Point;
       trade.Sell(lotSize,_Symbol,bid,slVal,tpVal);
       }
   }

Nach dem fehlerfreien Kompilieren des Codes dieser Strategie und dem Anhängen der Datei an den Chart können wir feststellen, dass Kauf- und Verkaufspositionen wie in den folgenden Beispielen platziert werden können.

Kaufposition:

DPO_TV_kaufen

Position verkaufen:

DPO_TV_verkaufen

Wir müssen diese Strategie auf dem EURUSD-Paar über den gleichen Zeitraum des gesamten Jahres 2023 testen, wie oben auf den Zeitrahmen von 15 Minuten und 5 Minuten erwähnt. Die Testergebnisse der DPO-Trendvalidierungsstrategie für den 15-Minuten-Zeitrahmen sind die gleichen wie unten:

Ergebnisse_TV_15m-700

Ergebnisse2_TV_15m-700

Ergebnisse3_TV_15m-700

Nach den obigen Ergebnissen des Tests eines 15-Minuten-Zeitraums sind die folgenden Werte für diesen Test wichtig:

  • Net Profit: 21866.08.
  • DD des Saldos relative: 16.22%.
  • Profit factor: 1.19.
  • Expected payoff: 42.79.
  • Recovery factor: 1.24.
  • Sharpe Ratio: 1.42.

Die Testergebnisse für den 5-Minuten-Zeitrahmen sind wie folgt:

Ergebnisse_TV_5m-700

Ergebnisse2_TV_5m-700

Ergebnisse3_TV_5m-700

In Abhängigkeit von den oben genannten wichtigen Zahlen sind die Ergebnisse des Tests für einen 15-Minuten-Zeitrahmen die gleichen wie unten:

  • Net Profit: 87010.62.
  • DD des Saldos relative: 30.24%.
  • Profit factor: 1.24.
  • Expected payoff: 51.67.
  • Recovery factor: 2.08.
  • Sharpe Ratio: 1.36.

Jetzt können wir das beste aller Ergebnisse sehen:

  • Der höchste Nettogewinn: 91520,53. die 15-min der DPO Zero Crossover Strategie.
  • Der niedrigste DD des Saldos relativ: 16,22 % für den 15-minütigen Zeitraum der DPO-Trendvalidierungsstrategie.
  • Der höchste Gewinnfaktor: 1,24 für den 5-minütigen Zeitraum der DPO-Trendvalidierungsstrategie.
  • Die höchste erwartete Auszahlung: 51,67 für den 5-minütigen Zeitraum der DPO-Trendvalidierungsstrategie.
  • Der höchste Rückgewinnungsfaktor: 2,08 für den 5-minütigen Zeitraum der DPO-Trendvalidierungsstrategie.
  • Die höchste Sharpe Ratio: 1,42 für den 15-minütigen Zeitraum der DPO-Trendvalidierungsstrategie.

Es ist klar, dass der optimierte EA auf der Grundlage der gleitenden Durchschnittskonformation bei den meisten Messungen insgesamt bessere Ergebnisse liefert. Ich empfehle Ihnen daher, mehrere Tests mit verschiedenen Konzepten durchzuführen, z. B. mit anderen technischen Instrumenten, verschiedenen Zeitrahmen und verschiedenen Zeiträumen, um sicherzustellen, dass Sie je nach Test bessere Ergebnisse erzielen können.



Schlussfolgerung

Am Ende dieses Artikels wird vorausgesetzt, dass Sie verstehen, was der technische Indikator Detrended Price Oscillator (DPO) ist, indem Sie verstehen, was er misst, wie er berechnet werden kann und wie Händler ihn nutzen können. Darüber hinaus erfahren Sie, wie Sie Ihr eigenes System nach Ihren Wünschen programmieren können, und das Interessanteste ist, wie Sie ein Handelssystem auf der Grundlage von zwei einfachen Strategien erstellen können:

  • DPO-Strategie des Kreuzens der Nulllinie.
  • DPO-Strategie zur Trendvalidierung

Es wird davon ausgegangen, dass Sie verstanden haben, wie Sie sie optimieren können, indem Sie verschiedene Konzepte als Beispiele befolgen, um bessere Ergebnisse in Ihrem Handel zu erzielen. Auf der Grundlage der Methoden, die wir zur Optimierung der genannten Handelssysteme verwendet haben, haben wir festgestellt, welches das beste System im Hinblick auf die meisten Testmessungen ist. Ich hoffe, Sie haben diesen Artikel als nützlich empfunden, und wenn Sie weitere Artikel von mir lesen möchten, z. B. über den Aufbau von Handelssystemen auf der Grundlage der beliebtesten technischen Indikatoren und andere, können Sie diese über meine Publikationsseite auswählen.

Die angehängten Quellcode-Dateien finden Sie unten:

Dateiname Beschreibung
customDPO Er ist der nutzerdefinierte DPO-Indikator
DPO_Val Es ist der EA der dynamischen DPO-Werte
DPO_Zero_Crossover Es ist der EA mit der DPO-Strategie des Kreuzens von Null
DPO_trendValidation Sie ist der EA der DPO-Strategie der Trendvalidierung 

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

Beigefügte Dateien |
customDPO.mq5 (2.19 KB)
DPO_Val.mq5 (0.65 KB)
Letzte Kommentare | Zur Diskussion im Händlerforum (2)
Dan Dumbraveanu
Dan Dumbraveanu | 27 Sept. 2025 in 18:05
Das ist sehr interessant, aber ich habe einige Schwierigkeiten, die Theorie mit dem Code zu korrelieren. Wenn ich es richtig verstanden habe, müsste man, um den SMA für den aktuellen Balken zu berechnen, die betrachtete Periode um N/2 + 1 Balken zurückschieben und dann den SMA mit N Balken von dort aus berechnen. Ich bin nur ein Anfänger, also gebe ich nicht vor, ein gutes Verständnis des Indikatorcodes zu haben, aber von dem, was ich entziffern konnte, sieht es für mich so aus, als ob N nur verwendet wird, um den Namen, aber nicht in Berechnungen, als SMA-Periode festzulegen, und stattdessen wird N/2 + 1 (maPeriod im Code) als SMA-Periode verwendet, aber nicht, um irgendeine Verschiebung durchzuführen. Verzeihen Sie mir, wenn ich alles falsch verstanden habe, aber bitte zeigen Sie mir, wo ich nicht korrekt bin, damit ich es besser verstehen kann.
Mark Anthony Graham
Mark Anthony Graham | 29 Sept. 2025 in 04:06

Ich danke Ihnen, Sir.

Im Grunde genommen liebe ich Ihren Indikator.

Ich finde es effektiv, wenn auf höheren Zeitrahmen zB verwendet: wöchentlich

Die Übertragung der Trading-Signale in einem universalen Expert Advisor. Die Übertragung der Trading-Signale in einem universalen Expert Advisor.
In diesem Artikel wurden die verschiedenen Möglichkeiten beschrieben, um die Trading-Signale von einem Signalmodul des universalen EAs zum Steuermodul der Positionen und Orders zu übertragen. Es wurden die seriellen und parallelen Interfaces betrachtet.
Vereinfachen von Datenbanken in MQL5 (Teil 2): Verwendung von Metaprogrammierung zur Erstellung von Entitäten Vereinfachen von Datenbanken in MQL5 (Teil 2): Verwendung von Metaprogrammierung zur Erstellung von Entitäten
Wir haben die fortgeschrittene Verwendung von #define für die Metaprogrammierung in MQL5 erforscht, indem wir Entitäten erstellt haben, die Tabellen und Spaltenmetadaten (Typ, Primärschlüssel, Autoinkrement, Nullbarkeit usw.) darstellen. Wir haben diese Definitionen in TickORM.mqh zentralisiert, wodurch die Generierung von Metadatenklassen automatisiert und der Weg für eine effiziente Datenmanipulation durch den ORM geebnet wird, ohne dass SQL manuell geschrieben werden muss.
Eine alternative Log-datei mit der Verwendung der HTML und CSS Eine alternative Log-datei mit der Verwendung der HTML und CSS
In diesem Artikel werden wir eine sehr einfache, aber leistungsfähige Bibliothek zur Erstellung der HTML-Dateien schreiben, dabei lernen wir auch, wie man eine ihre Darstellung einstellen kann (nach seinem Geschmack) und sehen wir, wie man es leicht in seinem Expert Advisor oder Skript hinzufügen oder verwenden kann.
Pipelines in MQL5 Pipelines in MQL5
In diesem Beitrag befassen wir uns mit einem wichtigen Schritt der Datenaufbereitung für das maschinelle Lernen, der zunehmend an Bedeutung gewinnt. Pipelines für die Datenvorverarbeitung. Dabei handelt es sich im Wesentlichen um eine rationalisierte Abfolge von Datenumwandlungsschritten, mit denen Rohdaten aufbereitet werden, bevor sie in ein Modell eingespeist werden. So uninteressant dies für den Laien auch erscheinen mag, diese „Datenstandardisierung“ spart nicht nur Trainingszeit und Ausführungskosten, sondern trägt auch zu einer besseren Generalisierung bei. In diesem Artikel konzentrieren wir uns auf einige SCIKIT-LEARN Vorverarbeitungsfunktionen, und während wir den MQL5-Assistenten nicht ausnutzen, werden wir in späteren Artikeln darauf zurückkommen.