English Русский 中文 Español 日本語 Português
Neuronale Netze In MetaTrader verwenden

Neuronale Netze In MetaTrader verwenden

MetaTrader 4Beispiele | 4 Mai 2016, 14:22
3 946 0
Mariusz Woloszyn
Mariusz Woloszyn


Einführung

Viele von Ihnen haben wahrscheinlich die Möglichkeit der Verwendung von neuronalen Netzen in Ihrem EA betrachtet. Dieses Thema war sehr heiß, besonders nach der 2007 Automated Trading Championship und dem spektakulären Gewinn von Better mit seinem System auf Basis neuronaler Netze. Viele Internet-Foren wurden mit Themen überflutet im Zusammenhang mit neuronalen Netzen und Devisenhandel. Leider ist die native MQL4 Umsetzung von NN nicht einfach. Es erfordert einige Programmierkenntnisse und das Ergebnis würde nicht sehr effizient sein, besonders, wenn Sie möchten, dass Ihr abschließendes Ergenis im Tester auf große Mengen von Daten testen wollen.

In diesem Artikel werde ich Ihnen zeigen, wie Sie die frei verfügbare (unter LGPL), bekannte Fast Artificial Neural Network Library (FANN) in Ihrem MQL4 Code verwenden, während bestimmte Hindernisse und Einschränkungen vermieden werden. Außerdem gehe ich davon aus, dass der Leser mit Artificial Neural Networks (ann) und der Terminologie in Zusammenhang mit diesem Thema vertraut ist, so dass ich mich auf praktische Aspekte der Verwendung von bestimmter Implementierung von ann in MQL4 Sprache konzentrieren werde.


FANN Eigenschaften

Um in vollem Umfang die Möglichkeiten der FANN Implementierung zu verstehen, muss man sich mit ihrer Dokumentation und den am häufigsten verwendeten Funktionen vertraut machen. Die typische Verwendung von FANN ist ein einfaches vorgeschaltetes Netzwerk zu schaffen, es mit einigen Daten trainieren und laufen. Das erstellte und trainierte Netzwerk könnte dann in eine Datei gespeichert werden und später zur weiteren Verwendung wieder hergestellt werden. Um ein ann zu erstellenmuss man die fann_create_standard() Funktion verwende. Sehen wir uns die Syntax an:

FANN_EXTERNAL struct fann *FANN_API fann_create_standard(unsigned int num_layers, int lNnum, ... )

Wo num_layers die Gesamtzahl von Schichten darstellt, einschließlich der Eingabe- und der Ausgabeschicht. Das lNnum und folgende Argumente repräsentiert die Anzahl von Neuronen in jeder Schicht, beginnend mit der Eingabeschicht, und endend mit der Ausgabeschicht. Um ein Netz mit einer verborgenen Schicht mit 5 Neuronen, 10 Eingaben und 1 Ausgabe zu erzeugen, müsste man es nennen, wie folgt:

fann_create_standard(3,10,5,1);

Sobald das ann erstellt ist, wäre die nächste Operation, es mit einigen Eingabe- und Ausgabedaten zu trainieren. Die einfachste Ausbildungsmethode ist inkrementelles Training, das durch die folgende Funktion erreicht werden kann:

FANN_EXTERNAL void FANN_API fann_train(
        struct  fann    *       ann,
                fann_type       *       input,
                fann_type       *       desired_output  )

Diese Funktion nimmt den Pointer zu struct fann zuvor zurückgegeben von fann_create_standard () und beide Eingabedatenvektor und Ausgabedatenvektor. Die Eingabe- und Ausgabevektoren sind Arrays des fann_type Typs. Diese Art ist in Tat ein double- oder ein float-Typ, abhängig von der Art wie FANN kompiliert wird. In dieser Implementierung werden die Eingabe- und Ausgabevektoren double Arrays sein>.

Sobald das ann aussgebildet ist, wäre das nächte gewünscht Feature die Asuführung des Netzes. Die Funktion die dies Umsetzt, ist wie folgt definiert:

FANN_EXTERNAL fann_type * FANN_API fann_run(    struct  fann    *       ann,
                fann_type       *       input   )

Diese Funktion nimmt den Pointer zu struct fann, das zuvor erstellte Netzwerk darstellend, und einen Eingabevektor des definierten Typs (double Array). Der Rückgabewert ist ein Ausgabevektor-Array. Diese Tatsache ist wichtig, da wir für ein Ausgabe-Netzwerk immer ein Element Array mit dem Ausgangswert, anstelle des Ausgangswert selbst erhalten.

Leider verwenden die meisten FANN Funktionen einen Pointer auf ein struct fann das ann darstellend, was nicht direkt gehandhabt werden kann von MQL4, das Strukturen als Datentypen nicht unterstützt. Um diese Einschränkung zu vermeiden müssen wir das ummanteln, und in irgendeiner Art und Weise vor MQL4 verbergen. Die einfachste Methode ist ein Array von struct fann Pointern zu erstellen, das die richtigen Werte hält und sich auf sie mit einem Index bezieht, dargestellt von einer int Variable.. So können wir den nicht unterstützten Variablentyp mit einem unterstützten ersetzen und eine Wrapper-Bibliothek erstellen, die mit MQL4 Code leicht integriert werden kann.

Die FANN rundum Einwickeln

Nach bestem Wissen, unterstützt MQL4 keine Funktionen mit variabler Argumente-Liste, so müssen wir auch damit umgehen. Auf der anderen Seite, wenn die C-Funktion (mit variabler Argumente Länge) mit zu vielen Argumenten aufgerufen wird, passiert nichts falsches, also übernehmen wir eine feste maximale Anzahl von Argumenten in MQL4 Funktion übergeben an C-Bibliothek. Die resultierende Wickel-Funktion würde aussehen wie folgt:

/* Creates a standard fully connected backpropagation neural network.
* num_layers - The total number of layers including the input and the output layer.
* l1num - number of neurons in 1st layer (inputs)
* l2num, l3num, l4num - number of neurons in hidden and output layers (depending on num_layers).
* Returns:
* handler to ann, -1 on error
*/

int __stdcall f2M_create_standard(unsigned int num_layers, int l1num, int l2num, int l3num, int l4num);

Wir wechselten die führende fann_ mit f2M_ (die für FANN TO MQL steht), verwendeten statische Anzahl von Argumenten (4 Schichten) und der zurückgebende Wert ist jetzt ein Index für interne Array von anns die struct fann Daten haltend, erforderlich für FANN zum Arbeiten. Auf diese Weise können wir leicht eine solche Funktion innerhalb MQL-Code aufrufen.

Das gleiche geht für:

/* Train one iteration with a set of inputs, and a set of desired outputs.
* This training is always incremental training, since only one pattern is presented.
* ann - network handler returned by f2M_create_*
* *input_vector - array of inputs
* *output_vector - array of outputs
* Returns:
* 0 on success and -1 on error
*/

int __stdcall f2M_train(int ann, double *input_vector, double *output_vector);

und

/* Run fann network
* ann - network handler returned by f2M_create_*
* *input_vector - array of inputs
* Returns:
* 0 on success, negative value on error
* Note:
* To obtain network output use f2M_get_output().
* Any existing output is overwritten
*/

int __stdcall f2M_run(int ann, double *input_vector);

Last, but not least ist die Tatsache, dass Sie zu Ihr einmal erstelltes ann zerstören müssen, durch den Aufruf zu:

/* Destroy fann network
* ann - network handler returned by f2M_*
* Returns:
* 0 on success -1 on error
* WARNING: the ann handlers cannot be reused if ann!=(_ann-1)
* Other handlers are reusable only after the last ann is destroyed.
*/

int __stdcall f2M_destroy(int ann);

Um ann Handles freizugeben, müssen Sie die Netze in umgekehrter Reihenfolge zerstören, wie Sie sie erzeugt haben. Alternativ können Sie verwenden:

/* Destroy all fann networks
* Returns:
* 0 on success -1 on error
*/
int __stdcall f2M_destroy_all_anns();

Allerdings bin ich ziemlich sicher, dass einige von Ihnen bevorzugen ihre trainierten Netze zu speichern, für eine spätere Verwendung mit:

/* Save the entire network to a configuration file.
* ann - network handler returned by f2M_create*
* Returns:
* 0 on success and -1 on failure
*/
int __stdcall f2M_save(int ann,char *path);

Natürlich kann das gespeicherte Netz später neu geladen (oder eher neu erstellt) werden, mit:

/* Load fann ann from file
* path - path to .net file
* Returns:
* handler to ann, -1 on error
*/
int __stdcall f2M_create_from_file(char *path);

Sobald wir die Grundfunktionen kennen, könnten wir versuchen das in unserem EA zu verwenden, aber zuerst müssen das Fann2MQL Paket installieren.



Fann2MQL Installieren

Um die Verwendung des Pakets zu vereinfachen, habe ich den msi installer erstellt, der den gesamten Quellcode enthält, plus vorkompilierte Bibliotheken und Fann2MQL.mqh Header-Datei, die alle Fann2MQL Funktionen deklariert.

Der Installationsvorgang ist sehr einfach. Zuerst werden Sie informiert, dass Fann2MQL unter GPL Lizenz ist:


Installation von Fann2MQL, Schritt 1

Dann wählen Sie denn Ordner um das Paket zu installieren. Sie können den Standard verwenden Program Files\Fann2MQL\ oder installieren direkt in Ihr Meta Trader\experts\ Verzeichnis. Später werden alle Dateien direkt an ihre Orte platziert, ansonsten werden Sie alle manuell kopieren müssen.


Installation von Fann2MQL, Schritt 2

Der Installer platziert Dateien in die folgenden Ordner:


include\ Ordner


libraries\ Ordner


src\ Ordner

Wenn Sie in einen dedizierten Fann2MQL Ordner installieren möchten, kopieren Sie bitte den Inhalt von den include und libraries Unterordner Ihr entsprechendes MetaTrader Verzeichnis.

Der Installer installiert auch die FANN Bibliothek in Ihren Systembibliotheken Ordner (Windows\system32 in den meisten Fällen). Der src enthält den gesamten Quellcode von Fann2MQL. Sie können den Quellcode lesen, der eine ultimative Dokumentation ist, wenn Sie mehr Informationen über die Interna benötigen. Sie können den Code auch verbessern und zusätzliche Features hinzufügen, wenn Sie das möchten. Ich ermutige Sie mir Ihre Patches zu schicken, wenn Sie etwas interessantes implementieren.



Neuronale Netze in Ihrem EA verwenden

Sobald die Fann2MQL installiert ist, können Sie beginnen, Ihren eigene EA oder Indikator zu schreiben. Es gibt viele mögliche Verwendung von NN. Sie können sie verwenden, um zukünftige Kursbewegungen zu prognostizieren aber die Qualität solcher Prognosen und die Möglichkeit einen echten Vorteil zu erlangen, ist zweifelhaft. Sie können versuchen, Ihre eigene Strategie mit Reinforcement Learning Techniken, etwa ein Q-Learning oder etwas ähnliches zu schreiben. Sie können versuchen NN als einen Signalfolter für Ihren heuristischen EA zu verwenden oder alle diese Techniken kombinieren, was auch immer Sie möchten. Sie sind nur durch Ihre eigene Fantasie gegrenzt.

Hier zeige ich Ihnen ein Beispiel der Verwendung eines NN als einfachen Filter für Signale, generiert von MACD. Bitte betrachten Sie es nicht als wertvollen EA, sondern als Beispielanwendung von Fann2MQL. Während der Erklärung über das Vorgehen, hier der Beispiel EA: NeuroMACD.mq4 funktioniert. Ich werde ihnen zeigen, wie die Fann2MQL effektiv in MQL verwendet werden kann.

Die allererste Sache für jeden EA, ist die Deklaration der globalen Variablen definiert und Abschnitt umfassen. Hier ist der Beginn des NeuroMACD, der diese Dinge enthält:

// Include Neural Network package
#include <Fann2MQL.mqh>

// Global defines
#define ANN_PATH "C:\\ANN\\"
// EA Name
#define NAME "NeuroMACD"

//---- input parameters
extern double Lots=0.1;
extern double StopLoss=180.0;
extern double TakeProfit=270.0;
extern int FastMA=18;
extern int SlowMA=36;
extern int SignalMA=21;
extern double Delta=-0.6;
extern int AnnsNumber=16;
extern int AnnInputs=30;
extern bool NeuroFilter=true;
extern bool SaveAnn=false;
extern int DebugLevel=2;
extern double MinimalBalance=100;
extern bool Parallel=true;

// Global variables

// Path to anns folder
string AnnPath;

// Trade magic number
int MagicNumber=65536;

// AnnsArray[ann#] - Array of anns
int AnnsArray[];

// All anns loded properly status
bool AnnsLoaded=true;

// AnnOutputs[ann#] - Array of ann returned returned
double AnnOutputs[];

// InputVector[] - Array of ann input data
double InputVector[];

// Long position ticket
int LongTicket=-1;

// Short position ticket
int ShortTicket=-1;

// Remembered long and short network inputs
double LongInput[];
double ShortInput[];

Der Include-Befehl gibt an die Fann2MQL.mqh Header-Datei zu laden, welche die Deklaration aller Fann2MQL Funktionen enthält. Danach sind alle Fann2MQL Paket-Funktionen für den Einsatz im Skript verfügbar. Die ANN_PATH-Konstante definiert den Pfad zum Speichern und laden von Dateien mit ausgebildeten FANN Netzwerken. Sie müssen diesen Ordner erstellen, d.h. C:\ANN. Die NAME- Konstante enthält den Namen dieses EA, der später für das Laden und Speichern Netzwerk-Dateien verwendet wird. Eingabeparameter sind ziemlich offensichtlich und die es nicht sind, werden später erläutert, sowie globale Variablen..


Der Einstiegspunkt für jeden EA ist seine init() Funktion:

int init()
  {
   int i,ann;

   if(!is_ok_period(PERIOD_M5)) 
     {
      debug(0,"Wrong period!");
      return(-1);
     }

   AnnInputs=(AnnInputs/3)*3; // Make it integer divisible by 3

   if(AnnInputs<3) 
     {
      debug(0,"AnnInputs too low!");
     }
// Compute MagicNumber and AnnPath
   MagicNumber+=(SlowMA+256*FastMA+65536*SignalMA);
   AnnPath=StringConcatenate(ANN_PATH,NAME,"-",MagicNumber);

// Initialize anns
   ArrayResize(AnnsArray,AnnsNumber);
   for(i=0;i<AnnsNumber;i++) 
     {
      if(i%2==0) 
        {
         ann=ann_load(AnnPath+"."+i+"-long.net");
           } else {
         ann=ann_load(AnnPath+"."+i+"-short.net");
        }
      if(ann<0)
         AnnsLoaded=false;
      AnnsArray[i]=ann;
     }
   ArrayResize(AnnOutputs,AnnsNumber);
   ArrayResize(InputVector,AnnInputs);
   ArrayResize(LongInput,AnnInputs);
   ArrayResize(ShortInput,AnnInputs);

// Initialize Intel TBB threads
   f2M_parallel_init();

   return(0);
  }

Zunächst wird geprüft, ob der EA auf den richtigen Zeitrahmen angewendet wird. Die AnnInputs Variable enthält die Anzahl der neuronalen Netzwerkeingänge. Da wir drei Sätze von verschiedenen Argumenten verwenden werden, soll es teilbar durch 3 sein. AnnPath wird berechnet zum Reflektieren von EA NAME und MagicNumber, die aus den SlowMA, FastMA and SignalMA Eingabeargumenten berechnet wird, die später für die MACD-Indikator Signale verwendet werden . Sobald er den AnnPath kennt, versucht der EA neuronale Netze mit der ann_load() Funktion zu laden, die ich weiter unten beschreiben werde. Die Hälfte der geladenen Netze ist für die Long-Position Filterung gedacht und die andere Hälfte für Shorts. Die AnnsLoaded Variable wird verwendet um die Tatsache anzuzeigen, dass alle Netze korrekt initialisiert wurden. Wie Sie wahrscheinlich bemerkt haben, dieser Beispiel EA versucht mehrere Netzwerke zu laden. Ich bezweifle, dass es in dieser Anwendung wirklich notwendig ist, aber ich wollte Ihnen das volle Potenzial von Fann2MQL zeigen, die mehrere Netzwerke gleichzeitig handhabt und kann sie parallel verarbeiten kann, unter Ausnutzung von mehreren Kernen oder CPUs. Um es zu ermöglichen nutzt Fann2MQL die Intel® Threading Building Blocks Technologie. Die Funktion f2M_parallel_init() wird verwendet, die eine Schnittstelle zu initialisieren.

Hier ist der Weg, den ich zum Initialisieren der Netze verwendet habe:

int ann_load(string path)
  {
   int ann=-1;

   /* Load the ANN */
   ann=f2M_create_from_file(path);
   if(ann!=-1) 
     {
      debug(1,"ANN: '"+path+"' loaded successfully with handler "+ann);
     }
   if(ann==-1) 
     {

      /* Create ANN */
      ann=
          f2M_create_standard(4,AnnInputs,AnnInputs,AnnInputs/2+1,1);
      f2M_set_act_function_hidden(ann,FANN_SIGMOID_SYMMETRIC_STEPWISE);
      f2M_set_act_function_output(ann,FANN_SIGMOID_SYMMETRIC_STEPWISE);
      f2M_randomize_weights(ann,-0.4,0.4);
      debug(1,"ANN: '"+path+"' created successfully with handler "+ann);
     }
   if(ann==-1) 
     {
      debug(0,"INITIALIZING NETWORK!");
     }
   return(ann);
  }

Wie Sie sehen können, wenn die f2M_create_from_file() versagt, was durch den negativen Rückgabewert angezeigt wird, wird das Netzwerk mit der f2M_create_standard() Funktion erstellt mit Argumenten darauf hinweist, dass das erstellte Netzwerk 4 Schichten (einschließlich Ein- und Ausgabe) haben sollte, AnnInput/2+1 Neuronen in der zweiten verborgenen Schicht und 1 Neurons in der Ausgabeschicht. in der Eingänge haben sollte, AnnInput Neuronen in der ersten verborgenen Schicht, f2M_set_act_function_hidden() wird verwendet, um die Aktivierungsfunktion von verborgenen Schichten auf SIGMOID_SYMMETRIC_STEPWISE einzustellen (siehe FANN Dokumentation von fann_activationfunc_enum) und das gleiche gilt für die Ausgabeschicht. Dann gibt es den Aufruf an f2m_randomize_weights(), die verwendet wird neuron Verbindungsgewichte innerhalb des Netzes zu initialisieren. Hier habe ich den Bereich von <-0,4; 0,4> verwendet, Sie können aber jede andere verwenden, abhängig von Ihrer Anwendung.

An dieser Stelle haben Sie wahrscheinlich die Funktion debug() bemerkt, die ich ein paar Mal verwendete. Es ist eine der einfachsten Methoden, um die Verbose Ebene Ihres EA zu ändern. Zusammen mit ihr und dem Eingabeparameter DebugLevel können Sie die Art und Weise abstimmen, dass Ihr Code die Debug-Ausgabe produziert.

void debug(int level,string text)
  {
   if(DebugLevel>=level) 
     {
      if(level==0)
         text="ERROR: "+text;
      Print(text);
     }
  }

Wenn das erste Argument der Funktion debug(), der Debug-level höher als DebugLevel erzeugt die Funktion keine Ausgabe. Wenn es niedriger als gleich ist, wird der text String ausgegeben. Wenn der Debug-Level 0 ist, wird dieser String "ERROR: " an den Beginn angehangen. Auf diese Weise können Debug von Ihrem Code erzeugt auf mehrere Ebenen aufteilen. Die wichtigsten sind wahrscheinlich Fehler, so dass sie auf die Ebene 0 zugeordnet sind. Sie werden ausgegeben, außer wenn Sie Ihre Debuglevel unter 0 senken (was nicht geraten wird). Auf Ebene 1 werden einige wichtige Informationen ausgegeben, wie Bestätigung des erfolgreichen Ladens des Netzes oder Erzeugung. Auf Stufe 2 oder höher nimmt die Bedeutung von gedruckten Informationen allmählich ab.

Vor der ausführlichen Erläuterung der start() Funktion, die sehr lang ist, muss ich Ihnen einige weitere Funktionen zeigen, die dazu gedacht sind die Netz Eingabe vorzubereiten und die tatsächlichen Netze auszuführen:

void ann_prepare_input()
  {
   int i;

   for(i=0;i<=AnnInputs-1;i=i+3) 
     {
      InputVector[i]=
         10*iMACD(NULL,0,FastMA,SlowMA,SignalMA,PRICE_CLOSE,
                  MODE_MAIN,i*3);
      InputVector[i+1]=
         10*iMACD(NULL,0,FastMA,SlowMA,SignalMA,PRICE_CLOSE,
                  MODE_SIGNAL,i*3);
      InputVector[i+2]=InputVector[i-2]-InputVector[i-1];
     }
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double ann_run(int ann,double &vector[])
  {
   int ret;
   double out;
   ret=f2M_run(ann,vector);
   if(ret<0) 
     {
      debug(0,"Network RUN ERROR! ann="+ann);
      return(FANN_DOUBLE_ERROR);
     }
   out=f2M_get_output(ann,0);
   debug(3,"f2M_get_output("+ann+") returned: "+out);
   return(out);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int anns_run_parallel(int anns_count,int &anns[],double &input_vector[])
  {
   int ret;

   ret=f2M_run_parallel(anns_count,anns,input_vector);

   if(ret<0) 
     {
      debug(0,"f2M_run_parallel("+anns_count+") returned: "+ret);
     }
   return(ret);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void run_anns()
  {
   int i;

   if(Parallel) 
     {
      anns_run_parallel(AnnsNumber,AnnsArray,InputVector);
     }

   for(i=0;i<AnnsNumber;i++) 
     {
      if(Parallel) 
        {
         AnnOutputs[i]=f2M_get_output(AnnsArray[i],0);
           } else {
         AnnOutputs[i]=ann_run(AnnsArray[i],InputVector);
        }
     }
  }
//+------------------------------------------------------------------+

Die Funktion ann_prepare_input() wird verwendet, um den Eingabenamen für die Netze vorzubereiten (daher der Name). Der Zweck ist recht einfach, aber das ist der Punkt an den ich Sie erinnern muss, dass die Eingabedaten richtig normalisiert werden müssen. Es gibt keine ausgefeilte Normalisierung in diesem Fall, ich habe einfach die MACD Haupt- und Signalwerte, die nie den gewünschten Bereich auf den berücksichtigten Daten überschreiten. In dem realen Beispiel sollten Sie wahrscheinlich mehr Aufmerksamkeit auf dieses Thema legen. Wie Sie wahrscheinlich vermuten, ist die Auswahl der richtigen Eingabeargumente für Netzeingabe, es Codieren, ist Zersetzung und Normalisierung einer der wichtigsten Faktoren bei der neuronalen Netz-Verarbeitung.

Wie ich bereits erwähnte, hat die Fann2MQL die Fähigkeit die normale Funktionalität des MetaTrader zu erweitern, was die parallel multithreaded Verarbeitung von neuronalen Netzen ist. Das globale Argument Parallel steuert dieses Verhalten. Die run_anns() Funktion führt alle initialisierten Netze aus und erhält die Ausgabrn von ihnen und speichert in dem AnnOutput[] Array. Die anns_run_parallel Funktion ist verantwortlich für die Handhabung des Jobs in der multithreaded Weise. Sie ruft die f2m_run_parallel() auf, die die Anzahl von zu verarbeitenden Netzen zu verarbeiten als ein erstes Argument nimmt, das zweite Argument ist ein Array mit Handles für alle Netze die Sie ausführen wollen, die Bereitstellung des Eingabevektor ist ein drittes Argument. Alle Netz müssen auf den gleichen Eingabedaten ausgeführt werden. Der Empfang der Ausgabe aus dem Netz erfolgt durch mehrere Aufrufe von f2m_get_output().

Betrachten wir nun die start() Funktion:

int
start()
  {
   int i;
   bool BuySignal=false;
   bool SellSignal=false;

   double train_output[1];

   /* Is trade allowed? */
   if(!trade_allowed()) 
     {
      return(-1);
     }

   /* Prepare and run neural networks */
   ann_prepare_input();
   run_anns();

   /* Calulate last and previous MACD values.
* Lag one bar as current bar is building up
*/
   double MacdLast=iMACD(NULL,0,FastMA,SlowMA,SignalMA,PRICE_CLOSE,
                         MODE_MAIN,1);
   double MacdPrev=iMACD(NULL,0,FastMA,SlowMA,SignalMA,PRICE_CLOSE,
                         MODE_MAIN,2);

   double SignalLast=iMACD(NULL,0,FastMA,SlowMA,SignalMA,PRICE_CLOSE,
                           MODE_SIGNAL,
                           1);
   double SignalPrev=iMACD(NULL,0,FastMA,SlowMA,SignalMA,PRICE_CLOSE,
                           MODE_SIGNAL,
                           2);

   /* BUY signal */
   if(MacdLast>SignalLast && MacdPrev<SignalPrev) 
     {
      BuySignal=true;
     }
   /* SELL signal */
   if(MacdLast<SignalLast && MacdPrev>SignalPrev) 
     {
      SellSignal=true;
     }

   /* No Long position */
   if(LongTicket==-1) 
     {
      /* BUY signal */
      if(BuySignal) 
        {
         /* If NeuroFilter is set use ann wise to decide :) */
         if(!NeuroFilter || ann_wise_long()>Delta) 
           {
            LongTicket=
          OrderSend(Symbol(),OP_BUY,Lots,Ask,3,
                    Bid-StopLoss*Point,
                    Ask+TakeProfit*Point,
                    NAME+"-"+"L ",MagicNumber,0,Blue);
           }
         /* Remember network input */
         for(i=0;i<AnnInputs;i++) 
           {
            LongInput[i]=InputVector[i];
           }
        }
        } else {
      /* Maintain long position */
      OrderSelect(LongTicket,SELECT_BY_TICKET);
      if(OrderCloseTime()==0) 
        {
         // Order is opened
         if(SellSignal && OrderProfit()>0) 
           {
            OrderClose(LongTicket,Lots,Bid,3);
           }
        }
      if(OrderCloseTime()!=0) 
        {
         // Order is closed
         LongTicket=-1;
         if(OrderProfit()>=0) 
           {
            train_output[0]=1;
              } else {
            train_output[0]=-1;
           }
         for(i=0;i<AnnsNumber;i+=2) 
           {
            ann_train(AnnsArray[i],LongInput,train_output);
           }
        }
     }

   /* No short position */
   if(ShortTicket==-1) 
     {
      if(SellSignal) 
        {
         /* If NeuroFilter is set use ann wise to decide ;) */
         if(!NeuroFilter || ann_wise_short()>Delta) 
           {
            ShortTicket=
          OrderSend(Symbol(),OP_SELL,Lots,Bid,3,
                    Ask+StopLoss*Point,
                    Bid-TakeProfit*Point,NAME+"-"+"S ",
                    MagicNumber,0,Red);
           }
         /* Remember network input */
         for(i=0;i<AnnInputs;i++) 
           {
            ShortInput[i]=InputVector[i];
           }
        }
        } else {
      /* Maintain short position */
      OrderSelect(ShortTicket,SELECT_BY_TICKET);
      if(OrderCloseTime()==0) 
        {
         // Order is opened
         if(BuySignal && OrderProfit()>0) 
           {
            OrderClose(LongTicket,Lots,Bid,3);
           }
        }
      if(OrderCloseTime()!=0) 
        {
         // Order is closed
         ShortTicket=-1;
         if(OrderProfit()>=0) 
           {
            train_output[0]=1;
              } else {
            train_output[0]=-1;
           }
         for(i=1;i<AnnsNumber;i+=2) 
           {
            ann_train(AnnsArray[i],ShortInput,train_output);
           }
        }
     }

   return(0);
  }
//+------------------------------------------------------------------+

Ich beschreibe es kurz, da es recht gut kommentiert ist. Die trade_allowed() überprüft, ob es der Handel erlaubt ist. Im Grunde ist es überprüft die AnnsLoaded Variable, die angibt, dass alle anns richtig initialisiert wurden, dann überprüft sie den richtigen Zeitrahmen, Mindestkontostand und erlaubt am Ende nur auf dem ersten Tick eines neuen Balken zu handeln. Die nächsten zwei Funktion werden verwendet Netzeingabe vorzubereiten und die Netz-Verarbeitung auszuführen, wurden nur wenige Zeilen weiter oben beschrieben. Als nächstes berechnen wir und setzen sie später in Variablen zur Verarbeitung, die MACD Werte der Signal- und Haupt-Linie für den zuletzt aufgebauten Balken und den vorherigen. Die aktuelle Balken wird ausgelassen, da er noch nicht aufgebaut ist und wird wahrscheinlich neu gezeichnet wird. Die SellSignal und BuySignal werden entsprechend der MACD Signal- und Haupt-Linie Kreuzung berechnet. Beide Signale werden für lange und kurze Positionsverarbeitung verwendet, die symmetrisch sind, also werde ich nur den Fall für Long-Positionen beschreiben.

Die LongTicket Variable enthält die Ticketnummer der aktuell geöffneten Position. Wenn es gleich zu -1 ist, wird keine Position geöffnet, so wenn der BuySignal gesetzt ist, könnte das auf eine gute Möglichkeit hinweisen eine Long-Position zu öffnen. Wenn die Variable NeuroFilter nicht gesetzt ist, wird die Long-Position geöffnet und das ist der Fall, ohne die neuronale Netz Filterung der Signale - die Order zu kaufen ist gesendet. An dieser Stelle soll die LongInput Variable die an von ann_prepare_input() für eine spätere Verwendung vorbereiteten InputVector erinnern.

Wenn LongTicekt Variable die gültige Ticket-Nummer hält, prüft der EA ob sie noch offen ist oder durch StopLoss oder TakeProfit geschlossen wurde. Wenn die Order nicht nicht geschlossen ist, passiert nichts, wenn die Order durch den train_output[] Vektor geschlossen ist, der nur eine Ausgabe hat, wird berechnet den Wert von -1 zu halten wenn die Order wurde mit Verlust geschlossen wurde oder 1, wenn die Order mit Gewinn abschloss Dieser Wert wird dann zu ann_train() Funktion übergeben und alle verantwortlichen Netze für den Umgang mit Long-Positionen sind mit ihm trainiert. Als Eingabevektor wird die Variable LongInput verwendet, die die InputVector im Moment des Öffnens der Position hält. Auf diese Weise wird das Netz gelehrt, welches Signal Gewinne erbringt und welches ist es nicht.

Sobald Sie ein ausgebildetes Netz haben, lässt das Umschalten von NeuroFilter auf truedas Netz filtern. Die ann_wise_long() verwendet neuronale Netz weise berechnet als ein Mittelwert der von allen Netzen zurückgegebenen Werte mit Bedeutung für die Behandlung einer Long-Position. wird unter Verwendung des neuronalen Netzes weise als Mittelwert der Werte von allen Netzen gemeint zu handhaben, die Long-Position zurück berechnet. Der Delta Parameter wird als ein Schwellenwert verwendet, das anzeigt, dass das gefilterte Signal gültig ist oder nicht. Wie viele andere Werte wurde er durch den Prozess der Optimierung erhalten.

Nun, sobald wir wissen, wie es funktioniert, werde ich Ihnen zeigen, wie es verwendet werden kann. Das Testpaar ist natürlich EURUSD. Ich benutzte die Daten von Alpari, zu M5 Zeitrahmen konvertiert. Ich nutzte die Zeit von 31.12.2007 bis 01.01.2009 für die Ausbildung / Optimierung und 01.01.2009-22.032009 für Testzwecke. Bereits im ersten Lauf habe ich versucht, die profitabelsten Werte für StopLoss, TakeProfit, SlowMA, FastMA und SignalMA Argument zu erhalten, die ich dann in die NeuroMACD.mq4 Datei codiert habe. Der NeuroFIlter wurde ebenfalls ausgeschaltet, sowie SaveAnn, die AnnsNumber wurde auf 0 gesetzt um neuronale Verarbeitung zu vermeiden. Ich benutzte den genetischen Algorithmus für Optimierungsprozess. Sobald die Werte empfangen wurden, sah der resultierende Bericht wie folgt aus:


Bericht über Trainigsdaten nach grundlegender Parameteroptimierung.

Wie Sie sehen können, habe ich den EA auf dem Mini-Konto mit einer Lot-Größe von 0.01 und einer Anfangseinzahlung von 200 eingesetzt. Sie können diese Parameter jedoch Kontoeinstellungen oder Vorlieben anpassen

An dieser Stelle haben wir genug rentable und verlierende Trades, so dass wir den SaveAnn anstellen und die AnnsNumber auf 30 setzen. Ist das getan, lasse ich den Teaster noch einmal durchlaufen Das Ergebnis war genau das gleiche, außer der Tatsache, dass der Prozess sehr viel langsamer war (als Folge der neuronalen Verarbeitung) und den Ordner C:\ANN wurde mit den trainierten Netzwerken besiedelt, wie unten auf dem Bild gezeigt. Stellen Sie sicher, dass der C:\ANN Ordner vor diesem Durchlauf vor dem existiert!


Der C:\\ANN\\ Ordner.

Sobald wir Netze trainiert haben, ist es Zeit, zu testen wie es sich verhält. Zuerst versuchen wir es auf den Trainingsdaten. Ändern Sie den NeuroFilter zu true und SaveAnn zu false und starten Sie den Tester. Das Ergebnis, das ich erhalten haben, wird unten gezeigt. Beachten Sie, dass es leicht für Sie Fall unterschiedlich sein könnte, da es einige Zufälligkeit innerhalb Netzwerke in Verbindung Gewichten Neuron ist bei dem Netzwerk Initialisierung vorgesehen (in diesem Beispiel, das ich expliziten Aufruf f2M_randomize_weights verwendet() innerhalb ann_load().


Ergebnis erhalten auf Trainingsdaten mit Signal neuronaler Filterung aktiviert.

Der Nettogewinn ist etwas größer (20,03 gegenüber 16,92), doch der Gewinnfaktor ist viel höher (1,25 gegenüber 1,1). Die Anzahl der Trades ist viel weniger (83 vs 1188) und die durchschnittlich aufeinanderfolgende Verluste Anzahl wird von 7 auf 2 gesenkt. Jedoch zeigt es nur, dass neuronale Signalfilterung funktioniert, aber es sagt nichts darüber, wie sie auf Daten arbeitet, die nicht für verwendet wurden während des Trainings. Das Ergebnis, das ich aus der Testphase erhalten haben (01.01.2009 - 20.03.2009) ist unten dargestellt:


Ergebnis erhalten von Testdaten mit neuronaler Filterung aktiviert.

Die Anzahl der ausgeführten Trades ist ziemlich gering, und es ist schwer zu sagen, ob es die Qualität dieser Strategie ist, aber ich wollte nicht zeigen, wie man die besten rentable nEA zu schreibt, aber erklären, wie man neuronale Netze in Ihrem MQL4 Code nutzen könnte. Der wirkliche Effekt der Verwendung von neuronalen Netzen in diesem Fall kann nur dann gesehen werden, wenn die Ergebnisse der EA auf Testdaten im Vergleich zu sehen sind mit NeuroFilter ein- und ausgeschaltet. Im Folgenden ist das erhaltene Ergebnis der Testdatenperiode ohne neuronale Signalfilterung:


Ergebnisse von Testdaten ohne neuronale Filterung.

Der Unterschied ist sehr offensichtlich. Wie sie sehen, macht die neuronale Filterung aus einem verlierenden EA einen rentablen!



Fazit

Ich hoffe, dass Sie aus diesem Artikel gelernt haben, wie man neuronale Netze in MetaTrader verwendet. Mit Hilfe des einfachen, freien und Open-Source-Paket Fann2MQL können Sie einfach die neuronale Netzschicht in praktisch jedem Expert Advisor hinzufügen oder fangen Sie an Ihren eigenen zu schreiben, der vollständig oder teilweise auf neuronalen Netzen basiert. Die einzigartige Multi-Threading-Fähigkeit kann Ihre Verarbeitung viele Male beschleunigen, abhängig von der Anzahl der CPU-Kerne, insbesondere wenn bestimmte Parameter optimiert werden. In einem Fall hat es die Optimierung meiner Reinforcement Learning basierten EA Verarbeitung von etwa 4 Tagen auf "nur" 28 Stunden verkürzt, mit einer 4-Kern Intel CPU.

Während des Schreibens dieses Artikels habe ich mich entschlossen Fann2MQL auf der eigenen Website zu setzen: http://fann2mql.wordpress.com/. Sie finden dort die neueste Version von Fann2MQL und möglicherweise alle zukünftigen Versionen sowie die Dokumentation aller Funktionen. Ich verspreche, diese Software für alle Versionen unter GPL-Lizenz zu halten, so, wenn Sie mir jegliche Kommentare senden, Feature-Anfragen oder Patches, die ich interessant finde, seien Sie sicher, es in nächsten Versionen zu finden.

Bitte beachten Sie, dass dieser Artikel nur die sehr grundlegende Verwendung von Fann2MQL zeigt. Da dieses Paket nicht viel mehr ist als FANN, können Sie alle Werkzeuge für die Verwaltung von FANN Netzen verwenden, wie:

Und es gibt viel mehr über FANN auf der Fast Artificial Neural Network Library Webseite: http://leenissen.dk/fann/!

Post Scriptum

Nach dem Schreiben dieses Artikels habe ich einen unedeutenden Fehler in NeuroMACD.mq4 gefunden. Die OrderClose() Funktion für Short-Positionen wurde mit der Ticket-Nummer für Long-Positionen gespeist. Es führte zu einer schiefen Strategie, was mehr in die Richtung ging Shorts zu halten und Longs zu schließen:

/* Maintain short position */
OrderSelect(ShortTicket,SELECT_BY_TICKET);
if(OrderCloseTime()==0) 
  {
// Order is opened
   if(BuySignal && OrderProfit()>0) 
     {
      OrderClose(LongTicket,Lots,Bid,3);
     }
  }

In der korrekten Version des Skripts habe ich diesen Fehler behoben und die OrderClose() Strategie insgesamt entfernt. Das hat das Gesamtbild des Einflusses neuronaler Filterung nicht verändert, noch war die Kontostandkurve völlig anders. Sie finden beide Versionen dieses EA an diesen Artikel angehangen.

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

Beigefügte Dateien |
NeuroMACD-fixed.mq4 (10.13 KB)
NeuroMACD.mq4 (10.12 KB)
Schützt Euch Selbst, Entwickler! Schützt Euch Selbst, Entwickler!
Der Schutz geistigen Eigentums ist immer noch ein großes Problem. Dieser Artikel beschreibt die grundlegenden Prinzipien des MQL4-Programme Schutz. Mit diesen Prinzipien können Sie sicherstellen, dass die Ergebnisse Ihrer Entwicklungen nicht von einem Dieb gestohlen werden, oder zumindest seine "Arbeit" zu erschweren, so sehr, dass er sich einfach weigern wird es zu tun.
Arbeiten mit Doubles in MQL4 Arbeiten mit Doubles in MQL4
In diesem Artikel werden wir typische Programmierfehler betrachten, die bei der Arbeit mit double Nummern in MQL4 Programmen auftreten.
Die Magie der Filtration Die Magie der Filtration
Die meisten Entwickler automatischer Handelssysteme verwenden irgendeine Form von Filtration für Handelssignale. In diesem Artikel erkunden wir die Erstellung und Implementierung von Bandpass- und diskreten Filtern für Expert Advisors, um die Eigenschaften des automatisierten Handelssystems zu verbessern.
Wie wir MetaTrader Signals Service und Social Trading entwickelt haben Wie wir MetaTrader Signals Service und Social Trading entwickelt haben
Wir werden weiterhin die Signale Service steigern, die Mechanismen verbessern, neue Funktionen hinzuzufügen und Fehler beheben. Der MetaTrader Signale-Service des Jahres 2012 und der aktuelle MetaTrader Signale Service sind wie zwei völlig unterschiedliche Dienste. Derzeit implementieren wir einen virtuellen Hosting-Cloud-Service, der aus einem Netzwerk von Servern besteht um bestimmte Versionen des MetaTrader Client-Terminal unterstützen. Händler müssen nur 5 Schritte abschließen, um die virtuelle Kopie ihres Terminals mit minimaler Netzwerklatenz zu den Handels-Servern ihrer Broker direkt aus dem MetaTrader Client-Terminal zu mieten.