
Klassische Strategien neu interpretieren (Teil 13): Minimale Verzögerung des Kreuzens von gleitenden Durchschnitten
In unseren vorangegangenen Diskussionen haben wir aus verschiedenen Blickwinkeln erörtert, wie wir unsere Effizienz beim Einsatz von Kreuzungs-Strategien mit gleitendem Durchschnitt maximieren können. Um es kurz zusammenzufassen: Wir haben zuvor einen Test mit mehr als 200 verschiedenen Symbolen durchgeführt und festgestellt, dass unser Computer offenbar lernt, künftige gleitende Durchschnittswerte besser vorherzusagen als ihre Tendenz, künftige Kursniveaus korrekt zu prognostizieren. Ich habe hier einen kurzen Link zu dem Artikel bereitgestellt. Wir haben diese Idee erweitert und 2 gleitende Durchschnitte modelliert, mit dem einzigen Ziel, das Kreuzen früher als die Marktteilnehmer vorherzusagen und unsere Handelsgeschäfte entsprechend anzupassen. Ich habe auch einen Link zum zweiten Artikel eingefügt, den Sie hier leicht finden können.
In unserer heutigen Diskussion werden wir die ursprüngliche Strategie noch einmal erweitern, um die mit der Handelsstrategie verbundene Verzögerung zu minimieren. Bei traditionellen Strategien des Kreuzens muss zwischen den Periodenlängen der beiden gleitenden Durchschnitten eine zeitliche Lücke bestehen. Wir werden jedoch von dieser konventionellen Denkweise abweichen und für beide gleitenden Durchschnitte dieselbe Periodenlänge verwenden.
An dieser Stelle werden sich einige Leser fragen, ob dies noch als ein Kreuzen von gleitenden Durchschnitten gelten kann, denn wie sollen sich Ihre gleitenden Durchschnitte kreuzen, wenn sie beide die gleiche Periodenlänge haben? Die Antwort ist überraschend einfach: Wir wenden die gleitenden Durchschnitte einmal auf den Eröffnungs- und einmal auf den Schlusskurs an.
Wenn der gleitende Eröffnungsdurchschnitt über dem gleitenden Schlussdurchschnitt liegt, enden die Kurse niedriger als sie begonnen haben. Dies ist identisch mit dem Wissen, dass das Preisniveau sinkt. Das Gegenteil ist der Fall, wenn der geschlossene gleitende Durchschnitt über dem Eröffnungswert liegt.
Eine gemeinsame Periodenlänge für beide gleitenden Durchschnitte ist unkonventionell, aber ich habe sie für unseren heutigen Test ausgewählt, um dem Leser eine Möglichkeit aufzuzeigen, wie wir die Kritik an der Verzögerung in Verbindung mit gleitenden Durchschnittsübergängen ausräumen können.
Bevor wir in den technischen Teil unserer Diskussion eintauchen, kann dieser Artikel als Beispiel für eine verallgemeinerte Klasse von Handelsstrategien gelesen werden, die leicht auf viele andere technische Indikatoren ausgeweitet werden können. Der Relative Strength Indikator oder RSI kann beispielsweise auch unabhängig voneinander auf den Eröffnungs-, den Schluss-, den Höchst- oder den Tiefstkurs angewandt werden, und wir werden feststellen, dass der RSI, der den Eröffnungskurs verfolgt, tendenziell über den RSI steigt, der den Schlusskurs verfolgt, wenn sich die Märkte in einem Abwärtstrend befinden.
Überblick über unseren Backtest
Um die Bedeutung unserer heutigen Erörterung zu verstehen, werden wir zunächst eine Benchmark aufstellen, die durch die traditionelle Kreuzungs-Strategie geschaffen wurde. Dann werden wir die bisherige Leistung mit dem vergleichen, was wir mit unserer neu konzipierten Version der Strategie erreichen können.Für unsere heutige Diskussion habe ich das Währungspaar EURUSD ausgewählt. Der EURUSD ist das am aktivsten gehandelte Währungspaar der Welt. Er ist deutlich volatiler als die meisten Währungspaare und eignet sich im Allgemeinen nicht für einfache Kreuzungs-Strategien. Wie wir bereits besprochen haben, werden wir uns auf den täglichen Zeitrahmen konzentrieren. Wir werden unsere Strategie über einen Zeitraum von etwa 4 Jahren historischer Daten testen, vom 1. Januar 2020 bis zum 24. Dezember 2024; der Backtest-Zeitraum ist in Abbildung 1 dargestellt.
Abb. 1: Betrachtung des 4-Jahres-Zeitraums für den EURUSD-Test auf unserem MetaTrader 5-Terminal unter Verwendung des monatlichen Zeitrahmens
Obwohl herkömmliche Kreuzungs-Strategien intuitiv zu verstehen sind und auf einigermaßen soliden Grundprinzipien beruhen, erfordern diese Strategien oft eine endlose Optimierung, um einen effektiven Einsatz zu gewährleisten. Außerdem sind die „richtigen“ Periodenlänge für den langsamen und schnellen Indikator nicht sofort ersichtlich und können sich dramatisch ändern.
Zur Erinnerung: Die ursprüngliche Strategie basiert auf den Schnittpunkten von 2 gleitenden Durchschnitten, die beide den Schlusskurs desselben Wertpapiers verfolgen, jedoch mit unterschiedlichen Periodenlängen. Wenn der gleitende Durchschnitt mit der kürzeren Periodenlänge über dem anderen steht, interpretieren wir dies als ein Signal, dass sich die Preise in einem Aufwärtstrend befinden und wahrscheinlich weiter steigen werden. Das Gegenteil ist der Fall, wenn der gleitende Durchschnitt mit der längeren Periodenlänge über der mit der kürzeren steht. Wir interpretieren dies als ein Abwärtssignal, ein illustriertes Beispiel finden Sie in der folgenden Abbildung 2.
Abb. 2: Ein Beispiel für die traditionelle Kreuzungs-Strategie für gleitende Durchschnitte in Aktion: Die gelbe Linie ist der schnelle gleitende Durchschnitt, die weiße der langsame.
In Abb. 2 haben wir zufällig einen Zeitraum ausgewählt, der die Grenzen der klassischen Strategie aufzeigt. Links von der vertikalen Linie auf dem Chart können Sie sehen, dass der Kurs etwa 2 Monate lang in einem Bereich feststeckte. Diese lethargische Kursentwicklung erzeugt Handelssignale, die schnell wieder rückgängig gemacht werden und höchstwahrscheinlich unrentabel sind. Nach dieser Periode der miserablen Performance kam es schließlich zu einem Ausbruch der Kurse in einen echten Trend auf der rechten Seite der vertikalen Linie. Traditionelle Kreuzungs-Strategien funktionieren am besten in einem Markt mit einem Trend. Die in diesem Artikel vorgeschlagene Strategie löst diese Probleme jedoch auf elegante Weise.
Festlegen einer Benchmark
Unsere Handelsanwendung besteht aus 4 Hauptkomponenten, die zusammenarbeiten, um uns beim Handel zu helfen:
Merkmal | Beschreibung |
---|---|
Systemkonstanten | Sie helfen uns, die Verbesserungen zu isolieren, die sich aus den Änderungen ergeben, die wir an der Handelslogik unserer Anwendung vornehmen. |
Globale Variablen | Sie sind verantwortlich für die Verfolgung von Indikatorwerten, aktuellen Marktpreisen und möglicherweise weiteren Informationen, die wir benötigen. |
Ereignisbehandlung | Sie führen verschiedene Aufgaben zum richtigen Zeitpunkt aus, um unser Ziel des effektiven Handels mit gleitenden Durchschnittsüberkreuzungen zu erreichen. |
Nutzerdefinierte Funktionen | Jede nutzerdefinierte Funktion in unserem System hat eine bestimmte Aufgabe, und alle Funktionen zusammen helfen uns, unser Ziel zu erreichen. |
Die Benchmark-Version unserer Anwendung wird in ihrer Implementierung minimalistisch sein. Als erstes müssen wir Systemkonstanten einrichten, die für beide Tests, die wir durchführen, gleich bleiben. Unsere Systemkonstanten sind wichtig, um faire Vergleiche zwischen verschiedenen Handelsstrategien anstellen zu können. Sie verhindern, dass wir unbeabsichtigt Einstellungen ändern, die in den verschiedenen Tests nicht geändert werden sollten, wie z. B. die Größe unseres Stop-Loss, der in beiden Tests konstant sein sollte, um einen fairen Vergleich zu gewährleisten.
//+------------------------------------------------------------------+ //| Channel And MA.mq5 | //| Gamuchirai Zororo Ndawana | //| https://www.mql5.com/en/gamuchiraindawa | //+------------------------------------------------------------------+ #property copyright "Gamuchirai Zororo Ndawana" #property link "https://www.mql5.com/en/gamuchiraindawa" #property version "1.00" //+------------------------------------------------------------------+ //| System constants | //+------------------------------------------------------------------+ #define TF_1 PERIOD_D1 //--- Our main time frame #define TF_2 PERIOD_H1 //--- Our lower time frame #define ATR_PERIOD 14 //--- The period for our ATR #define ATR_MULTIPLE 3 //--- How wide should our stops be? #define VOL 0.01 //--- Trading volume
Wir werden auch einige wichtige globale Variablen definieren, die wir zum Abrufen von Indikatorwerten und aktuellen Marktpreisen verwenden werden.
//+------------------------------------------------------------------+ //| Global variables | //+------------------------------------------------------------------+ int trade; int ma_f_handler,ma_s_handler,atr_handler; double ma_f[],ma_s[],atr[]; double bid,ask; double o,h,l,c; double original_sl;
Wir werden die Handelsbibliothek nutzen, um unsere Positionen zu verwalten.
//+------------------------------------------------------------------+ //| Libraries | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> CTrade Trade;
In MQL5 basieren Expert Advisors auf der Ereignisbehandlung. Es gibt viele verschiedene Arten von Ereignissen, die im MetaTrader 5-Terminal auftreten. Diese Ereignisse können durch Aktionen des Nutzers ausgelöst werden, oder wenn neue Preise notiert werden. Jedem Ereignis ist ein spezielle Behandlung zugeordnet, die jedes Mal aufgerufen wird, wenn das Ereignis ausgelöst wird. Daher habe ich unsere Anwendung so konzipiert, dass jede Ereignisbehandlung eine eigene Funktion hat, die er nacheinander aufruft, um die für eine Kreuzungs-Strategie mit gleitendem Durchschnitt erforderlichen Aufgaben zu erfüllen.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Setup setup(); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Release our indicators release(); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Update system variables update(); } //+------------------------------------------------------------------+
Die Setup-Funktion wird aufgerufen, wenn unser System gestartet wird. OnInit wird aufgerufen, wenn die Handelsanwendung auf ein Chart gestartet wird, und er leitet die Befehlskette an unsere angepasste Setup-Funktion weiter, die unsere technischen Indikatoren für uns anwendet.
//+------------------------------------------------------------------+ //| Custom functions | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Setup our system | //+------------------------------------------------------------------+ void setup(void) { atr_handler = iATR(Symbol(),TF_1,ATR_PERIOD); ma_f_handler = iMA(Symbol(),TF_1,10,0,MODE_EMA,PRICE_CLOSE); ma_s_handler = iMA(Symbol(),TF_1,60,0,MODE_EMA,PRICE_CLOSE); }
Wenn wir unsere Handelsanwendung nicht mehr verwenden, wird der OnDeinit aufgerufen, der unsere Release-Funktion aufruft, die die Systemressourcen freigibt, die zuvor von unseren technischen Indikatoren verbraucht wurden.
//+------------------------------------------------------------------+ //| Release variables we do not need | //+------------------------------------------------------------------+ void release(void) { IndicatorRelease(atr_handler); IndicatorRelease(ma_f_handler); IndicatorRelease(ma_s_handler); }
Immer wenn neue Marktpreise notiert werden, wird OnTick aufgerufen, der wiederum die Aktualisierungsfunktion aufruft, um die neuen verfügbaren Marktinformationen zu speichern. Danach, wenn wir keine offenen Positionen haben, suchen wir nach einem Handelsaufbau. Andernfalls werden wir die offenen Positionen verwalten.
/+------------------------------------------------------------------+ //| Update system variables | //+------------------------------------------------------------------+ void update(void) { //--- Update the system static datetime time_stamp; datetime current_time = iTime(Symbol(),TF_2,0); if(current_time != time_stamp) { time_stamp = current_time; CopyBuffer(atr_handler,0,0,1,atr); CopyBuffer(ma_s_handler,0,0,1,ma_s); CopyBuffer(ma_f_handler,0,0,1,ma_f); o = iOpen(Symbol(),TF_1,0); h = iHigh(Symbol(),TF_1,0); l = iLow(Symbol(),TF_1,0); c = iClose(Symbol(),TF_1,0); bid = SymbolInfoDouble(Symbol(),SYMBOL_BID); ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK); if(PositionsTotal() == 0) find_position(); if(PositionsTotal() > 0) manage_position(); } }
Unsere Regeln für die Eingabe von Positionen sind einfach und wurden oben bereits ausführlich erläutert. Wir gehen Kaufpositionen ein, wenn unser sich der schnelle gleitende Durchschnitt über dem langsam liegt, und wenn das Gegenteil der Fall ist, gehen wir Verkaufspositionen ein.
//+------------------------------------------------------------------+ //| Find a position | //+------------------------------------------------------------------+ void find_position(void) { if((ma_s[0] > ma_f[0])) { Trade.Sell(VOL,Symbol(),bid,0,0,""); trade = -1; } if((ma_s[0] < ma_f[0])) { Trade.Buy(VOL,Symbol(),ask,0,0,""); trade = 1; } }
Schließlich wird unser Stop-Loss mit Hilfe des Indikators Average True Range (ATR) dynamisch angepasst. Wir fügen ein festes Vielfaches des ATR-Wertes über und unter unserem Einstiegskurs hinzu, um unsere Stop-Loss und Take-Profit zu markieren. Zusätzlich fügen wir den durchschnittlichen ATR-Wert der letzten 90 Tage (1 Konjunkturzyklus) hinzu, um der jüngsten Volatilität des Marktes Rechnung zu tragen. Schließlich werden wir einen ternären Operator verwenden, um Take-Profit und Stop-Loss anzupassen. Unsere Regel lautet, dass die Stopps nur dann aktualisiert werden sollten, wenn die neue Position profitabler ist als die alte. Ternäre Operatoren ermöglichen es uns, diese Logik auf kompakte Weise auszudrücken. Darüber hinaus geben uns ternäre Operatoren die Flexibilität, Take-Profit und Stop-Loss unabhängig voneinander anzupassen.
//+------------------------------------------------------------------+ //| Manage our positions | //+------------------------------------------------------------------+ void manage_position(void) { //--- Select the position if(PositionSelect(Symbol())) { //--- Get ready to update the SL/TP double initial_sl = PositionGetDouble(POSITION_SL); double initial_tp = PositionGetDouble(POSITION_TP); //--- Calculate the average ATR move vector atr_mean; atr_mean.CopyIndicatorBuffer(atr_handler,0,0,90); double buy_sl = (ask - ((ATR_MULTIPLE * atr[0]) + atr_mean.Mean())); double sell_sl = (bid + ((ATR_MULTIPLE * atr[0]) + atr_mean.Mean())); double buy_tp = (ask + ((ATR_MULTIPLE * 0.5 * atr[0]) + atr_mean.Mean())); double sell_tp = (bid - ((ATR_MULTIPLE * 0.5 * atr[0]) + atr_mean.Mean())); double new_sl = ((trade == 1) && (initial_sl < buy_sl)) ? (buy_sl) : ((trade == -1) && (initial_sl > sell_sl)) ? (sell_sl) : (initial_sl); double new_tp = ((trade == 1) && (initial_tp < buy_tp)) ? (buy_tp) : ((trade == -1) && (initial_tp > sell_tp)) ? (sell_tp) : (initial_tp); if(initial_sl == 0 && initial_tp == 0) { if(trade == 1) { original_sl = buy_sl; Trade.PositionModify(Symbol(),buy_sl,buy_tp); } if(trade == -1) { original_sl = sell_sl; Trade.PositionModify(Symbol(),sell_sl,sell_tp); } } //--- Update the position else if((initial_sl * initial_tp) != 0) { Trade.PositionModify(Symbol(),new_sl,new_tp); } } } //+------------------------------------------------------------------+
Zusammengefasst sieht unser aktueller Code bisher wie folgt aus.
//+------------------------------------------------------------------+ //| Channel And MA.mq5 | //| Gamuchirai Zororo Ndawana | //| https://www.mql5.com/en/gamuchiraindawa | //+------------------------------------------------------------------+ #property copyright "Gamuchirai Zororo Ndawana" #property link "https://www.mql5.com/en/gamuchiraindawa" #property version "1.00" //+------------------------------------------------------------------+ //| This version off the application is mean reverting | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| System constants | //+------------------------------------------------------------------+ #define TF_1 PERIOD_D1 //--- Our main time frame #define TF_2 PERIOD_H1 //--- Our lower time frame #define ATR_PERIOD 14 //--- The period for our ATR #define ATR_MULTIPLE 3 //--- How wide should our stops be? #define VOL 0.01 //--- Trading volume //+------------------------------------------------------------------+ //| Global variables | //+------------------------------------------------------------------+ int trade; int ma_f_handler,ma_s_handler,atr_handler; double ma_f[],ma_s[],atr[]; double bid,ask; double o,h,l,c; double original_sl; //+------------------------------------------------------------------+ //| Libraries | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> CTrade Trade; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Setup setup(); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Release our indicators release(); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Update system variables update(); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Custom functions | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Setup our system | //+------------------------------------------------------------------+ void setup(void) { atr_handler = iATR(Symbol(),TF_1,ATR_PERIOD); ma_f_handler = iMA(Symbol(),TF_1,10,0,MODE_EMA,PRICE_CLOSE); ma_s_handler = iMA(Symbol(),TF_1,60,0,MODE_EMA,PRICE_CLOSE); } //+------------------------------------------------------------------+ //| Release variables we do not need | //+------------------------------------------------------------------+ void release(void) { IndicatorRelease(atr_handler); IndicatorRelease(ma_f_handler); IndicatorRelease(ma_s_handler); } //+------------------------------------------------------------------+ //| Update system variables | //+------------------------------------------------------------------+ void update(void) { //--- Update the system static datetime time_stamp; datetime current_time = iTime(Symbol(),TF_2,0); if(current_time != time_stamp) { time_stamp = current_time; CopyBuffer(atr_handler,0,0,1,atr); CopyBuffer(ma_s_handler,0,0,1,ma_s); CopyBuffer(ma_f_handler,0,0,1,ma_f); o = iOpen(Symbol(),TF_1,0); h = iHigh(Symbol(),TF_1,0); l = iLow(Symbol(),TF_1,0); c = iClose(Symbol(),TF_1,0); bid = SymbolInfoDouble(Symbol(),SYMBOL_BID); ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK); if(PositionsTotal() == 0) find_position(); if(PositionsTotal() > 0) manage_position(); } } //+------------------------------------------------------------------+ //| Find a position | //+------------------------------------------------------------------+ void find_position(void) { if((ma_s[0] > ma_f[0])) { Trade.Sell(VOL,Symbol(),bid,0,0,""); trade = -1; } if((ma_s[0] < ma_f[0])) { Trade.Buy(VOL,Symbol(),ask,0,0,""); trade = 1; } } //+------------------------------------------------------------------+ //| Manage our positions | //+------------------------------------------------------------------+ void manage_position(void) { //--- Select the position if(PositionSelect(Symbol())) { //--- Get ready to update the SL/TP double initial_sl = PositionGetDouble(POSITION_SL); double initial_tp = PositionGetDouble(POSITION_TP); //--- Calculate the average ATR move vector atr_mean; atr_mean.CopyIndicatorBuffer(atr_handler,0,0,90); double buy_sl = (ask - ((ATR_MULTIPLE * atr[0]) + atr_mean.Mean())); double sell_sl = (bid + ((ATR_MULTIPLE * atr[0]) + atr_mean.Mean())); double buy_tp = (ask + ((ATR_MULTIPLE * 0.5 * atr[0]) + atr_mean.Mean())); double sell_tp = (bid - ((ATR_MULTIPLE * 0.5 * atr[0]) + atr_mean.Mean())); double new_sl = ((trade == 1) && (initial_sl < buy_sl)) ? (buy_sl) : ((trade == -1) && (initial_sl > sell_sl)) ? (sell_sl) : (initial_sl); double new_tp = ((trade == 1) && (initial_tp < buy_tp)) ? (buy_tp) : ((trade == -1) && (initial_tp > sell_tp)) ? (sell_tp) : (initial_tp); if(initial_sl == 0 && initial_tp == 0) { if(trade == 1) { original_sl = buy_sl; Trade.PositionModify(Symbol(),buy_sl,buy_tp); } if(trade == -1) { original_sl = sell_sl; Trade.PositionModify(Symbol(),sell_sl,sell_tp); } } //--- Update the position else if((initial_sl * initial_tp) != 0) { Trade.PositionModify(Symbol(),new_sl,new_tp); } } } //+------------------------------------------------------------------+
Da unsere derzeitige Strategie keine KI oder Kurvenanpassungstechniken verwendet, können wir einen einfachen Backtest durchführen, ohne uns Gedanken über eine Überanpassung an die uns zur Verfügung stehenden Daten zu machen. Außerdem haben wir keine Eingabeparameter, die angepasst werden müssen. Daher haben wir „Forward“ auf „No“ gesetzt, da wir die Funktionen des Vorwärtstests des MetaTrader 5-Terminals im Moment nicht benötigen.
Abb. 3: Auswahl der Termine für unseren Backtest
Darüber hinaus ist es eine gute Praxis, Ihre Handelsanwendungen unter der stressigsten Umgebung zu testen, die Sie simulieren können. Daher haben wir für unseren heutigen Backtest den Modus „Zufällige Verzögerung“ gewählt, wobei unsere Modellierung auf echte Ticks eingestellt ist. Bei der Verwendung von echten Ticks kann die zum Herunterladen historischer Daten benötigte Zeit länger sein als bei der Verwendung anderer Modi wie z. B. „Nur Öffnungspreise“. Die Ergebnisse dürften jedoch näher an den tatsächlichen Ergebnissen bei realen Ticks liegen.
Abb. 4: Unsere zweite Gruppe von Einstellungen für unseren EURUSD Backtest des Kreuzens
Wenn wir die Ergebnisse analysieren, die wir mit der einfachen Kreuzungs-Strategie erzielt haben, können wir schnell potenzielle Probleme erkennen, die auftreten würden, wenn wir die Strategie in ihrer ursprünglichen Form anwenden würden. Beachten Sie, dass unsere Strategie von Beginn des Backtests an bis Juli 2022 hart daran gearbeitet hat, die Gewinnschwelle zu erreichen. Dies entspricht einem Zeitraum von fast der Hälfte unseres Backtests, also 2 Jahren. Dies ist unerwünscht und kein Merkmal einer Strategie, der wir vertrauen können, um unser Geld unbeaufsichtigt zu handeln.
Abb. 5: Analyse unserer Gewinn- und Verlustkurve, die sich aus der MA-Kreuzungs-Strategie ergibt
Unsere Strategie ist in ihrer ursprünglichen Form kaum profitabel und verliert fast 61 % aller von ihr getätigten Handelsgeschäfte. Daraus ergeben sich negative Erwartungen an die Strategie, und unser Pessimismus wird durch die Tatsache bestätigt, dass unsere Sharpe Ratio sehr nahe bei 0 liegt. Beobachten Sie jedoch, wie drastisch wir unsere Strategie verbessern können, indem wir ein paar einfache Anpassungen an der angewandten Handelslogik vornehmen.
Abb. 6: Eine ausführliche Zusammenfassung unserer Leistung bei Verwendung der alten MA-Kreuzungs-Strategie
Die Verbesserung der ursprünglichen Strategie
In der folgenden Abbildung 7 habe ich Ihnen eine visuelle Darstellung unserer neuen vorgeschlagenen Kreuzungs-Strategie gegeben. Die blaue und die grüne Linie sind gleitende Durchschnitte über 5 Perioden, die dem Schlusskurs (blau) und dem Eröffnungskurs (grün) folgen. Wenn der blaue gleitende Durchschnitt über dem grünen gleitenden Durchschnitt liegt, sind die Kurse gestiegen. Achten Sie außerdem darauf, wie stark unsere Kreuzungen auf Veränderungen des Preisniveaus reagieren. Traditionelle Kreuzungs-Strategien benötigen eine variable Zeitspanne, um Änderungen des Markttrends zu berücksichtigen. Wenn wir jedoch die Preisniveaus mit unserer neuen Strategie verfolgen, können wir schnell Trendänderungen und sogar Konsolidierungsphasen beobachten, wobei sich 2 gleitende Durchschnitte immer wieder kreuzen, aber keine wirklichen Fortschritte machen.
Abb. 7: Visualisierung unserer neuen Kreuzungs-Strategie auf dem EURUSD Tages-Zeitrahmen
Bislang konnte unser System gewinnbringend handeln, aber wir können noch besser werden. Die einzige Änderung, die wir an unserem System vornehmen werden, ist die Änderung der Bedingungen, unter denen unsere Positionen ausgelöst werden:
Änderung | Beschreibung |
---|---|
Handels- regeln | Nach unseren traditionellen Regeln wird gekauft, wenn der schnelle Durchschnitt über dem langsamen liegt. Stattdessen werden wir jetzt kaufen, wenn unser gleitender Durchschnitt der Eröffnungspreise über dem gleitenden Durchschnitt der Schlusskurse liegt. |
Um die gewünschte Verbesserung zu erreichen, müssen wir einige Änderungen an der ursprünglichen Form unserer derzeitigen Handelsstrategie vornehmen:
Änderung | Beschreibung |
---|---|
Zusätzliche Systemvariablen | Wir benötigen eine neue Systemvariable, die für die Festlegung der Periodenlänge des gleitenden Durchschnitts bei Eröffnung und Abschluss verantwortlich ist. |
Neue globale Variablen | Es werden neue globale Variablen erstellt, um die neuen Informationen, denen wir Aufmerksamkeit schenken, zu verfolgen. |
Modifikation der nutzerdefinierten Funktionen | Einige der maßgeschneiderten Funktionen, die wir bisher entwickelt haben, müssen im Hinblick auf das neue Systemdesign, das wir verfolgen, erweitert werden. |
Alle anderen Teile unseres Systems werden größtenteils erhalten bleiben. Wir wollen die Verbesserungen isolieren, die durch die Änderung unserer Sichtweise auf das Kreuzen von gleitenden Durchschnitte erzielt werden. Um unser Ziel zu erreichen, werden wir daher zunächst eine neue Systemkonstante erstellen, um die Periodenlänge unserer gleitenden Durchschnitte festzulegen.
//--- Omitted code that has not changed #define MA_PERIOD 2 //--- The period for our moving average following the close
Dann müssen wir neue globale Variablen für die neuen Informationen definieren, die wir im Auge behalten wollen. Wir erstellen nun die Handles der gleitende Durchschnitte für jeden der 4 angegebenen Preise (Open, High, Low & Close).
//--- Omitted code that have not changed int ma_h_handler,ma_l_handler,ma_c_handler,ma_o_handler; double ma_h[],ma_l[],ma_c[],ma_o[];
Wenn unsere Handelsanwendung gestartet wird, müssen wir zusätzlich zu den bereits bekannten Indikatoren einige weitere laden.
//+------------------------------------------------------------------+ //| Setup our system | //+------------------------------------------------------------------+ void setup(void) { atr_handler = iATR(Symbol(),TF_1,ATR_PERIOD); ma_h_handler = iMA(Symbol(),TF_1,MA_PERIOD,0,MODE_EMA,PRICE_HIGH); ma_l_handler = iMA(Symbol(),TF_1,MA_PERIOD,0,MODE_EMA,PRICE_LOW); ma_c_handler = iMA(Symbol(),TF_1,MA_PERIOD,0,MODE_EMA,PRICE_CLOSE); ma_o_handler = iMA(Symbol(),TF_1,MA_PERIOD,0,MODE_EMA,PRICE_OPEN); }
Das Gleiche gilt für unsere angepasste Funktion, die für das Entfernen unseres Expert Advisors verantwortlich ist. Sie wird erweitert, um die neuen Indikatoren aufzunehmen, die wir in das System eingeführt haben.
//+------------------------------------------------------------------+ //| Release variables we do not need | //+------------------------------------------------------------------+ void release(void) { IndicatorRelease(atr_handler); IndicatorRelease(ma_h_handler); IndicatorRelease(ma_l_handler); IndicatorRelease(ma_c_handler); IndicatorRelease(ma_o_handler); }
Schließlich müssen die Bedingungen, die wir zur Eröffnung unserer Handelsgeschäfte verwenden, aktualisiert werden, damit sie mit unserer neuen Handelslogik übereinstimmen.
//+------------------------------------------------------------------+ //| Find a position | //+------------------------------------------------------------------+ void find_position(void) { if((ma_o[0] > ma_c[0])) { Trade.Sell(VOL,Symbol(),bid,0,0,""); trade = -1; } if((ma_o[0] < ma_c[0])) { Trade.Buy(VOL,Symbol(),ask,0,0,""); trade = 1; } }
Lassen Sie uns nun sehen, welche Auswirkungen dies auf unser Endergebnis hat. Wir werden zunächst unsere neue Handelsanwendung so einrichten, dass sie über denselben Zeitraum handelt, den wir in unserem ersten Test verwendet haben.
Abb. 8: Einrichten unseres neuen und verbesserten Handelsalgorithmus für den Handel über denselben Zeitraum, den wir in unserem ersten Test verwendet haben
Außerdem wollen wir beide Backtests unter identischen Bedingungen durchführen, um sicherzustellen, dass unser Test unvoreingenommen ist. Andernfalls würde die Integrität unserer bisherigen Tests in Frage gestellt, wenn einer Strategie ein unfairer Vorteil eingeräumt wird.
Abb. 9: Wir wollen sicherstellen, dass die Testbedingungen in beiden Tests identisch sind, um einen fairen Vergleich zu ermöglichen
Wir können bereits jetzt eine große Verbesserung gegenüber den ersten Ergebnissen feststellen, die wir erzielt haben. Wir haben die ersten 2 Jahre unseres ersten Backtests damit verbracht, den Break-Even zu erreichen. Mit unserer neuen Handelsstrategie konnten wir diese Beschränkung jedoch durchbrechen und waren in allen 4 Jahren profitabel.
Abb. 10: Unsere neue Handelsstrategie durchbrach die unrentable Phase des Handels, die mit traditionellen Kreuzungs-Strategien nur schwer zu lösen gewesen wäre.
In unserem ersten Backtest tätigte unser System insgesamt 41 Handelsgeschäfte und in unserem letzten Backtest haben wir insgesamt 42 Trades gemacht. Daher geht unser neues System ein höheres Risiko ein als die alte Art des Handels. Denn wenn wir mehr Zeit verstreichen lassen, könnte die Kluft zwischen ihnen noch größer werden. Doch obwohl unser neues System anscheinend mehr Handelsgeschäfte platziert als unser altes System, betrug unser Gesamtgewinn aus unserem alten System 59,57 $, und jetzt hat sich unser Gesamtgewinn auf 125,36 $ mehr als verdoppelt. Erinnern Sie sich daran, dass wir unser Handelssystem vorerst auf 1 Handelsgeschäft mit einem minimaler Losgröße beschränkt haben. Außerdem betrug unser Bruttoverlust in unserem ersten System 410,02 $, und mit unserer neuen Strategie ist unser Bruttoverlust auf 330,74 $ gesunken.
Bei der Entwicklung von Systemen müssen wir in Kompromissen denken. Unser neues System funktioniert besser für uns. Wir sollten jedoch auch zur Kenntnis nehmen, dass die Höhe unseres durchschnittlichen Gewinns von 29,35 $ auf 22,81 $ gesunken ist. Der Grund dafür ist, dass unser neues System sporadisch Handelsgeschäfte verpasst, von denen unser altes System profitiert hat. Dieses gelegentliche Bedauern kann jedoch angesichts der Leistungsgewinne, die wir erzielen, gerechtfertigt sein.
Unsere Sharpe Ratio ist von 0,18 in unserem ersten Test auf 0,5 in unserem aktuellen Test gestiegen. Dies ist ein gutes Zeichen dafür, dass wir unser Kapital besser nutzen. Außerdem ist unser Anteil an Verlustgeschäften von 60,98 % im ersten Test auf einen neuen Tiefstand von 52,38 % gesunken.
Abb. 11: Eine detaillierte Überprüfung der Leistung unserer neuen Handelsstrategie
Schlussfolgerung
Die meisten Mitglieder unserer Gemeinschaft sind unabhängige Entwickler, die allein an ihren Projekten arbeiten. Ich glaube, dass einfache Algorithmen, wie die, die wir Ihnen hier vorgeschlagen haben, für die Mitglieder unserer Gemeinschaft, die sich in diesen Positionen befinden, praktischere Lösungen sein können. Sie ist leichter zu pflegen, zu entwickeln und zu erweitern. Die Verwaltung einer komplexen und großen Code-Basis als einzelner Entwickler ist keine leichte Aufgabe, selbst für erfahrene Entwickler. Und wenn Sie neu in unserer algorithmischen Handelsgemeinschaft sind, dann könnte diese Strategie besonders hilfreich für Sie sein. Wenn Ihnen die Lektüre dieses Artikels gefallen hat, dann nehmen Sie an unserer nächsten Diskussion teil, in der wir versuchen werden, die besten Ergebnisse, die wir heute erzielt haben, noch zu übertreffen.
Datei Name | Beschreibung |
---|---|
Open & Close MA Cross | Diese Datei enthält unsere neue, überarbeitete Version der gleitenden Durchschnitts-Kreuzungs-Strategie. |
Traditional MA Cross | Diese Datei enthält die klassische Implementierung von gleitenden Durchschnitts-Crossovers. |
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/16758





- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.