English 日本語
preview
Analyse mehrerer Symbole mit Python und MQL5 (Teil 3): Dreieck der Wechselkurse

Analyse mehrerer Symbole mit Python und MQL5 (Teil 3): Dreieck der Wechselkurse

MetaTrader 5Beispiele | 24 Juni 2025, 07:37
63 0
Gamuchirai Zororo Ndawana
Gamuchirai Zororo Ndawana

Finanzmärkte sind von Natur aus unruhig. Es kommt häufig vor, dass Händler unnötige Verluste erleiden, die durch falsche Handelssignale verursacht werden, die den Händler dazu ermutigen, seine Positionen vorzeitig zu eröffnen. Es gibt viele verschiedene Handelsstrategien und -prinzipien, die im Hinblick auf dieses Thema entwickelt worden sind. Die meisten dieser Axiome lehren den Händler im Wesentlichen, zu warten, bevor er handelt, und stattdessen nach alternativen Quellen der Bestätigung oder zusätzlichen Anzeichen für Stärke zu suchen.

Diese Regeln haben im Allgemeinen keinen bestimmten Zeithorizont, in dem die Bestätigungssignale gefunden werden müssen, und können den Händler gelegentlich etwas Bedauern kosten, da Händler, die solche Faustregeln befolgen, dazu neigen, gute Einstiegsniveaus bei den meisten Geschäften zu verlieren, für die sie sich schließlich entscheiden.

Es ist für den Leser offensichtlich, dass wir versuchen müssen, Handelsstrategien zu entwickeln, die in der Lage sind, die Stärke eines Marktes so nah an der Echtzeit zu messen, wie es möglich ist. Das kann uns gelingen, wenn wir versuchen, die Vorteile der vernetzten Märkte zu nutzen und nach vorhersehbaren Mustern zu suchen, die für uns Sinn machen. Wir können nach marktübergreifenden Mustern Ausschau halten, die wir für vorhersehbar halten, anstatt bei jedem Handel, an dem wir interessiert sind, möglicherweise Pips zu verlieren und unsere Verluste im Laufe der Zeit zu vergrößern.

Für Leser, die möglicherweise neu in der Denkschule der marktübergreifenden Muster sind, werden wir eine kurze Einführung in das Thema geben, damit Sie verstehen können, warum einige Händler Ihnen sagen werden, dass diese marktübergreifenden Muster typischerweise vorhersehbar sind, und wenn man sie richtig erwischt, haben sie das Potenzial, robust zu sein.

Rund 90 % der Rohstoffe weltweit werden in amerikanischen Dollars gehandelt. Einige Rohstoffe werden jedoch so häufig gehandelt, dass sie oft in mehreren Währungen gleichzeitig notiert werden. 

Edelmetalle wie Silber werden in der Regel gleichzeitig in amerikanischen Dollars und in Euro notiert. Wir werden uns heute auf Silber als unser Beispiel konzentrieren. Man kann feststellen, dass sein Broker gleichzeitig Notierungen des Silberpreises in Dollar (XAGUSD) und in Euro (XAGEUR) anbietet.

Wenn wir mit dem XAGUSD handeln wollen und wissen, dass er grundsätzlich mit dem XAGEUR verbunden ist, wie können wir dieses Wissen zu unserem Vorteil nutzen, um Handelsmöglichkeiten, die sich uns bieten, anzunehmen oder abzulehnen, ohne unendlich lange auf eine Bestätigung warten zu müssen?

Wenn man den aktuellen Wechselkurs zwischen Euro und Dollar (EURUSD) berücksichtigt, kann man eine dreieckige Handelsstrategie entwickeln, die vorhersehbare Muster zwischen dem Silberpreis in Dollar, Euro und dem fairen Wechselkurs zwischen Euro und Dollar (EURUSD) aufzeigt. Unser Ziel ist es, dem Leser eine Handelsstrategie an die Hand zu geben, die robust gegenüber Rauschen ist und dabei helfen kann, „versteckte“ Marktstimmungen in den gewöhnlichen Marktnotierungen, die Sie in Ihrem MetaTrader 5 Terminal erhalten, zu entdecken. 


Überblick über unsere Handelsstrategie

Bevor wir in die Details unserer Handelsstrategie eintauchen, wollen wir zunächst sicherstellen, dass jeder Leser ein grundlegendes Verständnis der Basis und des Kurses eines gehandelten Paares hat. Wenn wir den EURUSD als Beispiel nehmen, ist der EUR (Euro) unsere Basis. Die verkürzte, linke Teil des Paares ist die Basis. Wenn der in einem Chart angezeigte Wechselkurs von 0 weg steigt, erhöht sich der Wert der Basiswährung. Wenn wir uns also ein Chart des EURUSD-Wechselkurses ansehen und das Chart steigt, bedeutet dies, dass wir mehr amerikanische Dollar verkaufen müssen, um 1 Euro auf dem Devisenkassamarkt zu erhalten.

Abb. 1: Den Unterschied zwischen der Basis und dem Kurs in einem Paar verstehen

Das zweite aufgelistete Symbol hingegen ist das Kurs. Der Wert der Notierung steigt, wenn der angezeigte Wechselkurs im Chart näher an 0 herankommt. Das bedeutet, dass wir immer weniger amerikanische Dollars verkaufen müssen, um 1 Euro zu erhalten.

Abb. 2: Konzentration auf den Kurs

Wir können nun unsere speziellen Handelseinstellungen in Betracht ziehen, da wir wissen, dass kein Leser auf der Strecke bleiben wird. Wir ziehen drei verschiedene Märkte in Betracht, mit dem Ziel, nur einen zu handeln. Unser Ziel ist der XAGUSD. Um herauszufinden, in welche Richtung sich der XAGUSD bewegen könnte, werden wir zunächst den EURUSD-Kurs untersuchen.

Steigt der EURUSD-Wechselkurs, dann wertet der Euro (Kurs) auf und der Dollar (Basis) verliert an Wert. Man könnte erwarten, dass eine Ware, die sowohl in Euro als auch in Dollar gehandelt wird, in Euro billiger wird, da der Euro an Kaufkraft gewonnen hat, und in Dollar teurer wird, da der Dollar auf dem Devisenmarkt an Wert verloren hat.

Dieses Gedankenexperiment hat den Lesern, die mit solchen Handelsstrategien nicht vertraut sind, hoffentlich eine Vorstellung davon vermittelt, wie sich drei Märkte unter bestimmten Annahmen gegenseitig „spiegeln“ könnten. 

Indem wir die Wertentwicklung dieser drei Märkte gleichzeitig analysieren, können wir im Wesentlichen eine Strategie entwickeln, die nicht immer auf eine Bestätigung warten muss. Wenn unsere Annahmen über die Verflechtung dieser Märkte zutreffen, sollten wir über einen brauchbaren Rahmen verfügen, der auf andere Märkte übertragen werden kann, an deren Handel der Leser interessiert ist. 

Ein visuelles Mindmap der Handelsstrategie ist in Abbildung 4 dargestellt. Wenn der EURUSD-Wechselkurs fällt, ist im Allgemeinen zu erwarten, dass die in Euro gehandelten Waren teurer und die in Dollar gehandelten Waren billiger werden. Unsere Strategie prüft den EURUSD-Wechselkurs, den Silberpreis in Euro und den Silberpreis in Dollar und sucht nach diesem speziellen Muster. Wenn wir dieses Kreuzmuster des Marktes gefunden haben, können wir Silber gegen Dollar verkaufen.

Abb. 3: Visualisierung unserer Handelsregeln, XAUUSD zu verkaufen

Wir wenden die gleichen Regeln im umgekehrten Sinne an, wenn wir auf Silber gegen Dollar setzen wollen. Wir wünschen uns einen starken Devisenmarkt, der die beobachteten Preisbewegungen sowohl in Europa als auch in den USA unterstützt. Andernfalls können Bewegungen, die nicht durch marktübergreifende Muster gestützt werden, zerbrechlich sein und leicht rückgängig gemacht werden. Wenn unsere Annahmen über das Verhalten des Marktes zutreffen, dann sollte unsere Strategie solide sein. Und kann die Notwendigkeit des „Wartens auf Bestätigung“ ersetzen, wenn eine marktübergreifende Analyse für Ihr individuelles Portfolio sinnvoll ist.

Abb. 4: Visualisierung unserer Handelsregeln, XAUUSD zu kaufen

Der Leser sollte sich darüber im Klaren sein, dass es in Wahrheit höchstwahrscheinlich zahlreiche komplizierte Faktoren gibt, die den Silberpreis nach oben oder unten treiben. Wir versuchen lediglich, diese komplizierten Beziehungen mit Hilfe einfacherer Beziehungen zusammenzufassen.


Überblick über den Backtest-Zeitraum

Wir werden unsere Strategie vom ersten November 2023 bis zum ersten Januar 2025 testen. Die restlichen Daten, die uns vom ersten November 2022 bis Ende Oktober 2023 zur Verfügung stehen, werden für das Training unserer Anwendung verwendet. In künftigen Versionen des Artikels werden wir unser einfaches Modell der Verflechtung dieser Märkte durch Modelle ersetzen, die unser Computer selbständig erlernen kann. Obwohl wir die Trainingsdaten in dieser Diskussion nicht verwenden werden, werden wir sie in zukünftigen Diskussionen nutzen.

Bildschirmfoto 2

Abb. 5: Verständnis für den Zeitraum der Backtests für unsere Diskussion


Erste Schritte in MQL5

Unsere Strategie sieht vor, dass wir drei Märkte gleichzeitig beobachten. Beginnen wir also mit der Erstellung von Systemkonstanten, um die einzelnen Märkte, an denen wir ein offenes Interesse haben, zu verfolgen. So können wir leicht zwischen diesen Märkten wechseln und ihre Leistung vergleichen.

//+------------------------------------------------------------------+
//|                                               Baseline Model.mq5 |
//|                                               Gamuchirai Ndawana |
//|                    https://www.mql5.com/en/users/gamuchiraindawa |
//+------------------------------------------------------------------+
#property copyright "Gamuchirai Ndawana"
#property link      "https://www.mql5.com/en/users/gamuchiraindawa"
#property version   "1.00"

//+------------------------------------------------------------------+
//| System Constants                                                 |
//+------------------------------------------------------------------+
#define SYMBOL_ONE   "XAGUSD"                                                       //--- Our primary   symbol, the price of Silver in USD
#define SYMBOL_TWO   "XAGEUR"                                                       //--- Our secondary symbol, the price of Silver in EUR
#define SYMBOL_THREE "EURUSD"                                                       //--- Our EURUSD exchange rate.
#define FETCH        24                                                             //--- How many bars of data should we fetch?
#define TF_1         PERIOD_H1                                                      //--- Our intended time frame
#define VOLUME       SymbolInfoDouble(SYMBOL_ONE,SYMBOL_VOLUME_MIN) * 10            //--- Our trading volume

Wir werden den Vektortyp MQL5 verwenden, um Marktdaten abzurufen und sie schnell umzuwandeln.

//+------------------------------------------------------------------+
//| Global variables                                                 |
//+------------------------------------------------------------------+
vector eurusd,xagusd,xageur;
double eurusd_growth,xagusd_growth,xageur_growth,bid,ask;
double sl_width = 3e2 * _Point;

Die Handelsbibliothek ermöglicht es uns, unsere Handelsgeschäfte zu eröffnen und zu verwalten, wir benötigen sie für unsere heutige Übung.

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

Die Sprache MQL5 soll uns helfen, effektiv zu handeln. Jedes denkbare Ereignis auf dem Markt wird bestimmten Ereignissen zugeordnet. Der Erhalt neuer Preise ist ein Ereignis. Wenn das Ereignis registriert ist, wird die Ereignisbehandlung durch OnTikc() aufgerufen. Die Funktionen innerhalb dieser Ereignisbehandlung werden ausgeführt.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Setup our technical indicators
   setup();
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- New prices have been quoted
   new_quotes_received();
  }
//+------------------------------------------------------------------+

Immer wenn wir neue Kurse erhalten, wollen wir zunächst prüfen, ob sich eine vollständige Kerze gebildet hat. In den Fällen, in denen sich eine neue Kerze gebildet hat, aktualisieren wir unsere Systemvariablen und versuchen, eine Handelsmöglichkeit zu finden. 

//+------------------------------------------------------------------+
//| Custom functions                                                 |
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Updates system variables accordingly                             |
//+------------------------------------------------------------------+
void new_quotes_received(void)
  {
   static datetime time_stamp;
   datetime time = iTime(SYMBOL_ONE,TF_1,0);

   if(time_stamp != time)
     {
      time_stamp = time;
      update();
     }
  }

Wenn unser System zum ersten Mal geladen wird, wollen wir sicherstellen, dass alle Märkte, die wir benötigen, aktiviert und sofort verfügbar sind.

//+------------------------------------------------------------------+
//| Setup our technical indicators and select the symbols we need    |
//+------------------------------------------------------------------+
void setup(void)
  {
//--- Select the symbols we need
   SymbolSelect(SYMBOL_ONE,true);
   SymbolSelect(SYMBOL_TWO,true);
   SymbolSelect(SYMBOL_THREE,true);
  }

Sobald sich eine neue Kerze gebildet hat, lesen wir die aktualisierten Preise von jedem der Märkte ein, die wir verfolgen. Wir können das Wachstum des Marktes messen, indem wir seinen gegenwärtigen Wert durch seinen vergangenen Wert dividieren. Wenn die Sparte eine Menge produziert, die kleiner als 1 ist, nimmt der Wert des Marktes ab. Ansonsten ist der Markt im Aufwind.

//+------------------------------------------------------------------+
//| Update our system setup                                          |
//+------------------------------------------------------------------+
void update(void)
  {
//--- Fetch updated prices
   xagusd.CopyRates(SYMBOL_ONE,TF_1,COPY_RATES_CLOSE,1,FETCH);
   xageur.CopyRates(SYMBOL_TWO,TF_1,COPY_RATES_CLOSE,1,FETCH);
   eurusd.CopyRates(SYMBOL_THREE,TF_1,COPY_RATES_CLOSE,1,FETCH);

//--- Calculate the growth in market prices
   eurusd_growth = eurusd[0] / eurusd[FETCH - 1];
   xageur_growth = xageur[0] / xageur[FETCH - 1];
   xagusd_growth = xagusd[0] / xagusd[FETCH - 1];

//--- Update system variables
   SymbolSelect(SYMBOL_ONE,true);

   bid = SymbolInfoDouble(SYMBOL_ONE,SYMBOL_BID);
   ask = SymbolInfoDouble(SYMBOL_ONE,SYMBOL_ASK);

//--- Check if we need to setup a new position
   if(PositionsTotal() == 0)
      find_setup();

//--- Check if we need to manage our positions
   if(PositionsTotal() > 0)
      manage_setup();

//--- Give feedback on the market growth
   Comment("EURUSD Growth: ",eurusd_growth,"\nXAGEUR Growth: ",xageur_growth,"\nXAGUSD Grwoth: ",xagusd_growth);
  }

Unser Handels-Setup kann man sich als eine bestimmte Konfiguration vorstellen, in der wir die Märkte im Verhältnis zueinander vorfinden wollen. Wir wollen im Wesentlichen die Bewegungen des XAGUSD-Marktes sehen, die von den Bewegungen des EURUSD- bzw. XAGEUR-Marktes unterstützt werden. Die Regeln, die wir im Folgenden aufgeführt haben, entsprechen der visuellen Darstellung in den Abbildungen 4 und 5. 

//+------------------------------------------------------------------+
//| Find setup                                                       |
//+------------------------------------------------------------------+
void find_setup(void)
  {
   //--- Check if the current market setup matches our expectations for selling
   if((eurusd_growth < 1) && (xageur_growth > 1) && (xagusd_growth < 1))
     {
      Trade.Sell(VOLUME,SYMBOL_ONE,bid,(ask + sl_width),(ask - sl_width),"");
     }

   //--- Check if the current market setup matches our expectations for buying
   if((eurusd_growth > 1) && (xageur_growth < 1) && (xagusd_growth > 1))
     {
      Trade.Buy(VOLUME,SYMBOL_ONE,ask,(bid - sl_width),(bid + sl_width),"");
     }
  }

Sobald das Handelsgeschäft eröffnet ist, wird es mit einem Trailing-Stop-Loss überwacht, damit wir unsere Gewinne halten können.

//+------------------------------------------------------------------+
//| Manage setup                                                     |
//+------------------------------------------------------------------+
void manage_setup(void)
  {
   //--- Select our open position
   if(PositionSelect(SYMBOL_ONE))
     {
      double current_sl = PositionGetDouble(POSITION_SL);
      double current_tp = PositionGetDouble(POSITION_TP);

      //--- Buy setup
      if(current_sl < current_tp)
        {
         if((bid - sl_width) > current_sl)
            Trade.PositionModify(SYMBOL_ONE,(bid - sl_width),(bid + sl_width));
        }

      //--- Sell setup
      if(current_sl > current_tp)
        {
         if((ask + sl_width) < current_sl)
            Trade.PositionModify(SYMBOL_ONE,(ask + sl_width),(ask - sl_width));
        }
     }
  }
Zum Schluss werden wir die zuvor definierten Systemkonstanten aufheben. 
//+------------------------------------------------------------------+
//| Undefine system constants                                        |
//+------------------------------------------------------------------+
#undef TF_1
#undef SYMBOL_ONE
#undef SYMBOL_TWO
#undef SYMBOL_THREE
#undef VOLUME
#undef FETCH

Wenn wir alle Komponenten unseres Systems zusammengefügt haben, ist unsere Anwendung nun vollständig.

//+------------------------------------------------------------------+
//|                                               Baseline Model.mq5 |
//|                                               Gamuchirai Ndawana |
//|                    https://www.mql5.com/en/users/gamuchiraindawa |
//+------------------------------------------------------------------+
#property copyright "Gamuchirai Ndawana"
#property link      "https://www.mql5.com/en/users/gamuchiraindawa"
#property version   "1.00"

//+------------------------------------------------------------------+
//| System Constants                                                 |
//+------------------------------------------------------------------+
#define SYMBOL_ONE   "XAGUSD"                                                       //--- Our primary   symbol, the price of Silver in USD
#define SYMBOL_TWO   "XAGEUR"                                                       //--- Our secondary symbol, the price of Silver in EUR
#define SYMBOL_THREE "EURUSD"                                                       //--- Our EURUSD exchange rate.
#define FETCH        24                                                             //--- How many bars of data should we fetch?
#define TF_1         PERIOD_H1                                                      //--- Our intended time frame
#define VOLUME       SymbolInfoDouble(SYMBOL_ONE,SYMBOL_VOLUME_MIN) * 10            //--- Our trading volume

//+------------------------------------------------------------------+
//| Global variables                                                 |
//+------------------------------------------------------------------+
vector eurusd,xagusd,xageur;
double eurusd_growth,xagusd_growth,xageur_growth,bid,ask;
double sl_width = 3e2 * _Point;

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

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Setup our technical indicators
   setup();
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- New prices have been quoted
   new_quotes_received();
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Custom functions                                                 |
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Updates system variables accordingly                             |
//+------------------------------------------------------------------+
void new_quotes_received(void)
  {
   static datetime time_stamp;
   datetime time = iTime(SYMBOL_ONE,TF_1,0);

   if(time_stamp != time)
     {
      time_stamp = time;
      update();
     }
  }

//+------------------------------------------------------------------+
//| Setup our technical indicators and select the symbols we need    |
//+------------------------------------------------------------------+
void setup(void)
  {
//--- Select the symbols we need
   SymbolSelect(SYMBOL_ONE,true);
   SymbolSelect(SYMBOL_TWO,true);
   SymbolSelect(SYMBOL_THREE,true);
  }

//+------------------------------------------------------------------+
//| Update our system setup                                          |
//+------------------------------------------------------------------+
void update(void)
  {
//--- Fetch updated prices
   xagusd.CopyRates(SYMBOL_ONE,TF_1,COPY_RATES_CLOSE,1,FETCH);
   xageur.CopyRates(SYMBOL_TWO,TF_1,COPY_RATES_CLOSE,1,FETCH);
   eurusd.CopyRates(SYMBOL_THREE,TF_1,COPY_RATES_CLOSE,1,FETCH);

//--- Calculate the growth in market prices
   eurusd_growth = eurusd[0] / eurusd[FETCH - 1];
   xageur_growth = xageur[0] / xageur[FETCH - 1];
   xagusd_growth = xagusd[0] / xagusd[FETCH - 1];

//--- Update system variables
   SymbolSelect(SYMBOL_ONE,true);

   bid = SymbolInfoDouble(SYMBOL_ONE,SYMBOL_BID);
   ask = SymbolInfoDouble(SYMBOL_ONE,SYMBOL_ASK);

//--- Check if we need to setup a new position
   if(PositionsTotal() == 0)
      find_setup();

//--- Check if we need to manage our positions
   if(PositionsTotal() > 0)
      manage_setup();

//--- Give feedback on the market growth
   Comment("EURUSD Growth: ",eurusd_growth,"\nXAGEUR Growth: ",xageur_growth,"\nXAGUSD Grwoth: ",xagusd_growth);
  }

//+------------------------------------------------------------------+
//| Find setup                                                       |
//+------------------------------------------------------------------+
void find_setup(void)
  {
  
   //--- Check if the current market setup matches our expectations for selling
   if((eurusd_growth < 1) && (xageur_growth > 1) && (xagusd_growth < 1))
     {
      Trade.Sell(VOLUME,SYMBOL_ONE,bid,(ask + sl_width),(ask - sl_width),"");
     }

   //--- Check if the current market setup matches our expectations for buying
   if((eurusd_growth > 1) && (xageur_growth < 1) && (xagusd_growth > 1))
     {
      Trade.Buy(VOLUME,SYMBOL_ONE,ask,(bid - sl_width),(bid + sl_width),"");
     }
  }

//+------------------------------------------------------------------+
//| Manage setup                                                     |
//+------------------------------------------------------------------+
void manage_setup(void)
  {
   //--- Select our open position
   if(PositionSelect(SYMBOL_ONE))
     {
      double current_sl = PositionGetDouble(POSITION_SL);
      double current_tp = PositionGetDouble(POSITION_TP);

      //--- Buy setup
      if(current_sl < current_tp)
        {
         if((bid - sl_width) > current_sl)
            Trade.PositionModify(SYMBOL_ONE,(bid - sl_width),(bid + sl_width));
        }

      //--- Sell setup
      if(current_sl > current_tp)
        {
         if((ask + sl_width) < current_sl)
            Trade.PositionModify(SYMBOL_ONE,(ask + sl_width),(ask - sl_width));
        }
     }
  }

//+------------------------------------------------------------------+
//| Undefine system constants                                        |
//+------------------------------------------------------------------+
#undef TF_1
#undef SYMBOL_ONE
#undef SYMBOL_TWO
#undef SYMBOL_THREE
#undef VOLUME
#undef FETCH

Zu Beginn unserer Diskussion haben wir festgelegt, dass unser Backtest vom ersten November 2023 bis Januar 2025 durchgeführt wird. Wir werden den H1-Zeitrahmen für unseren Test verwenden. Der H1 wird uns hoffentlich mehr Handelsmöglichkeiten bieten als höhere Zeitrahmen wie der des Tages, während uns der Handel auf noch kleineren Zeitrahmen, wie dem M1, mit mehr Rauschen verwirrt.

Abb. 6: Die Daten, die wir für unseren Backtest der XAGUSD-Strategie verwenden werden

Unsere Handelsbedingungen werden so gewählt, dass sie die Erfahrung des realen Handels simulieren. Wir möchten eine zufällige Verzögerung wählen, wobei jeder Tick auf echten Ticks basiert. Dies ermöglicht eine zuverlässige Simulation der in der Vergangenheit herrschenden Marktbedingungen.

Abb. 7: Unsere Handelsbedingungen mit echten Ticks sind die realistischste Einstellung, die uns angeboten wird.

Unsere Strategie führte zu einer Kapitalkurve, die durch Gewinnschübe, gefolgt von längeren Verlustperioden, gekennzeichnet ist. Die Strategie ist profitabel, aber in dieser Form instabil. Da unsere Strategie ständig zwischen profitablen und unprofitablen Perioden „oszilliert“, liegt es nahe, dass wir lernen, dieses periodische Verhalten zu erkennen, um die Strategie stabiler zu machen.

Abb. 8: Die durch unsere Handelsstrategie erzeugte Kapitalkurve.

Die Durchführung eines Backtests mit unserer Handelsstrategie zeigt, dass unser System potenziell viel profitabler hätte sein können. Unser Ziel, drei Märkte zu nutzen, um einen erfolgreich zu handeln, ist in Reichweite. Es ist möglich, dass unsere Handelsregeln überarbeitet werden müssen und möglicherweise durch Regeln in Frage gestellt werden, die unser Computer aus den Daten generieren kann. Dies kann als Lösung für unsere Bedenken hinsichtlich der Stabilität des Algorithmus dienen. 

Abb. 9: Eine detaillierte Analyse der Performance unserer Handelsstrategie

Verbesserung unserer anfänglichen Leistung

Was unsere ursprüngliche Strategie betrifft, so gibt es noch viel Raum für Verbesserungen. Wir werden versuchen, das instabile Verhalten unserer ursprünglichen Strategie zu korrigieren und die von uns beobachteten periodischen Schwankungen zwischen gewinnbringenden und anhaltend unrentablen Perioden auszugleichen. Wir fassen kurz die Änderungen zusammen, die am System vorgenommen wurden, um bessere Ergebnisse mit unserer Handelsstrategie zu erzielen:

Vorgeschlagene VerbesserungVerwendungszweck
Integration von technischen Indikatoren für zusätzliche BestätigungenWir können beträchtliches Marktrauschen herausfiltern, indem wir zusätzliche Bestätigungen von unseren technischen Indikatoren in unserem MetaTrader 5-Terminal verwenden, um die Verzögerung unserer Entscheidungen zu reduzieren.
Erstellung individueller statistischer Modelle für jeden Markt, den wir verfolgenEin Marktmodell kann uns dabei helfen, Veränderungen in der Marktrichtung oder der Volatilität zu antizipieren, und uns sogar dabei helfen, unsere Positionsgröße in unsicheren Zeiten zu reduzieren.

Um den Ball ins Rollen zu bringen, müssen wir zunächst historische Marktkurse abrufen, damit wir Daten zum Trainieren unseres Modells haben. Bei der Verwendung statistischer Modelle sind wir uns nicht von Anfang an sicher, welche Eingaben das beste Modell ergeben werden. Daher ist es im Allgemeinen eine gute Praxis, sich so viele verschiedene Funktionen wie möglich vorzustellen und die Auswahl später einzugrenzen. 

//+------------------------------------------------------------------+
//|                                                      ProjectName |
//|                                      Copyright 2020, CompanyName |
//|                                       http://www.companyname.net |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property script_show_inputs

//--- File name
string file_name = "XAGEUR XAGUSD EURUSD Triangular Exchange Rates.csv";

//--- Amount of data requested
input int size = 3000;

//+------------------------------------------------------------------+
//| Our script execution                                             |
//+------------------------------------------------------------------+
void OnStart()
  {
//---Write to file
   int file_handle=FileOpen(file_name,FILE_WRITE|FILE_ANSI|FILE_CSV,",");

   for(int i=size;i>=1;i--)
     {
      if(i == size)
        {
         FileWrite(file_handle,"Time","XAGUSD Open","XAGUSD High","XAGUSD Low","XAGUSD Close","XAGEUR Open","XAGEUR High","XAGEUR Low","XAGEUR Close","EURUSD Open","EURUSD High","EURUSD Low","EURUSD Close","Open Squared","High Squared","Low Squared","Close Squared","Open Cubed","High Cubed","Low Cubed","Close Cubed","Open Squre Root","High Square Root","Low Square Root","Close Square Root","Open Growth","High Growth","Low Grwoth","Close Growth","O / H","O / L","O / C","H / L","Log Open Growth","Log High Grwoth","Log Low Growth","Log Close Grwoth","Sin H / L","Cos O / C");
        }

      else
        {
         FileWrite(file_handle,
                   iTime("XAGUSD",PERIOD_CURRENT,i),
                   iOpen("XAGUSD",PERIOD_CURRENT,i), 
                   iHigh("XAGUSD",PERIOD_CURRENT,i),
                   iLow("XAGUSD",PERIOD_CURRENT,i),
                   iClose("XAGUSD",PERIOD_CURRENT,i),
                   iOpen("XAGEUR",PERIOD_CURRENT,i), 
                   iHigh("XAGEUR",PERIOD_CURRENT,i),
                   iLow("XAGEUR",PERIOD_CURRENT,i),
                   iClose("XAGEUR",PERIOD_CURRENT,i),
                   iOpen("EURUSD",PERIOD_CURRENT,i), 
                   iHigh("EURUSD",PERIOD_CURRENT,i),
                   iLow("EURUSD",PERIOD_CURRENT,i),
                   iClose("EURUSD",PERIOD_CURRENT,i),
                   MathPow(iOpen("XAGUSD",PERIOD_CURRENT,i),2),
                   MathPow(iHigh("XAGUSD",PERIOD_CURRENT,i),2),
                   MathPow(iLow("XAGUSD",PERIOD_CURRENT,i),2),
                   MathPow(iClose("XAGUSD",PERIOD_CURRENT,i),2),
                   MathPow(iOpen("XAGUSD",PERIOD_CURRENT,i),3),
                   MathPow(iHigh("XAGUSD",PERIOD_CURRENT,i),3),
                   MathPow(iLow("XAGUSD",PERIOD_CURRENT,i),3),
                   MathPow(iClose("XAGUSD",PERIOD_CURRENT,i),3),
                   MathSqrt(iOpen("XAGUSD",PERIOD_CURRENT,i)),
                   MathSqrt(iHigh("XAGUSD",PERIOD_CURRENT,i)),
                   MathSqrt(iLow("XAGUSD",PERIOD_CURRENT,i)),
                   MathSqrt(iClose("XAGUSD",PERIOD_CURRENT,i)),
                   (iOpen("XAGUSD",PERIOD_CURRENT,i) / iOpen("XAGUSD",PERIOD_CURRENT,i+1)),
                   (iHigh("XAGUSD",PERIOD_CURRENT,i) / iHigh("XAGUSD",PERIOD_CURRENT,i+1)),
                   (iLow("XAGUSD",PERIOD_CURRENT,i) / iLow("XAGUSD",PERIOD_CURRENT,i+1)),
                   (iClose("XAGUSD",PERIOD_CURRENT,i) / iClose("XAGUSD",PERIOD_CURRENT,i+1)),
                   (iOpen("XAGUSD",PERIOD_CURRENT,i) / iHigh("XAGUSD",PERIOD_CURRENT,i+1)),
                   (iOpen("XAGUSD",PERIOD_CURRENT,i) / iLow("XAGUSD",PERIOD_CURRENT,i+1)),
                   (iOpen("XAGUSD",PERIOD_CURRENT,i) / iClose("XAGUSD",PERIOD_CURRENT,i+1)),
                   (iHigh("XAGUSD",PERIOD_CURRENT,i) / iLow("XAGUSD",PERIOD_CURRENT,i+1)),
                   MathLog10(iOpen("XAGUSD",PERIOD_CURRENT,i) / iOpen("XAGUSD",PERIOD_CURRENT,i+1)),
                   MathLog10(iHigh("XAGUSD",PERIOD_CURRENT,i) / iHigh("XAGUSD",PERIOD_CURRENT,i+1)),
                   MathLog10(iLow("XAGUSD",PERIOD_CURRENT,i) / iLow("XAGUSD",PERIOD_CURRENT,i+1)),
                   MathLog10(iClose("XAGUSD",PERIOD_CURRENT,i) / iClose("XAGUSD",PERIOD_CURRENT,i+1)),
                   (MathSin(iHigh("XAGUSD",PERIOD_CURRENT,i) / iLow("XAGUSD",PERIOD_CURRENT,i))),
                   (MathCos(iOpen("XAGUSD",PERIOD_CURRENT,i) / iClose("XAGUSD",PERIOD_CURRENT,i)))
                   );
        }
     }
//--- Close the file
   FileClose(file_handle);
  }
//+------------------------------------------------------------------+


Analysieren unserer Daten in Python

Sobald wir unsere Trainingsdaten gesammelt haben, können wir mit der Erstellung eines statistischen Modells der Daten beginnen. Zuerst importieren wir die Python-Bibliotheken, die wir benötigen.

#Import libraries we need
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt

Nun werden wir die Daten kennzeichnen. Erinnern Sie sich daran, dass wir mit dem H1-Zeitrahmen handeln, also stellen wir unser Kennzeichnung auf die Veränderung der Kursniveaus über 24 Stunden (1 Handelstag) ein.

#Clean up the data
LOOK_AHEAD = 24
data = pd.read_csv("../XAGEUR XAGUSD EURUSD Triangular Exchange Rates.csv")
data["Target"] = data["XAGUSD Close"].shift(-LOOK_AHEAD) - data["XAGUSD Close"]
data.dropna(inplace=True)
data.reset_index(inplace=True,drop=True)

Dies ist der wichtigste Schritt in dem gesamten Prozess, den wir gemeinsam verfolgen. Unsere Daten werden zunächst Marktnotierungen enthalten, die sich mit unserem Backtest-Zeitraum überschneiden. Dies ist für Praktiker, die eine ehrliche Einschätzung des Wertes ihrer Handelsstrategie erhalten wollen, unerwünscht.

Wir werden daher alle Marktdaten löschen, die sich mit dem vorgesehenen Backtest-Zeitraum überschneiden. Erinnern Sie sich daran, dass in Abb. 6 unser Backtest-Zeitraum eindeutig am ersten November 2023 beginnt und in Abb. 10 unsere Trainingsdaten am einunddreißigsten Oktober 2023 enden.

#Drop the dates corresponding to our backtest
_    = data.iloc[-((24 * 365) - 918):,:]
#Keep the dates before our backtest
data = data.iloc[:-((24 * 365) - 918),:]
data

Abb. 10: Stellen Sie sicher, dass die Trainingsdaten, die Sie für Ihre statistischen Modelle verwenden, keine Informationen aus der Zukunft enthalten.

Betrachtet man das Wachstum des XAGUSD und des XAGEUR-Marktes, so stellt man fest, dass die Spanne zwischen den beiden Märkten im Laufe der Zeit immer größer und kleiner wird. Dies könnte darauf hindeuten, dass es zwischen diesen beiden Märkten Arbitragemöglichkeiten gibt. Andernfalls, wenn es keine Arbitragemöglichkeiten gäbe, hätten sich die rote und die grüne Linie seit Anbeginn der Zeit bis heute perfekt überlappen müssen, ohne jemals auseinander zu driften. Das ist natürlich nicht der Fall. Die beiden Märkte entkoppeln sich eindeutig für eine gewisse Zeit, bevor sie anschließend wieder korrigiert werden.

plt.title("Comparing XAGUSD & XAGEUR Growth")
plt.plot((data['XAGUSD Close'] / data.loc[0,"XAGUSD Close"]) / (data['XAGUSD Close'].max() - data['XAGUSD Close'].min()),color="red")
plt.plot((data['XAGEUR Close'] / data.loc[0,"XAGEUR Close"]) / (data['XAGEUR Close'].max() - data['XAGEUR Close'].min()),color="green")
plt.ylabel("Commodity Growth")
plt.xlabel("Time")
plt.legend(["XAGUSD","XAGEUR"])
plt.grid()

Abb. 11: Die Überlagerung unserer beiden Finanzmärkte zeigt uns potenzielle Arbitragemöglichkeiten auf, die wir nutzen können.

Kennzeichnen wir unsere Eingänge und das Ziel. 

X = data.iloc[:,1:-1].columns
y = "Target"

Wir bereiten einen Gradient-Boosting-Baum auf unsere Daten vor und exportieren ihn in das ONNX-Format. Gradient Boosted Trees sind bekannt für ihre Fähigkeit, Wechselwirkungen innerhalb eines bestimmten Datensatzes zu erkennen. Wir hoffen, dass wir ihre leistungsstarken Funktionen zur Mustererkennung nutzen können, um unsere Handelsstrategie zu verbessern.

import onnx
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorTypea
from sklearn.ensemble import GradientBoostingRegressor

Wir wollen nun für jeden Markt, an dem wir interessiert sind, eigene statistische Modelle erstellen. Dies wird uns hoffentlich helfen, falsche Ausbrüche herauszufiltern. Wir passen jedes Modell an die Daten an und exportieren es in das ONNX-Format, damit wir anschließend alle drei Modelle in unsere MetaTrader 5-Anwendung importieren können. 

Lassen Sie uns zuerst ein Modell des XAGUSD-Marktes erstellen.

model = GradientBoostingRegressor()
model.fit(data.loc[:,["XAGUSD Open","XAGUSD High","XAGUSD Low","XAGUSD Close"]],data.loc[:,y])
initial_types = [("float_input",FloatTensorType([1,4]))]
xagusd_model_proto = convert_sklearn(model,initial_types=initial_types,target_opset=12)
onnx.save(xagusd_model_proto,"../XAGUSD State Model.onnx")

Zweitens werden wir den XAGEUR-Markt beobachten.

model = GradientBoostingRegressor()
model.fit(data.loc[:,["XAGEUR Open","XAGEUR High","XAGEUR Low","XAGEUR Close"]],data.loc[:,"XAGEUR Target"])
initial_types = [("float_input",FloatTensorType([1,4]))]
xageur_model_proto = convert_sklearn(model,initial_types=initial_types,target_opset=12)
onnx.save(xageur_model_proto,"../XAGEUR State Model.onnx")

Und schließlich werden wir unser statistisches Modell des EURUSD-Marktes exportieren.

model = GradientBoostingRegressor()
model.fit(data.loc[:,["EURUSD Open","EURUSD High","EURUSD Low","EURUSD Close"]],data.loc[:,"EURUSD Target"])
initial_types = [("float_input",FloatTensorType([1,4]))]
eurusd_model_proto = convert_sklearn(model,initial_types=initial_types,target_opset=12)
onnx.save(eurusd_model_proto,"../EURUSD State Model.onnx")

Unser System misst Marktbewegungen, die von allen drei Modellen gemeinsam gestützt werden, mehr Gewicht bei. Wir wollen zunächst beobachten, wie sich die in Abb. 3 und Abb. 4 beschriebenen Muster auf dem Markt ausbilden, und anschließend unsere drei Modelle vorhersagen, dass sich unsere Muster nicht schnell auflösen, sondern im Laufe der Zeit halten werden. Dies wird uns ein angemessenes Maß an Vertrauen in die Stärke der Marktbewegungen geben, die wir erwarten, um zu spielen.


Implementierung unserer Verbesserungen in MQL5

Wir können nun damit beginnen, unsere Verbesserungen an der ursprünglichen Version unserer Handelsstrategie umzusetzen. Zunächst werden wir zusätzliche technische Indikatoren in unser Arsenal aufnehmen, um unserem Computer eine bessere Orientierung und Trenderkennung zu ermöglichen. Der gleitende Durchschnitt ist eine gute Strategie für diese Aufgabe. Wir werden jedoch eine reaktionsschnellere Version der Strategie verwenden, die die Verzögerung bei den Handelssignalen minimiert. Leser, die daran interessiert sind, mehr über diese Version der gleitenden Durchschnitts-Crossover-Strategie zu erfahren, können hier mehr erfahren.

#define XAGUSD_MA_PERIOD 8

Laden Sie die ONNX-Modelle, die wir gerade erstellt haben, als Systemressourcen.

//+------------------------------------------------------------------+
//| System resources                                                 |
//+------------------------------------------------------------------+
#resource "\\Files\\XAGUSD State Model.onnx" as  uchar xagusd_onnx_buffer[]
#resource "\\Files\\XAGEUR State Model.onnx" as  uchar xageur_onnx_buffer[]
#resource "\\Files\\EURUSD State Model.onnx" as  uchar eurusd_onnx_buffer[]

Wir benötigen neue, globale Variablen, die den gleitenden Durchschnittsindikatoren entsprechen, die wir einführen, sowie die einzelnen Komponenten, die zur Vervollständigung unseres ONNX-Modells erforderlich sind.

//+------------------------------------------------------------------+
//| Global variables                                                 |
//+------------------------------------------------------------------+
vector  eurusd,xagusd,xageur;
double  eurusd_growth,xagusd_growth,xageur_growth,bid,ask;
double  sl_width = 3e2 * _Point;
int     xagusd_f_ma_handler,xagusd_s_ma_handler;
double  xagusd_f[],xagusd_s[];
vectorf model_output = vectorf::Zeros(1);
long    onnx_model;
vectorf xageur_model_output = vectorf::Zeros(1);
long    xageur_onnx_model;
vectorf eurusd_model_output = vectorf::Zeros(1);
long    eurusd_onnx_model;

Einige unserer Funktionen müssen umgestaltet werden, um unseren neuen Erwartungen zu entsprechen. Wenn unsere Handelsanwendung nicht mehr verwendet wird, müssen wir die Ressourcen, die für unsere ONNX-Modelle und unsere beiden gleitenden Durchschnitte gebunden waren, sicher freigeben, um zu beginnen.

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   OnnxRelease(onnx_model);
   OnnxRelease(xageur_onnx_model);
   OnnxRelease(eurusd_onnx_model);
   IndicatorRelease(xagusd_f_ma_handler);
   IndicatorRelease(xagusd_s_ma_handler);
   Print("System deinitialized");
  }

Darüber hinaus muss unser Setup-Verfahren auch unseren ONNX-Modellen und unseren technischen Indikatoren gerecht werden. Wir müssen überprüfen, ob die von uns gewünschten Indikatoren und Modelle korrekt geladen und spezifiziert worden sind. Andernfalls brechen wir den Initialisierungsvorgang ab und melden dem Nutzer, was schief gelaufen ist.

//+------------------------------------------------------------------+
//| Setup our technical indicators and select the symbols we need    |
//+------------------------------------------------------------------+
bool setup(void)
  {
//--- Select the symbols we need
   SymbolSelect(SYMBOL_ONE,true);
   SymbolSelect(SYMBOL_TWO,true);
   SymbolSelect(SYMBOL_THREE,true);

//--- Setup the moving averages
   xagusd_f_ma_handler = iMA(SYMBOL_ONE,TF_1,XAGUSD_MA_PERIOD,0,MODE_SMA,PRICE_OPEN);
   xagusd_s_ma_handler = iMA(SYMBOL_ONE,TF_1,XAGUSD_MA_PERIOD,0,MODE_SMA,PRICE_CLOSE);

   if((xagusd_f_ma_handler == INVALID_HANDLE) || (xagusd_s_ma_handler == INVALID_HANDLE))
     {
      Comment("Failed to load our technical indicators correctly. ", GetLastError());
      return(false);
     }

//--- Setup our statistical models
   onnx_model        = OnnxCreateFromBuffer(xagusd_onnx_buffer,ONNX_DEFAULT);
   xageur_onnx_model = OnnxCreateFromBuffer(xageur_onnx_buffer,ONNX_DEFAULT);
   eurusd_onnx_model = OnnxCreateFromBuffer(eurusd_onnx_buffer,ONNX_DEFAULT);

   if(onnx_model == INVALID_HANDLE)
     {
      Comment("Failed to create our XAGUSD ONNX model correctly. ",GetLastError());
      return(false);
     }

   if(xageur_onnx_model == INVALID_HANDLE)
     {
      Comment("Failed to create our XAGEUR ONNX model correctly. ",GetLastError());
      return(false);
     }

   if(eurusd_onnx_model == INVALID_HANDLE)
     {
      Comment("Failed to create our EURUSD ONNX model correctly. ",GetLastError());
      return(false);
     }

   ulong input_shape[] = {1,4};
   ulong output_shape[] = {1,1};

   if(!(OnnxSetInputShape(onnx_model,0,input_shape)))
     {
      Comment("Failed to specify XAGUSD model input shape. ",GetLastError());
      return(false);
     }


   if(!(OnnxSetInputShape(xageur_onnx_model,0,input_shape)))
     {
      Comment("Failed to specify XAGEUR model input shape. ",GetLastError());
      return(false);
     }


   if(!(OnnxSetInputShape(eurusd_onnx_model,0,input_shape)))
     {
      Comment("Failed to specify EURUSD model input shape. ",GetLastError());
      return(false);
     }

   if(!(OnnxSetOutputShape(onnx_model,0,output_shape)))
     {
      Comment("Failed to specify XAGUSD model output shape. ",GetLastError());
      return(false);
     }

   if(!(OnnxSetOutputShape(xageur_onnx_model,0,output_shape)))
     {
      Comment("Failed to specify XAGEUR model output shape. ",GetLastError());
      return(false);
     }

   if(!(OnnxSetOutputShape(eurusd_onnx_model,0,output_shape)))
     {
      Comment("Failed to specify EURUSD model output shape. ",GetLastError());
      return(false);
     }

   Print("System initialized succefully");

//--- If we have gotten this far, everything went fine.
   return(true);
  }

Außerdem benötigen wir eine spezielle Funktion zum Abrufen von Vorhersagen aus unserem ONNX-Modell. Unsere Funktion bereitet zunächst die Eingaben des ONNX-Modells vor und ruft dann die Funktion OnnxRun auf, um eine Vorhersage von unserem Modell zu erhalten.

//+------------------------------------------------------------------+
//| Fetch a prediction from our model                                |
//+------------------------------------------------------------------+
void model_predict(void)
  {
   vectorf model_inputs =  { (float) iOpen(SYMBOL_ONE,TF_1,1), (float) iClose(SYMBOL_ONE,TF_1,1)};
   OnnxRun(onnx_model,ONNX_DATA_TYPE_FLOAT,model_inputs,model_output);
   Print(StringFormat("Model forecast: %d",model_output));
  }

Auch unsere Vorgehensweise bei der Suche nach einem Handelsaufbau muss überarbeitet werden. Der erste Schritt ist die Erstellung einer Prognose durch unser Modell. Sobald diese abgeschlossen ist, suchen wir nach einer zusätzlichen Bestätigung durch das Kreuzen unserer gleitenden Durchschnitte. Erinnern Sie sich daran, dass unsere spezielle Version der gleitenden Durchschnittsüberkreuzungen einen gleitenden Durchschnitt auf den Eröffnungskurs und den zweiten auf den Schlusskurs setzt, wobei beide gleitenden Durchschnitte denselben Zeitraum haben. Wenn der offene gleitende Durchschnitt oben liegt, interpretieren wir dies als ein Verkaufssignal. Wenn nicht, kaufen wir.

Unsere letzte Bedingung ist daher, dass unser Gradient Boosted Regressor davon ausgeht, dass die Kursentwicklung mit beiden beobachteten Signalen übereinstimmt. Wenn dies der Fall ist, betrachten wir dies als ein Handels-Setup mit hoher Wahrscheinlichkeit und verdoppeln unsere Losgröße. Ansonsten werden wir konservativ handeln.  

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

//--- Check if the current market setup matches our expectations for selling
   if((eurusd_growth < 1) && (xageur_growth > 1) && (xagusd_growth <  1))
     {
      if(xagusd_s[0] < xagusd_f[0])
        {
         if(model_output[0] < 0)
           {
            //--- If all our systems align, we have a high probability trade setup
            Trade.Sell(VOLUME * 2,SYMBOL_ONE,bid,(ask + sl_width),(ask - sl_width),"");
           }
         //--- Otherwise, we should trade conservatively
         Trade.Sell(VOLUME,SYMBOL_ONE,bid,(ask + sl_width),(ask - sl_width),"");
        }
     }

//--- Check if the current market setup matches our expectations for buying
   if((eurusd_growth > 1) && (xageur_growth < 1) && (xagusd_growth > 1))
     {
      if(xagusd_s[0] > xagusd_f[0])
        {
         if(model_output[0] > 0)
           {
            Trade.Buy(VOLUME * 2,SYMBOL_ONE,ask,(bid - sl_width),(bid + sl_width),"");
           }

         Trade.Buy(VOLUME,SYMBOL_ONE,ask,(bid - sl_width),(bid + sl_width),"");
        }
     }
  }

Wiederholen wir nun den gleichen Test, den wir durchgeführt haben, aber dieses Mal verwenden wir unsere verfeinerte Version der Handelsstrategie. Wir haben darauf geachtet, dass sich die Daten unserer Backtests nicht mit den Daten überschneiden, die wir zum Trainieren unserer statistischen Modelle verwendet haben. 

Abb. 12: Wir verwenden für beide Tests denselben Testzeitraum. Außerdem hat keines unserer statistischen Modelle diese Daten zuvor gesehen.

Wie der Leser erwarten würde, werden alle Einstellungen konstant gehalten, um einen fairen Vergleich beider Handelsstrategien zu gewährleisten.

Abb. 13: Idealerweise sollten diese Einstellungen für beide Tests gleich bleiben.

Analysieren wir nun die Ergebnisse, die wir erhalten haben. In unserem ersten Backtest haben wir eine Sharpe Ratio von 0,14 erreicht, während unsere überarbeitete Strategie eine Sharpe Ratio von 1,85 aufweist. Dies ist eine deutliche Verbesserung unserer Sharpe Ratio, was bedeutet, dass wir es geschafft haben, profitabler zu sein und gleichzeitig verantwortungsvoll zusätzliche Risiken einzugehen. Niedrige Sharpe Ratios sind mit einer hohen Varianz bei niedrigen Renditen verbunden. 

Darüber hinaus sank unser durchschnittlicher Verlust von etwa 115 $ pro Handel auf etwa 109 $ pro Handel, während unser durchschnittlicher Gewinn andererseits von durchschnittlich 188 $ auf durchschnittlich 213 $ anstieg. Das ist ein positives Feedback für uns. Unser Gesamtgewinn stieg ebenfalls von 395 $ in unserer ersten Iteration der Strategie auf 1.449 $ in dieser aktuellen Iteration. Und das alles, während wir weniger Handelsgeschäfte eingehen als unsere manuell konfigurierte Version der Strategie. 

Abb. 14: Eine detaillierte Zusammenfassung der historischen Performance unserer Handelsstrategie auf dem XAGUSD-Markt.

Die Änderungen, die wir an unserem System vorgenommen haben, haben die instabilen Schwankungen des Kontostandes behoben, die wir in der ersten Version unserer Strategie beobachtet haben. Unser Backtest hat ergeben, dass unsere neue Strategie im Durchschnitt bei jedem Handel mehr Gewinn als Verlust einfährt. Dies führt dazu, dass unsere neue Gewinn- und Verlustkurve flachere Tiefs aufweist als die Verlusttiefen, die in der ursprünglichen und risikoreicheren Version unserer Handelsstrategie angehäuft wurden.

Abb. 15: Visualisierung der Gewinn- und Verlustkurve, die sich aus unserer überarbeiteten Version der Handelsstrategie ergibt.

Schlussfolgerung

Nach der Lektüre dieses Artikels wird der Leser hoffentlich eine algorithmische Strategie für den Handel mit vernetzten Märkten entwickeln. Der Leser wird verstehen, wie er sein Fachwissen kombinieren kann, um Dreiecksmärkte zu finden und gewinnbringend zu handeln. Mit Hilfe von statistischen Modellen kann der Leser besser genau die Handels-Setups finden, nach denen er sucht. Indem wir uns die Verflechtung bestimmter Märkte zunutze machen, können wir stets Echtzeit-Indikatoren für die wahre Marktstärke erstellen.

Name der DateiBeschreibung
Baseline_ModelUnsere erste Version der Handelsstrategie des Dreiecks.
Second VersionDie überarbeitete und profitablere Version unserer Handelsstrategie.
EURUSD State ModelUnser statistisches Modell des EURUSD-Marktes.
XAGEUR State ModelUnser statistisches Modell des XAGEUR-Marktes.
XAGUSD State ModelUnser statistisches Modell des XAGUSD-Marktes.
Triangular Exchange RatesDas Jupyter-Notebook, das wir für die Analyse unserer Marktdaten und die Erstellung unserer statistischen Marktmodelle verwendet haben.

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

MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 56): Bill Williams Fraktale MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 56): Bill Williams Fraktale
Die Fraktale von Bill Williams sind ein wirkungsvoller Indikator, der leicht übersehen wird, wenn man ihn zum ersten Mal auf einem Kurschart entdeckt. Er wirkt zu ereignisreich und wahrscheinlich nicht prägnant genug. Wir wollen den Vorhang über diesen Indikator lüften, indem wir untersuchen, was seine verschiedenen Muster bewirken könnten, wenn sie mit Vorwärtstests auf allen mit dem Assistenten zusammengestellten Expert Advisor untersucht werden.
Handel mit dem MQL5 Wirtschaftskalender (Teil 6): Automatisierung des Handelseinstiegs mit der Analyse von Nachrichtenereignissen und Countdown-Timern Handel mit dem MQL5 Wirtschaftskalender (Teil 6): Automatisierung des Handelseinstiegs mit der Analyse von Nachrichtenereignissen und Countdown-Timern
In diesem Artikel implementieren wir einen automatischen Handelseinstieg mit dem MQL5-Wirtschaftskalender, indem wir nutzerdefinierte Filter und Zeitverschiebungen anwenden, um qualifizierte Nachrichtenereignisse zu identifizieren. Wir vergleichen die prognostizierten und die vorherigen Werte, um zu entscheiden, ob ein KAUF oder VERKAUF eröffnet werden soll. Dynamische Countdown-Timer zeigen die verbleibende Zeit bis zur Veröffentlichung von Nachrichten an und werden nach einem Handel automatisch zurückgesetzt.
Installation von MetaTrader 5 und anderen MetaQuotes-Anwendungen auf HarmonyOS NEXT Installation von MetaTrader 5 und anderen MetaQuotes-Anwendungen auf HarmonyOS NEXT
Eine einfache Installation des MetaTrader 5 und andere MetaQuotes-Applikationen auf Geräten mit HarmonyOS NEXT mit DroiTong. Eine detaillierte Schritt-für-Schritt-Anleitung für Ihr Handy oder Ihren Laptop.
Entwicklung eines Toolkit zur Analyse von Preisaktionen (Teil 17): Der TrendLoom EA Entwicklung eines Toolkit zur Analyse von Preisaktionen (Teil 17): Der TrendLoom EA
Als Beobachter und Händler von Preisaktionen habe ich festgestellt, dass sich ein Trend in der Regel in diese Richtung fortsetzt, wenn er von mehreren Zeitrahmen bestätigt wird. Wie lange der Trend anhält, hängt davon ab, welcher Art von Händler Sie sind, ob Sie Positionen langfristig halten oder Scalping betreiben. Die Zeiträume, die Sie für die Bestätigung wählen, spielen eine entscheidende Rolle. In diesem Artikel finden Sie ein schnelles, automatisiertes System, mit dem Sie den Gesamttrend über verschiedene Zeiträume hinweg mit nur einem Mausklick oder regelmäßigen Updates analysieren können.