Der Algorithmus CatBoost von Yandex für das maschinelle Lernen, Kenntnisse von Python- oder R sind nicht erforderlich

Aleksey Vyazmikin | 4 Dezember, 2020

Vorwort

Dieser Artikel befasst sich mit der Erstellung von Modellen zur Beschreibung von Marktmustern mit einem begrenzten Satz von Variablen und der Hypothese über die Verhaltensmuster unter Verwendung des maschinellen Lernalgorithmus CatBoost von Yandex. Um das Modell zu entwickeln, benötigen Sie keine Kenntnisse von Python- oder R. Es reichen grundlegende MQL5-Kenntnisse aus — das ist genau mein Niveau. Daher hoffe ich, dass der Artikel als gutes Tutorial für ein breites Publikum hilft, um diejenigen zu unterstützen, die daran interessiert sind, Fähigkeiten des maschinellen Lernens zu evaluieren und in ihre Programme zu implementieren. Der Artikel liefert wenig akademisches Wissen. Wenn Sie zusätzliche Informationen benötigen, lesen Sie bitte die Artikelserie von Vladimir Perervenko.


Der Unterschied zwischen dem klassischen Ansatz und dem maschinellen Lernen im Handel

Das Konzept einer Handelsstrategie ist wohl jedem Händler vertraut. Darüber hinaus ist die Handelsautomatisierung ein wichtiger Aspekt für diejenigen, die das Glück haben, das Produkt von MetaQuotes zu nutzen. Wenn wir die Handelsumgebung im Code eliminieren, implizieren die meisten Strategien hauptsächlich die Auswahl von Ungleichheiten (meistens zwischen dem Preis und einem Indikator auf dem Chart) oder verwenden Indikatorwerte und ihre Bereiche, um Einstiegs- (Positionseröffnung) und Ausstiegsentscheidungen zu treffen.

Fast jeder Entwickler von Handelsstrategien hat immer wieder Einsichten erlangt, die dazu führen, dass mehr Handelsbedingungen und neue Ungleichheiten hinzukommen. Jede solche Hinzufügung bewirkt eine Veränderung der finanziellen Ergebnisse in einem bestimmten Zeitintervall. Aber ein anderes Zeitintervall, ein anderer Zeitrahmen oder ein anderes Handelsinstrument kann enttäuschende Ergebnisse zeigen — das Handelssystem ist nicht mehr effizient und der Händler muss nach neuen Mustern und Bedingungen suchen. Darüber hinaus verringert das Hinzufügen jeder neuen Bedingung die Anzahl der Handelsgeschäfte.

Dem Suchprozess folgt in der Regel die Optimierung von Ungleichheiten, die für Handelsentscheidungen herangezogen werden. Der Optimierungsprozess überprüft eine Reihe von Parametern, die oft über die ursprünglichen Datenwerte hinausgehen. Ein anderer Fall ist, wenn die durch die Parameteroptimierung erzeugten Ungleichheitswerte so selten auftreten, dass sie eher als statistische Abweichung denn als gefundenes Muster betrachtet werden können, obwohl sie die Gleichgewichtskurve oder jeden anderen optimierbaren Parameter verbessern könnten. Infolgedessen führt die Optimierung zu einer Überanpassung der in der Handelsstrategie implementierten heuristischen Idee an die verfügbaren Marktdaten. Ein solcher Ansatz ist im Hinblick auf die Rechenressourcen, die für die Suche nach einer optimalen Lösung aufgewendet werden, nicht effizient, wenn die Strategie die Verwendung einer großen Anzahl von Variablen und deren Werten impliziert.

Die Methoden des maschinellen Lernens können die Parameteroptimierungs- und Mustersuchprozesse beschleunigen, indem Ungleichheitsregeln generiert werden, die nur diejenigen Parameterwerte überprüfen, die auf den analysierten Daten vorhanden waren. Verschiedene Modellerstellungsmethoden verwenden unterschiedliche Ansätze. Im Allgemeinen geht es jedoch darum, die Lösungssuche auf die für das Training verfügbaren Daten zu beschränken. Anstatt Ungleichheiten zu erzeugen, die für die Handelsentscheidungslogik verantwortlich sind, liefert das maschinelle Lernen nur die Werte von Variablen, die Informationen über den Preis und die Faktoren, die die Preisbildung beeinflussen, enthalten. Solche Daten werden als Merkmale (oder Prädiktoren) bezeichnet.

Merkmale müssen das Ergebnis beeinflussen, das wir durch ihre Abfrage erzielen wollen. Das Ergebnis wird normalerweise als numerischer Wert ausgewiesen: Dies kann eine Klassennummer der Klassifizierung oder ein Sollwert für die Regression sein. Ein solches Ergebnis ist die Zielvariable. Einige Trainingsmethoden haben keine Zielvariable, wie z.B. Clustering-Methoden, aber wir werden in diesem Artikel nicht auf sie eingehen.

Wir brauchen also Prädiktoren und Zielvariablen.


Prädiktoren

Für Prädiktoren können Sie die Zeit, den OHLC-Preis eines Handelsinstruments und seiner Derivate, also verschiedene Indikatoren, verwenden. Es ist auch möglich, andere Prädiktoren zu verwenden, wie z.B. Wirtschaftsindikatoren, Handelsvolumen, Open Interest, Orderbuchmuster, Optionsstreiks Griechenlands und andere Datenquellen, die den Markt beeinflussen. Ich glaube, dass das Modell zusätzlich zu den Informationen, die sich bis zum aktuellen Moment gebildet haben, auch die Informationen erhalten sollte, die die Bewegung beschreiben, die zum aktuellen Moment geführt hat. Streng genommen sollten die Prädiktoren Informationen über die Preisbewegung über einen bestimmten Zeitraum liefern.

Ich lege einige Typen von Prädiktoren fest, die beschreiben:

Für Prädiktoren können wir Informationen aus verschiedenen Zeitrahmen und Handelsinstrumenten entnehmen, die sich auf denjenigen beziehen, der für den Handel verwendet werden soll. Natürlich gibt es viel mehr mögliche Methoden, um Informationen zu liefern. Die einzige Empfehlung besteht darin, genügend Daten bereitzustellen, um die Hauptpreisdynamik des Handelsinstruments zu reproduzieren. Nachdem Sie einmal Prädiktoren vorbereitet haben, können Sie diese für verschiedene Zwecke weiter verwenden. Dadurch wird die Suche nach einem Modell, das nach den grundlegenden Bedingungen der Handelsstrategie funktioniert, erheblich vereinfacht.


Das Ziel

In diesem Artikel verwenden wir eine binäre Klassifikation, d. h. 0 und 1. Diese Auswahl ergibt sich aus einer Einschränkung, auf die später noch eingegangen wird. Was kann also durch Null und Eins repräsentiert werden? Ich habe zwei Varianten:

Um die Zielvariable eines Signals zu erzeugen, können wir einfache Grundstrategien verwenden, vorausgesetzt, sie erzeugen eine ausreichende Anzahl von Signalen für das maschinelle Lernen:

Versuchen Sie, eine grundlegende Strategie zu finden, die die Generierung einer ungefähr ähnlichen Anzahl von Null und Einsen ermöglicht, da dies ein besseres Lernen erleichtert.


Software für maschinelles Lernen

Wir werden maschinelles Lernen mit der Software CatBoost durchführen, die über diesem Link heruntergeladen werden kann. Dieser Artikel zielt darauf ab, eine unabhängige Version zu erstellen, die keine andere Programmiersprache erfordert, und dafür brauchen Sie nur die neueste Version der Exe-Datei herunterzuladen, z.B. catboost-0.24.1.exe.

CatBoost ist ein Open-Source-Algorithmus für maschinelles Lernen von der bekannten Firma Yandex. Wir können also relevanten Produktsupport, Verbesserungen und Fehlerbehebungen erwarten.

Sie können die Präsentation von Yandex hier ansehen (aktivieren Sie englische Untertitel, da die Präsentation auf Russisch ist).

Kurz gesagt, CatBoost baut ein Ensemble von Entscheidungsbäumen so auf, dass jeder nachfolgende Baum die Werte der gesamten probabilistischen Antwort aller vorherigen Bäume verbessert. Dies wird als Gradientenverstärkung bezeichnet.


Datenvorbereitung für das maschinelle Lernen

Die Daten, die Prädiktoren und Zielvariable enthalten, werden als Stichprobe bezeichnet. Es handelt sich um ein Datenfeld, das eine Aufzählung von Prädiktoren als Spalten enthält, in denen jede Zeile den Messzeitpunkt darstellt, der die Prädiktorwerte zu diesem Zeitpunkt anzeigt. Die in der Zeichenfolge aufgezeichneten Messungen können in bestimmten Zeitintervallen erhalten werden oder verschiedene Objekte, z.B. Bilder, darstellen. Normalerweise hat die Datei ein CSV-Format, das ein bedingtes Trennzeichen für Spaltenwerte und Kopfzeilen (optional) verwendet.

In unserem Beispiel verwenden wir die folgenden Prädiktoren:

Die Zielvariable ist ein Signal durch einen Schnittpunkt eines MA, das beim nächsten Balken unangetastet bleibt. Wenn der Preis über dem MA liegt, dann Kaufen. Liegt der Preis unter dem MA, dann Verkaufen. Jedes Mal, wenn ein Signal eintrifft, sollte eine bestehende Position geschlossen werden. Die Zielvariable zeigt an, ob eine Position eröffnet werden soll oder nicht.

Ich empfehle nicht die Verwendung eines Skripts zur Generierung der Zielvariablen und der Prädiktorvariablen. Verwenden wir stattdessen einen Expert Advisor, der das Erkennen von logischen Fehlern im Code bei der Generierung einer Stichprobe sowie eine detaillierte Simulation des Eintreffens der Daten ermöglicht - dies wird dem Eintreffen der Daten im realen Handel ähnlich sein. Darüber hinaus werden wir in der Lage sein, unterschiedliche Eröffnungszeiten verschiedener Instrumente zu berücksichtigen, wenn die Zielvariable mit unterschiedlichen Symbolen arbeitet, sowie die Verzögerung beim Datenempfang und bei der Datenverarbeitung zu berücksichtigen, um zu verhindern, dass der Algorithmus in die Zukunft blickt, um Indikatorneuzeichnungen und eine für das Training ungeeignete Logik abzufangen. Infolgedessen werden die Prädiktoren auf dem Balken in Echtzeit berechnet, wenn das Modell in der Praxis angewendet wird.

Einige algorithmische Händler, insbesondere diejenigen, die maschinelles Lernen verwenden, geben an, dass Standardindikatoren meist nutzlos sind, da sie verzögert und vom Preis abgeleitet werden, d.h. sie liefern keine neuen Informationen, während Neuronale Netze jeden Indikator erstellen können. Die Möglichkeiten von Neuronalen Netzen sind in der Tat groß, aber sie erfordern oft eine Rechenleistung, die den meisten gewöhnlichen Händlern nicht zur Verfügung steht. Außerdem erfordert es Zeit, solche neuronalen Netze zu erlernen. Auf Entscheidungsbäumen basierende maschinelle Lernmethoden können bei der Erstellung neuer mathematischer Einheiten nicht mit neuronalen Netzen konkurrieren, da sie die Eingabedaten nicht transformieren. Sie gelten jedoch als effizienter als neuronale Netze, wenn es notwendig ist, direkte Abhängigkeiten zu identifizieren, insbesondere in großen und heterogenen Datenarrays. Tatsächlich besteht der Zweck von Neuronalen Netzen darin, neue Muster zu erzeugen, d.h. Parameter, die den Markt beschreiben. Modelle von Entscheidungsbäumen zielen auf die Identifizierung von Mustern aus den Mengen solcher Muster ab. Indem wir Standardindikatoren vom Terminal als Prädiktor verwenden, nehmen wir das Muster, das von Tausenden von Händlern in verschiedenen Börsen- und OTC-Märkten in verschiedenen Ländern verwendet wird. Daher können wir davon ausgehen, dass wir in der Lage sein werden, eine entgegengesetzte Abhängigkeit des Händlerverhaltens von Indikatorwerten zu identifizieren, die sich schließlich auf das Handelsinstrument auswirkt. Ich habe bisher noch keine Oszillatoren verwendet, daher bin ich selber interessiert, das Ergebnis zu sehen.

Die folgenden Indikatoren aus der Standardterminallieferung werden verwendet:

Die Indikatoren werden für alle in MetaTrader 5 verfügbaren Zeitrahmen bis hin zum Tagesrahmen berechnet.

Als ich diesen Artikel schrieb, fand ich heraus, dass die Werte der folgenden Indikatoren stark vom Datum des Testbeginns im Terminal abhängen, deshalb habe ich beschlossen, sie auszuschließen. Es ist möglich, die Differenz zwischen den Werten auf verschiedenen Balken für diese Indikatoren zu verwenden, aber das geht über diesen Artikel hinaus.

Die Liste der ausgeschlossenen Indikatoren:

Um mit CSV-Tabellen zu arbeiten, werden wir die wunderbare Bibliothek csv_fast.mqh von Aliaksandr Hryshyn verwenden. Die Eigenschaften der Bibliothek:


Komponenten des Expert Advisors

Grundlegende Strategie:

Ich entschied mich für eine Strategie mit einfachen Bedingungen als Basisstrategie, die das Signal erzeugt. Demnach sollte der Markteintritt erfolgen, wenn die folgenden Bedingungen erfüllt sind:

  1. Der Preis hat den gleitenden Durchschnittspreis überschritten.
  2. Nachdem Bedingung 1 erfüllt ist, hat der Preis zum ersten Mal den gekreuzten MA auf dem vorherigen Balken nicht berührt.

Dies war meine erste Strategie, die ich Anfang der 00er Jahre erstellt habe. Es ist eine einfache Strategie, die zur Trendklasse gehört. Sie zeigt gute Ergebnisse bei entsprechenden Teilen der Handelsgeschichte. Lassen Sie uns versuchen, die Zahl der Falscheingaben in flachen Bereichen mit Hilfe des maschinellen Lernens zu reduzieren.

Der Signalgenerator ist wie folgt:

//+-----------------------------------------------------------------+
//| Returns a buy or Sell signal - basic strategy                   |
//+-----------------------------------------------------------------+
bool Signal()
{
// Reset position opening blocking flag
   SellPrIMA=false;  // Open a pending sell order
   BuyPrIMA=false;   // Open a pending buy order
   SellNow=false;    // Open a market sell order
   BuyNow=false;     // Open a market buy order
   bool Signal=false;// Function operation result
   int BarN=0;       // The number of bars on which MA is not touched
   if(iOpen(Symbol(),Signal_MA_TF,0)>MA_Signal(0) && iLow(Symbol(),Signal_MA_TF,1)>MA_Signal(1))
   {
      for(int i=2; i<100; i++)
      {
         if(iLow(Symbol(),Signal_MA_TF,i)>MA_Signal(i))break;// Signal has already been processed on this cycle
         if(iClose(Symbol(),Signal_MA_TF,i+1)<MA_Signal(i+1) && iClose(Symbol(),Signal_MA_TF,i)>=MA_Signal(i))
         {
            for(int x=i+1; x<100; x++)
            {
               if(iLow(Symbol(),Signal_MA_TF,x)>MA_Signal(x))break;// Signal has already been processed on this cycle
               if(iHigh(Symbol(),Signal_MA_TF,x)<MA_Signal(x))
               {
                  BarN=x;
                  BuyNow=true;
                  break;
               }
            }
         }
      }
   }
   if(iOpen(Symbol(),Signal_MA_TF,0)<MA_Signal(0) && iHigh(Symbol(),Signal_MA_TF,1)<MA_Signal(1))
   {
      for(int i=2; i<100; i++)
      {
         if(iHigh(Symbol(),Signal_MA_TF,i)<MA_Signal(i))break;// Signal has already been processed on this cycle
         if(iClose(Symbol(),Signal_MA_TF,i+1)>MA_Signal(i+1) && iClose(Symbol(),Signal_MA_TF,i)<=MA_Signal(i))
         {
            for(int x=i+1; x<100; x++)
            {
               if(iHigh(Symbol(),Signal_MA_TF,x)<MA_Signal(x))break;// Signal has already been processed on this cycle
               if(iLow(Symbol(),Signal_MA_TF,x)>MA_Signal(x))
               {
                  BarN=x;
                  SellNow=true;
                  break;
               }
            }
         }
      }
   }
   if(BuyNow==true || SellNow==true)Signal=true;
   return Signal;
}


Erhalt von Prädiktoren:

Die Prädiktoren werden mit Hilfe von Funktionen ermittelt (ihr Code ist unten angehängt). Ich werde Ihnen jedoch zeigen, wie dies für eine große Anzahl von Indikatoren leicht möglich ist. Wir werden Indikatorwerte in drei Punkten verwenden: den ersten und zweiten geformten Balken, der es ermöglicht, das Niveau des Indikators zu bestimmen, und einen Balken mit einer Verschiebung von 15 - dies ermöglicht es, die Bewegungsdynamik des Indikators zu verstehen. Dies ist natürlich eine vereinfachte Art und Weise der Informationsbeschaffung und kann erheblich erweitert werden.

Alle Prädiktoren werden in eine Tabelle geschrieben, die im Arbeitsspeicher des Computers gebildet wird. Die Tabelle hat eine Zeile; sie wird später als numerischer Eingangsdatenvektor für den CatBoost-Modellinterpreter verwendet. 

#include "CSV fast.mqh";                 // Class for working with tables
CSV *csv_CB=new CSV();                   // Create a table class instance, in which current predictor values will be stored

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   CB_Tabl();// Creating a table with predictors
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Create a table with predictors                                   |
//+------------------------------------------------------------------+
void CB_Tabl()
{
//--- Columns for oscillators
   Size_arr_Buf_OSC=ArraySize(arr_Buf_OSC);
   Size_arr_Name_OSC=ArraySize(arr_Name_OSC);
   Size_TF_OSC=ArraySize(arr_TF_OSC);
   for(int n=0; n<Size_arr_Buf_OSC; n++)SummBuf_OSC=SummBuf_OSC+arr_Buf_OSC[n];
   Size_OSC=3*Size_TF_OSC*SummBuf_OSC;
   for(int S=0; S<3; S++)// Loop by the number of shifts
   {
      string Shift="0";
      if(S==0)Shift="1";
      if(S==1)Shift="2";
      if(S==2)Shift="15";
      for(int T=0; T<Size_TF_OSC; T++)// Loop by the number of timeframes
      {
         for(int o=0; o<Size_arr_Name_OSC; o++)// Loop by the number of indicators
         {
            for(int b=0; b<arr_Buf_OSC[o]; b++)// Loop by the number of indicator buffers
            {
               name_P=arr_Name_OSC[o]+"_B"+IntegerToString(b,0)+"_S"+Shift+"_"+arr_TF_OSC[T];
               csv_CB.Add_column(dt_double,name_P);// Add a new column with a name to identify a predictor
            }
         }
      }
   }
}
//+------------------------------------------------------------------+
//--- Call predictor calculation
//+------------------------------------------------------------------+
void Pred_Calc()
{
//--- Get information from oscillator indicators
   double arr_OSC[];
   iOSC_Calc(arr_OSC);
   for(int p=0; p<Size_OSC; p++)
   {
      csv_CB.Set_value(0,s(),arr_OSC[p],false);
   }
}
//+------------------------------------------------------------------+
//| Get values of oscillator indicators                              |
//+------------------------------------------------------------------+
void iOSC_Calc(double &arr_OSC[])
{
   ArrayResize(arr_OSC,Size_OSC);
   int n=0;// Indicator handle index
   int x=0;// Total number of iterations
   for(int S=0; S<3; S++)// Loop by the number of shifts
   {
      n=0;
      int Shift=0;
      if(S==0)Shift=1;
      if(S==1)Shift=2;
      if(S==2)Shift=15;
      for(int T=0; T<Size_TF_OSC; T++)// Loop by the number of timeframes
      {
         for(int o=0; o<Size_arr_Name_OSC; o++)// Loop by the number of indicators
         {
            for(int b=0; b<arr_Buf_OSC[o]; b++)// Loop by the number of indicator buffers
            {
               arr_OSC[x++]=iOSC(n, b,Shift);
            }
            n++;// Mark shift to the next indicator handle for calculation
         }
      }
   }
}
//+------------------------------------------------------------------+
//| Get the value of the indicator buffer                            |
//+------------------------------------------------------------------+
double iOSC(int OSC, int Bufer,int index)
{
   double MA[1]= {0.0};
   int handle_ind=arr_Handle[OSC];// Indicator handle
   ResetLastError();
   if(CopyBuffer(handle_ind,0,index,1,MA)<0)
   {
      PrintFormat("Failed to copy data from the OSC indicator, error code %d",GetLastError());
      return(0.0);
   }
   return (MA[0]);
}


Akkumulation und Markierung von Proben:

Um eine Probe zu erstellen und zu speichern, werden wir Prädiktorwerte akkumulieren, indem wir sie aus der Tabelle csv_CB in die Tabelle csv_Arhiv kopieren.

Wir lesen das Datum des vorhergehenden Signals, bestimmen den Ein- und Ausstiegspreis des Handels und definieren das Ergebnis, nach dem das entsprechende Etikett vergeben wird: "1" — positiv, "0" — negativ. Wir markieren auch die Art des durch das Signal durchgeführten Geschäfts. Dies wird weiter dazu beitragen, ein Saldendiagramm zu erstellen: "1" - Kauf und "-1" - Verkauf. Lassen Sie uns hier auch das finanzielle Ergebnis einer Handelsoperation berechnen. Für das finanzielle Ergebnis werden getrennte Spalten mit Kauf- und Verkaufsergebnissen verwendet: dies ist praktisch, wenn die Grundstrategie schwieriger ist oder Elemente des Positionsmanagements enthält, die das Ergebnis beeinflussen können. 

//+-----------------------------------------------------------------+
//| The function copies predictors to archive                       |
//+-----------------------------------------------------------------+
void Copy_Arhiv()
{
   int Strok_Arhiv=csv_Arhiv.Get_lines_count();// Number of rows in the table
   int Stroka_Load=0;// Starting row in the source table
   int Stolb_Load=1;// Starting column in the source table
   int Stroka_Save=0;// Starting row to write in the table
   int Stolb_Save=1;// Starting column to write in the table
   int TotalCopy_Strok=-1;// Number of rows to copy from the source. -1 copy to the last row
   int TotalCopy_Stolb=-1;// Number of columns to copy from the source, if -1 copy to the last column

   Stroka_Save=Strok_Arhiv;// Copy the last row
   csv_Arhiv.Copy_from(csv_CB,Stroka_Load,Stolb_Load,TotalCopy_Strok,TotalCopy_Stolb,Stroka_Save,Stolb_Save,false,false,false);// Copying function

//--- Calculate the financial result and set the target label, if it is not the first market entry
   int Stolb_Time=csv_Arhiv.Get_column_position("Time",false);// Find out the index of the "Time" column
   int Vektor_P=0;// Specify entry direction: "+1" - buy, "-1" - sell
   if(BuyNow==true)Vektor_P=1;// Buy entry
   else Vektor_P=-1;// Sell entry
   csv_Arhiv.Set_value(Strok_Arhiv,Stolb_Time+1,Vektor_P,false);
   if(Strok_Arhiv>0)
   {
      int Stolb_Target_P=csv_Arhiv.Get_column_position("Target_P",false);// Find out the index of the "Time" column
      int Load_Vektor_P=csv_Arhiv.Get_int(Strok_Arhiv-1,Stolb_Target_P,false);// Find out the previous operation type
      datetime Load_Data_Start=StringToTime(csv_Arhiv.Get_string(Strok_Arhiv-1,Stolb_Time,false));// Read the position opening date
      datetime Load_Data_Stop=StringToTime(csv_Arhiv.Get_string(Strok_Arhiv,Stolb_Time,false));// Read the position closing date
      double F_Rez_Buy=0.0;// Financial result in case of a buy operation
      double F_Rez_Sell=0.0;// Financial result in case of a sell operation
      double P_Open=0.0;// Position open price
      double P_Close=0.0;// Position close price
      int Metka=0;// Label for target variable
      P_Open=iOpen(Symbol(),Signal_MA_TF,iBarShift(Symbol(),Signal_MA_TF,Load_Data_Start,false));
      P_Close=iOpen(Symbol(),Signal_MA_TF,iBarShift(Symbol(),Signal_MA_TF,Load_Data_Stop,false));
      F_Rez_Buy=P_Close-P_Open;// Previous entry was buying
      F_Rez_Sell=P_Open-P_Close;// Previous entry was selling
      if((F_Rez_Buy-comission*Point()>0 && Load_Vektor_P>0) || (F_Rez_Sell-comission*Point()>0 && Load_Vektor_P<0))Metka=1;
      else Metka=0;
      csv_Arhiv.Set_value(Strok_Arhiv-1,Stolb_Time+2,Metka,false);// Write label to a cell
      csv_Arhiv.Set_value(Strok_Arhiv-1,Stolb_Time+3,F_Rez_Buy,false);// Write the financial result of a conditional buy operation to the cell
      csv_Arhiv.Set_value(Strok_Arhiv-1,Stolb_Time+4,F_Rez_Sell,false);// Write the financial result of a conditional sell operation to the cell
      csv_Arhiv.Set_value(Strok_Arhiv,Stolb_Time+2,-1,false);// Add a negative label to the labels to control labels
   }
}


Wir verwenden das Modell:

Verwenden wir die Klasse "Catboost.mqh" von Aliaksandr Hryshyn, die von hier heruntergeladen werden kann, um die mit dem Modell CatBoost erhaltenen Daten zu interpretieren.

Ich habe die Tabelle "csv_Chek" für das Debugging hinzugefügt, in der der Wert des Modells CatBoost bei Bedarf gespeichert wird.

//+-----------------------------------------------------------------+
//| The function applies predictors in the CatBoost model           |
//+-----------------------------------------------------------------+
void Model_CB()
{
   CB_Siganl=1;
   csv_CB.Get_array_from_row(0,1,Solb_Copy_CB,features);
   double model_result=Catboost::ApplyCatboostModel(features,TreeDepth,TreeSplits,BorderCounts,Borders,LeafValues);
   double result=Logistic(model_result);
   if (result<Porog || result>Pridel)
   {
      BuyNow=false;
      SellNow=false;
      CB_Siganl=0;
   }
   if(Use_Save_Result==true)
   {
      int str=csv_Chek.Add_line();
      csv_Chek.Set_value(str,1,TimeToString(iTime(Symbol(),PERIOD_CURRENT,0),TIME_DATE|TIME_MINUTES));
      csv_Chek.Set_value(str,2,result);
   }
}


Speichern einer Auswahl in einer Datei:

Speichern der Tabelle am Ende des Testdurchlaufs, Angabe des Dezimaltrennzeichens als Komma

//+------------------------------------------------------------------+
// Function writing predictors to a file                             |
//+------------------------------------------------------------------+
void Save_Pred_All()
{
//--- Save predictors to a file
   if(Save_Pred==true)
   {
      int Stolb_Target=csv_Arhiv.Get_column_position("Target_100",false);// Find out the index of the Target_100 column
      csv_Arhiv.Filter_rows_add(Stolb_Target,op_neq,-1,true);// Exclude lines with label "-1" in target variable
      csv_Arhiv.Filter_rows_apply(true);// Apply filter

      csv_Arhiv.decimal_separator=',';// Set a decimal separator
      string name=Symbol()+"CB_Save_Pred.csv";// File name
      csv_Arhiv.Write_to_file("Save_Pred\\"+name,true,true,true,true,false,5);// Save the file up to 5 characters
   }
//--- Save the model values to a debug file
   if(Use_Save_Result==true)
   {
      csv_Chek.decimal_separator=',';// Set a decimal separator
      string name=Symbol()+"Chek.csv";// File name
      csv_Chek.Write_to_file("Save_Pred\\"+name,true,true,true,true,false,5);// Save file up to 5 decimal places
   }
}


Nutzerdefinierte Qualitätsbewertung für Strategieeinstellungen:

Als Nächstes müssen wir geeignete Einstellungen für den Indikator finden, der vom Basismodell verwendet wird. Lassen Sie uns also einen Wert für den Strategietester berechnen, der das Minimum an Positionen bestimmt und den Prozentsatz der profitablen Positionen zurückgibt. Je mehr Objekte für das Training zur Verfügung stehen (Positionen), desto ausgewogener wird die Stichprobe sein (je näher der Prozentsatz der profitablen Positionen bei 50% liegt), desto besser wird das Training sein. Die nutzerdefinierte Variable wird in der nachstehenden Funktion berechnet.

//+------------------------------------------------------------------+
//| Custom variable calculating function                             |
//+------------------------------------------------------------------+
double CustomPokazatelf(int VariantPokazatel)
{
   double custom_Pokazatel=0.0;
   if(VariantPokazatel==1)
   {
      double Total_Tr=(double)TesterStatistics(STAT_TRADES);
      double Pr_Tr=(double)TesterStatistics(STAT_PROFIT_TRADES);
      if(Total_Tr>0 && Total_Tr>15000)custom_Pokazatel=Pr_Tr/Total_Tr*100.0;
   }
   return(custom_Pokazatel);
}


Steuerung der Ausführungshäufigkeit des Hauptcodeteils:

Handelsentscheidungen sollten bei einer neuen Balken-Eröffnung generiert werden. Dies wird durch die folgende Funktion überprüft:

//+-----------------------------------------------------------------+
//| Returns TRUE if a new bar has appeared on the current TF        |
//+-----------------------------------------------------------------+
bool isNewBar()
{
   datetime tm[];
   static datetime prevBarTime=0;

   if(CopyTime(Symbol(),Signal_MA_TF,0,1,tm)<0)
   {
      Print("%s CopyTime error = %d",__FUNCTION__,GetLastError());
   }
   else
   {
      if(prevBarTime!=tm[0])
      {
         prevBarTime=tm[0];
         return true;
      }
      return false;
   }
   return true;
}

Handelsfunktionen:

Der Expert Advisor verwendet die Handelsklasse "cPoza6". Die Idee wurde von mir entwickelt, und die Hauptimplementierung wurde von Vasiliy Pushkaryov geliefert. Ich habe die Klasse an der Moskauer Börse getestet, aber ihr Konzept wurde nicht vollständig umgesetzt. Deshalb lade ich alle ein, es zu verbessern - nämlich, es braucht Funktionen für die Arbeit mit der Geschichte. Für diesen Artikel habe ich die Überprüfung der Kontoarten deaktiviert. Seien Sie also bitte vorsichtig. Die Klasse wurde ursprünglich für Netting-Konten entwickelt, aber ihre Funktionsweise wird für den Expert Advisor ausreichen, damit die Leser in diesem Artikel maschinelles Lernen studieren können.


Hier ist der Code des Expert Advisors ohne Funktionsbeschreibungen (der Klarheit halber).

Wenn wir einige Hilfsfunktionen nicht einbeziehen und obige Funktionsbeschreibungen entfernen, sieht der EA-Code in diesem Schritt wie folgt aus:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
//--- Check the correctness of model response interpretation values
   if(Porog>=Pridel || Pridel<=Porog)return(INIT_PARAMETERS_INCORRECT);
   if(Use_Pred_Calc==true)
   {
      if(Init_Pred()==INIT_FAILED)return(INIT_FAILED);// Initialize indicator handles
      CB_Tabl();// Creating a table with predictors
      Solb_Copy_CB=csv_CB.Get_columns_count()-3;// Number of columns in the predictor table
   }
// Declare handle_MA_Slow
   handle_MA_Signal=iMA(Symbol(),Signal_MA_TF,Signal_MA_Period,1,Signal_MA_Metod,Signal_MA_Price);
   if(handle_MA_Signal==INVALID_HANDLE)
   {
      PrintFormat("Failed to create handle of the handle_MA_Signal indicator for the symbol %s/%s, error code %d",
                  Symbol(),EnumToString(Period()),GetLastError());
      return(INIT_FAILED);
   }
//--- Create a table to write model values - for debugging purposes
   if(Use_Save_Result==true)
   {
      csv_Chek.Add_column(dt_string,"Data");
      csv_Chek.Add_column(dt_double,"Rez");
   }
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   if(Save_Pred==true)Save_Pred_All();// Call a function to write predictors to a file
   delete csv_CB;// Delete the class instance
   delete csv_Arhiv;// Delete the class instance
   delete csv_Chek;// Delete the class instance
}
//+------------------------------------------------------------------+
//| Test completion event handler                                    |
//+------------------------------------------------------------------+
double OnTester()
{
   return(CustomPokazatelf(1));
}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
//--- Operations are only executed when the next bar appears
   if(!isNewBar()) return;
//--- Get information on trading environment (deals/orders)
   OpenOrdersInfo();
//--- Get signal from the basic strategy
   if(Signal()==true)
   {
//--- Calculate predictors
      if(Use_Pred_Calc==true)Pred_Calc();
//---Apply the CatBoost model
      if(Use_Model_CB==true)Model_CB();
//--- If there is an open position at the signal generation time, close it
      if(PosType!="0")ClosePositions("Close Signal");
//--- Open a new position
      if (BuyNow==true)OpenPositions(BUY,1,0,0,"Open_Buy");
      if (SellNow==true)OpenPositions(SELL,1,0,0,"Open_Sell");
//--- Copy the table with the current predictors to the archive table
      if(Save_Pred==true)Copy_Arhiv();
   }
}


Einstellungen für externe Expert Advisors:

Nachdem wir nun den EA-Funktionscode betrachtet haben, wollen wir sehen, welche Einstellungen der EA hat:

1. Konfigurieren der Aktionen von Prädiktoren:

2. MA-Indikatorparameter für das grundlegende Strategiesignal:

3. Anwendungsparameter des CatBoost-Modells:


Die richtigen strategischen Grundeinstellungen finden

Jetzt gilt es den grundlegenden Strategieindikator optimieren. Wählen Sie ein nutzerdefiniertes Kriterium zur Bewertung der Qualität der Strategieeinstellungen. Im Rahmen des Artikels wurden Tests mit den zusammengeführten Si-Futures-Kontrakten des Brokers Otkritie für den Basiswert USDRUB_TOM durchgeführt. Das Instrument heißt "Si Splice". Der Zeitraum reicht vom 01.06.2014 bis zum 31.10.2020 mit dem Zeitrahmen M1. Testmodellierung: 1 Minute OHLC.

Optimierungsparameter des Expert Advisors:


Ergebnisse der Optimierung

Abb. 1 "Optimierungsergebnisse".


Aus diesen Ergebnissen müssen wir Werte mit einem hohen nutzerdefinierten Parameter auswählen — vorzugsweise 35% und höher, mit einer Anzahl von 15.000 Positionen und mehr (je mehr, desto besser). Optional können auch andere ökonometrische Variablen analysiert werden.

Ich habe den folgenden Parametersatz vorbereitet, um das Potenzial der Erstellung von Handelsstrategien mit Hilfe des maschinellen Lernens zu demonstrieren:

Wir führen einen einzigen Test durch und überprüfen die Ergebnisgrafik.

Abb. 2 Saldo vor dem Training

Abb. 2 "Saldo vor dem Training"

Solche Strategieeinstellungen können im Handel kaum verwendet werden. Das Signal ist sehr verrauscht und führt zu einer Menge falscher Eröffnungen auf. Versuchen wir, sie zu eliminieren. Im Gegensatz zu denjenigen, die mehrere Parameter verschiedener Indikatoren testen, um ein Signal zu filtern und damit zusätzliche Rechenleistung in Bereichen aufwenden, in denen es keinen oder nur sehr selten (was statistisch unbedeutend ist) Indikatorwerte gab, werden wir nur mit den Bereichen arbeiten, in denen Indikatorwerte tatsächlich Informationen lieferten.

Ändern wir die EA-Einstellungen, um die Prädiktoren zu berechnen und zu speichern. Machen wir jetzt einen einzigen Test:

Konfigurieren der Aktionen von Prädiktoren:

Der Rest der Einstellungen bleibt unverändert. Lassen Sie uns einen einzigen Test im Strategietester durchführen. Die Berechnungen werden langsamer durchgeführt, da wir jetzt Daten aus fast 2000 Indikatorpuffern berechnen und sammeln sowie andere Prädiktormodelle berechnen.

Finden Sie die Datei im Ausführungspfad der Agenten (Ich verwende einen 'portable' Modus, also ist meiner "F:\FX\FX\Otkritie Broker_Demo\Tester\Agent-127.0.0.1-3002\MQL5\Files", "3002" (bezeichnet den Thread, der für die Agenten-Operation verwendet wird ) und überprüfen Sie den Inhalt. Wenn die Datei mit der Tabelle erfolgreich geöffnet wird, dann ist alles in Ordnung.


Abb. 3  "Zusammenfassung der Prädiktortabelle"

Aufspalten der Probe

Für weiteres Training teilen Sie die Probe in drei Teile auf und speichern Sie diese in Dateien:

Um das Beispiel aufzuteilen, verwenden Sie das Skript CB_CSV_to3x.mq5.

Geben Sie den Pfad zu dem Verzeichnis an, in dem die Erstellung eines Handelsmodells durchgeführt wird, sowie den Namen der Datei, die das Muster enthält.

Eine weitere erstellte Datei ist Test_CB_Setup_0_00000000000 — sie gibt die Indizes der Spalten an, die mit 0 beginnen und für die folgende Bedingung gelten kann: Deaktivieren Sie das Label "Auxiliary" und markieren Sie die Zielspalte mit "Label". Der Inhalt der Datei für unser Beispiel ist wie folgt:

2408    Auxiliary
2409    Auxiliary
2410    Label
2411    Auxiliary
2412    Auxiliary

Die Datei befindet sich an der gleichen Stelle, an der sich auch das durch das Skript vorbereitete Muster befindet.


CatBoost Parameter

CatBoost hat verschiedene Parameter und Einstellungen, die das Trainingsergebnis beeinflussen; sie werden alle hier erklärt. Ich werde hier die Hauptparameter (und ihre Schlüssel, falls vorhanden) erwähnen, die einen größeren Einfluss auf die Modelltrainingsergebnisse haben und die im CB_Bat-Skript konfiguriert werden können:

Das Skript ermöglicht die Suche nach einer Reihe von Parametern der Modelleinrichtung. Wählen Sie dazu ein anderes Objekt als NONE und geben Sie den Startwert, den Endwert und den Schritt an.


Lernstrategie

Ich unterteile die Lernstrategie in drei Stufen:

  1. Grundeinstellungen sind Parameter, die für die Tiefe und die Anzahl der Bäume im Modell verantwortlich sind, sowie für die Trainingsrate, die Klassengewichte und andere Einstellungen, um den Trainingsprozess zu starten. Diese Parameter werden nicht durchsucht; in den meisten Fällen reichen die vom Skript generierten Standardeinstellungen aus.
  2. Suche nach optimalen Aufteilungsparametern — CatBoost verarbeitet die Tabelle der Prädiktoren vor, um Wertebereiche entlang der Gittergrenzen zu durchsuchen, und daher müssen wir ein Gitter finden, in dem das Training besser ist. Es macht Sinn, über alle Gittertypen mit einem Bereich von 8-512 zu iterieren; ich benutze bei jedem Wert Stufenschritte: 8, 16, 32 und so weiter.
  3. Konfigurieren wir das Skript erneut, geben Sie das gefundene Prädiktor-Quantisierungsgitter an, und danach können wir zu weiteren Parametern übergehen. Normalerweise verwende ich "Seed" nur im Bereich von 1-1000. 

In diesem Artikel werden wir für die erste Stufe der "Lernstrategie" die Standardeinstellungen CB_Bat verwenden. Die Splitting-Methode wird auf "MinEntropie" eingestellt, das Raster wird Parameter von 16 bis 512 mit einem Schritt von 16 testen.

Um die oben beschriebenen Parameter einzurichten, verwenden wir das Skript "CB_Bat", das Textdateien mit den erforderlichen Schlüsseln für Trainingsmodelle sowie eine Hilfsdatei erstellt:

Wir könnten eine Datei erstellen, die Aktionen nach dem Training sequentiell ausführt. Aber um die CPU-Auslastung zu optimieren (was in früheren Versionen von CatBoost besonders wichtig war), starte ich nach dem Training 6 Dateien.


Modell-Training

Sobald die Dateien fertig sind, benennen wir die Datei "_00_Dir_All.txt" in "_00_Dir_All.bat" um und führen sie aus - sie erstellt die erforderlichen Verzeichnisse zum Auffinden von Modellen und ändert die Erweiterung anderer Dateien in "bat".

Nun enthält unser Projektverzeichnis den Ordner "Setup" mit folgendem Inhalt:

Führen Sie "_01_Train_All.bat" aus und beobachten Sie den Trainingsprozess.

Abb. 4 CatBoost-Trainingsprozess


In der obigen Abbildung habe ich zur Beschreibung der Spalten rote Zahlen hinzugefügt:

  1. Die Anzahl der Bäume, die der Anzahl der Iterationen entspricht
  2. Das Ergebnis der Berechnung der ausgewählten Verlustfunktion auf der Trainingsprobe
  3. Das Ergebnis der Berechnung der gewählten Verlustfunktion auf der Kontrollprobe
  4. Das beste Ergebnis der Berechnung der gewählten Verlustfunktion auf der Kontrollprobe
  5. Die seit Beginn des Modell-Trainings tatsächlich verstrichene Zeit
  6. Geschätzte verbleibende Zeit bis zum Ende des Trainings, wenn alle durch die Einstellungen festgelegten Bäume trainiert werden

Wenn wir in den Skript-Einstellungen einen Suchbereich wählen, werden die Modelle in einer Schleife so oft trainiert, wie es der Dateiinhalt erfordert:

FOR %%a IN (*.) DO (                                                                                                                                                                                                                                                                            
catboost-0.24.1.exe fit  --learn-set train.csv   --test-set test.csv     --column-description %%a        --has-header    --delimiter ;   --model-format CatboostBinary,CPP       --train-dir ..\Rezultat\RS_16\result_4_%%a      --depth 6       --iterations 1000       --nan-mode Forbidden    --learning-rate 0.03    --rsm 1         --fold-permutation-block 1      --boosting-type Plain   --l2-leaf-reg 6         --loss-function Logloss         --use-best-model        --eval-metric Logloss   --custom-metric Logloss         --od-type Iter  --od-wait 100   --random-seed 0         --random-strength 1     --auto-class-weights SqrtBalanced       --sampling-frequency PerTreeLevel       --border-count 16       --feature-border-type MinEntropy        --output-borders-file quant_4_00016.csv         --bootstrap-type Bayesian       --bagging-temperature 1         --leaf-estimation-method Newton         --leaf-estimation-iterations 10                
catboost-0.24.1.exe fit  --learn-set train.csv   --test-set test.csv     --column-description %%a        --has-header    --delimiter ;   --model-format CatboostBinary,CPP       --train-dir ..\Rezultat\RS_32\result_4_%%a      --depth 6       --iterations 1000       --nan-mode Forbidden    --learning-rate 0.03    --rsm 1         --fold-permutation-block 1      --boosting-type Plain   --l2-leaf-reg 6         --loss-function Logloss         --use-best-model        --eval-metric Logloss   --custom-metric Logloss         --od-type Iter  --od-wait 100   --random-seed 0         --random-strength 1     --auto-class-weights SqrtBalanced       --sampling-frequency PerTreeLevel       --border-count 32       --feature-border-type MinEntropy        --output-borders-file quant_4_00032.csv         --bootstrap-type Bayesian       --bagging-temperature 1         --leaf-estimation-method Newton         --leaf-estimation-iterations 10                
catboost-0.24.1.exe fit  --learn-set train.csv   --test-set test.csv     --column-description %%a        --has-header    --delimiter ;   --model-format CatboostBinary,CPP       --train-dir ..\Rezultat\RS_48\result_4_%%a      --depth 6       --iterations 1000       --nan-mode Forbidden    --learning-rate 0.03    --rsm 1         --fold-permutation-block 1      --boosting-type Plain   --l2-leaf-reg 6         --loss-function Logloss         --use-best-model        --eval-metric Logloss   --custom-metric Logloss         --od-type Iter  --od-wait 100   --random-seed 0         --random-strength 1     --auto-class-weights SqrtBalanced       --sampling-frequency PerTreeLevel       --border-count 48       --feature-border-type MinEntropy        --output-borders-file quant_4_00048.csv         --bootstrap-type Bayesian       --bagging-temperature 1         --leaf-estimation-method Newton         --leaf-estimation-iterations 10                
catboost-0.24.1.exe fit  --learn-set train.csv   --test-set test.csv     --column-description %%a        --has-header    --delimiter ;   --model-format CatboostBinary,CPP       --train-dir ..\Rezultat\RS_64\result_4_%%a      --depth 6       --iterations 1000       --nan-mode Forbidden    --learning-rate 0.03    --rsm 1         --fold-permutation-block 1      --boosting-type Plain   --l2-leaf-reg 6         --loss-function Logloss         --use-best-model        --eval-metric Logloss   --custom-metric Logloss         --od-type Iter  --od-wait 100   --random-seed 0         --random-strength 1     --auto-class-weights SqrtBalanced       --sampling-frequency PerTreeLevel       --border-count 64       --feature-border-type MinEntropy        --output-borders-file quant_4_00064.csv         --bootstrap-type Bayesian       --bagging-temperature 1         --leaf-estimation-method Newton         --leaf-estimation-iterations 10                
catboost-0.24.1.exe fit  --learn-set train.csv   --test-set test.csv     --column-description %%a        --has-header    --delimiter ;   --model-format CatboostBinary,CPP       --train-dir ..\Rezultat\RS_80\result_4_%%a      --depth 6       --iterations 1000       --nan-mode Forbidden    --learning-rate 0.03    --rsm 1         --fold-permutation-block 1      --boosting-type Plain   --l2-leaf-reg 6         --loss-function Logloss         --use-best-model        --eval-metric Logloss   --custom-metric Logloss         --od-type Iter  --od-wait 100   --random-seed 0         --random-strength 1     --auto-class-weights SqrtBalanced       --sampling-frequency PerTreeLevel       --border-count 80       --feature-border-type MinEntropy        --output-borders-file quant_4_00080.csv         --bootstrap-type Bayesian       --bagging-temperature 1         --leaf-estimation-method Newton         --leaf-estimation-iterations 10                
catboost-0.24.1.exe fit  --learn-set train.csv   --test-set test.csv     --column-description %%a        --has-header    --delimiter ;   --model-format CatboostBinary,CPP       --train-dir ..\Rezultat\RS_96\result_4_%%a      --depth 6       --iterations 1000       --nan-mode Forbidden    --learning-rate 0.03    --rsm 1         --fold-permutation-block 1      --boosting-type Plain   --l2-leaf-reg 6         --loss-function Logloss         --use-best-model        --eval-metric Logloss   --custom-metric Logloss         --od-type Iter  --od-wait 100   --random-seed 0         --random-strength 1     --auto-class-weights SqrtBalanced       --sampling-frequency PerTreeLevel       --border-count 96       --feature-border-type MinEntropy        --output-borders-file quant_4_00096.csv         --bootstrap-type Bayesian       --bagging-temperature 1         --leaf-estimation-method Newton         --leaf-estimation-iterations 10                
catboost-0.24.1.exe fit  --learn-set train.csv   --test-set test.csv     --column-description %%a        --has-header    --delimiter ;   --model-format CatboostBinary,CPP       --train-dir ..\Rezultat\RS_112\result_4_%%a     --depth 6       --iterations 1000       --nan-mode Forbidden    --learning-rate 0.03    --rsm 1         --fold-permutation-block 1      --boosting-type Plain   --l2-leaf-reg 6         --loss-function Logloss         --use-best-model        --eval-metric Logloss   --custom-metric Logloss         --od-type Iter  --od-wait 100   --random-seed 0         --random-strength 1     --auto-class-weights SqrtBalanced       --sampling-frequency PerTreeLevel       --border-count 112      --feature-border-type MinEntropy        --output-borders-file quant_4_00112.csv         --bootstrap-type Bayesian       --bagging-temperature 1         --leaf-estimation-method Newton         --leaf-estimation-iterations 10                
catboost-0.24.1.exe fit  --learn-set train.csv   --test-set test.csv     --column-description %%a        --has-header    --delimiter ;   --model-format CatboostBinary,CPP       --train-dir ..\Rezultat\RS_128\result_4_%%a     --depth 6       --iterations 1000       --nan-mode Forbidden    --learning-rate 0.03    --rsm 1         --fold-permutation-block 1      --boosting-type Plain   --l2-leaf-reg 6         --loss-function Logloss         --use-best-model        --eval-metric Logloss   --custom-metric Logloss         --od-type Iter  --od-wait 100   --random-seed 0         --random-strength 1     --auto-class-weights SqrtBalanced       --sampling-frequency PerTreeLevel       --border-count 128      --feature-border-type MinEntropy        --output-borders-file quant_4_00128.csv         --bootstrap-type Bayesian       --bagging-temperature 1         --leaf-estimation-method Newton         --leaf-estimation-iterations 10                
catboost-0.24.1.exe fit  --learn-set train.csv   --test-set test.csv     --column-description %%a        --has-header    --delimiter ;   --model-format CatboostBinary,CPP       --train-dir ..\Rezultat\RS_144\result_4_%%a     --depth 6       --iterations 1000       --nan-mode Forbidden    --learning-rate 0.03    --rsm 1         --fold-permutation-block 1      --boosting-type Plain   --l2-leaf-reg 6         --loss-function Logloss         --use-best-model        --eval-metric Logloss   --custom-metric Logloss         --od-type Iter  --od-wait 100   --random-seed 0         --random-strength 1     --auto-class-weights SqrtBalanced       --sampling-frequency PerTreeLevel       --border-count 144      --feature-border-type MinEntropy        --output-borders-file quant_4_00144.csv         --bootstrap-type Bayesian       --bagging-temperature 1         --leaf-estimation-method Newton         --leaf-estimation-iterations 10                
catboost-0.24.1.exe fit  --learn-set train.csv   --test-set test.csv     --column-description %%a        --has-header    --delimiter ;   --model-format CatboostBinary,CPP       --train-dir ..\Rezultat\RS_160\result_4_%%a     --depth 6       --iterations 1000       --nan-mode Forbidden    --learning-rate 0.03    --rsm 1         --fold-permutation-block 1      --boosting-type Plain   --l2-leaf-reg 6         --loss-function Logloss         --use-best-model        --eval-metric Logloss   --custom-metric Logloss         --od-type Iter  --od-wait 100   --random-seed 0         --random-strength 1     --auto-class-weights SqrtBalanced       --sampling-frequency PerTreeLevel       --border-count 160      --feature-border-type MinEntropy        --output-borders-file quant_4_00160.csv         --bootstrap-type Bayesian       --bagging-temperature 1         --leaf-estimation-method Newton         --leaf-estimation-iterations 10                
)       

Sobald das Training abgeschlossen ist, werden wir alle 6 verbleibenden bat-Dateien auf einmal starten, um Trainingsergebnisse in Form von Etiketten und statistischen Werten zu erhalten.


Ausdrückliche Bewertung der Lernergebnisse

Verwenden wir das Skript CB_Calc_Svod.mq5, um die metrischen Variablen der Modelle und ihre finanziellen Ergebnisse zu erhalten.

Dieses Skript verfügt über einen Filter für die Auswahl von Modellen nach der Endbilanz der Untersuchungsstichprobe: Wenn die Bilanz höher als ein bestimmter Wert ist, kann ein Bilanzgraph aus der Stichprobe erstellt und die Stichprobe in mqh konvertiert und in einem separaten Verzeichnis des CatBoost-Modellprojekts gespeichert werden.

Warten Sie, bis das Skript abgeschlossen ist - in diesem Fall sehen Sie das neu erstellte "Analiz", das die Datei CB_Svod.csv enthält, und die Bilanzgrafiken nach dem Modellnamen, falls deren Darstellung in den Einstellungen ausgewählt wurde, sowie das Verzeichnis "Models_mqh", das die in das mqh-Format konvertierten Modelle enthält.

Die Datei "CB_Svod.csv" enthält die Metriken der einzelnen Modelle, für die die einzelnen Stichproben erstellt wurden, sowie die Finanzergebnisse.


Abb. 5 Teil der Tabelle mit den Ergebnissen der Modellerstellung - CB_Svod.csv


Wählen Sie das gewünschte Modell aus dem Unterverzeichnis Models_mqh des Verzeichnisses aus, in dem unsere Modelle trainiert wurden, und fügen Sie es dem Verzeichnis Expert Advisor hinzu. Kommentieren Sie die Zeile mit leeren Puffern am Anfang des EA-Codes mit "//" aus. Jetzt brauchen wir nur noch die Modelldatei mit dem EA zu verbinden:

//If the CatBoost model is in an mqh file, comment the below line
//uint TreeDepth[];uint TreeSplits[];uint BorderCounts[];float Borders[];double LeafValues[];double Scale[];double Bias[];
#include "model_RS_208_0.mqh";                 // Model file

Nachdem Sie den Expert Advisor kompiliert haben, setzen Sie die Einstellung "Apply CatBoost model on data" auf "true", deaktivieren Sie die Probenspeicherung und führen Sie den Strategietester mit den folgenden Parametern aus.

1. Konfigurieren der Aktionen von Prädiktoren:

2. MA-Indikatorparameter für das grundlegende Strategiesignal:

3. Anwendungsparameter des CatBoost-Modells:


Das folgende Ergebnis wurde für den gesamten Probenzeitraum erhalten.


Abb. 6 Saldenkurve nach Training über den Zeitraum 01.06.2014 - 31.10.2020


Vergleichen wir zwei Saldenkurve auf dem Intervall vom 01.08.2019 bis 31.10.2020, das außerhalb der Trainingszeit liegt - dies entspricht der Stichprobe exam.csv, vor und nach dem Training.


Abb. 7 Bilanz vor Training für den Zeitraum 01.08.2019 - 31.10.2020



Abb. 8 Saldenkurve nach Training für den Zeitraum 01.08.2019 - 31.10.2020


Die Ergebnisse sind nicht sehr beeindruckend, aber es kann festgestellt werden, dass die Haupthandelsregel "Geldverlust vermeiden" eingehalten wird. Selbst wenn wir ein anderes Modell aus der Datei CB_Svod.csv wählen würden, wäre der Effekt immer noch positiv, denn das finanzielle Ergebnis des erfolglosesten Modells, das wir erhalten haben, beträgt -25 Punkte, und das durchschnittliche finanzielle Ergebnis aller Modelle beträgt 3889,9 Punkte.


Abb. 9 Finanzielles Ergebnis der trainierten Modelle für den Zeitraum 01.08.2019 - 31.10.2020


Analyse der Prädiktoren

Jedes Modellverzeichnis (für mich MQL5\Files\CB_Stat_03p50Q\Rezultat\RS_208\resultat_4_Test_CB_Setup_0_000000000) hat 3 Dateien:

Der Inhalt wird unterschiedlich sein, je nachdem, welche "Modellanalysemethode" bei der Generierung der Dateien für das Training gewählt wird. Betrachten wir den Inhalt mit "PredictionValuesChange".


Abb. 10 Zusammenfassende Tabelle der Analyse der Wichtigkeit der Prädiktoren

Aus der Bewertung der Bedeutung des Prädiktors können wir schließen, dass die ersten vier Prädiktoren für das resultierende Modell durchweg wichtig sind. Bitte beachten Sie, dass die Bedeutung der Prädikatoren nicht nur vom Modell selbst, sondern auch von der ursprünglichen Stichprobe abhängt. Wenn der Prädiktor in dieser Stichprobe nicht genügend Werte hatte, kann er nicht objektiv bewertet werden. Diese Methode erlaubt es, die allgemeine Idee der Wichtigkeit des Prädiktors zu verstehen. Seien Sie jedoch bitte vorsichtig damit, wenn Sie mit auf Handelssymbolen basierenden Stichproben arbeiten.

Schlussfolgerungen

  1. Die Wirksamkeit von Methoden des maschinellen Lernens, wie z.B. Gradientenverstärkung, kann mit der einer endlosen Iteration von Parametern und der manuellen Schaffung zusätzlicher Handelsbedingungen in dem Bemühen um eine Verbesserung der Strategieleistung verglichen werden.
  2. Standard MetaTrader 5 Indikatoren können für die Zwecke des maschinellen Lernens helfen.
  3. CatBoost - ist eine hochwertige Bibliothek mit einem Wrapper, der die effiziente Nutzung von Gradienten-Boosting ermöglicht, ohne Python oder R lernen zu müssen.


Schlussfolgerung

Der Zweck dieses Artikels ist es, Ihre Aufmerksamkeit auf maschinelles Lernen zu lenken. Ich hoffe wirklich, dass die detaillierte Beschreibung der Methodik und die zur Verfügung gestellten Reproduktionswerkzeuge dazu führen werden, dass neue Fans des maschinellen Lernens auftauchen. Lassen Sie uns gemeinsam versuchen, neue Ideen zum maschinellen Lernen zu finden, insbesondere ideale Ideen für die Suche nach Prädiktoren. Die Qualität eines Modells hängt von den Eingabedaten und dem Ziel ab, und wenn wir unsere Anstrengungen vereinen, können wir das gewünschte Ergebnis schneller erreichen.

Sie sind mehr als willkommen, Fehler in meinem Artikel und Code zu melden.