English Русский 中文 Español 日本語 Português
preview
Neuronale Netze im Handel: Ein Multi-Agenten-System mit konzeptioneller Verstärkung (FinCon)

Neuronale Netze im Handel: Ein Multi-Agenten-System mit konzeptioneller Verstärkung (FinCon)

MetaTrader 5Handelssysteme |
18 0
Dmitriy Gizlyk
Dmitriy Gizlyk

Einführung

Die Finanzmärkte zeichnen sich durch hohe Volatilität und Komplexität aus, was optimale Anlageentscheidungen vor große Herausforderungen stellt. Händler und Portfoliomanager müssen eine Vielzahl von multimodalen Daten berücksichtigen, z. B. makroökonomische Indikatoren, technische Signale und Verhaltensfaktoren der Marktteilnehmer. Das Hauptziel dieser Bemühungen ist die Maximierung der Erträge bei gleichzeitiger Minimierung der Risiken.

In traditionellen Finanzinstituten sind an der Datenverarbeitung und Entscheidungsfindung verschiedene Spezialisten beteiligt: Analysten führen Marktforschungen durch, Risikomanager bewerten potenzielle Bedrohungen, und Führungskräfte treffen strategische Entscheidungen. Trotz der hierarchischen Struktur behindern jedoch menschliche Faktoren und begrenzte Ressourcen häufig die Fähigkeit, sich schnell an die raschen Marktveränderungen anzupassen. Infolgedessen ist der Einsatz automatisierter Systeme immer wichtiger geworden: Systeme, die nicht nur den Analyseprozess beschleunigen, sondern auch die Wahrscheinlichkeit menschlicher Fehler verringern.

Die moderne Forschung im Bereich der künstlichen Intelligenz und der Finanztechnologie konzentriert sich auf die Entwicklung adaptiver Softwarelösungen. Solche Systeme können aus historischen Daten lernen, Marktmuster erkennen und fundiertere Entscheidungen treffen. Eine der vielversprechendsten neuen Richtungen ist die Integration von Methoden der natürlichen Sprachverarbeitung (NLP), die die Analyse von Finanznachrichten, Expertenprognosen und anderen textbasierten Daten zur Verbesserung der Vorhersagegenauigkeit und Risikobewertung ermöglichen.

Die Wirksamkeit solcher Systeme hängt weitgehend von zwei Schlüsselaspekten ab: der Interaktion zwischen den Systemkomponenten und ihrer Fähigkeit zum kontinuierlichen Selbstlernen. Studien haben gezeigt, dass Systeme, die die Zusammenarbeit von Spezialisten modellieren, eine höhere Leistung aufweisen, und mit der Einführung neuer Ansätze werden diese Modelle immer anpassungsfähiger an sich ändernde Marktbedingungen.

Bestehende Lösungen, wie z.B. FinMem und FinAgent, zeigen deutliche Fortschritte bei der Automatisierung von Finanzvorgängen. Diese Systeme haben jedoch auch ihre Grenzen: Sie konzentrieren sich in der Regel auf die kurzfristige Marktdynamik und verfügen häufig nicht über umfassende Mechanismen für das langfristige Risikomanagement. Darüber hinaus kann die Qualität der Empfehlungen durch begrenzte Rechenressourcen und eine begrenzte Flexibilität der Algorithmen beeinträchtigt werden.

Diese Herausforderungen werden in dem Artikel „FinCon: A Synthesized LLM Multi-Agent System with Conceptual Verbal Reinforcement for Enhanced Financial Decision Making“. Die Autoren schlagen FinCon vor, ein Multi-Agenten-System, das speziell für die Integration von Aktienhandels- und Portfolio-Management-Prozessen entwickelt wurde.

FinCon simuliert den Arbeitsablauf eines professionellen Investmentteams. Die Analysten sammeln und analysieren Daten aus verschiedenen Quellen, darunter Marktindikatoren, Nachrichten und historische Daten, während die Manager diese Erkenntnisse zusammenfassen und endgültige Entscheidungen treffen. Dieser Ansatz minimiert die redundante Kommunikation zwischen den Teilnehmern und optimiert die Nutzung der Rechenressourcen.

Die Autoren haben FinCon so konzipiert, dass es sowohl mit einzelnen Finanzanlagen als auch mit diversifizierten Portfolios funktioniert. Dies macht das System sehr vielseitig.

FinCon führt mithilfe von Analysemodellen und Algorithmen des maschinellen Lernens Risikobewertungen in Echtzeit durch. Nach jeder Handelsepisode führt das System eine Post-Operation-Analyse durch, um Fehler zu erkennen und seine Modelle auf der Grundlage neu gewonnener Daten zu aktualisieren.

Die experimentellen Ergebnisse zeigen, dass FinCon das Risikomanagement deutlich verbessert und die Gesamtperformance des Portfolios steigert.


FinCon-Architektur

Die Architektur von FinCon weist eine zweistufige Hierarchie auf, die aus zwei Hauptkomponenten besteht: der Agentengruppe Manager-Analyst und dem Subsystem Risikokontrolle. Diese Struktur gewährleistet eine effiziente Informationsverarbeitung, verringert die Fehlerwahrscheinlichkeit, minimiert die Kosten für die Entscheidungsfindung und ermöglicht eine tiefgehende Marktanalyse.

Der Rahmen von FinCon ähnelt einer gut organisierten Investmentfirma, in der alle Ressourcen für maximale Effizienz optimiert sind. Sein Hauptziel ist die Verbesserung der Informationswahrnehmung und -analyse bei gleichzeitiger Minimierung des Kommunikations- und Datenverarbeitungsaufwands.

Analystenagenten spielen eine Schlüsselrolle bei FinCon, indem sie aus riesigen Mengen von Marktdaten primäre Anlageideen herausfiltern. Jeder Agent hat eine enge Spezialisierung, wodurch eine Datendopplung und eine kognitive Überlastung vermieden werden. In der Referenzimplementierung verwendet der Rahmen sieben Analystenagenten. Textagenten analysieren Nachrichtenartikel, Pressemitteilungen und Finanzberichte, um potenzielle Risiken und Chancen zu erkennen. Audio-Agenten dolmetschen aufgezeichnete Gewinnmitteilungen von Führungskräften des Unternehmens und erkennen emotionale Nuancen und kritische Diskussionspunkte. Die Agenten für Datenanalyse und Aktienauswahl berechnen quantitative Kennzahlen, die es dem Manager ermöglichen, Markttrends mit hoher Präzision vorherzusagen.

Die Analysten-Agenten liefern ihre Ergebnisse an den Manager-Agenten, die einzige Entscheidungsinstanz. Bei Handelsentscheidungen erfüllt der Manager vier Schlüsselfunktionen: Konsolidierung der Analyseergebnisse, Risikoüberwachung in Echtzeit, kontinuierliche Überprüfung früherer Entscheidungen und Verfeinerung seiner Anlageüberzeugungen.

FinCon setzt einen zweistufigen Risikokontrollmechanismus ein. Die Risikobewertung während eines Ereignisses ermöglicht Korrekturmaßnahmen in Echtzeit, um kurzfristige Verluste zu minimieren. Bei der Risikobewertung zwischen den einzelnen Episoden werden die Ergebnisse der verschiedenen Episoden verglichen, um Fehler zu erkennen und Strategien zu verbessern. Dieser zweigleisige Ansatz gewährleistet die Widerstandsfähigkeit des Systems gegenüber externen Veränderungen und fördert seine kontinuierliche Verbesserung.

Die Aktualisierung der Investitionsüberzeugungen spielt eine entscheidende Rolle bei der Modellanpassung. Es ermöglicht FinCon, den Fokus seiner Analysten bei der Datenextraktion anzupassen und die Entscheidungslogik des Managers zu verfeinern. Der Mechanismus von Akteur-Kritiker ermöglicht es FinCon, seine Handelsstrategie auf der Grundlage der vorgegebenen Handelsziele periodisch zu optimieren. Dieser Ansatz stützt sich auf die Analyse erfolgreicher und nicht erfolgreicher Maßnahmen und trägt zur ständigen Verbesserung der Entscheidungsstrategien bei.

Die episodische Reflexion von FinCon wird durch einen einzigartigen Mechanismus angetrieben, der als Conceptual Verbal Reinforcement (CVRF) bekannt ist. Diese Komponente bewertet die Effektivität von aufeinanderfolgenden Handelsepisoden, indem sie die Erkenntnisse der Analysten mit den Entscheidungen des Managers vergleicht. CVRF verknüpft wichtige Erkenntnisse mit spezifischen strategischen Aspekten des Handels. Durch die Gegenüberstellung von konzeptionellen Erkenntnissen aus mehr und weniger erfolgreichen Episoden gibt das Modell Empfehlungen für die Anpassung von Überzeugungen. Diese Erkenntnisse helfen den Vermittlern, sich auf die wichtigsten Marktinformationen zu konzentrieren und so die Gesamtleistung des Portfolios zu verbessern.

Empfehlungen zur Anpassung von Überzeugungen werden zunächst an den Manager übermittelt und dann selektiv an die Analysten weitergegeben. Dadurch wird redundante Kommunikation minimiert und eine Informationsüberlastung vermieden. Die Methode misst den Anteil der sich überschneidenden Handelsaktionen zwischen aufeinanderfolgenden Lerntrajektorien und verbessert so die Effizienz des Systems. Dies macht sich besonders in Umgebungen bemerkbar, in denen jeder Agent eine klar definierte, spezialisierte Rolle hat, was die Synergie innerhalb des Modells fördert.

FinCon bietet ein fortschrittliches Speichermodul, das in drei Schlüsselkomponenten unterteilt ist: Arbeitsspeicher, prozeduralen Speicher und episodischen Speicher.

Der Arbeitsspeicher (working memory) speichert vorübergehend Daten, die für laufende Operationen benötigt werden. Dies ermöglicht die schnelle Verarbeitung großer Informationsmengen, ohne dass der Kontext verloren geht.

Der prozedurale Speicher (procedural memory) speichert Algorithmen und Strategien, die in früheren Episoden erfolgreich angewendet wurden. Dieser Speicher ermöglicht eine schnelle Anpassung an wiederkehrende Aufgaben und die Wiederverwendung bewährter Methoden.

Der episodische Speicher (episodic memory) speichert Schlüsselereignisse, Handlungen und Ergebnisse, die für die Verfeinerung von Strategien auf hoher Ebene besonders wichtig sind. Dieser Speichertyp spielt eine Schlüsselrolle im Lernprozess des Modells, denn er hilft ihm, vergangene Erfolge und Misserfolge zu nutzen, um seine zukünftige Leistung zu verbessern.

Die mehrschichtige Struktur von FinCon macht es zu einem hocheffizienten und anpassungsfähigen System. Die Originalvisualisierung des Rahmens finden Sie unten.

Visualisierung des FinCon-Rahmens der Autoren


Implementation in MQL5

Nachdem wir uns mit den theoretischen Aspekten des FinCon-Rahmens befasst haben, kommen wir nun zum praktischen Teil unseres Artikels, in dem wir unsere Interpretation der vorgeschlagenen Konzepte mit MQL5 umsetzen.

Es ist wichtig anzumerken, dass der ursprüngliche FinCon-Rahmen auf der Verwendung von vortrainierten großen Sprachmodellen (LLMs) beruht. In unseren Projekten verwenden wir keine Sprachmodelle. Stattdessen setzen wir die vorgeschlagenen Grundsätze nur mit den in der MQL5-Umgebung verfügbaren Tools um. Beginnen wir also damit, das Speichermodul zu modernisieren.

Ansätze zur Modernisierung von Speichermodulen


Wie Sie sich vielleicht erinnern, besteht unser zuvor entwickelter Speicherblock aus zwei wiederkehrenden Modulen mit unterschiedlichem Architekturdesign. Dieses Design ermöglicht die Schaffung eines zweistufigen Speichersystems mit unterschiedlichen Raten des Informationsabfalls, wodurch es flexibler und anpassungsfähiger für verschiedene Aufgaben wird. Dieser Ansatz ist besonders nützlich in Situationen, in denen sowohl kurzfristige als auch langfristige Abhängigkeiten berücksichtigt werden müssen.

Beachten Sie, dass in unserem Modell die Zerfallsrate nicht explizit definiert ist. Stattdessen ergibt sie sich auf natürliche Weise aus den architektonischen Unterschieden zwischen den wiederkehrenden Modulen. Ein Modul konzentriert sich auf den Kurzzeitspeicher zur schnellen Aktualisierung von Informationen. Die andere ist für langfristige Abhängigkeiten zuständig und bewahrt wichtige Daten über längere Zeiträume auf. Diese architektonischen Unterscheidungen schaffen ein natürliches Gleichgewicht zwischen Verarbeitungsgeschwindigkeit und analytischer Tiefe.

Die Informationen innerhalb des Speicherblocks werden in den verborgenen Zuständen der rekurrenten Module gespeichert. Dieser Ansatz ermöglicht eine erhebliche Verringerung des Speicherbedarfs, bringt aber auch Herausforderungen mit sich, wenn es notwendig wird, bestimmte Episoden zu extrahieren und zu vergleichen. Diese Fähigkeit ist jedoch entscheidend für die Umsetzung des konzeptionellen Überprüfungsmechanismus, der die Möglichkeit erfordert, den aktuellen Kontext mit zuvor gespeicherten Episoden zu vergleichen.

Darüber hinaus betonen die Autoren des FinCon-Rahmens, wie wichtig die Verwendung eines dreistufigen Speichersystems für eine genauere Analyse und Vorhersage ist. Folglich wird es notwendig, den bestehenden Speicherblock durch die Einführung einer zusätzlichen episodischen Speicherschicht zu erweitern.

Der Prozess der Optimierung des Speichermoduls umfasst mehrere wichtige Schritte. Zunächst einmal wird der Verbrauch von Rechenressourcen minimiert. Dies kann durch die Integration von Datenkomprimierungsalgorithmen erreicht werden. Solche Algorithmen eliminieren redundante Informationen und reduzieren so den Speicherbedarf pro Episode, während wesentliche Merkmale der Daten erhalten bleiben.

Eine weitere wichtige Aufgabe besteht darin, einen schnellen und genauen Zugang zu relevanten Informationen zu gewährleisten. Um dies zu erreichen, ist es ratsam, Vektorähnlichkeitsalgorithmen zu verwenden. Diese Algorithmen ermöglichen es dem System, schnell die Episoden zu finden, die der aktuellen Episode am ähnlichsten sind – eine wichtige Funktion für Echtzeitanwendungen.

Damit wird der modernisierte Speicherblock zu einem Eckpfeiler der verbesserten Gesamteffizienz des Modells. Sie gewährleistet nicht nur eine kompakte Datenspeicherung, sondern auch einen schnellen Zugriff auf relevante Informationen, was den Entscheidungsprozess erheblich verbessert.

Implementierung des neuen Speichermoduls


Wir implementieren die vorgeschlagenen Methoden in einem neuen Objekt namens CNeuronMemoryDistil. Seine Struktur wird im Folgenden dargestellt.

class CNeuronMemoryDistil  :  public CNeuronMemory
  {
protected:
   CNeuronBaseOCL       cConcatenated;
   CNeuronTransposeOCL  cTransposeConc;
   CNeuronConvOCL       cConvolution;
   CNeuronEmbeddingOCL  cDistilMemory;
   CNeuronRelativeCrossAttention cCrossAttention;
   //---
   virtual bool      feedForward(CNeuronBaseOCL *NeuronOCL) override;
   virtual bool      calcInputGradients(CNeuronBaseOCL *NeuronOCL) override;
   virtual bool      updateInputWeights(CNeuronBaseOCL *NeuronOCL) override;

public:
                     CNeuronMemoryDistil(void) {};
                    ~CNeuronMemoryDistil(void) {};
   //---
   virtual bool      Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                          uint window, uint window_key, uint units_count,
                          uint heads, uint stack_size,
                          ENUM_OPTIMIZATION optimization_type, uint batch);
   //---
   virtual int       Type(void) override
 const   {  return defNeuronMemoryDistil; }
   //---
   virtual bool      Save(int const file_handle) override;
   virtual bool      Load(int const file_handle) override;
   //---
   virtual bool      WeightsUpdate(CNeuronBaseOCL *source, float tau) override;
   virtual void      SetOpenCL(COpenCLMy *obj) override;
   //---
   virtual bool      Clear(void) override;
  };

In unserer Implementierung ist die übergeordnete Klasse das zuvor entwickelte Speicherobjekt, von dem wir alle Kernschnittstellen sowie die beiden wiederkehrenden Module für den Kurz- und den Langzeitspeicher erben. Die internen Objekte, die in unserer neuen Klasse deklariert werden, bilden die Grundlage für den episodischen Speicher. Dies ermöglicht die Speicherung und Verarbeitung von Daten, die sich auf bestimmte Episoden beziehen. Jedes Objekt erfüllt eine spezielle Aufgabe innerhalb der gesamten Speicherstruktur und ermöglicht eine genauere und umfassendere Datenanalyse. Ihre Funktionsweise wird bei der Implementierung der Speichermodulmethoden ausführlich beschrieben.

Alle internen Objekte werden statisch deklariert, sodass wir den Konstruktor und Destruktor leer lassen können. Dieser Ansatz optimiert die Speichernutzung und erhöht die Systemstabilität, indem er das Risiko von Fehlern bei der Erstellung oder Zerstörung von Objekten minimiert.

Die Initialisierung sowohl von geerbten als auch von neu deklarierten Objekten erfolgt in der Methode Init. Die Methodenparameter umfassen eine Reihe von Konstanten, die die Architektur des Objekts definieren und die nötige Flexibilität bieten, um das Modell für bestimmte Aufgaben zu konfigurieren und dabei Integrität und Funktionalität zu erhalten.

bool CNeuronMemoryDistil::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                               uint window, uint window_key, uint units_count,
                               uint heads, uint stack_size,
                               ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronRelativeCrossAttention::Init(numOutputs, myIndex, open_cl, window,
                                         window_key, units_count, heads, window,
                                       2 * stack_size, optimization_type, batch))
      return false;

Innerhalb des Methodenkörpers rufen wir in der Regel die Init-Methode der übergeordneten Klasse auf, um geerbte Objekte zu initialisieren, und übergeben die entsprechenden Parameter. Aufgrund der einzigartigen Eigenschaften unseres neuen Objekts kann die Initialisierungsmethode des Basisspeichermoduls jedoch nicht direkt verwendet werden. Stattdessen verwenden wir eine analoge Methode aus dem Kreuzaufmerksamkeits-Objekt, das als Elternteil für das Basisspeichermodul dient. Dies gewährleistet die Konsistenz und das ordnungsgemäße Funktionieren aller Elemente der neuen Architektur.

Mit Blick auf die Zukunft soll die Ausgabe unseres Speichermoduls den aktuellen Zustand im Kontext des episodischen Speichers analysieren. Hier spielt der Kreuzaufmerksamkeits-Block eine entscheidende Rolle, der zwei Hauptfunktionen erfüllt. Die erste besteht darin, die wichtigsten Episoden zu finden. Um diese Aufgabe zu erfüllen, werden Algorithmen zur Vektorähnlichkeit verwendet, die die Abhängigkeitskoeffizienten des Aufmerksamkeitsmechanismus unterstützen.

Die zweite Funktion des Kreuzaufmerksamkeits-Blocks besteht darin, den ursprünglichen Zustand mit Kontext aus ausgewählten Episoden anzureichern. So entsteht eine vollständigere und informativere Darstellung, die die Datenverarbeitung und die Entscheidungsgenauigkeit verbessert.

Bei der Initialisierung werden die Quelldatenparameter an das Kreuzaufmerksamkeits-Objekt übergeben, und die Länge der Kontextfolge wird auf das Doppelte der Puffergröße des episodischen Speichers festgelegt. So können sowohl Ergebnisse aus dem Kurzzeit- als auch aus dem Langzeitspeicher gespeichert werden.

Nach erfolgreicher Initialisierung des Kreuzaufmerksamkeits-Objekts rufen wir die Initialisierungsmethoden der vererbten kurz- und langfristig wiederkehrenden Module auf. Wir übernehmen diesen Prozess von der Methode der übergeordneten Klasse.

   uint index = 0;
   if(!cLSTM.Init(0, index, OpenCL, iWindow, iUnits, optimization, iBatch))
      return false;
   index++;
   if(!cMamba.Init(0, index, OpenCL, iWindow, 2 * iWindow, iUnits, optimization,
                                                                        iBatch))
      return false;

Die Ausgänge dieser Module werden in einem einzigen Puffer zusammengeführt, was den gleichzeitigen Zugriff auf beide Speichertypen ermöglicht. Zu diesem Zweck wird ein Basisobjekt von ausreichender Größe erstellt.

   index++;
   if(!cConcatenated.Init(0, index, OpenCL, 2 * iWindow * iUnits, optimization,
                                                                       iBatch))
      return false;
   cConcatenated.SetActivationFunction(None);

Anschließend werden die verketteten Informationen komprimiert und in das Speicherobjekt des episodischen Speichers integriert.

Da wir mit einer multimodalen Zeitreihe arbeiten, die einen einzigen Umweltzustand repräsentiert, ist es wichtig, dass die Hauptmerkmale der einzelnen Sequenzen während der Kompression erhalten bleiben. Es wird ein zweistufiges Komprimierungsverfahren angewandt. Der erste Schritt ist die vorläufige Komprimierung der einzelnen Sequenzen, um die wesentlichen Merkmale zu extrahieren und das Datenvolumen ohne Verlust der Integrität zu reduzieren.

Der verkettete Tensor wird transponiert, um die Daten als univariate Sequenzen darzustellen.

   index++;
   if(!cTransposeConc.Init(0, index, OpenCL, iUnits, 2 * iWindow, optimization,
                                                                       iBatch))
      return false;

Die Daten werden dann mithilfe einer Faltungsschicht komprimiert.

   index++;
   if(!cConvolution.Init(0, index, OpenCL, iUnits, iUnits, iWindowKey, iWindow,
                                                      1, optimization, iBatch))
      return false;
   cConvolution.SetActivationFunction(GELU);

Um den episodischen Speicher zu speichern, wenden wir eine Einbettungsschicht an, bei der teilweise komprimierte Zustände in eine kompakte latente Repräsentation projiziert und zu einem FIFO-Speicherstapel (First In, First Out) fester Länge hinzugefügt werden.

Jeder Tensorblock wird mit trainierbaren Projektionsmatrizen in den latenten Raum projiziert. Diese Matrizen wandeln multimodale Daten aus dem Rohformat in eine einheitliche Darstellung um. Dieser Ansatz vereinfacht die weitere Analyse und gewährleistet die Datenkonsistenz bei der Integration von Informationen aus verschiedenen Quellen. In diesem Fall handelt es sich um Kurzzeit- und Langzeitspeichermodule.

   index++;
   uint windows[] = {iWindowKey * iWindow, iWindowKey * iWindow};
   if(!cDistilMemory.Init(0, index, OpenCL, iUnitsKV / 2, iWindow, windows))
      return false;

Wie bereits erwähnt, erfolgt die Analyse der Rohdaten im Rahmen des episodischen Speichers anhand des Basisobjekts Kreuzaufmerksamkeit. Für eine umfassendere Analyse wird ein zusätzliches aufmerksamkeitsübergreifendes Objekt eingeführt, um Quelldaten im Kontext des Kurz- und Langzeitspeichers zu verarbeiten. Dieser Ansatz hilft dabei, Synergieeffekte zwischen verschiedenen Speichertypen zu erfassen.

Das zuvor erstellte Verkettungsobjekt vereinfacht diese Integration. So kann ein einziges Kreuzaufmerksamkeits-Objekt Daten anreichern und gleichzeitig beide Speichermodule berücksichtigen. Dadurch werden die Rechenressourcen optimiert und die Genauigkeit der Analyse erhöht.

   index++;
   if(!cCrossAttention.Init(0, index, OpenCL, iWindow, iWindowKey, iUnits, iHeads,
                                       iWindow, 2 * iUnits, optimization, iBatch))
      return false;
//---
   return true;
  }

Nachdem alle internen Objekte initialisiert sind, gibt die Methode ein logisches Ergebnis an das aufrufende Programm zurück.

Als Nächstes wird der Vorwärtsdurchlauf für den neuen Block in der Methode feedForward konstruiert.

bool CNeuronMemoryDistil::feedForward(CNeuronBaseOCL *NeuronOCL)
  {
   if(!cLSTM.FeedForward(NeuronOCL))
      return false;
   if(!cMamba.FeedForward(NeuronOCL))
      return false;

In den Methodenparametern wird ein Zeiger auf das Quelldatenobjekt empfangen und sofort an die entsprechenden Methoden der Kurzzeit- und Langzeit-Recurrent-Module weitergegeben. Ihre Ausgaben werden in einem einzigen Datenpuffer verkettet.

   if(!Concat(cLSTM.getOutput(), cMamba.getOutput(), cConcatenated.getOutput(),
                                                     iWindow, iWindow, iUnits))
      return false;
   if(!cTransposeConc.FeedForward(cConcatenated.AsObject()))
      return false;

Beachten Sie, dass wir die Datenverkettung in Form von einzelnen Zeitschritten durchführen. Bei einer solchen schrittweisen Verkettung bleibt die Struktur aller Einheitsfolgen erhalten.

Der verkettete Tensor wird transponiert und mit einer Faltungsschicht komprimiert.

   if(!cConvolution.FeedForward(cTransposeConc.AsObject()))
      return false;

Die komprimierte Darstellung wird über die Einbettungsschicht in den episodischen Speicher projiziert.

   if(!cDistilMemory.FeedForward(cConvolution.AsObject()))
      return false;

In diesem Stadium haben wir die erforderliche Informationsmenge in drei Ebenen unseres Speicherblocks gespeichert. Als Nächstes müssen wir den aktuellen Zustand der Umgebung mit dem Kontext der wichtigsten Ereignisse anreichern. Zunächst analysieren wir die Quelldaten im Kontext des Kurz- und Langzeitspeichers.

   if(!cCrossAttention.FeedForward(NeuronOCL, cConcatenated.getOutput()))
      return false;

Dann reichern wir die Daten mit dem Kontext aus dem episodischen Speicher an.

   return CNeuronRelativeCrossAttention::feedForward(cCrossAttention.AsObject(),
                                                     cDistilMemory.getOutput());
  }

Das logische Ergebnis wird an das aufrufende Programm zurückgegeben.

Nach der Implementierung der Vorwärtsdurchlauf-Methode gehen wir zu den Rückwärtsdurchlauf-Algorithmen über. Der Rückwärtsdurchlauf besteht aus zwei Hauptmethoden:

  • calcInputGradients – überträgt Fehlergradienten.
  • updateInputWeights – aktualisiert die Modellparameter.

Der Datenfluss bei der Methode der Fehlergradientenverteilung entspricht dem Algorithmus des Vorwärtsdurchlaufs in umgekehrter Reihenfolge.

In den Parametern der Methode calcInputGradients erhalten wir einen Zeiger auf das gleiche Quelldatenobjekt wie im Vorwärtsdurchlauf. Diesmal propagieren wir jedoch den Fehlergradienten, der dem Einfluss der Daten auf den endgültigen Output des Modells entspricht.

bool CNeuronMemoryDistil::calcInputGradients(CNeuronBaseOCL *NeuronOCL)
  {
   if(!NeuronOCL)
      return false;

Zu Beginn der Methode überprüfen wir sofort die Gültigkeit des empfangenen Zeigers, denn wenn er ungültig ist, werden weitere Operationen innerhalb der Methode sinnlos.

Der Vorwärtsdurchlauf endete mit einem Aufruf der aufmerksamkeitsübergreifenden übergeordneten Klassenmethode. Dementsprechend beginnt der Prozess der Fehlergradientenverteilung mit der entsprechenden Methode desselben Objekts. In dieser Phase wird der Gradient durch die Informationsflüsse der Speicherhierarchie weitergegeben.

  if(!CNeuronRelativeCrossAttention::calcInputGradients(cCrossAttention.AsObject(),
                                                         cDistilMemory.getOutput(),
                                                       cDistilMemory.getGradient(),
                                      (ENUM_ACTIVATION)cDistilMemory.Activation()))
      return false;

Der episodische Speichergradient wird durch die Datenkompressionsschichten bis hinunter auf die Ebene der Verkettungsobjekte des Kurz- und Langzeitspeichers geleitet.

   if(!cConvolution.calcHiddenGradients(cDistilMemory.AsObject()))
      return false;
   if(!cTransposeConc.calcHiddenGradients(cConvolution.AsObject()))
      return false;
   if(!cConcatenated.calcHiddenGradients(cTransposeConc.AsObject()))
      return false;

Dieses Verkettungsobjekt wird jedoch auch für die Analyse von Eingabedaten im Kontext beider Speichertypen verwendet. Daher muss der Fehlergradient auch über den zweiten Informationsfluss an ihn weitergegeben werden.

   if(!NeuronOCL.calcHiddenGradients(cCrossAttention.AsObject(),
                                     cConcatenated.getOutput(),
                                     cConcatenated.getPrevOutput(),
                                     (ENUM_ACTIVATION)cConcatenated.Activation()))
      return false;

Es ist wichtig zu beachten, dass wir in diesem Fall den freien Puffer des Verkettungsobjekts und nicht seinen speziellen Puffer verwenden, um den Fehlergradienten zu erhalten. Auf diese Weise bleiben bereits berechnete Daten erhalten, und es werden keine wertvollen Informationen überschrieben.

Anschließend addieren wir die aus beiden Informationsflüssen gewonnenen Werte und verteilen die resultierenden Gradienten auf die entsprechenden Speicherobjekte.

   if(!SumAndNormilize(cConcatenated.getGradient(), cConcatenated.getPrevOutput(),
                       cConcatenated.getGradient(), iWindow, false, 0, 0, 0, 1) ||
      !DeConcat(cLSTM.getGradient(), cMamba.getGradient(), 
                cConcatenated.getGradient(), iWindow, iWindow, iUnits))
      return false;

An dieser Stelle ist es wichtig darauf hinzuweisen, dass wir absichtlich keine Aktivierungsfunktion auf das Verkettungsobjekt anwenden, um Datenverzerrungen zu vermeiden. Das liegt daran, dass die Module des Kurzzeit- und des Langzeitspeichers unterschiedliche Aktivierungsfunktionen verwenden können. Nach der Verteilung der Fehlergradienten auf die Speichermodule muss jedoch geprüft werden, ob jedes Objekt eine Aktivierungsfunktion enthält, und wenn ja, müssen die Gradientenwerte anhand der Ableitungen dieser Aktivierungsfunktionen angepasst werden.

   if(cLSTM.Activation() != None)
      if(!DeActivation(cLSTM.getOutput(), cLSTM.getGradient(), 
                       cLSTM.getGradient(), cLSTM.Activation()))
         return false;
   if(cMamba.Activation() != None)
      if(!DeActivation(cMamba.getOutput(), cMamba.getGradient(), 
                       cMamba.getGradient(), cMamba.Activation()))
         return false;

An diesem Punkt wird der Fehlergradient durch die Kurzzeit- und Langzeitspeicherpipelines bis auf die Ebene der ursprünglichen Eingabedaten fortgeschrieben. Es sei jedoch daran erinnert, dass der Gradientenpuffer dieses Objekts bereits Daten enthält, die bei der Informationsanalyse auf Kontextebene gewonnen wurden. Um diese zuvor berechneten Werte zu erhalten, müssen wir die Pufferzeiger vorübergehend austauschen, bevor wir weitere Operationen durchführen.

   CBufferFloat *temp = NeuronOCL.getGradient();
   if(!NeuronOCL.SetGradient(cConcatenated.getPrevOutput(), false) ||
      !NeuronOCL.calcHiddenGradients(cLSTM.AsObject()) ||
      !SumAndNormilize(temp, NeuronOCL.getGradient(), temp, iWindow,
                                                  false, 0, 0, 0, 1))
      return false;

Sobald diese Substitution abgeschlossen ist, propagieren wir die Fehlergradienten sequentiell durch die Informationsflüsse der entsprechenden Speichermodule und fügen die neuen Werte zu den zuvor akkumulierten Daten hinzu.

   if(!NeuronOCL.calcHiddenGradients(cMamba.AsObject()) ||
      !SumAndNormilize(temp, NeuronOCL.getGradient(), temp, iWindow,
                                               false, 0, 0, 0, 1) ||
      !NeuronOCL.SetGradient(temp, false))
      return false;
//---
   return true;
  }

Nach diesem Schritt werden die Pufferzeiger wieder in ihren ursprünglichen Zustand versetzt. Wir geben das logische Ergebnis der Operationen an das aufrufende Programm zurück und schließen die Ausführung der Methode ab.

Damit ist unsere Untersuchung der im modernisierten Speicherblock CNeuronMemoryDistil implementierten Methoden abgeschlossen. Der vollständige Code dieses Objekts und alle seine Methoden werden im Anhang zur weiteren Untersuchung bereitgestellt.

Der nächste Schritt unserer Arbeit besteht darin, das Objekt „Analyst Agent“ zu konstruieren. Da wir uns jedoch der Grenze des Artikelformats nähern, werden wir eine kurze Pause einlegen und diese Arbeit im nächsten Teil fortsetzen.


Schlussfolgerung

In diesem Artikel haben wir den FinCon-Rahmen untersucht, ein innovatives Multi-Agenten-System, das zur Verbesserung der Entscheidungsfindung im Finanzbereich entwickelt wurde. Der Rahmen integriert die hierarchische Interaktionsstruktur „Manager-Analyst“ mit konzeptionellen verbalen Verstärkungsmechanismen, die eine koordinierte Zusammenarbeit der Agenten und ein effektives Risikomanagement ermöglichen. Dank dieser Merkmale kann das Modell ein breites Spektrum von Finanzaufgaben erfolgreich bewältigen und zeigt eine hohe Anpassungsfähigkeit und Leistungsfähigkeit in einem dynamischen Marktumfeld.

Im praktischen Teil haben wir damit begonnen, die vorgeschlagenen Ansätze mit MQL5 umzusetzen. Diese Arbeit wird im nächsten Artikel fortgesetzt, in dem wir die Leistung und Effizienz der implementierten Lösungen anhand echter historischer Marktdaten bewerten werden.


Referenzen


Programme, die im diesem Artikel verwendet werden

# Name Typ Beschreibung
1 Research.mq5 Expert Advisor Expert Advisor für die Probenahme
2 ResearchRealORL.mq5
Expert Advisor
Expert Advisor für die Probenahme mit der Real-ORL-Methode
3 Study.mq5 Expert Advisor EA für das Modelltraining
4 Test.mq5 Expert Advisor Expert Advisor für Modelltests
5 Trajectory.mqh Klassenbibliothek Struktur der Beschreibung des Systemzustands und der Modellarchitektur
6 NeuroNet.mqh Klassenbibliothek Eine Bibliothek von Klassen zur Erstellung eines neuronalen Netzes
7 NeuroNet.cl Code-Bibliothek OpenCL-Programmcode

Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/16916

Beigefügte Dateien |
MQL5.zip (2352.32 KB)
Marktsimulation (Teil 06): Übertragen von Informationen von MetaTrader 5 nach Excel Marktsimulation (Teil 06): Übertragen von Informationen von MetaTrader 5 nach Excel
Viele Menschen, insbesondere Nicht-Programmierer, finden es sehr schwierig, Informationen zwischen MetaTrader 5 und anderen Programmen zu übertragen. Ein solches Programm ist Excel. Viele verwenden Excel, um ihre Risikokontrolle zu verwalten und aufrechtzuerhalten. Es ist ein ausgezeichnetes Programm und leicht zu erlernen, auch für diejenigen, die keine VBA-Programmierer sind. Im Folgenden werden wir uns ansehen, wie man eine Verbindung zwischen MetaTrader 5 und Excel herstellt (eine sehr einfache Methode).
Von der Grundstufe bis zur Mittelstufe: Template und Typename (V) Von der Grundstufe bis zur Mittelstufe: Template und Typename (V)
In diesem Artikel werden wir einen letzten einfachen Anwendungsfall für Vorlagen untersuchen und die Vorteile und die Notwendigkeit der Verwendung von typename in Ihrem Code diskutieren. Auch wenn dieser Artikel auf den ersten Blick etwas kompliziert erscheint, ist es wichtig, ihn richtig zu verstehen, um später Vorlagen und typename verwenden zu können.
Neuronale Netze im Handel: Ein Multi-Agenten-System mit konzeptioneller Verstärkung (letzter Teil) Neuronale Netze im Handel: Ein Multi-Agenten-System mit konzeptioneller Verstärkung (letzter Teil)
Wir setzen weiterhin die von den Autoren des FinCon-Rahmens vorgeschlagenen Ansätze um. FinCon ist ein Multi-Agenten-System, das auf Large Language Models (LLMs) basiert. Heute werden wir die erforderlichen Module implementieren und umfassende Tests des Modells mit realen historischen Daten durchführen.
Marktsimulation (Teil 04): Erstellen der Klasse C_Orders (I) Marktsimulation (Teil 04): Erstellen der Klasse C_Orders (I)
In diesem Artikel beginnen wir mit der Erstellung der Klasse C_Orders, um Aufträge an den Handelsserver senden zu können. Wir werden dies nach und nach tun, denn unser Ziel ist es, im Detail zu erklären, wie dies über das Nachrichtensystem geschehen wird.