English Русский 日本語
preview
MQL5-Assistent - Techniken, die Sie kennen sollten (14): Zeitreihenvorhersage mit mehreren Zielvorgaben durch STF

MQL5-Assistent - Techniken, die Sie kennen sollten (14): Zeitreihenvorhersage mit mehreren Zielvorgaben durch STF

MetaTrader 5Tester | 30 Mai 2024, 10:53
114 0
Stephen Njuki
Stephen Njuki

Einführung

Dieser Artikel über Spatial Temporal Fusion (STF) hat mein Interesse an diesem Thema geweckt, da es einen zweiseitigen Ansatz für die Vorhersage bietet. Zur Auffrischung: Der Artikel ist inspiriert von der Lösung eines wahrscheinlichkeitsbasierten Prognoseproblems, das sowohl für das Angebot als auch für die Nachfrage in zweiseitigen Ride-Hailing-Plattformen, wie Uber und Didi, kollaborativ ist. Kollaborative Beziehungen zwischen Angebot und Nachfrage sind in verschiedenen zweiseitigen Märkten wie Amazon, Airbnb und eBay üblich, in denen das Unternehmen im Wesentlichen nicht nur den traditionellen „Kunden“ oder Käufer bedient, sondern auch die Lieferanten des Kunden beliefert.

Daher kann eine zweiseitige Vorhersage in einem Fall, in dem das Angebot teilweise von der Nachfrage abhängt, für diese Unternehmen häufig wichtig sein. Diese doppelte Projektion von Angebot und Nachfrage war allerdings ein Bruch mit dem herkömmlichen Ansatz, einen bestimmten Wert für eine Zeitreihe oder einen Datensatz zu prognostizieren. In dem Artikel wurde auch ein so genannter Kausaltrans-Rahmen eingeführt, bei dem die kausale „kollaborative“ Beziehung zwischen Angebot und Nachfrage durch eine Matrix G erfasst wurde und alle Prognosen über ein Transformer-Netz erstellt wurden, und die Ergebnisse waren bemerkenswert.

In Anlehnung daran versuchen wir, Angebot und Nachfrage nach gehandelten Wertpapieren zu prognostizieren, indem wir Baisse und Hausse als Näherungswerte für diese beiden Messgrößen verwenden. Streng genommen berechnet die typische Expert-Signal-Klasse jedoch beide Werte als Ganzzahlen im Bereich von 0-100, wie in den MQL5-Bibliotheksdateien oder den Dateien zu sehen ist, die wir in dieser Serie bisher kodiert haben. Neu wäre jedoch die Hinzufügung einer räumlichen Matrix und eines Zeitparameters bei der Erstellung unserer Prognosen (die beiden zusätzlichen Eingaben, die wir in dem Artikel zitieren).

Die räumliche Quantisierung von Wertpapieren ist subjektiv, ebenso wie die Wahl der Zeitmetrik. Wir verwenden die Hoch- und Niedrigpreisreihen von Wertpapieren als Anker für Angebot und Nachfrage und nutzen die Autokorrelationswerte zwischen diesen Puffern als Koordinaten für eine räumliche Matrix sowie den Wochentagsindex als Zeitindikator. Dieser rudimentäre Ansatz, der angepasst und verbessert werden kann, dient unseren Zwecken für diesen Artikel.

In dem Artikel wurden Transformer-Netzwerke verwendet, die wir nicht einsetzen werden, da sie für unsere Zwecke ineffizient sind, aber alle Prognosen werden durch ein nutzerdefiniertes handcodiertes mehrschichtiges Perzeptron erstellt. Bei so vielen Bibliotheken und Codebeispielen zu diesem Thema scheint es Zeitverschwendung zu sein, sein eigenes mehrschichtiges Perzeptron zu programmieren. Die verwendete Netzwerkklasse ist jedoch weniger als 300 Zeilen lang und lässt sich in Bezug auf die Anzahl der Schichten und die Größe der einzelnen Schichten gut skalieren, was in den meisten der verfügbaren Standardbibliotheken noch fehlt.

Durch die Verwendung eines singulären, neuronalen Netzes und nicht von Transformer wird die Umsetzung des Kausaltrans-Rahmens des Artikels hier nicht realisiert. Wir haben jedoch noch alle Hände voll zu tun, da wir immer noch eine duale Prognose für Angebot und Nachfrage durchführen und dabei auch eine räumliche Matrix und die Zeit verwenden werden. Und wie immer gibt es in jedem Handelssystem inhärente Risiken, sodass der Leser willkommen ist, seine eigene Sorgfalt zu unternehmen, bevor er irgendwelches hier geteiltes Material zur weiteren Verwendung übernimmt.



STF Illustration

STF wird vor allem in der Fernerkundung und bei visuell orientierten Aktivitäten eingesetzt, bei denen Raum- und Zeitmetriken greifbar miteinander verbunden werden können.

Wenn Sie nicht allzu sehr daran interessiert sind, das STF-Potenzial außerhalb des Handels zu erkunden, können Sie diesen Abschnitt überspringen und mit der MQL5-Implementierung fortfahren.

Bei der Fernerkundung beispielsweise erfassen Satellitenbilder die räumliche Komponente der Daten und der untersuchten Region, während sich die Zeit auf den Zeitpunkt der Aufnahme der Bilder beziehen würde. Solche Informationen können nicht nur eine Zeitreihe bilden, sondern auch für Vorhersagen über Veränderungen des Wetters, der Pflanzenwelt oder sogar des Lebensraums von Tieren in dem untersuchten Gebiet wichtig sein - alles dank der STF.

Betrachten wir nun Schritt für Schritt, wie STF bei der Entwaldung und den mit der Vegetation verbundenen Problemen helfen könnte. Der erste Schritt ist, wie so oft bei Problemen des maschinellen Lernens, die Datensammlung. Fernerkundungssatelliten würden über einen bestimmten Zeitraum multispektrale Bilder für das Untersuchungsgebiet aufnehmen. Da Satelliten multispektral sein können, können sie reflektierte und absorbierte Wellenlängeninformationen erfassen, die mit dem bloßen Auge nicht sichtbar sind, aber in Wellenlängenbereichen liegen, die mit der Vegetation, Gewässern usw. in Verbindung stehen. All dies macht die Art von Daten, die in einer Serie modelliert werden können, reichhaltiger (und komplexer), da diese Bilder über einen längeren Zeitraum aufgenommen werden.

Da jedes Bild bereits mit einem Zeitstempel versehen ist, könnte ein geeignetes GPS-Koordinatensystem verwendet werden, um jedes Bild abzubilden und so die Konsistenz der räumlichen Informationen über alle Bilder und alle Aufnahmezeitpunkte hinweg zu gewährleisten.

Als Nächstes müssen wir unsere „Daten“ normalisieren, um sicherzustellen, dass sie in einem Format vorliegen, das für Vegetation und Entwaldung relevant ist. Dies wird u. a. dadurch erreicht, dass die Spektralsignaturen jedes Bildes zu verschiedenen Zeitpunkten durch Normalized Difference Vegetation Index aufgezeichnet werden, sodass die Daten nicht nur für das Training von Modellen leichter zu handhaben sind, sondern auch auf das jeweilige Thema ausgerichtet sind.

Diese Normalisierung würde auch die Erkennung von Veränderungen in den aufgenommenen Bildern im Laufe der Zeit beinhalten, sodass Merkmale, die für die Vegetation wichtig sind, extrahiert oder definiert werden können, z. B. Wachstumsraten, saisonale Schwankungen, Verteilungsmuster usw. Jedes dieser Merkmale könnte eine Dimension der Daten bilden.

Die Wahl des Modells kann variieren, aber man kann davon ausgehen, dass ein neuronales Netz der Hauptkandidat ist. Ein beträchtlicher Teil der Bilder, die nun als normalisierte Daten vorliegen, würde hierfür verwendet werden, während ein kleinerer Datensatz, wie es normalerweise der Fall ist, für Tests reserviert ist.

Die Vorhersage mit dem Modell würde also auf der Grundlage erfolgreicher Trainings- und Testergebnisse erfolgen, wobei das Wichtigste die Interpretation ist, da bereits eine Vielzahl von Normalisierungen vorgenommen wurde, die für die Modellausgaben sorgfältig umgekehrt werden müssen.

In dem von uns zitierten Artikel, der kürzlich von ACM akzeptiert wurde, werden die Gleichungen für die zukünftige Nachfrage und das Angebot wie folgt dargestellt:

x v (t+δt) = f x (x v (t), G v (t), δt),

y v (t+δt) = f y (x v (t), y v (t), G v (t), δt),

wobei:

  • x v ist die Nachfragefunktion in Abhängigkeit von der Zeit,
  • y v ist die Angebotsfunktion in Abhängigkeit von der Zeit,
  • G v ist ebenfalls ein Vektor oder eine Matrix für räumliche Informationen,
  • δt ist die Zeitspanne, bis zu der eine Prognose erstellt wird,
  • t ist Zeit.

In unserem Fall sind die Funktionen f x und f y also neuronale Netze, deren Parametervektoren oder -matrizen Daten enthalten, die in die jeweiligen Eingabeschichten eingespeist werden.


Einführung von STF mit MQL5

Um STF in MQL5 zu implementieren, würden wir die oben genannten gemeinsamen Gleichungen befolgen, um festzulegen, wie wir die Eingabedaten für unser Modell strukturieren. Aus den beiden Gleichungen ist ersichtlich, dass die Eingabedaten in der Regel in einem Vektor- oder Matrixformat vorliegen, was natürlich unzählige Möglichkeiten und Herausforderungen mit sich bringt. Die potenziellen Eingänge jeder f-Funktion in den beiden Gleichungen sind ähnliche Puffer, wobei der einzige Unterschied zwischen den beiden Gleichungen darin besteht, dass die Angebotsgleichung nicht nur von ihren vorherigen Werten, sondern auch von denen der Nachfrage abhängig ist. Dies folgt dem Artikel des Autors, dass das Angebot von der Nachfrage abhängig ist und nicht umgekehrt.

Die Gesamtzahl der Puffer beträgt also 4, wenn man die Überschneidung zwischen beiden Gleichungen berücksichtigt. Diese sind alle in der Angebotsgleichung enthalten, und es handelt sich dabei um die frühere Nachfrage, das frühere Angebot, die räumlichen Werte und das Zeitinkrement.

Der Nachfragepuffer wird in diesem Artikel als ein Zeitreihenpuffer von „bullischen“ Preispunkten interpretiert. Ein prägnanterer Puffer könnte das tatsächliche Volumen der Kauf-Kontrakte sein, aber solche Informationen werden von den Brokern nur selten weitergegeben, und selbst wenn sie es täten, wäre es angesichts der Fragwürdigkeit der Volumeninformationen auf den Devisenmärkten keine genaue Darstellung des Volumens. Daher werden die hohen Preise abzüglich der offenen Preise als alternativer Puffer für echte Long-Volumenverträge gewählt. Andere mögliche Puffer, die diese Rolle übernehmen könnten, wären währungsspezifische Messgrößen wie Zinssätze, Inflation oder sogar Geldmengenindizes der Zentralbanken. Die Wahl von Höchstkursen minus Eröffnungskursen als Puffer soll die Aufwärtsvolatilität messen, und da davon ausgegangen werden kann, dass diese positiv mit langlaufenden Verträgen korreliert, wird sie als nächstbester Proxy verwendet. Diese Werte können nur positiv oder Null sein, wobei ein Nullwert auf einen Hängemann oder einen flachen Kursbalken hinweist.

Der Angebotspuffer wird wie sein vorheriges Gegenstück ebenfalls durch die Eröffnungspreise abzüglich der Tiefstpreise angenähert. Dies kann auch als Hinweis auf eine sinkende Volatilität gewertet werden, die positiv mit rückläufigen Volumenverträgen korreliert. Auch hier sind die Werte für diesen Puffer nur positiv, wobei ein Nullwert einen Grabstein-Doji-Stern oder einen flachen Balken anzeigt. Die Gleichung für das Angebot unterscheidet sich von der für die Nachfrage insofern, als sie mehr Inputs erfordert, was bedeutet, dass auch ihr Modell ähnlich wie das der Nachfrage ist, aber anders. Es wird also zwei Instanzen des Prognosemodells geben, eine für die Nachfrage und eine für das Angebot. Da das Endergebnis ein einziges Signal sein soll, wird dies durch Subtraktion der Angebotsvorhersage von der Nachfragevorhersage ermittelt. Wir erinnern daran, dass alle Eingaben in das Nachfrage- und Angebotsmodell positiv oder Null sind, also sollten aller Wahrscheinlichkeit nach auch die Ausgaben positiv sein. Wenn wir also die Ausgaben des Angebotsmodells von den Ausgaben der Nachfrage subtrahieren, erhalten wir eine reelle Zahl, deren positiver Wert eine Hausse und deren negativer Wert eine Baisse bedeuten würde.

Die Wahl des Zeitpuffers ist einfach ein Index für den Wochentag. Die Tests, die im nächsten Abschnitt behandelt werden, werden auf täglicher Basis durchgeführt, sodass sich der Wochen-Tages-Index leicht mit diesem verbinden lässt. Wenn jedoch ein alternativer Zeitrahmen in Betracht gezogen werden soll, der kleiner ist als der tägliche Zeitrahmen, dann könnte ein Index in Betracht gezogen werden, der sich entweder über einen Tag oder sogar eine Woche erstreckt. Auf dem 8-Stunden-Zeitrahmen gibt es zum Beispiel fünfzehn 8-Stunden-Balken in einer Handelswoche, was fünfzehn mögliche Zeitindizes innerhalb der Woche ergibt. Sie könnten die Stunde des Tages oder die Handelssitzung eines Tages usw. auswählen. Hier gibt es zahlreiche Möglichkeiten, und es könnte besser sein, vorab zu testen, was für Ihr Handelssystem am besten funktioniert. Unsere einfache Funktion, die den Wochentagsindex zurückgibt, lautet wie folgt:

//+------------------------------------------------------------------+
//| Temporal (Time) Indexing function                                |
//+------------------------------------------------------------------+
int CSignalNetwork::T(datetime Time)
{  MqlDateTime _dt;
   if(TimeToStruct(Time,_dt))
   {  if(_dt.day_of_week==TUESDAY)
      {  return(1);
      }
      else if(_dt.day_of_week==WEDNESDAY)
      {  return(2);
      }
      else if(_dt.day_of_week==THURSDAY)
      {  return(3);
      }
      else if(_dt.day_of_week==FRIDAY||_dt.day_of_week==SATURDAY)
      {  return(4);
      }
   }
   return(0);
}

Die G-Matrix erfasst unsere räumlichen Daten für dieses Modell, und dies kann bei der Definition schwierig sein. Wie definieren wir den Raum in einem Umfeld des Wertpapierhandels? In unserem Referenzartikel wird zum Beispiel eine Kreuztabelle von Metadaten zwischen Angebot und Nachfrage „normalisiert“, indem sie durch so genannte Graph Attention Transformers (GAT) geleitet wird. Diese arbeiten auf zwei Ebenen: Die erste Ebene erfasst komplexe Knotenbeziehungen, und die zweite Ebene aggregiert Nachbar-Informationen für endgültige Knotenvorhersagen. Die GAT-Messwerte sind dann Teil dessen, was durch die jeweiligen neuronalen Netze zur Vorhersage von Nachfrage oder Angebot geleitet wird. In unserem Fall werden die Metadaten für unsere auf- und abwärts Kurspuffer aus den Korrelationswerten gewonnen, die diese Puffer gemeinsam nutzen. Diese Korrelationen werden wie im nachstehenden Code dargestellt erfasst:

//+------------------------------------------------------------------+
//| Spatial (Space) Indexing function. Returns Matrix Determinant    |
//| This however can be customised to return all matrix values as    |
//| a vector, depending on the detail required.                      |
//+------------------------------------------------------------------+
double CSignalNetwork::G(vector &X,vector &Y)
{  matrix _m;
   if(X.Size()!=2*m_train_set||Y.Size()!=2*m_train_set)
   {  return(0.0);
   }
   _m.Init(2,2);
   vector _x1,_x2,_y1,_y2;
   _x1.Init(m_train_set);_x1.Fill(0.0);
   _x2.Init(m_train_set);_x2.Fill(0.0);
   _y1.Init(m_train_set);_y1.Fill(0.0);
   _y2.Init(m_train_set);_y2.Fill(0.0);
   for(int i=0;i<m_train_set;i++)
   {  _x1[i] = X[i];
      _x2[i] = X[i+m_train_set];
      _y1[i] = Y[i];
      _y2[i] = Y[i+m_train_set];
   }
   _m[0][0] = _x1.CorrCoef(_x2);
   _m[0][1] = _x1.CorrCoef(_y2);
   _m[1][0] = _y1.CorrCoef(_x2);
   _m[1][1] = _y1.CorrCoef(_y2);
   return(_m.Det());
}


Beachten Sie, dass wir einen einzigen Wert zurückgeben, der die Matrix darstellt und nicht ihre einzelnen Werte, da wir ihre Determinante verwenden. Alternativ könnten auch einzelne Messwerte verwendet werden, da die Korrelationswerte immer von -1,0 bis +1,0 normiert sind und dies zu genaueren Ergebnissen für das Modell insgesamt führen könnte. Für unsere Zwecke haben wir uns jedoch an die Determinante gehalten, da die Effizienz ein wenig wichtiger ist, jedenfalls für die ersten Tests.

Da alle 4 Datenpuffer erwähnt wurden, ist es vielleicht hilfreich, auch über unser sehr rudimentäres Modell zu sprechen, bei dem es sich um ein einfaches neuronales Netz handelt, das ohne die Verwendung von Bibliotheken programmiert wurde. Wir zerlegen ein Netz in seine grundlegenden Komponenten: Eingänge, Gewichte, Verzerrungen, verborgene Ausgänge, Ausgänge und Ziel; und verwenden nur diese für die Vorwärts- und Rückwärtspropagation. Unsere Netzwerkschnittstelle sieht folgendermaßen aus:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class Cnetwork
{
protected:
   matrix            weights[];
   vector            biases[];

   vector            inputs;
   vector            hidden_outputs[];
   vector            target;

   int               hidden_layers;
   double            Softplus(double X);
   double            SoftplusDerivative(double X);

public:
   vector            output;

   void              Get(vector &Target)
   {                 target.Copy(Target);
   }

   void              Set(vector &Inputs)
   {                 inputs.Copy(Inputs);
   }
   
   bool              Get(string File, double &Criteria, datetime &Version);
   bool              Set(string File, double Criteria);

   void              Forward();
   void              Backward(double LearningRate);

   void              Cnetwork(int &Settings[],double InitialWeight,double InitialBias)
   {                 
                        
                        ...
                        ...

   };
   void              ~Cnetwork(void) { };
};

Neben den üblichen Funktionen zum Setzen und Abrufen von Eingaben und Zielen sind auch einige Exportfunktionen für Gewichte und Verzerrungen nach dem Training enthalten. Der Feed-Forward-Algorithmus ist ein gewöhnlicher Algorithmus, der Soft-Plus für die Aktivierung auf jeder Schicht verwendet, und die Back-Propagation-Funktion ist ebenfalls von der alten Schule und stützt sich auf die Kettenregel und Gradientenabstiegsmethoden zur Anpassung der Netzwerkgewichte und Vorspannungen. Für die Initialisierung des Netzes sind jedoch einige Eingaben erforderlich, und diese werden einer „Validierung“ unterzogen, bevor die Klasseninstanz sicher verwendet werden kann. Die Eingaben für die Erstellung des Netzes sind das Einstellungsfeld, die Werte für die anfänglichen Gewichte (im gesamten Netz) sowie die anfänglichen Vorspannungswerte. Das Einstellungsfeld bestimmt die Anzahl der versteckten Schichten, die das Netz haben wird, durch seine eigene Größe, und jede Ganzzahl an seinen Indizes legt die Größe der jeweiligen Schicht fest.

   void              Cnetwork(int &Settings[],double InitialWeight,double InitialBias)
   {                 int _size =    ArraySize(Settings);
                     if(_size >= 2 && _size <= USHORT_MAX && Settings[ArrayMinimum(Settings)] > 0 && Settings[ArrayMaximum(Settings)] < USHORT_MAX)
                     {  ArrayResize(weights, _size - 1);
                        ArrayResize(biases, _size - 1);
                        ArrayResize(hidden_outputs, _size - 2);
                        hidden_layers = _size - 2;
                        for(int i = 0; i < _size - 1; i++)
                        {  weights[i].Init(Settings[i + 1], Settings[i]);
                           weights[i].Fill(InitialWeight);
                           biases[i].Init(Settings[i + 1]);
                           biases[i].Fill(InitialBias);
                           if(i < _size - 2)
                           {  hidden_outputs[i].Init(Settings[i + 1]);
                              hidden_outputs[i].Fill(0.0);
                           }
                        }
                        output.Init(Settings[_size - 1]);
                        target.Init(Settings[_size - 1]);
                     }
                     else
                     {  printf(__FUNCSIG__ + " invalid network settings. ");
                        //~Cnetwork(void);
                     }
   };


Da wir sowohl die Nachfrage als auch das Angebot prognostizieren, benötigen wir zwei separate Instanzen unseres Netzes, eine für jede Aufgabe. Unsere Get-Output-Funktion, die als Feed für die Funktionen Check-Open-Long und Check-Open-Short dient, füllt die jeweiligen Eingangsschichten jedes Netzes mit den bereits oben erwähnten Datenpuffern. Da die Nachfrage nur von ihren vorherigen Werten sowie den Raum- und Zeitparametern abhängt, ist ihre Eingabeschicht auf 3 bemessen. Da das Angebot jedoch nicht nur von seinen vorherigen Werten abhängt, sondern auch von der vorherigen Nachfrage beeinflusst werden kann, ist ihre Eingabeschicht auf 4 bemessen, wenn man die ähnlichen Raum- und Zeitinputs berücksichtigt. Das Füllen dieser Schichten wird in der Funktion get output bei jedem neuen Balken wie folgt gehandhabt:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double CSignalNetwork::GetOutput()
{  

        ...
        ...

   for(int i = 0; i < m_train_set; i++)
   {  for(int ii = 0; ii < __LONGS_INPUTS; ii++)
      {  if(ii==0)//time
         {  m_model_longs.x[i][ii] = T(m_time.GetData(i));
         }
         else if(ii==1)//spatial matrix
         {  vector _x,_y;
            _x.CopyRates(m_symbol.Name(),m_period,2,StartIndex() + ii + i,2*m_train_set);
            _y.CopyRates(m_symbol.Name(),m_period,4,StartIndex() + ii + i,2*m_train_set);
            m_model_longs.x[i][ii] = G(_x,_y);
         }
         else if(ii==2)//demand
         {  m_model_longs.x[i][ii] = (m_high.GetData(StartIndex() + ii + i) - m_open.GetData(StartIndex() + ii + i));
         }
      }
      if(i > 0) //assign classifier 
      {  m_model_longs.y[i - 1] = (m_high.GetData(StartIndex() + i - 1) - m_open.GetData(StartIndex() + i - 1));
      }
   }
   for(int i = 0; i < m_train_set; i++)
   {  for(int ii = 0; ii < __SHORTS_INPUTS; ii++)
      {  if(ii==0)//time
         {  m_model_shorts.x[i][ii] = T(m_time.GetData(i));
         }
         else if(ii==1)//spatial matrix
         {  vector _x,_y;
            _x.CopyRates(m_symbol.Name(),m_period,4,StartIndex() + ii + i,2*m_train_set);
            _y.CopyRates(m_symbol.Name(),m_period,2,StartIndex() + ii + i,2*m_train_set);
            m_model_shorts.x[i][ii] = G(_x,_y);
         }  
         else if(ii==2)//demand
         {  m_model_shorts.x[i][ii] = (m_high.GetData(StartIndex() + ii + i) - m_open.GetData(StartIndex() + ii + i));
         }  
         else if(ii==3)//supply
         {  m_model_shorts.x[i][ii] = (m_open.GetData(StartIndex() + ii + i) - m_low.GetData(StartIndex() + ii + i));
         }
      }
      if(i > 0) //assign classifier
      {  m_model_shorts.y[i - 1] = (m_open.GetData(StartIndex() + i - 1) - m_low.GetData(StartIndex() + i - 1));
      }
   }

        ...
        ...

}

Danach weisen wir den beiden Netzen Eingangs- und Zielwerte zu, indem wir diese Informationen aus der Modellstruktur in Trainingsschleifen abrufen, wobei jede Schleife die Netze für eine bestimmte Anzahl von Epochen durchläuft. Aus vorläufigen Tests ging hervor, dass die ideale Anzahl von Epochen etwa 10000 bei einer Lernrate von 0,5 pro Trainingsschleife beträgt. Dies ist sicherlich rechenintensiv, weshalb die im nächsten Abschnitt vorgestellten Testergebnisse sehr konservative Werte von etwa 250 Epochen verwenden.

Die Funktion „Set-Output“ ermöglicht es uns, die Gewichte und Verzerrungen des Netzes bei jedem Durchlauf zu protokollieren, sofern die Ergebnisse des Durchlaufs die durch die bei der Initialisierung verwendeten Gewichte festgelegten Kriterien übertreffen. Bei der Initialisierung besteht die Möglichkeit, Gewichte zu lesen, und die durch diese Gewichte festgelegten Kriterien dienen als Benchmark für den aktuellen Testlauf.


Testläufe

Wir führen Testläufe für EURUSD auf dem täglichen Zeitrahmen für das Jahr 2023 mit sehr einfachen Netzwerkeinstellungen durch. Das Netz für die Nachfrageprognose besteht aus insgesamt 3 Schichten mit einer versteckten Schicht und den Größen 3, 5 und 1. Das Netz für die Angebotsprognose besteht ebenfalls aus 3 Schichten, aber wie oben erwähnt, hat die Eingabeschicht eine andere Größe, sodass ihre Größe 4, 5 und 1 beträgt. Die 1 am Ende der beiden Netze speichert die Ausgänge der beiden Netze.

Wie immer wird der am Ende dieses Artikels beigefügte Code mit Hilfe des MQL5-Assistenten zu einem Expertenberater zusammengesetzt. Wenn Sie also neu sind oder keine Erfahrung haben, lesen Sie bitte die Artikel hier und hier.

Diese beiden Konfigurationen sind sehr einfach und wohl die einfachsten, die man sich ausdenken kann, da die referenzierte Netzklasse bis zu UCHAR_MAX-Schichten mit einer Größe von ebenfalls UCHAR_MAX erzeugen kann, wenn die Initialisierungseinstellungen dies vorsehen. Je mehr Schichten und je größer die Schichten sind, desto mehr Rechenressourcen werden benötigt. Dennoch ist es wichtig, darauf hinzuweisen, dass Schichten im Bereich von 100 Einheiten in der Regel als ausreichend angesehen werden, selbst wenn nur wenige Schichten vorhanden sind.

Wir führen die Läufe wie immer ohne Ausstiegskursziele durch, wobei die Positionen bis zur Umkehr des Signals gehalten werden, da dieser Ansatz tendenziell langfristiger ist und die großen Preistrends besser beurteilt. Ein Real-Tick-Durchlauf mit einigen der idealen Einstellungen ergibt den unten stehenden Bericht:

rep

Und die unten abgebildete Kapitalkurven:

curv

Wenn wir uns mit diesem Bericht befassen, wird deutlich, dass im Laufe des Jahres zu wenige Handelsgeschäfte platziert wurden, was in erster Linie keine schlechte Sache ist, da ihre Qualität solide war, da sie über längere Zeiträume gehalten wurden, bis Signalumkehrungen ausgelöst wurden, und für diesen getesteten Zeitraum haben sie minimale Drawdowns erlitten. Das Problem ist jedoch, dass es in stagnierenden oder schwankenden Märkten oft ratsam ist, auf die Mikrobewegungen der Preise zu achten, insbesondere wenn eine Hebelwirkung vorliegt. In dem System, das wir getestet haben, haben wir den täglichen Zeitrahmen verwendet, und das ist in Ordnung, da es dazu neigt, sich auf das große Bild zu konzentrieren, aber wenn wir es ein bisschen pragmatischer machen wollten, müssten wir kleinere Zeitrahmen in Betracht ziehen, wahrscheinlich 4 Stunden oder sogar 1 Stunde. Wenn wir dies tun, steigen unsere Rechenressourcen allein für die Tests um eine beträchtliche Größenordnung, und es ist nicht übertrieben zu sagen, dass dieses Verhältnis exponentiell ist. Dabei ist zu beachten, dass es sich um ein sehr rudimentäres Netz mit 3 Schichten und einer versteckten Schicht mit 5 Punkten handelt und die räumliche Matrix auf eine Determinante reduziert ist. All diese Faktoren sind wichtig, denn je kleiner die Zeitrahmen werden, desto wichtiger wird es, das Rauschen herauszufiltern, und eine der besten Methoden, dies zu tun, ist, ein wenig pedantisch zu sein. 

Darüber hinaus gibt es in der referenzierten Netzklasse Funktionen, mit deren Hilfe die Gewichtungs- und Bias-Einstellungen des Netzes zu Beginn und am Ende eines jeden Durchgangs importiert und exportiert werden können. Ich habe sie bei diesen Testläufen nicht verwendet, da dies den Prozess auch etwas verlangsamt hat, aber sie müssten bei Tests über längere Zeiträume verwendet werden (wir haben für diesen Test nur ein Jahr berücksichtigt). Wenn sie eingesetzt werden, müssen die Kriterien, nach denen die Testläufe durchgeführt werden, sorgfältig abgewogen werden, denn in der Netzklasse gilt standardmäßig: Je größer, desto besser. Wenn man also zum Beispiel mehr Wert auf Drawdowns legt, muss der Code in der Netzwerkklasse entsprechend geändert werden, sodass bei jedem Durchlauf die in die Datei geschriebenen Gewichte nur aktualisiert werden, wenn der Kriterienwert aus diesem Durchlauf WENIGER ist als der vorherige Wert. Dies bedeutet auch, dass der Start- oder Standardwert bei jedem Lauf DBL_MAX oder ein ausreichend hoher Wert sein muss, um unnötige Fehler zu vermeiden.


Schlussfolgerung

Die Fähigkeit der STF, doppelte Prognosen zu erstellen, stellt zweifellos einen interessanten Ansatz dar, der nicht sehr verbreitet ist und zweifellos Potenzial zur Verfeinerung und Verbesserung hat. Die G-Matrix für räumliche Informationen, die in unserem Test verwendet wurde, kann zum Beispiel auf n x n erweitert werden, indem die Vektoren der Eingabedaten in kleinere Teile aufgeteilt werden, und sogar jeder ihrer Werte könnte Eingabedatenpunkte für ein Netzwerk sein, sowie viele andere Anpassungen, aber diese Änderungen gehen auf Kosten der Rechenressourcen.

In der Tat ist die Implementierung von STF durch neuronale Netze im Allgemeinen ein rechenintensives Unterfangen, das Tests über ziemlich große Datenmengen erfordert, um eine zuverlässige Kreuzvalidierung zu erhalten. Darin liegt die größte Einschränkung, die noch deutlicher wird, wenn sie in der oben zitierten Artikel, in der Transformer-Netze verwendet wurden, umgesetzt wird.

Es ist jedoch ein Aspekt dieses Handwerks, der langsam von der Industrie akzeptiert wird, da wir sehen, dass NVIDIA in dieser Landschaft zunehmend an Bedeutung gewinnt. Alternative, effizientere Modelle wie „Random Forests“ könnten erforscht werden, sofern die zugehörigen Einstellungen nicht zu komplex sind, aber da die Rechenkosten zu sinken beginnen, ist das Kosten-Nutzen-Verhältnis möglicherweise nicht realisierbar.

Der MQL5-Assistent ist jedoch nach wie vor ein Werkzeug für die schnelle Erstellung von Prototypen und das Testen von Ideen, und dieser Artikel über Spatial Temporal Fusion ist ein weiteres Beispiel dafür.



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

Beigefügte Dateien |
Network.mqh (10.87 KB)
SignalWZ_14_aa.mqh (14.03 KB)
nn_a.mq5 (6.59 KB)
Selbstoptimierende Expert Advisors in MQL5 erstellen Selbstoptimierende Expert Advisors in MQL5 erstellen
Bauen wir Expert Advisor, die in die Zukunft blicken und sich an jeden Markt anpassen können.
Die Gruppenmethode der Datenverarbeitung: Implementierung des mehrschichtigen iterativen Algorithmus in MQL5 Die Gruppenmethode der Datenverarbeitung: Implementierung des mehrschichtigen iterativen Algorithmus in MQL5
In diesem Artikel beschreiben wir die Implementierung des mehrschichtigen iterativen Algorithmus der Gruppenmethode der Datenverarbeitung in MQL5.
MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 15): Support-Vektor-Maschinen mit dem Newtonschen Polynom MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 15): Support-Vektor-Maschinen mit dem Newtonschen Polynom
Support-Vektor-Maschinen klassifizieren Daten auf der Grundlage vordefinierter Klassen, indem sie die Auswirkungen einer Erhöhung der Dimensionalität untersuchen. Es handelt sich um eine überwachte Lernmethode, die angesichts ihres Potenzials, mit mehrdimensionalen Daten umzugehen, ziemlich komplex ist. In diesem Artikel wird untersucht, wie die sehr einfache Implementierung von 2-dimensionalen Daten mit dem Newton'schen Polynom bei der Klassifizierung von Preis-Aktionen effizienter durchgeführt werden kann.
Verschaffen Sie sich einen Vorteil auf jedem Markt Verschaffen Sie sich einen Vorteil auf jedem Markt
Erfahren Sie, wie Sie jedem Markt, mit dem Sie handeln möchten, einen Schritt voraus sein können, unabhängig von dem derzeitigen Niveau Ihrer Fähigkeiten.