
Verwendung von Indikatoren in MetaTrader 5 mit dem Machine Learning Framework ENCOG für die Prognostizierung von Zeitreihen
Einleitung
In diesem Beitrag wird MetaTrader 5 mit ENCOG vertraut gemacht, einem von Heaton Research entwickelten erweiterten neuronalen Netzwerk und Machine Learning Framework. Es existieren bereits beschriebene Methoden, von denen ich weiß, die es MetaTrader ermöglichen, mit Machine-Learning-Techniken zu arbeiten: FANN, NeuroSolutions, Matlab und NeuroShell. Ich hoffe, dass ENCOG eine gute ergänzende Lösung sein wird, da es ein robuster und gut konzipierter Code ist.
Warum habe ich mich für ENCOG entschieden? Es gibt mehrere Gründe.
- ENCOG wird in zwei weiteren Softwarepaketen für kommerziellen Handel verwendet. Eines basiert auf C#, das zweite auf Java. Das bedeutet, das ENCOG bereits für die Prognostizierung von Daten von finanziellen Zeitreihen getestet wurde.
- ENCOG ist kostenlos und Open Source. Wenn Sie sehen möchten, was im Inneren eines neuronalen Netzwerks geschieht, können Sie sich den Quellcode ansehen. Genau das habe ich getan, um bestimmte Teile der Aufgabe der Prognostizierung von Zeitreihen zu verstehen. C# ist eine saubere und leicht verständliche Programmiersprache.
- ENCOG ist sehr gut dokumentiert. Mr. Heaton, der Gründer von Heaton Research, bietet einen kostenlosen Online-Kurs zu neuronalen Netzwerken, maschinellem Lernen und der Verwendung von ENCOG zum Prognostizieren zukünftiger Daten an. Vor dem Verfassen dieses Beitrags habe ich mir viele seiner Lektionen angesehen. Sie haben mir sehr geholfen, künstliche neuronale Netzwerke zu verstehen. Zusätzlich stehen auf der Webseite von Heaton Research E-Books zur Programmierung von ENCOG in Java und C# zur Verfügung. Die vollständige ENCOG-Dokumentation ist online verfügbar.
- ENCOG ist kein totes Projekt. Während ich diesen Beitrag verfasse, wird ENCOG 2.6 noch entwickelt. Eine Roadmap für ENCOG 3.0 wurde kürzlich veröffentlicht.
- ENCOG ist robust. Es ist gut konzipiert und kann mehrere Prozessorkerne und Multithreading nutzen, um die Berechnungen eines neuronalen Netzwerks zu beschleunigen. Teile des Codes werden bereits nach OpenCL für GPU-fähige Berechnungen geportet.
- ENCOG unterstützt derzeit die folgenden Funktionen:
Methoden für maschinelles Lernen
- Feedforward und einfaches rekurrentes Netzwerk (Elman/Jordan)
- Genetische Algorithmen
- NEAT
- Probablistic Neural Network/ General Regression Neural Network (PNN/GRNN)
- Selbstorganisierende Karte (SOM/Kohonen)
- Simulated Annealing
- Support Vector Machine
Neuronale Netzwerkarchitekturen
Einlerntechniken
- ADALINE Neural Network
- Adaptive Resonanztheorie 1 (ART1)
- Bidirektionaler Assoziativspeicher (BAM)
- Boltzmann-Maschine
- Counterpropagation Neural Network (CPN)
- Elman'sches neuronales Netzwerk
- Feedforward Neural Network (Perceptron)
- Hopfield-Netz
- Jordan'sches rekurrentes neuronales Netzwerk
- Neuroevolution of Augmenting Topologies (NEAT)
- Radial Basis Function Network
- Rekurrente selbstorganisierende Karte (RSOM)
- Selbstorganisierende Karte (Kohonen)
Aktivierungsfunktionen
- Backpropagation
- Resilient Propagation (RPROP)
- Scaled Conjugate Gradient (SCG)
- Manhattan Update Rule Propagation
- Competitive Learning
- Hopfield Learning
- Levenberg-Marquardt Algorithm (LMA)
- Genetic Algorithm Training
- Instar Training
- Outstar Training
- ADALINE Training
- Training Data Models
- Supervised
- Unsupervised
- Temporal (Prediction)
- Financial (Download von Yahoo Finance)
- SQL
- XML
- CSV
- Image Downsampling
Randomisierungstechniken
- Competitive
- Sigmoid
- Hyperbolic Tangent
- Linear
- SoftMax
- Tangential
- Sin Wave
- Step
- Bipolar
- Gaussian
- Range Randomization
- Gaussian Random Numbers
- Fan-In
- Nguyen-Widrow
Geplante Funktionen:
- HyperNEAT
- Restrictive Boltzmann Machine (RBN/Deep Belief)
- Spiking Neural Networks
Sie sehen, dass die Liste der Funktionen ziemlich lang ist.
Dieser einführende Beitrag konzentriert sich auf die Feedforward-Architektur von neuronalen Netzwerken mit Einlernung durch Resilient Propagation (RPROP). Er behandelt auch die Grundlagen der Datenvorbereitung – Timeboxing und Normalisierung für die zeitliche Prognostizierung von Zeitreihen.
Das Wissen, das es mir möglich gemacht hat, diesen Beitrag zu verfassen, basiert auf Tutorials von der Webseite von Heaton Research und unlängst veröffentlichten Beiträgen zur Prognostizierung von finanziellen Zeitserien in NinjaTrader. Bitte beachten Sie, dass ENCOG auf Java und C# basiert. Dieser Beitrag könnte ohne meine vorhergehende Arbeit nicht verfasst werden: Offenlegen von C#-Code in MQL5 mithilfe nicht gemanagter Exporte. Diese Lösung ermöglichte die Nutzung von C#-DLLs als Brücke zwischen einem Indikator in MetaTrader 5 und der Zeitreihenprognostizierung von ENCOG.
1. Nutzung von Werten technischer Indikatoren als Eingabeparameter eines neuronalen Netzwerks
Das künstliche neuronale Netzwerk ist ein vom Menschen erschaffener Algorithmus, der versucht, das Neuronennetzwerk eines Gehirns zu emulieren.
Es sind unterschiedliche Typen von neuronalen Algorithmen verfügbar und es existiert eine Vielzahl von Architekturen für neuronale Netzwerke. Dieser Forschungsbereich ist so breit gefächert, dass es ganze Bücher gibt, die sich einem einzigen Typ von neuronalem Netzwerk widmen. Da solche Details die Grenzen dieses Beitrags sprengen würden, kann ich Ihnen nur empfehlen, die Tutorials von Heaton Research durchzugehen oder ein Buch zu diesem Thema zu lesen. Ich werde mich auf die Ein- und Ausgaben des neuronalen Feedforward-Netzwerks konzentrieren und versuchen, praktische Beispiele für die Prognostizierung von finanziellen Zeitreihen zu beschreiben.
Um mit dem Prognostizieren von finanziellen Zeitreihen zu beginnen, müssen wir uns überlegen, was wir einem neuronalen Netzwerk zur Verfügung stellen sollten und welche Ergebnisse wir erwarten können. Abstrahiert betrachtet, erzielen wir einen Gewinn oder Verlust, indem wir lange oder kurze Positionen für den Kontrakt eines bestimmten Wertpapiers eröffnen und das Geschäft nach bestimmter Zeit abschließen.
Durch die Beobachtung vergangener Preise eines Wertpapiers und der Werte der technischen Indikatoren versuchen wir, die zukünftige Stimmung oder Richtung der Preise zu prognostizieren, um einen Kontrakt zu kaufen oder zu verkaufen und sicherzustellen, dass unsere Entscheidung nicht auf einem Münzwurf basiert. Die Situation sieht in etwa so aus:
Abbildung 1. Prognose von finanziellen Zeitreihen mithilfe von technischen Indikatoren
Das Gleiche versuchen wir durch künstliche Intelligenz zu erreichen. Das neuronale Netzwerk versucht, Indikatorwerte zu erkennen, und bewertet die Chance, dass der Preis steigen oder sinken wird. Wie schaffen wir das? Da wir finanzielle Zeitreihen mithilfe der Feedforward-Architektur für neuronale Netzwerke prognostizieren werden, glaube ich, dass wir uns mit dieser Architektur vertraut machen müssen.
Das neuronale Feedforward-Netzwerk besteht aus Neuronen, die in Schichten gruppiert sind. Es muss mindestens 2 Schichten geben: eine Eingabeschicht, die Eingabeneuronen enthält, und eine Ausgabeschicht, die Ausgabeneuronen enthält. Es kann auch versteckte Schichten geben, die sich zwischen der Eingabe- und Ausgabeschicht befinden. Die Eingabeschicht können Sie sich einfach als Array von double-Werten vorstellen. Die Ausgabeschicht kann aus einem oder mehreren Neuronen bestehen, die ebenfalls ein Array aus double-Werten bilden. Sehen wir uns eine Abbildung an:
Abbildung 2. Schichten eines neuronalen Feedforward-Netzwerks
Um die Zeichnung zu vereinfachen, wurden die Verbindungen zwischen den Neuronen nicht eingezeichnet. Jedes Neuron aus der Eingabeschicht ist mit einem Neuron in der verborgenen Schicht verbunden. Jedes Neuron aus der verborgenen Schicht ist mit einem Neuron in der Ausgabeschicht verbunden.
Jede Verbindung hat ein gewisses Gesicht, das ebenfalls einen double-Wert und eine Aktivierungsfunktion mit einem Schwellenwert darstellt, der für die Aktivierung eines Neurons und die Übergabe der Informationen an das nächste Neuron zuständig ist. Deshalb wird diese Art von Netzwerk als 'Feedforward'-Netzwerk bezeichnet – Informationen auf Basis der Ausgabe aktivierter Neuronen werden von einer Neuronenschicht zur anderen vorgeschoben (engl. "fed forward"). Detaillierte Einführungsvideos zu neuronalen Feedforward-Netzwerken finden Sie unter den folgenden Links [Anm. d. Übers.: Zum Zeitpunkt der Übersetzung sind diese Seiten nicht erreichbar]:
- Neural Network Calculation (Part 1): Feedforward Structure
- Neural Network Calculation (Part 2): Activation Functions & Basic Calculation
- Neural Network Calculation (Part 3): Feedforward Neural Network Calculation
Nachdem Sie mehr über die Architektur des neuronalen Netzwerks und dessen Mechanismen erfahren haben, sind sie womöglich immer noch verwirrt.
Die Hauptprobleme sind:
- Welche Daten sollen wir an ein neuronales Netzwerk übergeben?
- Wie übergeben wir sie?
- Wie werden Eingabedaten für ein neuronales Netzwerk vorbereitet?
- Wie wird eine Architektur für ein neuronales Netzwerk ausgewählt? Wie viele Eingabeneuronen, verborgene Neuronen und Ausgabeneuronen brauchen wir?
- Wie wird das Netzwerk eingelernt?
- Welches Ergebnis erwarten wird?
2. Welche Daten an ein neuronales Netzwerk übergeben werden müssen
Da wir finanzielle Prognosen auf Basis von Indikatorausgaben behandeln, sollten wir die Ausgabewerte des Indikators an das Netzwerk übergeben. Für diesen Beitrag habe ich als Eingabeparameter Stochastic %K, Stochastic Slow %D und Williams %R gewählt.
Abbildung 3. Für die Prognose verwendete technische Indikatoren
Zum Extrahieren der Werte der Indikatoren können wir die MQL5-Funktionen iStochastic und iWPR nutzen:
double StochKArr[], StochDArr[], WilliamsRArr[]; ArraySetAsSeries(StochKArr, true); ArraySetAsSeries(StochDArr, true); ArraySetAsSeries(WilliamsRArr, true); int hStochastic = iStochastic(Symbol(), Period(), 8, 5, 5, MODE_EMA, STO_LOWHIGH); int hWilliamsR = iWPR(Symbol(), Period(), 21); CopyBuffer(hStochastic, 0, 0, bufSize, StochKArr); CopyBuffer(hStochastic, 1, 0, bufSize, StochDArr); CopyBuffer(hWilliamsR, 0, 0, bufSize, WilliamsRArr);
Nach der Ausführung dieses Codes müssen die drei Arrays StochKArr, StochDArr und WilliamsRArr mit Ausgabewerten von Indikatoren befüllt werden. Je nach Größe der für das Einlernen genutzten Stichprobe können dies mehrere tausend Werte sein. Bitte denken Sie daran, dass diese zwei Indikatoren nur zu Lernzwecken gewählt wurden.
Sie können gerne mit jedem beliebigen Indikator experimentieren, den Sie als für die Prognostizierung geeignet betrachten. Sie können Gold- und Ölpreise an das Netzwerk übergeben, um Aktienindizes zu prognostizieren, oder Sie können korrelierende Forex-Paare nutzen, um ein anderes Währungspaar zu prognostizieren.
3. Timeboxing-Eingabedaten
Nachdem wir Eingabedaten von mehreren Indikatoren gesammelt haben, müssen wir die Eingabe vorbereiten ('timeboxen'), bevor wir sie an das neuronale Netzwerk übergeben. Timeboxing ist eine Technik, die die Darstellung von Eingabeparametern für das Netzwerk als bewegliche Datenstücke ermöglicht. Stellen Sie sich einen Wagen voller Eingabedaten vor, der sich auf der Zeitachse vorwärts bewegt. Im Wesentlichen besteht dieser Vorgang aus zwei Schritten:
1. Sammeln von Eingabedaten aus jedem Indikatorpuffer. Wir müssen INPUT_WINDOW Elemente von der Startposition zur Zukunft hin kopieren. INPUT_WINDOW steht für die Anzahl der Balken, die für die Prognose verwendet werden.
Abbildung 4. Sammeln von Eingabefenster-Daten aus dem Indikatorpuffer
Wie Sie im oben aufgeführten Beispiel sehen können, entspricht INPUT_WINDOW 4 Balken, und wir haben Elemente in das Array I1 kopiert. I1[0] ist das erste Element, I1[3] das letzte. Genauso müssen Daten aus anderen Indikatoren in Arrays der Größe INPUT_WINDOW kopiert werden. Diese Abbildung gilt für Zeitreihen-Arrays mit AS_SERIES-Flag auf true.
2. Kombinieren von INPUT_WINDOW Arrays in einem Array, das an die Eingabeschicht des neuronalen Netzwerks übergeben wird.
Abbildung 5. Timeboxed input window arrays
Wir verfügen über 3 Indikatoren. Zuerst nehmen wir den ersten Wert jedes Indikators, dann den zweiten und fahren so fort, bis das Eingabefenster befüllt ist, wie in der oberen Abbildung dargestellt. Ein solches kombiniertes Array aus Indikatorausgaben kann an die Eingabeschicht unseres neuronalen Netzwerks übergeben werden. Wenn ein neuer Balken erscheint, verschieben sich die Daten um ein Element und das gesamte Verfahren wird wiederholt. Wenn Sie einen detaillierteren Einblick in die Vorbereitung von Daten für die Prognostizierung erhalten möchten, können Sie sich zu diesem Thema ein Video ansehen.
4. Normalisieren von Eingabedaten
Damit das neuronale Netzwerk effektiv arbeiten kann, müssen wir die Daten normalisieren. Dies ist für die korrekte Berechnung der Aktivierungsfunktionen erforderlich. Die Normalisierung ist ein mathematischer Prozess, der Daten in den Bereich 0..1 oder -1..1 konvertiert. Normalisierte Daten können denormalisiert, das heißt, in ihren ursprünglichen Bereich zurückkonvertiert werden.
Die Denormalisierung ist erforderlich, um die Ausgabe des neuronalen Netzwerks in eine menschlich lesbare Form zu dekodieren. Glücklicherweise übernimmt ENCOG die Normalisierung und Denormalisierung, deshalb müssen wir sie nicht implementieren. Wenn Sie wissen möchten, wie es funktioniert, können Sie den folgenden Code analysieren:
/** * Normalize the specified value. * @param value The value to normalize. * @return The normalized value. */ public static double normalize(final int value) { return ((value - INPUT_LOW) / (INPUT_HIGH - INPUT_LOW)) * (OUTPUT_HIGH - OUTPUT_LOW) + OUTPUT_LOW; } /** * De-normalize the specified value. * @param value The value to denormalize. * @return The denormalized value. */ public static double deNormalize(final double data) { double result = ((INPUT_LOW - INPUT_HIGH) * data - OUTPUT_HIGH * INPUT_LOW + INPUT_HIGH * OUTPUT_LOW) / (OUTPUT_LOW - OUTPUT_HIGH); return result; }
und für weitere Details einen Artikel zur Normalisierung lesen.
5. Auswahl der Netzwerkarchitektur und der Anzahl von Neuronen
Für einen Neueinsteiger ist die Auswahl der korrekten Netzwerkarchitektur schwierig. In diesem Beitrag grenze ich die Architektur des neuronalen Feedforward-Netzwerks auf drei Schichten ein: eine Eingabeschicht, eine verborgene Schicht und eine Ausgabeschicht. Sie können gerne mit mehr Schichten experimentieren.
Für die Ein- und Ausgabeschicht können wir die erforderliche Anzahl von Neuronen genau zählen. Für die verborgene Schicht versuchen wir, den Fehler des neuronalen Netzwerks mithilfe eines "forward selection"-Algorithmus zu verringern. Sie können gerne andere Methoden nutzen. Möglicherweise gibt es genetische Algorithmen für die Berechnung der Anzahl von Neuronen.
Eine weitere von ENCOG genutzte Methode ist der Algorithmus backward selection oder Pruning. Im Wesentlichen bewertet sie die Verbindungen zwischen den Schichten und entfernt verborgene Neuronen mit null gewichteten Verbindungen. Auch diese Methode können Sie gerne ausprobieren.
5,1. Eingabe-Neuronenschicht
Aufgrund des Timeboxings sollte die Anzahl der Neuronen in der Eingabeschicht der Anzahl von Indikatoren mal der Anzahl der für die Prognostizierung des nächsten Balkens genutzten Balken entsprechen. Wenn wir 3 Indikatoren als Eingaben nutzen und die Größe des Eingabefensters 6 Balken beträgt, wird die Eingabeschicht aus 18 Neuronen bestehen. An die Eingabeschicht werden die per Timeboxing vorbereiteten Daten übergeben.
5,2. Verborgene Neuronenschicht
Die Anzahl der verborgenen Netzwerke muss auf Basis der Performance des eingelernten neuronalen Netzwerks geschätzt werden. Für die Anzahl der verborgenen Neuronen gibt es keine geradlinige mathematische Gleichung. Vor dem Verfassen dieses Beitrags habe ich es mit mehreren Trial-and-Error-Ansätzen versucht und auf der Webseite von Heaton Research einen Algorithmus gefunden, der zum Verständnis des forward-selection-Algorithmus beiträgt:
Abbildung 6. Forward-selection-Algorithmus für die Anzahl der verborgenen Neuronen
5,3. Ausgabe-Neuronenschicht
Für unsere Zwecke ist die Anzahl der Ausgabeneuronen gleich der Anzahl der Balken, die wir prognostizieren möchten. Bitte denken Sie daran, dass es mit zunehmender Anzahl verborgener und Ausgabeneuronen länger dauert, das Netzwerk einzulernen. In diesem Beitrag versuche ich, einen Balken in der Zukunft zu prognostizieren, deshalb besteht die Ausgabeschicht aus einem Neuron.
6. Exportieren von Einlerndaten aus MetaTrader 5 nach ENCOG
Encog akzeptiert eine CSV-Datei für das Einlernen des neuronalen Netzwerks.
Ich habe das Dateiformat angesehen, das aus anderer Handelssoftware nach ENCOG exportiert wird, und ein MQL5-Script implementiert, das für das Einlernen das gleiche Dateiformat erstellt. Ich lege zuerst den Export von einem Indikator dar und fahre später mit mehreren Indikatoren fort.
Die erste Datenzeile ist eine kommagetrennte Kopfzeile:
DATE,TIME,CLOSE,Indicator_Name1,Indicator_Name2,Indicator_Name3
Die ersten drei Spalten beinhalten Datum, Zeit und Schließungswerte, die nächsten Spalten beinhalten die Indikatornamen. Die nächsten Zeilen der Einlerndatei enthalten kommagetrennte Daten. Indikatorwerte müssen im wissenschaftlichen Format geschrieben sein:
20110103,0000,0.93377000,-7.8970208860e-002
Nachfolgend sehen Sie das vorbereitete Script für einen Indikator.
//+------------------------------------------------------------------+ //| ExportToEncog.mq5 | //| Copyright 2011, Investeo.pl | //| http:/Investeo.pl | //+------------------------------------------------------------------+ #property copyright "Copyright 2011, Investeo.pl" #property link "http:/Investeo.pl" #property version "1.00" //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ // Export Indicator values for NN training by ENCOG extern string IndExportFileName = "mt5export.csv"; extern int trainSize = 400; extern int maPeriod = 210; MqlRates srcArr[]; double expBullsArr[]; void OnStart() { //--- ArraySetAsSeries(srcArr, true); ArraySetAsSeries(expBullsArr, true); int copied = CopyRates(Symbol(), Period(), 0, trainSize, srcArr); if (copied!=trainSize) { Print("Not enough data for " + Symbol()); return; } int hBullsPower = iBullsPower(Symbol(), Period(), maPeriod); CopyBuffer(hBullsPower, 0, 0, trainSize, expBullsArr); int hFile = FileOpen(IndExportFileName, FILE_CSV | FILE_ANSI | FILE_WRITE | FILE_REWRITE, ",", CP_ACP); FileWriteString(hFile, "DATE,TIME,CLOSE,BullsPower\n"); Print("Exporting indicator data to " + IndExportFileName); for (int i=trainSize-1; i>=0; i--) { string candleDate = TimeToString(srcArr[i].time, TIME_DATE); StringReplace(candleDate,".",""); string candleTime = TimeToString(srcArr[i].time, TIME_MINUTES); StringReplace(candleTime,":",""); FileWrite(hFile, candleDate, candleTime, DoubleToString(srcArr[i].close), DoubleToString(expBullsArr[i], -10)); } FileClose(hFile); Print("Indicator data exported."); } //+------------------------------------------------------------------+
Die resultierende Datei, die für Einlernzwecke verwendet werden kann, sollte folgendermaßen aussehen:
DATE,TIME,CLOSE,BullsPower 20110103,0000,0.93377000,-7.8970208860e-002 20110104,0000,0.94780000,-6.4962292188e-002 20110105,0000,0.96571000,-4.7640374727e-002 20110106,0000,0.96527000,-4.4878854587e-002 20110107,0000,0.96697000,-4.6178012364e-002 20110110,0000,0.96772000,-4.2078647318e-002 20110111,0000,0.97359000,-3.6029181466e-002 20110112,0000,0.96645000,-3.8335729509e-002 20110113,0000,0.96416000,-3.7054869514e-002 20110114,0000,0.96320000,-4.4259373120e-002 20110117,0000,0.96503000,-4.4835729773e-002 20110118,0000,0.96340000,-4.6420936126e-002 20110119,0000,0.95585000,-4.6868984125e-002 20110120,0000,0.96723000,-4.2709941621e-002 20110121,0000,0.95810000,-4.1918330800e-002 20110124,0000,0.94873000,-4.7722659418e-002 20110125,0000,0.94230000,-5.7111591557e-002 20110126,0000,0.94282000,-6.2231529077e-002 20110127,0000,0.94603000,-5.9997865295e-002 20110128,0000,0.94165000,-6.0378312069e-002 20110131,0000,0.94414000,-6.2038328069e-002 20110201,0000,0.93531000,-6.0710334438e-002 20110202,0000,0.94034000,-6.1446445012e-002 20110203,0000,0.94586000,-5.2580791504e-002 20110204,0000,0.95496000,-4.5246755566e-002 20110207,0000,0.95730000,-4.4439392954e-002
Wenn wir zum ursprünglichen Beispiel des Beitrags mit den Indikatoren Stochastic und Williams' R zurückblicken, müssen wir drei kommagetrennte Spalten exportieren. Jede Spalte beinhaltet separate Indikatorwerte, deshalb müssen wir die Datei erweitern und zusätzliche Puffer hinzufügen:
//+------------------------------------------------------------------+ //| ExportToEncog.mq5 | //| Copyright 2011, Investeo.pl | //| http:/Investeo.pl | //+------------------------------------------------------------------+ #property copyright "Copyright 2011, Investeo.pl" #property link "http:/Investeo.pl" #property version "1.00" //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ // Export Indicator values for NN training by ENCOG extern string IndExportFileName = "mt5export.csv"; extern int trainSize = 2000; MqlRates srcArr[]; double StochKArr[], StochDArr[], WilliamsRArr[]; void OnStart() { //--- ArraySetAsSeries(srcArr, true); ArraySetAsSeries(StochKArr, true); ArraySetAsSeries(StochDArr, true); ArraySetAsSeries(WilliamsRArr, true); int copied = CopyRates(Symbol(), Period(), 0, trainSize, srcArr); if (copied!=trainSize) { Print("Not enough data for " + Symbol()); return; } int hStochastic = iStochastic(Symbol(), Period(), 8, 5, 5, MODE_EMA, STO_LOWHIGH); int hWilliamsR = iWPR(Symbol(), Period(), 21); CopyBuffer(hStochastic, 0, 0, trainSize, StochKArr); CopyBuffer(hStochastic, 1, 0, trainSize, StochDArr); CopyBuffer(hWilliamsR, 0, 0, trainSize, WilliamsRArr); int hFile = FileOpen(IndExportFileName, FILE_CSV | FILE_ANSI | FILE_WRITE | FILE_REWRITE, ",", CP_ACP); FileWriteString(hFile, "DATE,TIME,CLOSE,StochK,StochD,WilliamsR\n"); Print("Exporting indicator data to " + IndExportFileName); for (int i=trainSize-1; i>=0; i--) { string candleDate = TimeToString(srcArr[i].time, TIME_DATE); StringReplace(candleDate,".",""); string candleTime = TimeToString(srcArr[i].time, TIME_MINUTES); StringReplace(candleTime,":",""); FileWrite(hFile, candleDate, candleTime, DoubleToString(srcArr[i].close), DoubleToString(StochKArr[i], -10), DoubleToString(StochDArr[i], -10), DoubleToString(WilliamsRArr[i], -10) ); } FileClose(hFile); Print("Indicator data exported."); } //+------------------------------------------------------------------+
Die resultierende Datei muss alle Indikatorwerte enthalten:
DATE,TIME,CLOSE,StochK,StochD,WilliamsR 20030707,0000,1.37370000,7.1743119266e+001,7.2390220187e+001,-6.2189054726e-001 20030708,0000,1.36870000,7.5140977444e+001,7.3307139273e+001,-1.2500000000e+001 20030709,0000,1.35990000,7.3831775701e+001,7.3482018082e+001,-2.2780373832e+001 20030710,0000,1.36100000,7.1421933086e+001,7.2795323083e+001,-2.1495327103e+001 20030711,0000,1.37600000,7.5398313027e+001,7.3662986398e+001,-3.9719626168e+000 20030714,0000,1.37370000,7.0955352856e+001,7.2760441884e+001,-9.6153846154e+000 20030715,0000,1.38560000,7.4975891996e+001,7.3498925255e+001,-2.3890784983e+000 20030716,0000,1.37530000,7.5354107649e+001,7.4117319386e+001,-2.2322435175e+001 20030717,0000,1.36960000,7.1775345074e+001,7.3336661282e+001,-3.0429594272e+001 20030718,0000,1.36280000,5.8474576271e+001,6.8382632945e+001,-3.9778325123e+001 20030721,0000,1.35400000,4.3498596819e+001,6.0087954237e+001,-5.4946524064e+001 20030722,0000,1.36130000,2.9036761284e+001,4.9737556586e+001,-4.5187165775e+001 20030723,0000,1.34640000,1.6979405034e+001,3.8818172735e+001,-6.5989159892e+001 20030724,0000,1.34680000,1.0634573304e+001,2.9423639592e+001,-7.1555555556e+001 20030725,0000,1.34400000,9.0909090909e+000,2.2646062758e+001,-8.7500000000e+001 20030728,0000,1.34680000,1.2264922322e+001,1.9185682613e+001,-8.2705479452e+001 20030729,0000,1.35250000,1.4960629921e+001,1.7777331716e+001,-7.2945205479e+001 20030730,0000,1.36390000,2.7553336360e+001,2.1035999930e+001,-5.3979238754e+001 20030731,0000,1.36990000,4.3307839388e+001,2.8459946416e+001,-4.3598615917e+001 20030801,0000,1.36460000,5.6996412096e+001,3.7972101643e+001,-5.2768166090e+001 20030804,0000,1.34780000,5.7070193286e+001,4.4338132191e+001,-8.1833910035e+001 20030805,0000,1.34770000,5.3512705531e+001,4.7396323304e+001,-8.2006920415e+001 20030806,0000,1.35350000,4.4481132075e+001,4.6424592894e+001,-7.1972318339e+001 20030807,0000,1.35020000,3.3740028156e+001,4.2196404648e+001,-7.7681660900e+001 20030808,0000,1.35970000,3.0395426394e+001,3.8262745230e+001,-6.1245674740e+001 20030811,0000,1.35780000,3.4155781326e+001,3.6893757262e+001,-6.4532871972e+001 20030812,0000,1.36880000,4.3488943489e+001,3.9092152671e+001,-4.5501730104e+001 20030813,0000,1.36690000,5.1160443996e+001,4.3114916446e+001,-4.8788927336e+001 20030814,0000,1.36980000,6.2467599793e+001,4.9565810895e+001,-2.5629290618e+001 20030815,0000,1.37150000,6.9668246445e+001,5.6266622745e+001,-2.1739130435e+001 20030818,0000,1.38910000,7.9908906883e+001,6.4147384124e+001,-9.2819614711e+000
Sie können das zweite Beispiel modifizieren, um einfach an ein Script zu gelangen, das Ihre Anforderungen erfüllt.
7. Einlernen des neuronalen Netzwerks
Die Einlernung des Netzwerks wurde bereits durch Heaton Research in C# vorbereitet. ENCOG 2.6 implementiert den Namensraum Encog.App.Quant, der eine Basis für die Prognostizierung von finanziellen Zeitreihen bildet. Das Einlernscript ist äußerst flexibel und lässt sich einfach an jede beliebige Anzahl von Eingabeindikatoren anpassen. Sie müssen nur in der Konstante DIRECTORY das Verzeichnis von MetaTrader 5 ändern.
Die Netzwerkarchitektur und Einlernparameter lassen sich durch eine Änderung der folgenden Variablen einfach anpassen:
/// <summary> /// The size of the input window. This is the number of bars used to predict the next bar. /// </summary> public const int INPUT_WINDOW = 6; /// <summary> /// The number of bars forward we are trying to predict. This is usually just 1 bar. The future indicator used in step 1 may /// well look more forward into the future. /// </summary> public const int PREDICT_WINDOW = 1; /// <summary> /// The number of bars forward to look for the best result. /// </summary> public const int RESULT_WINDOW = 5; /// <summary> /// The number of neurons in the first hidden layer. /// </summary> public const int HIDDEN1_NEURONS = 12; /// <summary> /// The target error to train to. /// </summary> public const double TARGET_ERROR = 0.01;
Der Code selbst ist selbsterklärend, deshalb ist es am besten, ihn sorgfältig durchzulesen:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Encog.App.Quant.Normalize; using Encog.Util.CSV; using Encog.App.Quant.Indicators; using Encog.App.Quant.Indicators.Predictive; using Encog.App.Quant.Temporal; using Encog.Neural.NeuralData; using Encog.Neural.Data.Basic; using Encog.Util.Simple; using Encog.Neural.Networks; using Encog.Neural.Networks.Layers; using Encog.Engine.Network.Activation; using Encog.Persist; namespace NetworkTrainer { public class Program { /// <summary> /// The directory that all of the files will be stored in. /// </summary> public const String DIRECTORY = "d:\\mt5\\MQL5\\Files\\"; /// <summary> /// The input file that starts the whole process. This file should be downloaded from NinjaTrader using the EncogStreamWriter object. /// </summary> public const String STEP1_FILENAME = DIRECTORY + "mt5export.csv"; /// <summary> /// We apply a predictive future indicator and generate a second file, with the additional predictive field added. /// </summary> public const String STEP2_FILENAME = DIRECTORY + "step2_future.csv"; /// <summary> /// Next the entire file is normalized and stored into this file. /// </summary> public const String STEP3_FILENAME = DIRECTORY + "step3_norm.csv"; /// <summary> /// The file is time-boxed to create training data. /// </summary> public const String STEP4_FILENAME = DIRECTORY + "step4_train.csv"; /// <summary> /// Finally, the trained neural network is written to this file. /// </summary> public const String STEP5_FILENAME = DIRECTORY + "step5_network.eg"; /// <summary> /// The size of the input window. This is the number of bars used to predict the next bar. /// </summary> public const int INPUT_WINDOW = 6; /// <summary> /// The number of bars forward we are trying to predict. This is usually just 1 bar. The future indicator used in step 1 may /// well look more forward into the future. /// </summary> public const int PREDICT_WINDOW = 1; /// <summary> /// The number of bars forward to look for the best result. /// </summary> public const int RESULT_WINDOW = 5; /// <summary> /// The number of neurons in the first hidden layer. /// </summary> public const int HIDDEN1_NEURONS = 12; /// <summary> /// The target error to train to. /// </summary> public const double TARGET_ERROR = 0.01; static void Main(string[] args) { // Step 1: Create future indicators Console.WriteLine("Step 1: Analyze MT5 Export & Create Future Indicators"); ProcessIndicators ind = new ProcessIndicators(); ind.Analyze(STEP1_FILENAME, true, CSVFormat.DECIMAL_POINT); int externalIndicatorCount = ind.Columns.Count - 3; ind.AddColumn(new BestReturn(RESULT_WINDOW,true)); ind.Process(STEP2_FILENAME); Console.WriteLine("External indicators found: " + externalIndicatorCount); //Console.ReadKey(); // Step 2: Normalize Console.WriteLine("Step 2: Create Future Indicators"); EncogNormalize norm = new EncogNormalize(); norm.Analyze(STEP2_FILENAME, true, CSVFormat.ENGLISH); norm.Stats[0].Action = NormalizationDesired.PassThrough; // Date norm.Stats[1].Action = NormalizationDesired.PassThrough; // Time norm.Stats[2].Action = NormalizationDesired.Normalize; // Close norm.Stats[3].Action = NormalizationDesired.Normalize; // Stoch K norm.Stats[4].Action = NormalizationDesired.Normalize; // Stoch Dd norm.Stats[5].Action = NormalizationDesired.Normalize; // WilliamsR norm.Stats[6].Action = NormalizationDesired.Normalize; // best return [RESULT_WINDOW] norm.Normalize(STEP3_FILENAME); // neuron counts int inputNeurons = INPUT_WINDOW * externalIndicatorCount; int outputNeurons = PREDICT_WINDOW; // Step 3: Time-box Console.WriteLine("Step 3: Timebox"); //Console.ReadKey(); TemporalWindow window = new TemporalWindow(); window.Analyze(STEP3_FILENAME, true, CSVFormat.ENGLISH); window.InputWindow = INPUT_WINDOW; window.PredictWindow = PREDICT_WINDOW; int index = 0; window.Fields[index++].Action = TemporalType.Ignore; // date window.Fields[index++].Action = TemporalType.Ignore; // time window.Fields[index++].Action = TemporalType.Ignore; // close for(int i=0;i<externalIndicatorCount;i++) window.Fields[index++].Action = TemporalType.Input; // external indicators window.Fields[index++].Action = TemporalType.Predict; // PredictBestReturn window.Process(STEP4_FILENAME); // Step 4: Train neural network Console.WriteLine("Step 4: Train"); Console.ReadKey(); INeuralDataSet training = (BasicNeuralDataSet)EncogUtility.LoadCSV2Memory(STEP4_FILENAME, inputNeurons, outputNeurons, true, CSVFormat.ENGLISH); BasicNetwork network = new BasicNetwork(); network.AddLayer(new BasicLayer(new ActivationTANH(), true, inputNeurons)); network.AddLayer(new BasicLayer(new ActivationTANH(), true, HIDDEN1_NEURONS)); network.AddLayer(new BasicLayer(new ActivationLinear(), true, outputNeurons)); network.Structure.FinalizeStructure(); network.Reset(); //EncogUtility.TrainToError(network, training, TARGET_ERROR); EncogUtility.TrainConsole(network, training, 3); // Step 5: Save neural network and stats EncogMemoryCollection encog = new EncogMemoryCollection(); encog.Add("network", network); encog.Add("stat", norm.Stats); encog.Save(STEP5_FILENAME); Console.ReadKey(); } } }
Ihnen könnte aufgefallen sein, dass ich eine Zeile auskommentiert und die Einlernfunktion von EncogUtility.TrainToError() in EncogUtility.TrainConsole() geändert habe.
EncogUtility.TrainConsole(network, training, 3);
Die Methode TrainConsole legt eine Anzahl von Minuten zum Einlernen des Netzwerks fest. Im aufgeführten Beispiel lerne ich das Netzwerk drei Minuten lang ein. Abhängig von der Komplexität des Netzwerks und der Größe der Einlerndaten kann das Einlernen des Netzwerks einige Minuten, Stunden oder sogar Tage dauern. Ich empfehle, auf der Webseite von Heaton Research oder in einem beliebigen sonstigen Buch zu diesem Thema mehr über die Fehlerberechnung und Einlernalgorithmen zu lesen.
Die Methode EncogUtility.TrainToError() beendet das Einlernen des Netzwerks, nachdem ein festgelegter Netzwerkfehler erzielt wurde. Sie können EncogUtility.TrainConsole() kommentieren und den Kommentar bei EncogUtility.TrainToError() entfernen, um das Netzwerk bis zu einem gewünschten Fehler einzulernen, wie es im ursprünglichen Beispiel der Fall ist.
EncogUtility.TrainToError(network, training, TARGET_ERROR);
Bitte beachten Sie, dass das Netzwerk manchmal nicht bis zu einem bestimmten Fehler eingelernt werden kann, weil die Anzahl der Neuronen möglicherweise zu klein ist.
8. Verwendung des eingelernten neuronalen Netzwerks für die Erstellung eines neuronalen Indikators in MetaTrader 5
Das eingelernte Netzwerk kann von einem neuronalen Netzwerkindikator genutzt werden, der versuchen wird, die beste Rendite zu prognostizieren.
Der neuronale Indikator ENCOG für MetaTrader 5 besteht aus zwei Teilen. Ein Teil ist in MQL5 geschrieben und nutzt grundsätzlich die gleichen Indikatoren wie jene, mit denen das Netzwerk eingelernt wurde, und übergibt Indikatorwerte des Eingabefensters an das Netzwerk. Der zweite Teil ist in C# geschrieben und timeboxt Eingabedaten und übermittelt die Ausgabe des neuronalen Netzwerks an MQL5. Der C#-Teil des Indikators basiert auf meinem vorherigen Artikel Offenlegen von C#-Code in MQL5.
using System; using System.Collections.Generic; using System.Text; using RGiesecke.DllExport; using System.Runtime.InteropServices; using Encog.Neural.Networks; using Encog.Persist; using Encog.App.Quant.Normalize; using Encog.Neural.Data; using Encog.Neural.Data.Basic; namespace EncogNeuralIndicatorMT5DLL { public class NeuralNET { private EncogMemoryCollection encog; public BasicNetwork network; public NormalizationStats stats; public NeuralNET(string nnPath) { initializeNN(nnPath); } public void initializeNN(string nnPath) { try { encog = new EncogMemoryCollection(); encog.Load(nnPath); network = (BasicNetwork)encog.Find("network"); stats = (NormalizationStats)encog.Find("stat"); } catch (Exception e) { Console.WriteLine(e.StackTrace); } } }; class UnmanagedExports { static NeuralNET neuralnet; [DllExport("initializeTrainedNN", CallingConvention = CallingConvention.StdCall)] static int initializeTrainedNN([MarshalAs(UnmanagedType.LPWStr)]string nnPath) { neuralnet = new NeuralNET(nnPath); if (neuralnet.network != null) return 0; else return -1; } [DllExport("computeNNIndicator", CallingConvention = CallingConvention.StdCall)] public static int computeNNIndicator([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] double[] t1, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] double[] t2, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] double[] t3, int len, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 5)] double[] result, int rates_total) { INeuralData input = new BasicNeuralData(3 * len); int index = 0; for (int i = 0; i <len; i++) { input[index++] = neuralnet.stats[3].Normalize(t1[i]); input[index++] = neuralnet.stats[4].Normalize(t2[i]); input[index++] = neuralnet.stats[5].Normalize(t3[i]); } INeuralData output = neuralnet.network.Compute(input); double d = output[0]; d = neuralnet.stats[6].DeNormalize(d); result[rates_total-1]=d; return 0; } } }
Wenn Sie mehr oder weniger als drei Indikatoren nutzen möchten, müssen Sie die Methode computeNNIndicator() an Ihre Anforderungen anpassen.
[DllExport("computeNNIndicator", CallingConvention = CallingConvention.StdCall)] public static int computeNNIndicator([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] double[] t1, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] double[] t2, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] double[] t3, int len, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 5)] double[] result, int rates_total)
In diesem Fall sind die ersten drei Eingabeparameter Tabellen, die Eingabewerte des Indikators enthalten, der vierte Parameter ist die Länge des Eingabefensters.
Der Wert SizeParamIndex = 3 deutet auf die Variable der Länge des Eingabefensters, da der Zähler der Eingabevariablen ab 0 steigt. Der fünfte Parameter ist eine Tabelle, die die Ergebnisse des neuronalen Netzwerks enthält.
Der MQL5-Teil des Indikators muss die C#-Bibliothek EncogNNTrain.dll importieren und die aus der DLL exportierten Funktionen initializeTrainedNN() und computeNNIndicator() nutzen.
//+------------------------------------------------------------------+ //| NeuralEncogIndicator.mq5 | //| Copyright 2011, Investeo.pl | //| http:/Investeo.pl | //+------------------------------------------------------------------+ #property copyright "Copyright 2011, Investeo.pl" #property link "http:/Investeo.pl" #property version "1.00" #property indicator_separate_window #property indicator_plots 1 #property indicator_buffers 1 #property indicator_color1 Blue #property indicator_type1 DRAW_LINE #property indicator_style1 STYLE_SOLID #property indicator_width1 2 #import "EncogNNTrainDLL.dll" int initializeTrainedNN(string nnFile); int computeNNIndicator(double& ind1[], double& ind2[],double& ind3[], int size, double& result[], int rates); #import int INPUT_WINDOW = 6; int PREDICT_WINDOW = 1; double ind1Arr[], ind2Arr[], ind3Arr[]; double neuralArr[]; int hStochastic; int hWilliamsR; int hNeuralMA; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping SetIndexBuffer(0, neuralArr, INDICATOR_DATA); PlotIndexSetInteger(0, PLOT_SHIFT, 1); ArrayResize(ind1Arr, INPUT_WINDOW); ArrayResize(ind2Arr, INPUT_WINDOW); ArrayResize(ind3Arr, INPUT_WINDOW); ArrayInitialize(neuralArr, 0.0); ArraySetAsSeries(ind1Arr, true); ArraySetAsSeries(ind2Arr, true); ArraySetAsSeries(ind3Arr, true); ArraySetAsSeries(neuralArr, true); hStochastic = iStochastic(NULL, 0, 8, 5, 5, MODE_EMA, STO_LOWHIGH); hWilliamsR = iWPR(NULL, 0, 21); Print(TerminalInfoString(TERMINAL_DATA_PATH)+"\\MQL5\Files\step5_network.eg"); initializeTrainedNN(TerminalInfoString(TERMINAL_DATA_PATH)+"\\MQL5\Files\step5_network.eg"); //--- return(0); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime& time[], const double& open[], const double& high[], const double& low[], const double& close[], const long& tick_volume[], const long& volume[], const int& spread[]) { //--- int calc_limit; if(prev_calculated==0) // First execution of the OnCalculate() function after the indicator start calc_limit=rates_total-34; else calc_limit=rates_total-prev_calculated; ArrayResize(neuralArr, rates_total); for (int i=0; i<calc_limit; i++) { CopyBuffer(hStochastic, 0, i, INPUT_WINDOW, ind1Arr); CopyBuffer(hStochastic, 1, i, INPUT_WINDOW, ind2Arr); CopyBuffer(hWilliamsR, 0, i, INPUT_WINDOW, ind3Arr); computeNNIndicator(ind1Arr, ind2Arr, ind3Arr, INPUT_WINDOW, neuralArr, rates_total-i); } //Print("neuralArr[0] = " + neuralArr[0]); //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+
Hier sehen Sie die Ausgabe des auf Tagesdaten von USDCHF und den Indikatoren Stochastic und Williams %R eingelernten Indikators:
Abbildung 7. Neuronaler Encog-Indikator
Der Indikator zeigt die prognostizierte beste Rendite auf dem nächsten Balken.
Ihnen könnte aufgefallen sein, dass ich den Indikator um einen Balken in die Zukunft verschoben habe:
PlotIndexSetInteger(0, PLOT_SHIFT, 1);
Dies dient der Verdeutlichung, dass es sich um einen prognostizierenden Indikator handelt. Nun, da wir einen neuronalen Indikator erstellt haben, sind wir bereit, einen Expert Advisor auf Basis dieses Indikators zu erstellen.
9. Expert Advisor auf Basis eines neuronalen Indikators
Der Expert Advisor nimmt die Ausgabe des neuronalen Indikators und entscheidet, ob ein Wertpapier gekauft oder verkauft werden soll. Mein erster Eindruck war, dass er kaufen soll, wenn der Indikator über Null liegt, und verkaufen, wenn er unter Null ist. Das bedeutet kaufen, wenn die beste prognostizierte Rendite in einem bestimmten Zeitfenster positiv ist, und verkaufen, wenn die beste prognostizierte Rendite negativ ist.
Nach einigen einleitenden Tests zeigte sich, dass die Performance besser sein könnte. Deshalb habe ich die Variablen 'strong uptrend' und 'strong downtrend' eingeführt, was bedeutet, dass es keinen Grund gibt, vom Abschluss zurückzutreten, wenn wir gemäß der berühmten Regel 'the trend is your friend' in einem starken Trend sind.
Zusätzlich wurde mir im Forum von Heaton Research geraten, ATR für bewegliche Stop Losses zu verwenden, also habe ich den Indikator Chandelier ATR genutzt, den ich im MQL5-Forum gefunden habe. Er führte tatsächlich zu gesteigertem Aktiengewinn beim Backtesting. Den Quellcode des Expert Advisors habe ich nachfolgend kopiert.
//+------------------------------------------------------------------+ //| NeuralEncogAdvisor.mq5 | //| Copyright 2011, Investeo.pl | //| http:/Investeo.pl | //+------------------------------------------------------------------+ #property copyright "Copyright 2011, Investeo.pl" #property link "http:/Investeo.pl" #property version "1.00" double neuralArr[]; double trend; double Lots=0.3; int INPUT_WINDOW=8; int hNeural,hChandelier; //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- ArrayResize(neuralArr,INPUT_WINDOW); ArraySetAsSeries(neuralArr,true); ArrayInitialize(neuralArr,0.0); hNeural=iCustom(Symbol(),Period(),"NeuralEncogIndicator"); Print("hNeural = ",hNeural," error = ",GetLastError()); if(hNeural<0) { Print("The creation of ENCOG indicator has failed: Runtime error =",GetLastError()); //--- forced program termination return(-1); } else Print("ENCOG indicator initialized"); hChandelier=iCustom(Symbol(),Period(),"Chandelier"); Print("hChandelier = ",hChandelier," error = ",GetLastError()); if(hChandelier<0) { Print("The creation of Chandelier indicator has failed: Runtime error =",GetLastError()); //--- forced program termination return(-1); } else Print("Chandelier indicator initialized"); //--- return(0); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- long tickCnt[1]; int ticks=CopyTickVolume(Symbol(),0,0,1,tickCnt); if(tickCnt[0]==1) { if(!CopyBuffer(hNeural,0,0,INPUT_WINDOW,neuralArr)) { Print("Copy1 error"); return; } // Print("neuralArr[0] = "+neuralArr[0]+"neuralArr[1] = "+neuralArr[1]+"neuralArr[2] = "+neuralArr[2]); trend=0; if(neuralArr[0]<0 && neuralArr[1]>0) trend=-1; if(neuralArr[0]>0 && neuralArr[1]<0) trend=1; Trade(); } } //+------------------------------------------------------------------+ //| Tester function | //+------------------------------------------------------------------+ double OnTester() { //--- //--- return(0.0); } //+------------------------------------------------------------------+ void Trade() { double bufChandelierUP[2]; double bufChandelierDN[2]; double bufMA[2]; ArraySetAsSeries(bufChandelierUP,true); ArraySetAsSeries(bufChandelierUP,true); ArraySetAsSeries(bufMA,true); CopyBuffer(hChandelier,0,0,2,bufChandelierUP); CopyBuffer(hChandelier,1,0,2,bufChandelierDN); MqlRates rates[]; ArraySetAsSeries(rates,true); int copied=CopyRates(Symbol(),PERIOD_CURRENT,0,3,rates); bool strong_uptrend=neuralArr[0]>0 && neuralArr[1]>0 && neuralArr[2]>0 && neuralArr[3]>0 && neuralArr[4]>0 && neuralArr[5]>0 && neuralArr[6]>0 && neuralArr[7]>0; bool strong_downtrend=neuralArr[0]<0 && neuralArr[1]<0 && neuralArr[2]<0 && neuralArr[3]<0 && neuralArr[4]<0 && neuralArr[5]<0 && neuralArr[6]<0 && neuralArr[7]<0; if(PositionSelect(_Symbol)) { long type=PositionGetInteger(POSITION_TYPE); bool close=false; if((type==POSITION_TYPE_BUY) && (trend==-1)) if(!(strong_uptrend) || (bufChandelierUP[0]==EMPTY_VALUE)) close=true; if((type==POSITION_TYPE_SELL) && (trend==1)) if(!(strong_downtrend) || (bufChandelierDN[0]==EMPTY_VALUE)) close=true; if(close) { CTrade trade; trade.PositionClose(_Symbol); } else // adjust s/l { CTrade trade; if(copied>0) { if(type==POSITION_TYPE_BUY) { if(bufChandelierUP[0]!=EMPTY_VALUE) trade.PositionModify(Symbol(),bufChandelierUP[0],0.0); } if(type==POSITION_TYPE_SELL) { if(bufChandelierDN[0]!=EMPTY_VALUE) trade.PositionModify(Symbol(),bufChandelierDN[0],0.0); } } } } if((trend!=0) && (!PositionSelect(_Symbol))) { CTrade trade; MqlTick tick; MqlRates rates[]; ArraySetAsSeries(rates,true); int copied=CopyRates(Symbol(),PERIOD_CURRENT,0,INPUT_WINDOW,rates); if(copied>0) { if(SymbolInfoTick(_Symbol,tick)==true) { if(trend>0) { trade.Buy(Lots,_Symbol,tick.ask); Print("Buy at "+tick.ask+" trend = "+trend+" neuralArr = "+neuralArr[0]); } if(trend<0) { trade.Sell(Lots,_Symbol,tick.bid); Print("Sell at "+tick.ask+" trend = "+trend+" neuralArr = "+neuralArr[0]); } } } } } //+------------------------------------------------------------------+
Der Expert Advisor wurde auf D1-Daten des Paares USDCHF ausgeführt. Für das Einlernen wurden etwa 50 % der Daten genutzt.
10. Backtesting-Ergebnisse des Expert Advisors
Ich führe nachfolgend die Backtesting-Ergebnisse auf. Das Backtesting wurde von 2000.01.01 bis 2011.03.26 durchgeführt.
Abbildung 8. Backtesting-Ergebnisse des neuronalen Expert Advisors
Abbildung 9. Bilanz-Kapital-Backtesting-Diagramm des neuronalen Expert Advisors
Denken Sie daran, dass diese Performance für andere Timeframes und Wertpapiere völlig anders ausfallen kann.
Bitte behandeln Sie diesen EA als Übungswerkzeug und nutzen Sie ihn als Grundlage für weitere Forschungen. Meine persönliche Meinung ist, dass das Netzwerk nach jedem bestimmten Zeitraum neu eingelernt werden könnte, um es robuster zu machen. Vielleicht findet jemand eine gute Möglichkeit dafür oder hat sie sogar schon gefunden. Vielleicht gibt es bessere Wege, Kauf-/Verkaufsprognosen auf Basis eines neuronalen Indikators anzustellen. Ich möchte die Leser zum Experimentieren ermutigen.
Fazit
Im vorliegenden Beitrag habe ich eine Möglichkeit für die Erstellung eines neuronalen prognostizierenden Indikators und Expert Advisors auf Basis dieses Indikators mithilfe des Machine-Learning-Frameworks ENCOG vorgestellt. Alle Quellcodes, kompilierten Binärdateien, DLLs und Beispiele für eingelernte Netzwerke sind an diesen Beitrag angehängt.
Aufgrund des "doppelten DLL-Wrappings in .NET" müssen sich die Dateien Cloo.dll, encog-core-cs.dll und log4net.dll im Ordner des Client Terminals befinden.
Die Datei EncogNNTrainDLL.dll muss sich im Ordner \Datenordner des Terminals\MQL5\Libraries befinden.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/252





- 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.