Neuronale Netze im Handel: Ein multimodaler, werkzeuggestützter Agent für Finanzmärkte (FinAgent)
Einführung
Die Finanzmärkte spielen eine wichtige Rolle bei der Wahrung der wirtschaftlichen Stabilität, indem sie an der Kapitalallokation und dem Risikomanagement beteiligt sind. Moderne Finanzhandelssysteme, die sich weitgehend auf die technische Analyse stützen, verbessern diese Prozesse; unter den Bedingungen hoher Marktvolatilität und -variabilität stoßen sie jedoch häufig an erhebliche Grenzen. Regelbasierte Handelssysteme sind starr und lassen sich nur schwer an die sich schnell ändernden Marktbedingungen anpassen, was häufig zu einer geringeren Effizienz führt. Auf Verstärkungslernen (RL) basierende Systeme weisen eine größere Anpassungsfähigkeit auf, haben aber auch ihre Schwächen:
- Hoher Bedarf an umfangreichen Schulungsdaten;
- Entscheidungen können schwer zu erklären sein;
- Es ist schwierig, verschiedene Marktbedingungen zu verallgemeinern;
- Empfindlich gegenüber Marktstörungen;
- Eingeschränkte Integration von multimodalen Marktinformationen.
In den letzten Jahren haben große Sprachmodelle (Large Language Models, LLMs) ein bemerkenswertes Potenzial für die Entscheidungsfindung gezeigt und ihre Anwendung über die Verarbeitung natürlicher Sprache hinaus erweitert. Durch die Integration von Gedächtnis- und Planungsmodulen können sich LLMs an dynamisch wechselnde Umgebungen anpassen. Multimodale LLMs verbessern diese Fähigkeiten weiter, indem sie sowohl textuelle als auch visuelle Informationen verarbeiten, während die Hinzunahme von externen Werkzeugen das Spektrum der Aufgaben erweitert, die diese Modelle bewältigen können, einschließlich komplexer Finanzszenarien.
Trotz der Erfolge bei der Analyse von Finanzdaten stoßen LLM-Agenten auf mehrere Einschränkungen:
- Begrenzte multimodale Verarbeitung von numerischen, textuellen und visuellen Daten;
- Die Notwendigkeit einer präzisen Integration von Daten aus verschiedenen Quellen;
- Geringe Anpassungsfähigkeit an sich schnell verändernde Märkte;
- Schwierigkeiten bei der Nutzung von Expertenwissen und traditionellen Methoden;
- Unzureichende Transparenz bei der Entscheidungsfindung.
Die Autoren der Studie „A Multimodal Foundation Agent for Financial Trading: Tool-Augmented, Diversified, and Generalist“ versuchen, diese Einschränkungen durch die Einführung des FinAgent-Frameworks zu lösen – ein multimodaler Basis-Agent, der textuelle und visuelle Informationen zur Analyse von Marktdynamik und historischen Daten integriert. Zu den wichtigsten Komponenten von FinAgent gehören die multimodale Datenverarbeitung zur Erkennung kritischer Markttrends, ein zweistufiges Reflexionsmodul, das sowohl kurz- als auch langfristige Entscheidungen analysiert, ein Speichersystem zur Minimierung des Rauschens in der Analyse sowie ein Entscheidungsfindungsmodul, das Expertenwissen und fortschrittliche Handelsstrategien einbezieht.
Der FinAgent-Algorithmus
FinAgent ist ein Werkzeug für die Datenanalyse und die fundierte Entscheidungsfindung auf den Finanzmärkten. Es bietet den Nutzern eine Reihe von Instrumenten zum Verständnis von Marktprozessen, zur Vorhersage von Dynamiken und zur Optimierung von Handelsstrategien. FinAgent besteht aus fünf Hauptmodulen, die zusammen ein Ökosystem für die Datenverarbeitung und Entscheidungsfindung bilden.
Das Marktanalysemodul ist für das Sammeln, Verarbeiten und Interpretieren verschiedener Datenquellen zuständig, darunter Marktnachrichten, Preisänderungen und Unternehmensberichte. Mit Hilfe fortschrittlicher Methoden identifiziert das Modul verborgene Muster, die es dem Agenten ermöglichen, seine Aktionen an die aktuellen Marktbedingungen anzupassen.
Um ein Maximum an Effizienz zu erreichen, analysiert FinAgent sowohl aktuelle Marktdaten als auch akkumulierte historische Informationen. Tagesaktuelle Informationen, wie Nachrichten, Preisschwankungen und andere operative Daten, bilden die Grundlage für kurzfristige Entscheidungen. Gleichzeitig trägt die Analyse vergangener Ereignisse dazu bei, langfristige Muster aufzudecken, was die Entwicklung belastbarer Strategien für künftige Operationen ermöglicht. Dieser duale Ansatz gewährleistet eine hohe Anpassungsfähigkeit und Flexibilität.
Der Datenerfassungsprozess von FinAgent verwendet ein umfangreiches Sprachmodell (LLM), das Marktinformationen in textuelle Abfragen umwandelt. Diese Abfragen werden dann für die Suche nach ähnlichen Daten in der historischen Datenbank des Speichermoduls verwendet. Die Anwendung von Vektorähnlichkeitsmethoden erhöht die Suchgenauigkeit und konzentriert sich auf die wichtigsten Informationen. Außerdem verwendet das System spezielle Textfelder, was die Datenverarbeitung verbessert und den Verlust wichtiger Details verhindert. Die daraus resultierenden Daten werden strukturiert und zusammengefasst, was die Analyse vereinfacht und den Einfluss von Sekundärinformationen minimiert.
Das zweistufige Reflexionsmodul funktioniert ähnlich wie menschliche Lernprozesse. Das Low-Level-Reflexionsmodul identifiziert Korrelationen zwischen Preisänderungen und Marktdynamik und ermöglicht so kurzfristige Marktschwankungsprognosen, was für Händler, die auf kleineren Zeitskalen agieren, besonders wichtig ist. In der Zwischenzeit analysiert das Reflexionsmodul auf hoher Ebene komplexere und tiefere Zusammenhänge auf der Grundlage historischer Daten und früherer Handelsergebnisse. Dies ermöglicht die Erkennung von Fehlern und die Entwicklung von Abhilfestrategien. Der Prozess umfasst die Visualisierung von Schlüsselpunkten, wie z. B. Kauf- und Verkaufszeitpunkten, und die Bewertung ihrer Wirksamkeit. Ein iterativer Lernansatz ermöglicht es dem Agenten, Erfahrungen zu sammeln und diese zur Verbesserung künftiger Aktionen zu nutzen.
Das Speichermodul spielt eine zentrale Rolle bei der Gewährleistung des stabilen Betriebs von FinAgent. Es ist für die Speicherung von Daten und die Ermöglichung eines effizienten Abrufs zuständig. Vektorsuchmethoden ermöglichen einen schnellen Zugriff auf relevante Informationen in umfangreichen Datensätzen, reduzieren das Rauschen und verbessern die analytische Präzision. Der Gedächtnismechanismus in FinAGent lieferte wichtige Kontext- und kognitive Fähigkeiten. Beim Finanzhandel ist das Gedächtnis besonders wichtig für die Genauigkeit, die Anpassungsfähigkeit und das Lernen aus früheren Erfahrungen. Sie ermöglicht es dem Agenten, aktuelle Nachrichten und Berichte zu nutzen, um künftige Marktveränderungen vorherzusagen, sich an unbeständige Bedingungen anzupassen und Strategien kontinuierlich zu verfeinern.
Das Entscheidungsfindungsmodul integriert und verarbeitet Schlüsseldaten, einschließlich Marktzusammenfassungen, Preisdynamikanalysen von Low-Level-Reflexionen und Erkenntnisse aus früheren Entscheidungen. Darüber hinaus enthält es Tools, die durch professionelle Anlageempfehlungen und bewährte Handelsstrategien ergänzt werden. Eine wichtige Funktion dieses Moduls ist die Analyse der Marktstimmung, die Vorhersage von Aufwärts- und Abwärtstrends auf der Grundlage der aktuellen Kursbewegungen und die Reflexion der gewonnenen Erkenntnisse. Darüber hinaus berücksichtigt es den Rat von Experten und bewertet die Wirksamkeit herkömmlicher Indikatoren.
Auf der Grundlage der Kombination dieser Analysen und des aktuellen finanziellen Kontextes generiert das Modul die endgültige Entscheidung: ob ein Vermögenswert gekauft, verkauft oder gehalten werden soll. Die Prinzipien des kontextuellen Lernens werden angewendet, um eine logische Entscheidungsstruktur zu schaffen. Dadurch wird sichergestellt, dass jede Handelsaktion gerechtfertigt ist und auf einem umfassenden Verständnis der Marktdynamik im jeweiligen Kontext beruht. Ein solcher Ansatz erlaubt eine bessere Anpassung an die Marktbedingungen und ermöglicht informierte, strategisch fundierte Entscheidungen.
FinAgent ist somit ein umfassendes Werkzeug, das Datenanalyse, Reflexion und Prozessautomatisierung integriert. Sie ermöglicht es Händlern und Analysten, sich effektiv an den Markt anzupassen, Risiken zu minimieren und die Rentabilität zu steigern, was neue Möglichkeiten für die strategische Planung eröffnet.
Die ursprüngliche Visualisierung des FinAgent-Rahmens sieht folgendermaßen aus:

Implementation in MQL5
Nachdem wir die theoretischen Aspekte von FinAgent untersucht haben, kommen wir nun zum praktischen Teil dieses Artikels, in dem wir unsere Vision der vorgeschlagenen Ansätze mit MQL5 umsetzen.
Es ist wichtig anzumerken, dass wir, ähnlich wie in früheren Arbeiten, die Verwendung großer Sprachmodelle ausschließen und die vorgeschlagenen Ansätze mit den uns zur Verfügung stehenden Werkzeugen umsetzen wollen.
Wir beginnen unsere Arbeit mit der Erstellung der Low- und High-Level-Reflexionsmodule.
Low-Level-Reflexionsmodul
Zu Beginn der Entwicklung des Low-Level-Reflection-Moduls sollte man sich die von den Autoren des FinAgent-Rahmens vorgeschlagene Architektur des Speichermoduls vor Augen führen. Das Speichermodul kann konzeptionell in drei Objekte unterteilt werden, die Informationen aus dem Marktanalysemodul und den beiden Reflexionsmodulen verschiedener Ebenen sammeln. So können wir die Module des Modells umstrukturieren, ohne den gesamten Informationsfluss zu verändern, und einzelne Speicherblöcke in die entsprechenden Module integrieren. Diese Eigenschaft machen wir uns zunutze, indem wir einen Speicherblock für diesen Informationsstrom in das Low-Level-Reflection-Modul einbauen.
Das geänderte Low-Level-Reflection-Modul ist als das Objekt CNeuronLowLevelReflection implementiert – seine Struktur ist unten dargestellt.
class CNeuronLowLevelReflection : public CNeuronMemory { protected: CNeuronLSTMOCL cChangeLSTM; CNeuronMambaOCL cChangeMamba; CNeuronRelativeCrossAttention cCrossAttention[2]; //--- virtual bool feedForward(CNeuronBaseOCL *NeuronOCL) override; virtual bool calcInputGradients(CNeuronBaseOCL *NeuronOCL) override; virtual bool updateInputWeights(CNeuronBaseOCL *NeuronOCL) override; public: CNeuronLowLevelReflection(void) {}; ~CNeuronLowLevelReflection(void) {}; //--- virtual bool Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint window, uint window_key, uint units_count, uint heads, ENUM_OPTIMIZATION optimization_type, uint batch) override; //--- virtual int Type(void) override const { return defNeuronLowLevelReflection; } //--- 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; };
Die Verwendung des Speicherobjekts als übergeordnete Klasse ermöglicht die nahtlose Integration von Marktinformationsanalyseprozessen mit kognitiven Speicherfunktionen in eine einheitliche Informationspipeline. Dadurch kann das System nicht nur Rohdaten effizient verarbeiten, sondern auch historische Informationen berücksichtigen, die den Kontext für die aktuelle Analyse liefern.
Die Klassenstruktur umfasst wiederkehrende Objekte verschiedener Architekturen und aufmerksamkeitsübergreifende Blöcke. Diese Komponenten spielen eine Schlüsselrolle bei der Informationsverarbeitung und Entscheidungsfindung. Ihr Zweck und ihre Funktionsweise werden bei der Implementierung der Objektmethoden im Detail erläutert, was ein tieferes Verständnis ihres Einflusses auf die Systemleistung ermöglicht.
Alle internen Objekte werden als statisch deklariert, sodass wir den Konstruktor und Destruktor der Klasse leer lassen können. Die Initialisierung dieser deklarierten und geerbten Objekte wird in der Methode Init durchgeführt.
bool CNeuronLowLevelReflection::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint window, uint window_key, uint units_count, uint heads, ENUM_OPTIMIZATION optimization_type, uint batch) { if(!CNeuronMemory::Init(numOutputs, myIndex, open_cl, window, window_key, units_count, heads, optimization_type, batch)) return false;
Die Parameterstruktur dieser Methode wird vollständig von der übergeordneten Klasse abgeleitet. Im Methodenrumpf rufen wir sofort die gleichnamige Methode der übergeordneten Methode auf und übergeben alle erhaltenen Parameter.
Die Methode der Elternklasse implementiert bereits den Algorithmus zur Kontrolle der empfangenen Parameter und zur Initialisierung der abgeleiteten Objekte.
Als Nächstes initialisieren wir die neu deklarierten Objekte. Wir initialisieren zunächst zwei rekurrente Objekte, die die Dynamik der analysierten Parameter identifizieren sollen. Die Verwendung unterschiedlicher architektonischer Lösungen für diese Objekte gewährleistet eine tiefer gehende Analyse, die es dem System ermöglicht, sich sowohl an kurzfristige als auch an langfristige Veränderungen anzupassen und Trends über mehrere Zeitskalen hinweg zu erkennen.
int index = 0; if(!cChangeLSTM.Init(0, index, OpenCL, window, units_count, optimization, iBatch)) return false; index++; if(!cChangeMamba.Init(0, index, OpenCL, window, 2 * window, units_count, optimization, iBatch)) return false;
Die Analyseergebnisse werden in eine einheitliche Lösung integriert, die Informationen aus verschiedenen Quellen und Ebenen effektiv kombiniert und sich auf die wichtigsten Beziehungen und Abhängigkeiten zwischen Parametern konzentriert. Kreuzaufmerksamkeit erleichtert die Entdeckung verborgener Muster und Zusammenhänge, verbessert die Entscheidungsqualität und ermöglicht eine genauere und kohärentere Informationswahrnehmung.
for(int i = 0; i < 2; i++) { index++; if(!cCrossAttention[i].Init(0, index, OpenCL, window, window_key, units_count, heads, window, units_count, optimization, iBatch)) return false; } //--- return true; }
Nach der Initialisierung aller internen Objekte geben wir das logische Ergebnis der Operationen an das aufrufende Programm zurück und beenden die Ausführung der Methode.
Der nächste Schritt ist die Entwicklung der Algorithmen der Vorwärtsdurchläufe für das Low-Level-Reflexionsmodul, die in der Methode des FeedForward implementiert sind.
bool CNeuronLowLevelReflection::feedForward(CNeuronBaseOCL *NeuronOCL) { if(!cChangeLSTM.FeedForward(NeuronOCL)) return false; if(!cChangeMamba.FeedForward(NeuronOCL)) return false;
Die Methode erhält einen Zeiger auf das Rohdatenobjekt, das eine Beschreibung des aktuellen Zustands der Umgebung enthält. Diese Daten werden an die entsprechenden Methoden unserer rekurrenten Objekte weitergegeben, um die Dynamik der Parameter zu ermitteln, sodass das System Veränderungen in der Umgebung verfolgen und seine Entscheidungen entsprechend anpassen kann.
Anschließend verwenden wir aufmerksamkeitsübergreifende Blöcke, um die Beschreibung des aktuellen Umweltzustands mit Informationen über erkannte Veränderungen anzureichern. Dies ermöglicht die Integration neuer Daten mit bestehenden Daten, wodurch der Kontext verbessert und die Wahrnehmung der Umweltdynamik verbessert wird. Bei diesem Ansatz wird eine „Trajektorie“ erstellt, die die Veränderungen des analysierten Zustands widerspiegelt.
if(!cCrossAttention[0].FeedForward(NeuronOCL, cChangeLSTM.getOutput())) return false; if(!cCrossAttention[1].FeedForward(cCrossAttention[0].AsObject(), cChangeMamba.getOutput())) return false;
Die Analyseergebnisse werden an das Speichermodul übergeben, das von der übergeordneten Klasse implementiert wird. Dieses Modul identifiziert anhaltende Trends, die die Grundlage für die Vorhersage künftiger Kursbewegungen bilden.
return CNeuronMemory::feedForward(cCrossAttention[1].AsObject()); }
Wie man sieht, verarbeiten drei interne Objekte die Rohdaten, die die Umgebung während des Vorwärtsdurchlaufs beschreiben. Folglich müssen wir während des Rückwärtsdurchlaufs (Backpropagation) Fehlergradienten aus allen drei Informationsströmen sammeln. Der Algorithmus zur Verteilung von Fehlergradienten ist in der Methode calcInputGradients implementiert.
bool CNeuronLowLevelReflection::calcInputGradients(CNeuronBaseOCL *NeuronOCL) { if(!NeuronOCL) return false;
Die Methode erhält einen Zeiger auf das gleiche Quelldatenobjekt. Diesmal wird jedoch ein Fehlergradient angegeben, der den Einfluss der Quelldaten auf die Ausgabe des Modells widerspiegelt. Die Methode prüft zunächst die Gültigkeit des empfangenen Zeigers, da sonst keine Datenübertragung möglich ist.
Mit Hilfe der übergeordneten Klasse propagieren wir den Fehlergradienten durch das Speichermodul bis hinunter zu den Kreuzaufmerksamkeits-Blöcken.
if(!CNeuronMemory::calcInputGradients(cCrossAttention[1].AsObject())) return false; if(!cCrossAttention[0].calcHiddenGradients(cCrossAttention[1].AsObject(), cChangeMamba.getOutput(), cChangeMamba.getGradient(), (ENUM_ACTIVATION)cChangeMamba.Activation())) return false;
Dann verteilen wir den resultierenden Fehler auf die rekurrenten Blöcke.
Als Nächstes propagieren wir den Fehlergradienten durch die drei Informationsströme zurück zum rohen Eingangsniveau. Hier berechnen wir zunächst den Gradienten entlang der Kreuzaufmerksamkeits-Block-Pipeline.
if(!NeuronOCL.calcHiddenGradients(cCrossAttention[0].AsObject(), cChangeLSTM.getOutput(), cChangeLSTM.getGradient(), (ENUM_ACTIVATION)cChangeLSTM.Activation())) return false;
Dann ersetzen wir den Zeiger auf den Gradientenpuffer des Rohdatenobjekts, propagieren den Fehler durch die rekurrenten Objektpipelines und summieren die aus verschiedenen Informationsströmen erhaltenen Werte.
CBufferFloat *temp = NeuronOCL.getGradient(); if(!NeuronOCL.SetGradient(cChangeMamba.getPrevOutput(), false) || !NeuronOCL.calcHiddenGradients(cChangeMamba.AsObject()) || !SumAndNormilize(NeuronOCL.getGradient(), temp, temp, iWindow, false, 0, 0, 0, 1)) return false;
if(!NeuronOCL.calcHiddenGradients(cChangeLSTM.AsObject()) || !SumAndNormilize(NeuronOCL.getGradient(), temp, temp, iWindow, false, 0, 0, 0, 1)) return false;
Schließlich werden die Zeiger auf die Datenpuffer in ihren ursprünglichen Zustand zurückversetzt, um eine ordnungsgemäße Vorbereitung für die weitere Verwendung zu gewährleisten.
if(!NeuronOCL.SetGradient(temp, false)) return false; //--- return true; }
Am Ende der Methode wird das logische Ergebnis der Operationen an das aufrufende Programm zurückgegeben.
Damit ist die Diskussion über die Algorithmen zur Konstruktion der Methoden des Low-Level-Reflexionsmoduls abgeschlossen. Den vollständigen Code dieser Klasse und alle ihre Methoden finden Sie im Anhang.
High-Level-Reflexionsmodul
Als Nächstes müssen wir das Reflexionsmodul auf hoher Ebene erstellen, das sich auf eine tiefer gehende und umfassendere Analyse der Interdependenzen konzentriert. Im Gegensatz zum Low-Level-Reflexionsmodul, das sich auf kurzfristige Veränderungen und die Dynamik des aktuellen Zustands konzentriert, untersucht das High-Level-Reflexionsmodul langfristige Trends und Beziehungen, die aus früheren Handelsoperationen ermittelt wurden.
Das Hauptziel dieses Moduls ist eine gründliche Bewertung der Gültigkeit früher getroffener Entscheidungen sowie eine Analyse der tatsächlichen Ergebnisse. Dieser Prozess ermöglicht es uns zu bewerten, wie effektiv die aktuelle Handelsstrategie angewendet wurde und wie gut sie mit den gesetzten Zielen übereinstimmt. Ein wichtiger Teil dieser Analyse ist die Ermittlung der Stärken und Schwächen der Strategie.
Darüber hinaus soll das High-Level-Reflexionsmodul spezifische Empfehlungen zur Optimierung der Handelsstrategie liefern. Diese Empfehlungen zielen darauf ab, die Rentabilität des Handels zu verbessern und/oder das Risiko zu verringern.
Um die Funktionalität des High-Level-Reflexionsmoduls vollständig zu implementieren, werden natürlich wesentlich mehr Eingabedaten benötigt. Zunächst einmal benötigen wir für die Analyse die tatsächlichen Handlungen des Agenten.
Außerdem brauchen wir eine Beschreibung des Umfelds zum Zeitpunkt der Entscheidungsfindung. Auf diese Weise kann bewertet werden, inwieweit jede Entscheidung unter bestimmten Marktbedingungen gerechtfertigt war und wie diese Bedingungen die Ergebnisse beeinflusst haben könnten.
Ebenso wichtig sind Gewinn- und Verlustinformationen, anhand derer beurteilt werden kann, wie sich Entscheidungen auf die finanziellen Ergebnisse auswirken. Diese Daten ermöglichen nicht nur eine Bewertung des Handelserfolgs, sondern zeigen auch mögliche Schwachstellen in der Strategie auf, die eine Anpassung erfordern könnten.
Außerdem müssen die Ergebnisse der Analyse für eine spätere Verwendung gespeichert werden, damit das Reflexionsmodul auf hoher Ebene effektiv funktionieren kann. Dadurch kann das System frühere Schlussfolgerungen berücksichtigen und die Entscheidungsfindung auf der Grundlage der gesammelten Erfahrungen verbessern. Die Autoren des FinAgent-Frameworks haben dieser Anforderung Rechnung getragen und einen entsprechenden Speichermodulblock in die Architektur integriert.
Somit ergeben sich vier Ströme von Eingangsinformationen:
- Agentenaktionen;
- Zustand der Umgebung;
- Finanzielle Ergebnisse (Kontostand);
- Speicher.
In der derzeitigen Implementierung unserer Datenaustauschschnittstellen zwischen Objekten sind nur zwei Informationsströme zulässig.
Ähnlich wie das Low-Level-Reflection-Modul verwendet auch das High-Level-Reflection-Modul das Speichermodul als Elternklasse für das neue Objekt. Durch diese Konstruktion kann der Speicherstrom direkt im Objekt gebildet werden, sodass keine zusätzlichen Eingabequellen erforderlich sind. Dadurch wird das Modul autonomer und kann Daten innerhalb seiner eigenen Struktur effizient verarbeiten und speichern.
Darüber hinaus können die Ergebnisse des High-Level-Reflexionsmoduls wahrscheinlich als latente Darstellung des Handlungstensors des Agenten interpretiert werden. Dies liegt daran, dass die Aktionen des Agenten im Wesentlichen von den Ergebnissen abhängen, die unser Block liefert. Folglich haben Änderungen der Analyseergebnisse einen direkten Einfluss auf die Anpassung der Handlungen der Agenten. Dieser Ansatz ermöglicht die Erstellung eines adaptiven Verhaltensmodells, bei dem die Aktionen des Agenten auf der Grundlage von Informationen, die aus der Reflexion auf hoher Ebene gewonnen werden, dynamisch optimiert werden.
Auf der Grundlage der obigen Annahmen und architektonischen Entscheidungen konstruieren wir also ein grundlegendes Modell der Schnittstellen des neuen Objekts, das mit zwei Quellen von Eingangsdaten arbeitet. Diese liefern die notwendigen Informationen für die Analyse der aktuellen Bedingungen und der Ergebnisse früherer Maßnahmen.
Es gibt jedoch noch einen weiteren wichtigen Aspekt. Um die gesamte Verhaltenspolitik des Agenten zu analysieren, anstatt sich nur auf einzelne Handelsgeschäfte zu konzentrieren, schlagen die Autoren des FinAgent-Rahmens vor, eine Analyse der Kontostandstabelle mit Markierungen für ausgeführte Geschäfte einzubeziehen. Dieser Ansatz bietet eine verallgemeinerte Sicht auf die Strategieergebnisse des Agenten. In unserer Implementierung reproduzieren wir diese Lösung, indem wir rekurrente Objekte zur Verarbeitung von Tensoren hinzufügen, die den Kontostand und die Aktionen des Agenten darstellen. Diese Objekte modellieren die Beziehung zwischen Gleichgewichtsdynamik und Handelsaktionen und ermöglichen so eine tiefere Analyse der angewandten Politik.
Die oben beschriebenen Lösungen sind in dem neuen Objekt CNeuronHighLevelReflection implementiert. Die Struktur dieses Objekts ist unten dargestellt.
class CNeuronHighLevelReflection : public CNeuronMemory { protected: CNeuronBaseOCL cAccount; CNeuronLSTMOCL cHistoryAccount; CNeuronRelativeCrossAttention cActionReason; CNeuronLSTMOCL cHistoryActions; CNeuronRelativeCrossAttention cActionResult; //--- virtual bool feedForward(CNeuronBaseOCL *NeuronOCL) override { return false; } virtual bool feedForward(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput) override; virtual bool calcInputGradients(CNeuronBaseOCL *NeuronOCL) override { return false; } virtual bool calcInputGradients(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput, CBufferFloat *SecondGradient, ENUM_ACTIVATION SecondActivation = None) override; virtual bool updateInputWeights(CNeuronBaseOCL *NeuronOCL) override {return false; } virtual bool updateInputWeights(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput) override; public: CNeuronHighLevelReflection(void) {}; ~CNeuronHighLevelReflection(void) {}; //--- virtual bool Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint window, uint window_key, uint units_count, uint heads, uint desc_account, uint actions_state, ENUM_OPTIMIZATION optimization_type, uint batch) override; //--- virtual int Type(void) override const { return defNeuronHighLevelReflection; } //--- 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; };
Die Objektstruktur umfasst die bekannte Reihe überschreibbarer Methoden, die Flexibilität und Anpassungsfähigkeit in der Funktionalität bieten, ohne die gesamte Systemarchitektur zu stören. Darüber hinaus enthält die neue Klasse mehrere interne Objekte, deren Funktionalität bei der Implementierung der Methoden näher erläutert wird.
Alle internen Objekte werden als statisch deklariert, sodass wir den Konstruktor und Destruktor der Klasse leer lassen können. Die Initialisierung aller deklarierten und geerbten Objekte wird in der Methode Init durchgeführt.
bool CNeuronHighLevelReflection::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint window, uint window_key, uint units_count, uint heads, uint desc_account, uint actions_state, ENUM_OPTIMIZATION optimization_type, uint batch) { if(!CNeuronMemory::Init(numOutputs, myIndex, open_cl, 3, window_key, actions_state / 3, heads, optimization_type, batch)) return false;
Die Initialisierungsmethode erhält eine Reihe von Konstanten, die die Architektur des zu erstellenden Objekts definieren. Einer dieser Parameter gibt die Dimensionalität des Aktionsvektors des Agenten an.
Wie bereits erwähnt, wird erwartet, dass die Ausgabe dieses Objekts eine latente Darstellung des Handlungstensors des Agenten ergibt. Jede Handelsoperation des Agenten wird durch drei Parameter charakterisiert: Handelsvolumen, Stop-Loss-Niveau und Take-Profit-Niveau. Kauf- und Verkaufstransaktionen werden als separate Tensorzeilen dargestellt, sodass kein zusätzlicher Parameter zur Angabe der Handelsrichtung erforderlich ist.
Im Methodenkörper rufen wir die gleichnamige Methode der übergeordneten Klasse auf (in diesem Fall verwenden wir das Speichermodul). Zu den Parametern gehören die Daten des Aktionstensors des Agenten, die gemäß den zuvor beschriebenen Annahmen strukturiert sind. Dieser Ansatz wahrt die funktionale Kontinuität und nutzt die Fähigkeiten der übergeordneten Klasse für die Datenverarbeitung. So können Sie die Analyseergebnisse des aktuellen Objekts in die gesamte Modellstruktur integrieren und die Konsistenz und Zugänglichkeit für nachfolgende Objekte sicherstellen.
Nachdem wir die Operationen der übergeordneten Klasse erfolgreich ausgeführt haben, initialisieren wir die verschachtelten Objekte. Zunächst initialisieren wir eine grundlegende, vollständig verbundene Schicht, die dazu dient, den zweiten Informationsstrom korrekt zu verarbeiten.
int index = 0; if(!cAccount.Init(0, index, OpenCL, desc_account, optimization, iBatch)) return false;
Als Nächstes initialisieren wir den rekurrenten Block zur Verfolgung von Kontostandsänderungen.
index++; if(!cHistoryAccount.Init(0, index, OpenCL, desc_account, 1, optimization, iBatch)) return false;
Um die Gültigkeit der letzten Handelsentscheidung zu analysieren, verwenden wir einen Kreuzaufmerksamkeits-Block, um Abhängigkeiten zwischen den Aktionen des Agenten und dem Zustandstensor der Umgebung zu untersuchen.
index++; if(!cActionReason.Init(0, index, OpenCL, iWindow, iWindowKey, iUnits, iHeads, window, units_count, optimization, iBatch)) return false;
Die Handlungsdynamik wird in dem entsprechenden rekurrenten Block gesammelt.
index++; if(!cHistoryActions.Init(0, index, OpenCL, iWindow, iUnits, optimization, iBatch)) return false;
Anschließend bewerten wir die Wirksamkeit der Strategie des Agenten, indem wir die Dynamik der Handelsaktivitäten mit dem Kontostand innerhalb des Kreuzaufmerksamkeits-Blocks vergleichen.
index++; if(!cActionResult.Init(0, index, OpenCL, iWindow, iWindowKey, iUnits, iHeads, desc_account, 1, optimization, iBatch)) return false;; //--- return true; }
Nach der Initialisierung aller internen Objekte geben wir das logische Ergebnis der Operationen an das aufrufende Programm zurück und beenden die Ausführung der Methode.
Als Nächstes konstruieren wir den Algorithmus des Vorwärtsdurchlaufs für das High-Level-Reflexionsmodul innerhalb der Methode feedForward.
bool CNeuronHighLevelReflection::feedForward(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput) { if(!NeuronOCL || !SecondInput) return false;
Die Methode erhält Zeiger auf die Eingabeobjekte der beiden Informationsströme. Wir überprüfen sofort die Gültigkeit des Zeigers.
Es ist zu beachten, dass der zweite Informationsstrom durch einen Datenpuffer dargestellt wird. Vor weiteren Operationen verwenden wir den Zeiger, um den Puffer durch ein speziell vorbereitetes internes Objekt zu ersetzen. So können wir die Basisschnittstellen der internen Objekte nutzen, um die empfangenen Daten zu verarbeiten.
if(cAccount.getOutput() != SecondInput) { if(cAccount.Neurons() != SecondInput.Total()) if(!cAccount.Init(0, 0, OpenCL, SecondInput.Total(), optimization, iBatch)) return false; if(!cAccount.SetOutput(SecondInput, true)) return false; }
Anschließend bewerten wir die Änderungen des Kontostandes mit Hilfe des rekurrenten Blocks.
if(!cHistoryAccount.FeedForward(cAccount.AsObject())) return false;
Die Gültigkeit der letzten Handelsentscheidung wird mit Hilfe des Kreuzaufmerksamkeits-Blocks anhand des aktuellen Umweltzustands überprüft.
if(!cActionReason.FeedForward(this.AsObject(), NeuronOCL.getOutput())) return false;
Es ist wichtig, zwischen dem Tensor, der den aktuellen Umweltzustand beschreibt, und dem Tensor, der für die Entscheidungsfindung verwendet wird, zu unterscheiden. Das Modell erhält den Zustandstensor der Umgebung, nachdem die Aktion ausgeführt wurde und das System in den neuen Zustand übergegangen ist. Es ist jedoch nicht zu erwarten, dass dieser Unterschied die Analyseergebnisse wesentlich beeinflusst.
Jeder neue Balken löst eine neue Iteration des Modells aus. Die Tiefe der analysierten Geschichte geht deutlich über einen Balken hinaus und ermöglicht eine detaillierte und umfassende Analyse. Bei jedem Zustandswechsel verschieben sich die Daten um ein Element in der multimodalen Zeitreihe, wobei der früheste Balken ausgeschlossen wird. Es wird erwartet, dass dieser Balken nur minimale Auswirkungen auf die aktuelle Aktion hat, sodass seine Entfernung keine nennenswerten Auswirkungen auf die Analyse haben wird.
Die Aufnahme eines neuen Balkens, der zum Zeitpunkt der Entscheidung noch nicht bekannt war, liefert zusätzliche Informationen, um die Korrektheit des Handels zu beurteilen. Dieser Mechanismus hilft, sowohl die Folgen des Handels als auch die Übereinstimmung zwischen den tatsächlichen Marktveränderungen und den vorherigen Prognosen zu bewerten.
Die Analyseergebnisse werden an den wiederkehrenden Block zur Überwachung der Politik des Agenten weitergeleitet.
if(!cHistoryActions.FeedForward(cActionReason.AsObject())) return false;
Anschließend analysieren wir die Politik im Zusammenhang mit den Finanzergebnissen unter Verwendung des Kreuzaufmerksamkeits-Blocks.
if(!cActionResult.FeedForward(cHistoryActions.AsObject(), cHistoryAccount.getOutput())) return false;
Anschließend wird der Ergebnispuffer aktualisiert, um die letzten Aktionen des Agenten zu erhalten, sodass ein ordnungsgemäßer Rückwärtsdurchlauf möglich ist, und die Methode der übergeordneten Klasse wird aufgerufen, um die Funktionen des Speichermoduls auszuführen.
if(!SwapBuffers(Output, PrevOutput)) return false; //--- return CNeuronMemory::feedForward(cActionResult.AsObject()); }
Das logische Ergebnis dieser Operationen wird an das aufrufende Programm zurückgegeben, und die Methode ist beendet.
Nach Abschluss der Implementierung der Methoden des Vorwärtsdurchlaufs gehen wir zur Organisation der Rückwärtsdurchläufe über. Die Operationen der Rückwärtsdurchläufe verwenden lineare Algorithmen, die bei der Implementierung keine Schwierigkeiten bereiten sollten. Daher halte ich eine ausführliche Diskussion für überflüssig. Der vollständige Code für das High-Level-Reflection-Objekt und alle seine Methoden sind in den Anhängen verfügbar.
Wir sind am Ende dieses Artikels angelangt, aber unsere Arbeit im Zusammenhang mit der Implementierung des FinAgent-Rahmens ist noch nicht abgeschlossen. Lassen Sie uns eine kurze Pause einlegen, und im nächsten Artikel werden wir das Projekt zu einem logischen Abschluss bringen.
Schlussfolgerung
In diesem Artikel haben wir das FinAgent-Framework untersucht, eine innovative Lösung, die textliche und visuelle Informationen für eine umfassende Analyse von Marktdynamik und historischen Daten integriert. Mit fünf Schlüsselkomponenten bietet FinAgent eine hohe Genauigkeit und Anpassungsfähigkeit bei der Entscheidungsfindung im Handel. Dies macht sie zu einem vielversprechenden Instrument für die Entwicklung effektiver und flexibler Handelsstrategien, die auch unter volatilen Marktbedingungen funktionieren.
Im praktischen Teil haben wir unsere Version der beiden Reflexionsmodule mit MQL5 implementiert. Im nächsten Artikel werden wir diese Arbeit fortsetzen, indem wir den Rahmen vervollständigen und die Wirksamkeit der implementierten Lösungen an realen historischen Daten testen.
Referenzen
- A Multimodal Foundation Agent for Financial Trading: Tool-Augmented, Diversified, and Generalist
- Andere Artikel dieser Serie
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/16850
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.
Neuronale Netze im Handel: Ein multimodaler, werkzeuggestützter Agent für Finanzmärkte (letzter Teil)
Erstellung eines Indikators für die Volatilitätsprognose mit Python
Marktsimulation (Teil 04): Erstellen der Klasse C_Orders (I)
Neuronale Netze im Handel: Ein Agent mit geschichtetem Gedächtnis (letzter Teil)
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.
Es wäre toll, wenn Sie auch Artikel auf Englisch schreiben könnten.
In welcher Forumssprache haben Sie gepostet? Der Artikel ist auch auf Englisch verfügbar ... Neuronale Netze im Handel: Ein multimodaler, werkzeuggestützter Agent für Finanzmärkte (FinAgent)
Dies ist ein mehrsprachiges Diskussionsthema, eben weil der Artikel in mehrere Sprachen übersetzt ist.