English Русский 中文 Español 日本語 Português
preview
Techniken des MQL5-Assistenten, die Sie kennen sollten (Teil 04): Die Lineare Diskriminanzanalyse

Techniken des MQL5-Assistenten, die Sie kennen sollten (Teil 04): Die Lineare Diskriminanzanalyse

MetaTrader 5Handelssysteme | 13 Dezember 2022, 10:25
508 0
Stephen Njuki
Stephen Njuki

Die Lineare Diskriminanzanalyse (LDA) ist eine sehr verbreitete Technik für die Reduktion von Dimensionen für Klassifizierungsprobleme. Wenn Sie , wie die Kohonen-Maps im vorherigen Artikel, hochdimensionale Daten (also mit einer großen Anzahl von Attributen oder Variablen) klassifizieren wollen, hilft Ihnen LDA, Ihre Daten so umzuwandeln, dass die Klassen so eindeutig wie möglich werden. Strenger ausgedrückt, LDA findet die lineare Projektion Ihrer Daten in einen Unterraum mit weniger Dimensionen, das ein gewisses Maß an optimierte Klassifizierung bedeutet. Die Dimension dieses Unterraums ist nie größer als die Anzahl der Klassen. In diesem Artikel werden wir uns ansehen, wie LDA als Signal, Trailing-Indikator und Geldmanagement-Tool verwendet werden kann. Doch zunächst wollen wir uns eine Definition ansehen und uns dann zu den Anwendungen vorarbeiten. 

LDA ist den Techniken von PCA, QDA und ANOVA sehr ähnlich, und die Tatsache, dass sie alle gewöhnlich abgekürzt werden, ist nicht sehr hilfreich. In diesem Artikel sollen diese verschiedenen Techniken nicht vorgestellt oder erklärt werden, sondern lediglich ihre Unterschiede herausgestellt werden.

1) Hauptkomponentenanalyse (PCA):

LDA ist der PCA (Principal components analysis oder Hauptkomponentenanalyse) sehr ähnlich: Einige haben sogar gefragt, ob es nicht sinnvoll wäre, eine PCA mit anschließender LDA-Regularisierung durchzuführen (um eine Kurvenanpassung zu vermeiden). Das ist ein langes Thema, das vielleicht ein Artikel für einen anderen Tag sein sollte.

Für diesen Artikel besteht der entscheidende Unterschied zwischen den beiden Methoden zur Dimensionalitätsreduktion darin, dass die PCA versucht, die Achsen mit maximaler Varianz für den gesamten Datensatz zu finden, wobei die Annahme gilt, dass die Trennbarkeit umso größer ist, je stärker die Daten gestreut sind, während die LDA versucht, die Achsen zu finden, die die Daten auf der Grundlage der Klassifizierung tatsächlich voneinander trennen.

lda

Aus der obigen Abbildung ist unschwer zu erkennen, dass wir mit PCA LD2 und mit LDA LD1 erhalten würden. Dies macht den Hauptunterschied (und damit die LDA-Präferenz) zwischen PCA und LDA schmerzlich deutlich: Nur weil ein Merkmal eine hohe Varianz (Streuung) aufweist, bedeutet dies nicht, dass es bei der Erstellung von Vorhersagen für die Klassen nützlich ist.

2) Quadratische Diskriminanzanalyse (QDA):

QDA ist eine Verallgemeinerung von LDA als Klassifikator. LDA geht davon aus, dass die bedingten Verteilungen der Klassen Gaußsche Verteilungen mit derselben Kovarianzmatrix sind, wenn es für uns eine Klassifizierung durchführen soll.

QDA geht nicht von der Annahme der Homoskedastizität aus und versucht, die Kovarianz aller Klassen zu schätzen. Dies mag zwar wie ein robusterer Algorithmus erscheinen (bei weniger Annahmen), bedeutet aber auch, dass eine viel größere Anzahl von Parametern zu schätzen ist. Es ist allgemein bekannt, dass die Anzahl der Parameter quadratisch mit der Anzahl der Klassen wächst! Wenn Sie also nicht garantieren können, dass Ihre Kovarianzschätzungen zuverlässig sind, sollten Sie QDA nicht verwenden.

Nach all dem könnte es einige Verwirrung über die Beziehung zwischen LDA und QDA geben, z. B. darüber, was besser für die Dimensionalitätsreduktion geeignet ist und was besser für die Klassifizierung usw. Dieser CrossValidated“-Beitrag und alles, was dort verlinkt ist, könnte helfen.

3) Analyse der Varianz (ANOVA):

LDA und ANOVA scheinen ähnliche Ziele zu verfolgen: beide versuchen, eine beobachtete Variable in mehrere unabhängige/abhängige Variablen zu zerlegen. Das von der ANOVA verwendete Instrument ist jedoch laut Wikipedia die gespiegelte Version dessen, was die LDA verwendet:

„LDA ist eng verwandt mit der Varianzanalyse (ANOVA) und der Regressionsanalyse, die ebenfalls versuchen, eine abhängige Variable als lineare Kombination anderer Merkmale oder Messungen auszudrücken. Die ANOVA verwendet jedoch kategoriale, unabhängige Variablen und eine kontinuierliche, abhängige Variable, während die Diskriminanzanalyse kontinuierliche, unabhängige Variablen und eine kategoriale, abhängige Variable (d. h. die Klassenbezeichnung) verwendet.“

LDA ist in der Regel wie folgt definiert.

Es sei:

  • n die Anzahl der Klassen,
  • μ der Mittelwert aller Beobachtungen,
  • N i die Anzahl der Beobachtungen in der i-ten Klasse,
  • μ i ist der Mittelwert der i-ten Klasse,
  • Σ i ist die Streumatrix der i-ten Klasse,


Definieren wir nun SW als die klasseninterne Streumatrix, gegeben durch

SW = ∑ i = 1 n Σ i


und definieren SB als die Streumatrix zwischen den Klassen, gegeben durch

SB = ∑ i = 1 n N i ( μ i − μ ) ( μ i − μ ) T


Wir diagonalisieren SW - 1 SB um die Eigenwerte und Eigenvektoren zu erhalten.

Wir wählen die k die größten Eigenwerte und ihre zugehörigen Eigenvektoren. Wir werden unsere Beobachtungen auf den Unterraum projizieren, der von diesen Vektoren aufgespannt wird.

Konkret bedeutet dies, dass wir die Matrix A bilden, deren Spalten die k die oben gewählten Eigenvektoren sind. Die CLDA-Klasse in der Bibliothek alglib tut genau dies und sortiert die Vektoren auf der Grundlage ihrer Eigenwerte in absteigender Reihenfolge, was bedeutet, dass wir nur den besten Vorhersagevektor auswählen müssen, um eine Prognose zu erstellen.

Wie in den vorangegangenen Artikeln werden wir die MQL-Code-Bibliothek für die Implementierung von LDA für unseren Expert Advisor verwenden. Konkret werden wir uns auf die Klasse „CLDA“ aus der Datei „dataanalysis.mqh“ stützen.

Wir werden die LDA für das Devisenpaar USDJPY im Laufe dieses Jahres 2022 auf dem täglichen Zeitrahmen untersuchen. Die Auswahl der Eingabedaten für unseren Experten ist weitgehend dem Nutzer überlassen. In unserem Fall für diese LDA haben die Eingabedaten eine Variablen- und eine Klassenkomponente. Wir müssen diese Daten vorbereiten, bevor wir Tests mit ihnen durchführen. Da es sich um geschlossene Preise handelt, werden sie standardmäßig „fortgeschrieben“ (in ihrem Rohzustand). Wir wenden die Normalisierung und Diskretisierung auf die Variablen- und Klassenkomponenten unserer Daten an. Normalisierung bedeutet, dass alle Daten zwischen einem festgelegten Minimum und Maximum liegen, während Diskretisierung bedeutet, dass die Daten in Boolesche Werte (wahr oder falsch) umgewandelt werden. Im Folgenden sind die Vorbereitungen für 5 Datensätze für unser Signal aufgeführt:

  1. Diskrete Variablendaten, die Preisänderungen in der Nähe von Klassenkategorien verfolgen.
  2. Normalisierte Variablendaten von rohen Änderungen der Schlusspreise in die Spanne von -1.0 bis +1.0.
  3. Fortlaufende Variablendaten der rohen Schlusspreisänderungen. 
  4. Rohe Schlusspreise.

Die Normalisierung gibt die Veränderung des Schlusskurses im Verhältnis zur Spanne der letzten 2 Balken in Dezimalzahlen an (von -1,0 bis +1,0), während die Diskretisierung angibt, ob der Kurs gestiegen ist (was einen Indexwert von 2 entspricht) oder in einer neutralen Spanne geblieben ist (was einen Indexwert von 1 bedeutet) oder gefallen ist (was zu einen Indexwert von 0 führt). Wir werden alle Datentypen testen, um die Leistung zu untersuchen. Diese Vorbereitung erfolgt nach der unten dargestellten Methode „Daten“. Alle 4 Datentypen werden mit dem Eingang „m_signal_regulizer“ reguliert, um eine neutrale Zone für unsere Daten zu definieren und so das weiße Rauschen zu reduzieren.

//+------------------------------------------------------------------+
//| Data Set method                                                  |
//| INPUT PARAMETERS                                                 |
//|     Index   -   int, read index within price buffer.             |
//|                                                                  |
//|     Variables                                                    |
//|             -   whether data component is variables or .         |
//|                  classifier.                                     |
//| OUTPUT                                                           |
//|     double  -   Data depending on data set type                  |
//|                                                                  |
//| DATA SET TYPES                                                   |
//| 1. Discretized variables. - 0                                    |
//|                                                                  |
//| 2. Normalized variables. - 1                                     |
//|                                                                  |
//| 3. Continuized variables. - 2                                    |
//|                                                                  |
//| 4. Raw data variables. - 3                                       |
//|                                                                  |
//+------------------------------------------------------------------+
double CSignalDA::Data(int Index,bool Variables=true)
   {
      m_close.Refresh(-1);
         
      m_low.Refresh(-1);
      m_high.Refresh(-1);
            
      if(Variables)
      {
         if(m_signal_type==0)
         {
            return(fabs(Close(StartIndex()+Index)-Close(StartIndex()+Index+1))<m_signal_regulizer*Range(Index)?1.0:((Close(StartIndex()+Index)>Close(StartIndex()+Index+1))?2.0:0.0));
         }
         else if(m_signal_type==1)
         {
            if(fabs(Close(StartIndex()+Index)-Close(StartIndex()+Index+1))<m_signal_regulizer*Range(Index))
            {
               return(0.0);
            }
            return((Close(StartIndex()+Index)-Close(StartIndex()+Index+1))/fmax(m_symbol.Point(),fmax(High(StartIndex()+Index),High(StartIndex()+Index+1))-fmin(Low(StartIndex()+Index),Low(StartIndex()+Index+1))));
         }
         else if(m_signal_type==2)
         {
            if(fabs(Close(StartIndex()+Index)-Close(StartIndex()+Index+1))<m_signal_regulizer*Range(Index))
            {
               return(0.0);
            }
            return(Close(StartIndex()+Index)-Close(StartIndex()+Index+1));
         }
         else if(m_signal_type==3)
         {
            if(fabs(Close(StartIndex()+Index)-Close(StartIndex()+Index+1))<m_signal_regulizer*Range(Index))
            {
               return(Close(StartIndex()+Index+1));
            }
            return(Close(StartIndex()+Index));
         }
      }
      
      return(fabs(Close(StartIndex()+Index)-Close(StartIndex()+Index+1))<m_signal_regulizer*Range(Index)?1.0:((Close(StartIndex()+Index)>Close(StartIndex()+Index+1))?2.0:0.0));
   }


Wir verwenden vier Dimensionen, was bedeutet, dass jeder Indikatorwert 4 Variablen liefert. Der Einfachheit halber betrachten wir in unserem Fall die letzten vier Indikatorwerte für jeden Datensatz im Training. Unsere Klassifizierung wird ebenfalls grundlegend sein und nur die zwei Klassen (das Minimum) in der Klassenkomponente eines jeden Datensatzes berücksichtigen. Wir müssen auch die Anzahl der Datenpunkte in unserer Trainingsstichprobe festlegen. Dieser Wert wird im Eingabeparameter „m_signal_points“ gespeichert.

Die Ausgabe von LDA ist in der Regel eine Matrix von Koeffizienten. Diese Koeffizienten werden in Vektoren sortiert, und ein Punktprodukt eines beliebigen dieser Vektoren mit dem aktuellen Indikatordatenpunkt sollte einen Wert ergeben, der dann mit ähnlichen Werten verglichen wird, die sich aus Produkten des Trainingsdatensatzes ergeben, um diesen neuen/aktuellen Datenpunkt zu klassifizieren. Wenn unser Trainingssatz also nur zwei Datenpunkte mit den LDA-Projektionen 0 und 1 enthält und unser neuer Wert ein Punktprodukt von 0,9 ergibt, würden wir zu dem Schluss kommen, dass er sich in der gleichen Kategorie befindet wie der Datenpunkt, dessen LDA-Projektion 1 war, da er ihm näher liegt. Ergibt sich hingegen ein Wert von z. B. 0,1, dann müsste dieser neue Datenpunkt zur gleichen Kategorie gehören wie der Datenpunkt, dessen LDA-Projektion 0 war. 

Da Trainingsdatensätze selten nur aus zwei Datenpunkten bestehen, würden wir in der Praxis den „Schwerpunkt“ jeder Klasse als Vergleich mit der Ausgabe des Punktprodukts des neuen Datenpunkts zum LDA-Ausgangsvektor nehmen. Dieser „Schwerpunkt“ wäre der Mittelwert der LDA-Projektion für jede Klasse.

Um jeden Datenpunkt als aufwärts oder abwärts zu klassifizieren, betrachten wir einfach die Veränderung des Schlusskurses nach dem Indikatorwert. Wenn er positiv ist, ist der Datenpunkt aufwärts, wenn er negativ ist, ist er abwärts. Vergessen wir nicht, dass er gleich bleiben könnte. Der Einfachheit halber nehmen wir eine Seitwärtsbewegung oder keine Preisänderungen ebenfalls als aufwärts an.

Die Klasse „ExpertSignal“ stützt sich in der Regel auf normalisierte ganzzahlige Werte (0-100) zur Gewichtung von Kauf- oder Verkaufs-Entscheidungen. Da die LDA-Projektionen zwangsläufig vom Typ double sind, werden wir sie wie unten gezeigt normalisieren, sodass sie in den Bereich von -1,0 bis +1,0 fallen (negativ für abwärts und positiv für aufwärts). 

         // best eigen vector is the first 
         for(int v=0;v<__S_VARS;v++){ _unknown_centroid+= (_w[0][v]*_z[0][v]); }
         
         //

         
         if(fabs(_centroids[__S_BULLISH]-_unknown_centroid)<fabs(_centroids[__S_BEARISH]-_unknown_centroid) && fabs(_centroids[__S_BULLISH]-_unknown_centroid)<fabs(_centroids[__S_WHIPSAW]-_unknown_centroid))
         {
            _da=(1.0-(fabs(_centroids[__S_BULLISH]-_unknown_centroid)/(fabs(_centroids[__S_BULLISH]-_unknown_centroid)+fabs(_centroids[__S_WHIPSAW]-_unknown_centroid)+fabs(_centroids[__S_BEARISH]-_unknown_centroid))));
         }
         else if(fabs(_centroids[__S_BEARISH]-_unknown_centroid)<fabs(_centroids[__S_BULLISH]-_unknown_centroid) && fabs(_centroids[__S_BEARISH]-_unknown_centroid)<fabs(_centroids[__S_WHIPSAW]-_unknown_centroid))
         {
            _da=-1.0*(1.0-(fabs(_centroids[__S_BEARISH]-_unknown_centroid)/(fabs(_centroids[__S_BULLISH]-_unknown_centroid)+fabs(_centroids[__S_WHIPSAW]-_unknown_centroid)+fabs(_centroids[__S_BEARISH]-_unknown_centroid))));
         }

Dieser Wert lässt sich dann leicht auf den typischen ganzzahligen Wert (0-100) normieren, der für die Signalklasse erwartet wird.

   if(_da>0.0)
     {
      result=int(round(100.0*_da));
     }

 für die Funktion Kaufprüfung und,

   if(_da<0.0)
     {
      result=int(round(-100.0*_da));
     }

für die Verkaufsprüfung.

Ein Testlauf für jeden der Eingabedatentypen ergibt also die nachstehenden Berichte des Strategietesters.

Datensatz 1 Bericht

sr1

cs1

Bericht zu Datensatz 2

sr2

cs2

Bericht zu Datensatz 3

sr3

cs3

Datensatz 4 Bericht

sr4

cs4

  

Diese Berichte zeigen das Potenzial von LDA als Werkzeug für einen Händler. 

Die Klasse „ExpertTrailing“ passt einen Stop-Loss für eine offene Position an oder setzt ihn. Die wichtigste Ausgabe ist hier ein double-Wert für den neuen Stop-Loss. Je nach offener Position betrachten wir also die Höchst- und Tiefstkurse als unsere primären Datensätze. Diese werden wie folgt sowohl für Höchst- als auch für Tiefstkurse vorbereitet, wobei die Auswahl von der Art der offenen Position abhängt: 

  1. Diskrete Variablendaten, die Preisänderungen (von Hoch und Tief) verfolgen, um den Klassenkategorien zu entsprechen.
  2. Normalisierte Variablendaten von rohen Preisänderungen (von Hoch und Tief) im Bereich von -1,0 bis +1,0. 
  3. Fortlaufende Variablendaten in Form von rohen Preisänderungen (von Hoch und Tief). 
  4. Rohe Preise (Hoch und Tief).

Die Ausgabe der LDA ist ein normalisierter double-Wert wie bei der Signalklasse. Da dies bei der Festlegung eines Stop-Loss nicht hilfreich ist, wird er wie unten gezeigt je nach Art der offenen Position angepasst, um einen Stop-Loss-Preis zu ermitteln.

      int _index   =StartIndex();
      double _min_l=Low(_index),_max_l=Low(_index),_min_h=High(_index),_max_h=High(_index);
      
      for(int d=_index;d<m_trailing_points+_index;d++)
      {
         _min_l=fmin(_min_l,Low(d));
         _max_l=fmax(_max_l,Low(d));
         _min_h=fmin(_min_h,High(d));
         _max_h=fmax(_max_h,High(d));
      }
      
      if(Type==POSITION_TYPE_BUY)
      {
         _da*=(_max_l-_min_l);
         _da+=_min_l;
      }
      else if(Type==POSITION_TYPE_SELL)
      {
         _da*=(_max_h-_min_h);
         _da+=_max_h;
      }

Hier sehen Sie auch, wie wir unsere neuen Stop-Loss-Niveaus anpassen und festlegen. Für die Kauf-Positionen:

   m_long_sl=ProcessDA(StartIndex(),POSITION_TYPE_BUY);

   double level =NormalizeDouble(m_symbol.Bid()-m_symbol.StopsLevel()*m_symbol.Point(),m_symbol.Digits());
   double new_sl=NormalizeDouble(m_long_sl,m_symbol.Digits());
   double pos_sl=position.StopLoss();
   double base  =(pos_sl==0.0) ? position.PriceOpen() : pos_sl;
//---
   sl=EMPTY_VALUE;
   tp=EMPTY_VALUE;
   if(new_sl>base && new_sl<level)
      sl=new_sl;

Wir bestimmen hier den wahrscheinlichen Tiefstkurs für eine offene Kauf-Position („m_long_sl“) bis zum nächsten Balken und setzen ihn dann als neuen Stop-Loss, wenn er entweder über dem offenen Preis der Position oder dem aktuellen Stop-Loss liegt und gleichzeitig unter dem Bid-Preis minus Stop-Level liegt. Der für die Berechnung verwendete Datentyp sind niedrige Preise.

Die Festlegung des Stop-Loss für Verkaufs-Positionen ist eine spiegelbildliche Version davon.

Ein Testlauf für jeden der Eingabedatentypen unter Verwendung des Datentyps ... für das Signal ergibt also die unten stehenden Berichte des Strategietesters.

Datensatz 1 Bericht

tr1

ct1

Bericht zu Datensatz 2

tr2



ct2

Bericht zu Datensatz 3

tr3

ct3

Datensatz 4 Bericht.

tr4

ct4

Diese Berichte deuten vielleicht darauf hin, dass der Datensatz der fortgesetzten rohen Veränderungen angesichts seines Recovery Factors von 6,82 am besten geeignet ist.

Die Klasse „ExpertMoney“ legt die Losgröße unserer Position fest. Dies kann eine Funktion der bisherigen Leistung sein, weshalb wir auf der Klasse „OptimizedVolume“ aufbauen. Die LDA kann jedoch bei der anfänglichen Größenbestimmung helfen, wenn wir die Volatilität oder die Spanne zwischen Höchst- und Tiefstkursen berücksichtigen. Unser primärer Datensatz wird daher die Preisspanne sein. Wir werden sehen, ob die Preisspanne zunimmt oder abnimmt. Damit haben wir die folgenden Daten vorbereitet: 

  1. Diskretisierte Variablendaten zur Verfolgung von Bereichswertänderungen zur Anpassung an Klassenkategorien.
  2. Normalisierte Variablendaten der Rohbereichswerte, die sich in den Bereich -1,0 bis +1,0 ändern. 
  3. Fortlaufende Variablendaten imRohbereichswertänderungen
  4. Rohbereichswerte.

Die Ausgabe der LDA ist ein normalisierter double-Wert mit der Signal- und Trailing-Klasse. Da dies wieder einmal nicht unmittelbar hilfreich ist, nehmen wir die unten gezeigten Anpassungen vor, um einen neuen Balkenbereich besser zu projizieren.

      int _index   =StartIndex();
      double _min_l=Low(_index),_max_h=High(_index);
      
      for(int d=_index;d<m_money_points+_index;d++)
      {
         _min_l=fmin(_min_l,Low(d));
         _max_h=fmax(_max_h,High(d));
      }
   
      _da*=(_max_h-_min_l);
      _da+=(_max_h-_min_l);

Die Einstellung des Eröffnungsvolumens wird von 2 gespiegelten Funktionen gehandhabt, je nachdem, ob der Experte eine Kauf- oder Verkaufs-Position eröffnet. Nachfolgend die wichtigsten Punkte für eine Kauf-Position.

   double _da=ProcessDA(StartIndex());
   
   if(m_symbol==NULL)
      return(0.0);
   
   sl=m_symbol.Bid()-_da;
   
//--- select lot size
   double _da_1_lot_loss=(_da/m_symbol.TickSize())*m_symbol.TickValue();
   double lot=((m_percent/100.0)*m_account.FreeMargin())/_da_1_lot_loss;
   
//--- calculate margin requirements for 1 lot
   if(m_account.FreeMarginCheck(m_symbol.Name(),ORDER_TYPE_BUY,lot,m_symbol.Ask())<0.0)
     {
      printf(__FUNCSIG__" insufficient margin for sl lot! ");
      lot=m_account.MaxLotCheck(m_symbol.Name(),ORDER_TYPE_BUY,m_symbol.Ask(),m_percent);
     }
   
//--- return trading volume
   return(Optimize(lot));

Bemerkenswert ist hier, dass wir die voraussichtliche Veränderung der Preisspanne bestimmen und diese Projektion von unserem Bid-Preis abziehen (wir hätten auch den Stopplevel abziehen sollen). Daraus ergibt sich ein „risikoangepasster“ Stop-Loss, aus dem wir, wenn wir den prozentualen Eingabeparameter als Parameter für den maximalen Risikoverlust verwenden, eine Losgröße berechnen können, die unseren Drawdown-Prozentsatz auf den prozentualen Wert des Eingabeparameters begrenzt, falls wir einen Drawdown unter dem projektierten Bid-Preis erleben.

Ein Testlauf für jeden der Eingabedatentypen unter Verwendung des Datentyps rohen Schlusskurses für das Signal und ... für das Trailing ergibt die folgenden Berichte des Strategietesters.

Datensatz 1 Bericht

mr1

cm1

Bericht zu Datensatz 2

mr2

cm2


Bericht zu Datensatz 3

mr3

cm3


Datensatz 4 Bericht

mr4


cm4


Es scheint, dass ein Datensatz mit diskreten Wertänderungen für das Geldmanagement am vielversprechendsten ist. Bemerkenswert ist auch die große Varianz in den Ergebnissen der Datensätze beim Geldmanagement, wenn man bedenkt, dass sie alle die gleichen Signal- und Trailing-Einstellungen verwenden.

Dieser Artikel hat das Potenzial der Diskriminanzanalyse als Handelsinstrument in einem Expert Advisor aufgezeigt. Sie war nicht erschöpfend. Weitere Analysen könnten mit vielfältigeren Datensätzen durchgeführt werden, die längere Zeiträume umfassen. 


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

Beigefügte Dateien |
TrailingDA.mqh (16.66 KB)
SignalDA.mqh (13.48 KB)
MoneyDA.mqh (15.19 KB)
Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 31): Der Zukunft entgegen (IV) Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 31): Der Zukunft entgegen (IV)
Wir fahren fort, einzelne Teile aus unserem EA zu entfernen. Dies ist der letzte Artikel in dieser Reihe. Und als letztes wird das Soundsystem entfernt. Dies kann etwas verwirrend sein, wenn Sie diese Artikelserie nicht verfolgt haben.
Verwenden von Linien in MQL5 Verwenden von Linien in MQL5
In diesem Artikel erfahren Sie, wie Sie mit den wichtigsten Linien wie Trendlinien, Unterstützung und Widerstand von MQL5 verwenden können.
Neuronale Netze leicht gemacht (Teil 31): Evolutionäre Algorithmen Neuronale Netze leicht gemacht (Teil 31): Evolutionäre Algorithmen
Im vorangegangenen Artikel haben wir uns mit nicht-gradientenbasierten Optimierungsmethoden befasst. Wir haben uns mit dem genetischen Algorithmus vertraut gemacht. Heute werden wir dieses Thema fortsetzen und eine andere Klasse von evolutionären Algorithmen besprechen.
DoEasy. Steuerung (Teil 23): Verbesserung der WinForms-Objekte TabControl und SplitContainer DoEasy. Steuerung (Teil 23): Verbesserung der WinForms-Objekte TabControl und SplitContainer
In diesem Artikel werde ich neue Mausereignisse relativ zu den Grenzen der Arbeitsbereiche von WinForms-Objekten hinzufügen und einige Mängel in der Funktionsweise der TabControl- und SplitContainer-Steuerelemente beheben.