English Русский Español Português
preview
Evolutionäre Strategie zur Anpassung der Kovarianzmatrix (CMA-ES)

Evolutionäre Strategie zur Anpassung der Kovarianzmatrix (CMA-ES)

MetaTrader 5Handel |
18 0
Andrey Dik
Andrey Dik

Inhalt

  1. Einführung
  2. Implementierung des Algorithmus
  3. Testergebnisse


Einführung

In der Welt der Optimierung gibt es viele Algorithmen, doch wir suchen nach den leistungsstärksten, um die Optimierungsprobleme unserer Handelsroboter zu lösen. CMA-ES (Covariance Matrix Adaptation Evolution Strategy) ist eines der seltenen Beispiele, bei denen mathematische Strenge mit biologischer Intuition kombiniert wird, wodurch ein Algorithmus entsteht, der nicht nur Optimierungsprobleme löst, sondern auch lernt, deren Struktur zu verstehen.

Die Geschichte von CMA-ES begann Ende der 1990er Jahre in Forschungslabors in Deutschland, wo Nikolaus Hansen und Andreas Ostermeier eine grundlegende Frage stellten: Ist es möglich, einen Optimierungsalgorithmus zu entwickeln, der nicht einfach nur nach einer Lösung sucht, sondern sich an die Geometrie des Problems anpasst? Herkömmliche evolutionäre Algorithmen erzeugten Nachkommen in kugelförmigen Bereichen um die Elternindividuen herum, was bei einfachen Funktionen gut funktionierte, sich jedoch bei komplexen, schlecht konditionierten Problemen als unwirksam erwies. Werfen wir einen Blick auf diesen interessanten Algorithmus.


Implementierung des Algorithmus

Stell dir vor, du suchst auf einer unregelmäßig geformten Insel nach einem Schatz. Üblicherweise sucht man in alle Richtungen gleichermaßen, als wäre die Insel rund. CMA-ES lernt nach und nach die Form der Insel kennen und verlagert seine Suche schrittweise in Richtungen, in denen die Wahrscheinlichkeit, einen Schatz zu finden, höher ist. Außerdem speichert es erfolgreiche Routen und nutzt diese Informationen zur Planung künftiger Suchvorgänge.

CMA-ES basiert auf einer täuschend einfachen Gleichung: x_k ~ N(m, σ²C). Hinter dieser Einfachheit verbirgt sich jedoch eine tiefgreifende mathematische Struktur. Jedes Symbol hier enthält wichtige Informationen über den Stand der Suche: m ist die derzeit beste Schätzung für die Lage des Optimums, σ ist ein Maß dafür, wie weit wir bereit sind, uns vom Bekannten zu entfernen, während C eine Kovarianzmatrix ist, die unser Verständnis der Funktionsgeometrie widerspiegelt. Die einzige Änderung, die wir rechtfertigen können, ist der Ersatz der Normalverteilung durch eine Potenzgesetzverteilung, was bedeutet, dass die Implementierung einer modifizierten Gleichung folgt: x_k ~ PowerDist(m, σ²C). Diese Änderung verändert die Art der Exploration des Suchraums (größere „Sprünge“), bewahrt jedoch den grundlegenden adaptiven Charakter des Algorithmus.

Die Kovarianzmatrix C ist das eigentliche Herzstück des Algorithmus. Sie beginnt als einfache Identitätsmatrix, die eine sphärische Verteilung darstellt. Doch mit jeder Iteration entwickelt sie sich weiter, dehnt sich in Bereichen aus, in denen rasche Verbesserungen erzielt werden, und schrumpft dort, wo der Fortschritt nur langsam voranschreitet. Allmählich verwandelt sich die Kugel in eine Ellipse, dann in ein längliches Ellipsoid, das idealerweise entlang der Konturen der zu optimierenden Funktion ausgerichtet ist.

Die wichtigste Neuerung von CMA-ES ist das Konzept der Evolutionspfade. Das ist sozusagen das genetische Gedächtnis des Algorithmus, das sich nicht nur merkt, wo die erfolgreichen Punkte lagen, sondern auch, wie der Algorithmus zu ihnen gelangt ist. Der evolutionäre Pfad sammelt Informationen über aufeinanderfolgende erfolgreiche Schritte und erzeugt einen solchen Richtungsvektor, der auf die vielversprechendsten Suchbereiche hinweist. Der zweite Evolutionspfad erfüllt eine subtilere Funktion: Er steuert die Schrittweite. Sind die aufeinanderfolgenden Schritte des Algorithmus miteinander verknüpft, das heißt, folgt jeder nachfolgende Schritt der Richtung des vorherigen, dann vergrößert sich die Schrittweite – der Algorithmus „spürt“, dass er sich in die richtige Richtung bewegt. Sind die Schritte zufällig und unkorreliert, verringert sich die Schrittweite – möglicherweise befindet sich der Algorithmus bereits nahe am Optimum und muss genauer gesucht werden.

Hinter der biologischen Metapher der Evolution in CMA-ES verbirgt sich ein strenges mathematisches Prinzip – die Maximum-Likelihood-Schätzung. Der Algorithmus fragt sich ständig: Welche Verteilungsparameter machen die beobachteten Erfolgspunkte am wahrscheinlichsten? Dadurch wird die evolutionäre Optimierung von einer heuristischen Methode zu einer statistisch fundierten Methode.

Jede Aktualisierung der C-Kovarianzmatrix besteht aus zwei Komponenten: einer Aktualisierung vom Rank-One auf der Grundlage des Evolutionspfads und einer Aktualisierung vom Rank-μ unter Verwendung von Informationen aus der gesamten ausgewählten Population. Die Aktualisierung von Rank-One sorgt für Stabilität und ermöglicht es, langfristige Trends zu erfassen, während die Aktualisierung von Rank-μ eine schnelle Anpassung an neue Informationen aus der aktuellen Generation ermöglicht.

Im Rahmen von CMA-ES wird eine modifizierte Heaviside-Funktion verwendet, die eine wichtige Rolle im Stagnationserkennungsmechanismus des Algorithmus spielt. Die Funktion vergleicht die Länge des Evolutionspfads mit der erwarteten Länge; ist der Pfad zu kurz, ist dies ein Anzeichen für ungerichtete Bewegungen. Deaktivierungsbedingungen (hsig = 0): Der Algorithmus „irrt“ zufällig umher und die Schritte heben sich gegenseitig auf; in diesem Fall ist die Schrittweite wahrscheinlich zu groß. Während einer Stagnationsphase nimmt der Einfluss der Aktualisierung von Rank-One ab, und wir stützen uns stärker auf Informationen aus der gesamten Population. Aktivierungsbedingungen (hsig = 1): Der Algorithmus macht Fortschritte in einer bestimmten Richtung, aufeinanderfolgende Schritte sind miteinander verknüpft, und die Schrittweite ist der aktuellen Situation angemessen. 

Eine der grundlegendsten Eigenschaften von CMA-ES ist seine Invarianz gegenüber Transformationen des Suchraums. Der Algorithmus löst die Funktion f(x) ebenso effizient wie die Funktion f(Ax + b), wobei A eine beliebige nicht-singuläre Matrix und b ein beliebiger Verschiebungsvektor ist. Das bedeutet, dass CMA-ES unabhängig von der Wahl des Koordinatensystems ist. Diese Invarianz ist kein Zufall; sie ist eine direkte Folge der Anwendung des Maximum-Likelihood-Prinzips und der Anpassung der Kovarianzmatrix. Der Algorithmus erkennt automatisch ein natürliches Koordinatensystem für das Problem, bei dem die Achsen mit den Hauptrichtungen der Funktionsvariation zusammenfallen.

Die Schönheit der Theorie muss mit praktischer Anwendbarkeit einhergehen. CMA-ES benötigt Speicherplatz im Ausmaß von O(n²) für die Speicherung der Kovarianzmatrix und O(n³) Rechenoperationen für die Eigenwertzerlegung, wodurch sich der Algorithmus für Probleme mit bis zu mehreren Hundert Variablen eignet. Für große Dimensionen wurden spezielle Varianten entwickelt: sep-CMA-ES beschränkt sich auf diagonale Kovarianzmatrizen, VkD-CMA nutzt variable Dimensionen und LM-CMA wendet die Prinzipien des begrenzten Speicherbedarfs an. Die Umsetzung dieser Methoden würde den Rahmen unseres Artikels sprengen, doch vielleicht können wir in späteren Artikeln darauf zurückkommen.

cmaes-Algorithmus

Abb. 1. Darstellung des CMA-ES-Algorithmus

Die Abbildung zeigt die Entwicklung über mehrere Generationen hinweg – drei Momentaufnahmen des Zustands des Algorithmus (Generationen 1, 5 und 15), die verdeutlichen, wie sich die Population allmählich dem Optimum annähert,

Anpassung der Kovarianzmatrix – die blauen Ellipsen zeigen, wie sich die Form der Verteilung in den Generationen ändert: 1 – rund (Einheitsmatrix), 5 – länglich und gedreht, 15 – genau auf die lokale Geometrie abgestimmt.
Die Algorithmuskomponenten:  Rote Punkte stellen die gesamte Population dar (λ Nachkommen), grüne Punkte die besten Lösungen (μ Eltern), der schwarze Punkt m ist der Populationsmittelwert, gestrichelte Kreise sind die Isolinien der Zielfunktion. 

Die Schlüsselformel ist unten aufgeführt, zusammen mit einem Hinweis zur Modifikation des Potenzgesetzes. Die Abbildung veranschaulicht deutlich, wie der Algorithmus seine Suchstrategie an die Landschaft der zu optimierenden Funktion anpasst, dabei den Suchraum schrittweise eingrenzt und sich dem Optimum nähert.

Bereiten wir den Pseudocode vor.

Initialisierung

  1. Einstellen der Algorithmusparameter:
    • Populationsgröße (Lambda) = 50
    • Anzahl der Eltern (mu) = 25
    • Lernrate für die Aktualisierung von Rank-One (c1) = 0,01
    • Lernrate für die Aktualisierung von Rank-μ (cμ) = 0,8
    • Schrittweiten-Dämpfung = 0,6
    • Anfängliche Schrittweite (Sigma) = 0,3
  2. Berechnen der Rekombinationsgewichte:
    • Berechne für jeden der μ Elternteile das Gewicht als log(μ + 0,5) – log(i + 1)
    • Normalisiere die Gewichte so, dass ihre Summe 1 ergibt
    • Berechne die effektive Selektionsmasse μ_eff
  3. Initialisieren der Strategieparameter:
    • Berechne die Lernraten für den Evolutionspfad (cs, cc)
    • Berechne die erwartete Norm eines Standardnormalvektors (chiN)
    • Bestimme das Intervall für die Eigenwertzerlegung
  4. Initialisieren der Datenstrukturen:
    • Kovarianzmatrix C = Einheitsmatrix
    • Matrix der Eigenvektoren B = Einheitsmatrix
    • Vektor der Eigenwerte D = Einheitsvektor
    • Evolutionspfade von PC und PS = Nullvektoren
    • Populationsmittelwert m = Zufallspunkt im Suchraum
  5. Erstellen einer Ausgangspopulation:
    • Generiere zufällige Lambda-Punkte im Suchraum

Grundlegender Evolutionszyklus

Wiederhole dies, bis das Stoppkriterium erfüllt ist:

Schritt 1: Nachkommen zeugen 

  1. Für jeden Nachkommen von lambda:
    • Erzeuge den Zufallsvektor z aus der Standardnormalverteilung
    • Wende die Transformation an: y = B × D × z
    • Erzeuge einen Nachkommen: x_k = m + σ × y
    • Wende Suchraum-Einschränkungen auf x_k an

Schritt 2: Überprüfen und aktualisieren 

  1. Bewerte die Fitness aller Nachkommen
  2. Sortiere die Population:
    • Sortiere die Nachkommen in absteigender Reihenfolge der Fitness
    • Aktualisiere gegebenenfalls die gefundene beste Lösung
  3. Aktualisiere den Mittelwert der Population:
    • Speichere den bisherigen Mittelwert m_old
    • Berechne den neuen Mittelwert als gewichtete Summe der μ besten Nachkommen: m_new = Σ(w_i × x_i) für i von 1 bis μ
  4. Aktualisiere die Evolutionspfade:
    • Berechne den Mittelwertversatz: y_w = (m_neu - m_alt) / σ
    • Berechne C^(-1/2) × y_w mittels Eigenwertzerlegung
    • Aktualisiere den Evolutionspfad für die Schrittweite: ps = (1 - cs) × ps + √(cs × (2 - cs) × μ_eff) × C^(-1/2) × y_w
    • Überprüfe die Stagnationsbedingung (Heaviside-Funktion)
    • Aktualisiere den Entwicklungsweg für die Kovarianzmatrix: pc = (1 - cc) × pc + stagnation_indicator × √(cc × (2 - cc) × μ_eff) × y_w
  5. Aktualisiere die Kovarianzmatrix:
    • Bereite die Aktualisierung der Matrix von Rank-One aus dem Außenprodukt von pc vor
    • Berechne die Aktualisierung des Rank-μ anhand der gewichteten Summe der Abweichungen des besten Nachfolgers
    • Aktualisiere C: C = (1 - c1 - cμ) × C + c1 × (pc × pc^T) + cμ × Σ(w_i × y_i × y_i^T)
    • Stelle die Symmetrie der Matrix sicher
  6. Initialisieren der Schrittweite:
    • Berechne den Anpassungskoeffizienten auf der Grundlage der ps-Weglänge
    • Aktualisiere σ: σ = σ × exp((cs/damps) × (||ps||/chiN - 1))
    • Begrenze σ auf ein vernünftiges Maß, um die numerische Stabilität zu gewährleisten
  7. Eigenwertzerlegung (falls erforderlich):
    • Wenn seit der letzten Zerlegung genügend Iterationen vergangen sind:
      • Führe die Jacobi-Zerlegung von C = B × D² × B^T durch
      • Eigenwerte und Eigenvektoren in absteigender Reihenfolge sortieren
      • Stelle sicher, dass die Matrix positiv definit ist
  8. Überprüfen und Korrigieren der Kovarianzmatrix:
    • Überprüfe regelmäßig die positive Definitheit von C
    • Falls erforderlich, füge einen kleinen Wert zur Diagonalen hinzu
    • Stelle die Symmetrie der Matrix sicher

Schreiben wir nun die von der Klasse C_AO abgeleitete Klasse C_AO_CMAES, die den CMA-ES-Optimierungsalgorithmus implementieren wird.

Öffentliche Felder, Methode:

  • SetParams() Legt die CMA-ES-Parameterwerte aus dem Array params fest.
  • Init() initialisiert den Algorithmus. Akzeptiert Minimal- und Maximalwerte sowie die Schrittweite für jede Variable und die Anzahl der Epochen.
  • Moving() implementiert die Hauptschleife des Algorithmus, die für die Erzeugung neuer Lösungen zuständig ist.
  • Revision() Bewertung neuer Lösungen und Aktualisierung des Algorithmuszustands.

Private Felder:

  • CMA-ES-Parameter die Variablen, die bestimmte Parameter des CMA-ES-Algorithmus enthalten: Anzahl der Eltern (mu), Schrittweite (sigma), Lernraten (learningRateC1,learningRateCMu), Dämpfungsfaktor (stepSizeDamping) und weitere für die Funktionsweise des Algorithmus erforderliche Parameter.
  • CMA-ES-DatenstrukturenArrays zur Speicherung von Rekombinationsgewichten, der Kovarianzmatrix covMatrix, den Eigenvektoren B, den Eigenwerten D, den Evolutionspfaden (pc,ps) sowie weiterer Hilfsdaten. Die Variablen mu_eff,counteval,eigeneval,chiN und eigenInterval dienen zur Steuerung und Verwaltung des Algorithmusverlaufs.
  • Die Variablen zur Leistungsoptimierung dienen dazu, Berechnungen während der Ausführung des Algorithmus zu beschleunigen, beispielsweise Lernraten und Dämpfung.
  • Die Hilfsarrays, die bei der Durchführung verschiedener Operationen als temporärer Speicher für Vektoren und Matrizen dienen.
  • Die Hilfsverfahren, die einzelne Schritte des CMA-ES-Algorithmus ausführen, wie beispielsweise die Initialisierung der Verteilung, die Aktualisierung der Verteilung, die Berechnung der Eigenwertzerlegung, die Sortierung der Population, die Berechnung der Gewichte, die Aktualisierung des Mittelwerts, die Überprüfung auf positive Definitheit und die Sicherstellung der positiven Definitheit.

Insgesamt kapselt die Klasse C_AO_CMAES die Logik des CMA-ES-Algorithmus und bietet eine Schnittstelle für die Initialisierung, die Parameteranpassung und die Durchführung der Optimierung. Die Klasse enthält die Parameter, Datenfelder und Methoden, die zur Implementierung des Algorithmus erforderlich sind. Die Trennung von öffentlichen und privaten Feldern ermöglicht die Zugriffskontrolle auf die internen Komponenten der Klasse.

//————————————————————————————————————————————————————————————————————
class C_AO_CMAES : public C_AO
{
  public: //----------------------------------------------------------
  ~C_AO_CMAES () { }
  C_AO_CMAES ()
  {
    ao_name = "CMAES";
    ao_desc = "Covariance Matrix Adaptation Evolution Strategy";
    ao_link = "https://www.mql5.com/de/articles/18227";

    // Default parameters
    popSize         = 50;          // Default population size (lambda)
    mu              = 25;          // Number of parents (half of the population)
    learningRateC1  = 0.01;        // Learning rate for rank-1 update
    learningRateCMu = 0.8;         // Learning rate for rank-μ update
    stepSizeDamping = 0.6;         // Damping for step size

    // Create and initialize the parameters array
    ArrayResize (params, 5);
    params [0].name = "popSize";         params [0].val = popSize;
    params [1].name = "mu";              params [1].val = mu;
    params [2].name = "learningRateC1";  params [2].val = learningRateC1;
    params [3].name = "learningRateCMu"; params [3].val = learningRateCMu;
    params [4].name = "stepSizeDamping"; params [4].val = stepSizeDamping;
  }

  void SetParams ()
  {
    popSize         = (int)params [0].val;
    mu              = (int)params [1].val;
    learningRateC1  = params      [2].val;
    learningRateCMu = params      [3].val;
    stepSizeDamping = params      [4].val;
  }

  bool Init (const double &rangeMinP  [],  // minimum values
             const double &rangeMaxP  [],  // maximum values
             const double &rangeStepP [],  // step size
             const int     epochsP = 0);

  void Moving   ();
  void Revision ();

  //------------------------------------------------------------------
  private: //---------------------------------------------------------
  // CMA-ES specific parameters
  int mu;                  // Number of parents (selected points)
  double sigma;            // Step size
  double learningRateC1;   // Learning rate for rank-1 update
  double learningRateCMu;  // Learning rate for rank-μ update
  double stepSizeDamping;  // Damping factor for step size update

  // CMA-ES specific data structures
  double weights   [];      // Recombination weights
  double covMatrix [];      // Covariance matrix (stored as a one-dimensional array)
  double B  [];             // C eigenvectors
  double D  [];             // C eigenvalues (square roots)
  double pc [];             // Evolution path for C
  double ps [];             // Evolution path for step-size control
  double mu_eff;            // Effective selection mass
  int    counteval;         // Counter of function evaluations since the last decomposition
  int    eigeneval;         // Generation counter when decomposition was performed
  double chiN;              // Expected norm N(0,I)
  int    eigenInterval;     // Interval for eigenvalue decomposition

  // Variables for performance optimization
  double cs;                // Learning rate for the sigma path
  double cc;                // Learning rate for rank-1 path 
  double damps;             // Damping for sigma
  double hsig_threshold;    // Threshold for the Heaviside function

  // Auxiliary arrays
  double y_vec     [];         // Mutation vector
  double arindex   [];         // Array of indices for sorting
  double arfitness [];         // Fitness array for sorting
  double temp_vec  [];         // Temporary vector for matrix operations
  double invsqrtC_times_yw []; // Temporary storage for C^(-1/2) * y_w

  // Caching variables for Box-Muller
  double cached_normal;
  bool   has_cached;

  // Auxiliary methods
  void   InitDistribution          ();
  void   UpdateDistribution        ();
  void   ComputeEigendecomposition ();
  double GetChiN                   ();
  void   SortPopulation            ();
  void   ComputeWeights            ();
  void   UpdateMean                ();
  bool   CheckPositiveDefinite     ();
  void   EnforcePositiveDefinite   ();
};
//————————————————————————————————————————————————————————————————————

Die Methode „Init“ der Klasse „C_AO_CMAES“ initialisiert den CMA-ES-Algorithmus. Als Eingabe benötigt es die Minimal- und Maximalwerte der Parameter, die Schrittweite und die Anzahl der Epochen. Zunächst wird die Methode „StandardInit“ aufgerufen, um die Standardinitialisierung durchzuführen. Das Flag „has_cached“ wird auf „false“ gesetzt, um anzuzeigen, dass kein zwischengespeicherter Wert für die Box-Muller-Zufallszahlengenerierung vorliegt. Anschließend wird die Sigma-Schrittweite auf den Anfangswert 0,3 (30 % des Suchbereichs) gesetzt. Die Methode „ComputeWeights“ wird aufgerufen, um die Rekombinationsgewichte zu berechnen, und die Methode „GetChiN“ wird aufgerufen, um die erwartete Norm N(0, I) zu berechnen.

Die Parameter cs, cc, damps und hsig_threshold werden auf der Grundlage von mu_eff (effektive Masse) und der Anzahl der Koordinaten (coords) berechnet. Diese Parameter dienen dazu, die CMA-ES-Strategie während des Betriebs anzupassen. „eigenInterval“ wird berechnet und festgelegt, um zu bestimmen, wie oft die Eigenwertzerlegung durchgeführt wird. Dieser Parameter ist so konfiguriert, dass die Leistung optimiert wird.

Für CMA-ES-spezifische Arrays wie die Kovarianzmatrix „covMatrix“, die Eigenvektoren „B“, die Eigenwerte „D“, die Evolutionspfade „pc“ und „ps“, „y_vec“, „arindex“, „arfitness“, „temp_vec“ sowie die Zeitvektoren „invsqrtC_times_yw“ wird Speicher zugewiesen. Arrays werden nur neu erstellt, wenn die Problemdimension geändert werden muss.

Die Arrays „pc“ und „ps“ für den Evolutionspfad werden mit Nullen initialisiert, die Kovarianzmatrix „covMatrix“ und die Eigenvektormatrix „B“ werden mit der Einheitsmatrix initialisiert, und das Eigenwert-Array „D“ wird mit Einsen initialisiert. Die Methode „InitDistribution“ wird aufgerufen, um die Anfangsverteilung zu initialisieren. Anschließend werden die Zählerstände der Funktionen „counteval“ und „eigeneval“ zurückgesetzt. Das Flag „revision“ wird auf „true“ gesetzt, um anzuzeigen, dass die Fitnesswerte neu berechnet werden sollen. Bei erfolgreicher Initialisierung wird „true“ zurückgegeben.

Zusammenfassend lässt sich sagen, dass die Methode„Init“ alle notwendigen Initialisierungen durchführt, damit der CMA-ES-Algorithmus funktioniert, einschließlich der Einstellung von Parametern, der Zuweisung von Speicherplatz für Arrays und der Initialisierung von Startwerten. 

//————————————————————————————————————————————————————————————————————
bool C_AO_CMAES::Init (const double &rangeMinP [],  // minimum values
                       const double &rangeMaxP [],  // maximum values
                       const double &rangeStepP [], // step size
                       const int     epochsP = 0)   // number of epochs
{
  if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;

  //------------------------------------------------------------------
  // Initialize Box-Muller caching
  has_cached = false;

  // Initialize CMA-ES specific variables
  sigma          = 0.3;  // Initial step size (30% of search range)

  // Calculate the effective mass of the variance selection
  ComputeWeights ();

  // Expected norm N(0,I)
  chiN = GetChiN ();

  // Calculate and save strategy parameters
  cs = (mu_eff + 2.0) / (coords + mu_eff + 5.0);
  cc = (4.0 + mu_eff / coords) / (coords + 4.0 + 2.0 * mu_eff / coords);
  damps = 1.0 + 2.0 * MathMax (0.0, MathSqrt ((mu_eff - 1.0) / (coords + 1.0)) - 1.0) + cs;
  hsig_threshold = 1.4 + 2.0 / (coords + 1.0);

  // Set the eigenvalue decomposition interval - tuning for performance
  eigenInterval = (int)(coords / (10.0 * MathSqrt (learningRateC1 + learningRateCMu)));
  eigenInterval = MathMax (1, eigenInterval);

  // Allocate arrays only once
  ArrayResize (covMatrix, coords * coords);
  ArrayResize (B, coords * coords);
  ArrayResize (D, coords);
  ArrayResize (pc, coords);
  ArrayResize (ps, coords);
  ArrayResize (y_vec, coords);
  ArrayResize (arindex, popSize);
  ArrayResize (arfitness, popSize);
  ArrayResize (temp_vec, coords);
  ArrayResize (invsqrtC_times_yw, coords);

  // Initialize evolution paths with zeros
  ArrayInitialize (pc, 0);
  ArrayInitialize (ps, 0);

  // Fast initialization of the identity covariance matrix and decomposition
  ArrayInitialize (covMatrix, 0.0);
  ArrayInitialize (B, 0.0);

  for (int i = 0; i < coords; i++)
  {
    covMatrix [i * coords + i] = 1.0;
    B [i * coords + i] = 1.0;
    D [i] = 1.0;
  }

  // Initialize initial distribution
  InitDistribution ();

  // Reset calculation counters
  counteval = 0;
  eigeneval = 0;

  // Forced fitness recalculation
  revision = true;

  return true;
}
//————————————————————————————————————————————————————————————————————

Die Methode „Moving“ in der Klasse C_AO_CMAES ist dafür zuständig, neue Nachkommen (Lösungen) in der Population zu erzeugen. Die Umwandlung von y = B * D * z: Innerhalb der Schleife über die Population befindet sich eine verschachtelte Schleife, die alle Koordinaten jedes einzelnen Individuums durchläuft. An jedem Punkt i wird der Wert von y_vec [i] berechnet. Dies geschieht mittels Matrixmultiplikation. Tatsächlich handelt es sich um die Multiplikation eines Vektors aus z Zufallszahlen mit der Matrix „B × D“, wobei B die Eigenvektoren der Kovarianzmatrix, D die Eigenwerte der Kovarianzmatrix und z die mit der Funktion u.PowerDistribution erzeugten Zufallszahlen sind. 

Die Generierung von Nachkommen: Der Wert (a [k].c [i]) wird für jede i-Koordinate jedes k-Nachfolgers berechnet. Dies geschieht, indem der skalierte Vektor y_vec[i] zum aktuellen Mittelwert cB[i] (dem Zentrum der aktuellen Verteilung) addiert wird. Die Skalierung erfolgt durch Multiplikation von y_vec[i] mit der Schrittweite „sigma“. Somit gilt: a[k].c[i] = cB[i] + σ * y_vec[i].

Die Funktion u.SeInDiSp stellt sicher, dass die Koordinatenwerte der Nachkommen innerhalb der durch rangeMin[i], rangeMax[i] und rangeStep[i] definierten gültigen Bereiche bleiben. Es kann Werte beschneiden, sie an Grenzen abprallen lassen und sie auf den nächsten gültigen Schrittwert quantisieren. Nachdem alle Nachkommen generiert wurden, wird das „Revision“-Flag auf „true“ gesetzt, und die Werte der Fitnessfunktion (Qualität) für die generierten Nachkommen sollten neu berechnet werden.

Infolgedessen erzeugt die Methode eine neue Lösungsmenge auf der Grundlage der aktuellen Verteilung (cB-Mittelwert und der Kovarianzmatrix, dargestellt durch B und D) sowie der Schrittweite σ. Sie nutzt Zufallszahlen, um kleine Abweichungen vom aktuellen Mittelwert zu erzeugen und so den Suchraum zu erkunden. Die Funktion SeInDiSp stellt sicher, dass neue Lösungen innerhalb der zulässigen Grenzen bleiben, und das „Revision“-Flag sorgt dafür, dass ihre Eignung anschließend bewertet wird.

//————————————————————————————————————————————————————————————————————
void C_AO_CMAES::Moving ()
{
  // Generate new lambda descendants
  for (int k = 0; k < popSize; k++)
  {

    // Apply the transformation y = B*D*z
    for (int i = 0; i < coords; i++)
    {
      y_vec [i] = 0.0;
      for (int j = 0; j < coords; j++)
      {
        y_vec [i] += B [i * coords + j] * D [j] * u.PowerDistribution (0.0, -8, 8, 20);
      }

      // Generate a descendant: x_k = m + σ * y
      a [k].c [i] = cB [i] + sigma * y_vec [i];
      a [k].c [i] = u.SeInDiSp (a [k].c [i], rangeMin [i], rangeMax [i], rangeStep [i]);
    }
  }

  // Fitness recalculation mark
  revision = true;
}
//————————————————————————————————————————————————————————————————————

Die Methode „Revision“ der Klasse C_AO_CMAES ist dafür zuständig, die Algorithmusparameter nach der Auswertung der Fitnessfunktion für eine neue Population zu aktualisieren. Zunächst überprüft die Methode den Wert des Flags „revision“. Anschließend wird die Methode „SortPopulation()“ aufgerufen, die die aktuelle Population so sortiert, dass die besten Individuen an den Anfang gestellt werden. Die Methode „UpdateDistribution()“ aktualisiert die Parameter der CMA-ES-Verteilung (den cB-Mittelwert des Strategievektors, die Kovarianzmatrix, die Sigma-Schrittweite und die evolutionären Pfade) auf der Grundlage von Informationen über die besten Individuen in der Population. Die Aktualisierung der Verteilung ist ein wesentlicher Schritt bei CMA-ES, da sie es dem Algorithmus ermöglicht, seine Suchstrategie auf der Grundlage der erzielten Ergebnisse anzupassen. 

Folglich stellt die Methode Revision den zentralen Anpassungszyklus von CMA-ES dar. Es sortiert die Population, aktualisiert die Verteilungsparameter auf der Grundlage der besten Individuen und erhöht den Berechnungszähler. 

//————————————————————————————————————————————————————————————————————
void C_AO_CMAES::Revision ()
{
  if (!revision) return;

  revision = false;

  // Sort the population by fitness
  SortPopulation ();

  // Update distribution parameters based on selected individuals
  UpdateDistribution ();

  // Update the calculation counter
  counteval++;
}
//————————————————————————————————————————————————————————————————————

Die Methode „InitDistribution“ der Klasse „C_AO_CMAES“ dient dazu, die Anfangsverteilung für die Lösungssuche zu initialisieren. Das Verfahren ermittelt für jede Koordinate im Suchraum einen anfänglichen Mittelwert (cB). Für jede i-Koordinate wird der Wert von cB[i] mithilfe der Funktion u.RNDfromCI() zufällig im Bereich zwischen rangeMin[i] und rangeMax[i] festgelegt, wobei der anfängliche Mittelwert für jede Variable in der Mitte des gültigen Bereichs liegt. Die äußere Schleife durchläuft die gesamte Population (popSize Individuen). Innerhalb dieser Schleife befindet sich eine innere Schleife, die alle Koordinaten jedes einzelnen Elements durchläuft. Für jeden Nachkommen werden Koordinaten generiert. Zunächst werden sie zufällig aus dem vorgegebenen Bereich RNDfromCI ausgewählt und anschließend mithilfe von SeInDiSp auf den Abtastschritt gerundet.

Die Funktion u.RNDfromCI erzeugt eine gleichverteilte Zufallszahl im angegebenen Intervall. Die Funktion u.SeInDiSp stellt sicher, dass die für jede einzelne Person generierten Koordinaten innerhalb der zulässigen Grenzen liegen, die durch rangeMin[i], rangeMax[i] und rangeStep[i] definiert sind.

Die Methode initialisiert die Startpopulation mit Zufallslösungen, die gleichmäßig im Suchraum verteilt sind. Der Mittelwert (cB) wird ebenfalls zufällig ausgewählt. Dies bildet den Ausgangspunkt für nachfolgende Iterationen von CMA-ES, in denen der Algorithmus die Suchstrategie anpasst, um die optimale Lösung zu finden.

//+------------------------------------------------------------------+
//| Initialize search distribution                                   |
//+------------------------------------------------------------------+
void C_AO_CMAES::InitDistribution ()
{
  // Set the initial mean to the center of the search space
  for (int i = 0; i < coords; i++)
  {
    cB [i] = u.RNDfromCI (rangeMin [i], rangeMax [i]);
  }

  for (int k = 0; k < popSize; k++)
  {
    for (int i = 0; i < coords; i++)
    {
      // Generate a uniformly distributed point
      a [k].c [i] = u.RNDfromCI (rangeMin [i], rangeMax [i]);
      a [k].c [i] = u.SeInDiSp (a [k].c [i], rangeMin [i], rangeMax [i], rangeStep [i]);
    }
  }
}
//————————————————————————————————————————————————————————————————————

Die Methode GetChiN berechnet die erwartete Norm eines Zufallsvektors, der aus der Verteilung N(0,I) für eine gegebene Anzahl von Koordinaten (coords) gezogen wird. Das Ergebnis wird auf der Grundlage der Dimension des Suchraums approximiert.

//+------------------------------------------------------------------+
//| Calculate the expected norm N(0,I)                               |
//+------------------------------------------------------------------+
double C_AO_CMAES::GetChiN ()
{
  double n = (double)coords;
  return MathSqrt (n) * (1.0 - 1.0 / (4.0 * n) + 1.0 / (21.0 * n * n));
}
//————————————————————————————————————————————————————————————————————

Die Funktion SortPopulation dient dazu, eine Population von Lösungen nach ihren Zielfunktionswerten (Fitness) zu sortieren, um die beste Lösung zu ermitteln. Zunächst kopiert die Methode die Fitnesswerte jedes Individuums aus der Population in das Hilfsarray „arfitness“. Außerdem wird das Array „arindex“ angelegt, das zunächst die Indizes der Individuen enthält. Dies ist notwendig, damit nach der Sortierung nach Fitness die Zuordnung zwischen der sortierten Fitness und dem ursprünglichen Individuum in der Population wiederhergestellt werden kann.

Es wird der Einfügungssortieralgorithmus verwendet. Es durchläuft das Array „arfitness“ und fügt jedes Element an der richtigen Stelle in den sortierten Teil des Arrays ein, wobei größere Elemente nach rechts verschoben werden. Es ist wichtig zu beachten, dass die Sortierung in absteigender Reihenfolge erfolgt (arfitness [j] < tempFitness in der „while“-Schleife); das Ziel besteht darin, die Zielfunktion (Fitness) zu maximieren. Neben arfitness wird auch arindex synchron sortiert, um die Position der ursprünglichen Indizes der einzelnen Elemente zu verfolgen.

Nach dem Sortieren prüft die Methode, ob die Fitness des besten Individuums besser ist als die der derzeit besten Lösung; ist dies der Fall, wird fB aktualisiert und die beste Lösung durch die Koordinaten des besten Individuums aus der Population ersetzt. Somit sortiert das Verfahren die Population nicht nur nach der Fitness, sondern behält auch die derzeit beste gefundene Lösung im Blick.

//+------------------------------------------------------------------+
//| Sort the population by fitness                                   |
//+------------------------------------------------------------------+
void C_AO_CMAES::SortPopulation ()
{
  // Copy fitness values and indices
  for (int i = 0; i < popSize; i++)
  {
    arindex [i] = i;
    arfitness [i] = a [i].f;
  }

  for (int i = 1; i < popSize; i++)
  {
    double tempFitness = arfitness [i];
    double tempIndex = arindex [i];
    int j = i - 1;

    // Sort in descending order (for maximization)
    while (j >= 0 && arfitness [j] < tempFitness)
    {
      arfitness [j + 1] = arfitness [j];
      arindex [j + 1] = arindex [j];
      j--;
    }

    arfitness [j + 1] = tempFitness;
    arindex [j + 1] = tempIndex;
  }

  // Update the best solution if necessary
  if (arfitness [0] > fB)
  {
    fB = arfitness [0];
    int bestIdx = (int)arindex [0];
    ArrayCopy (cB, a [bestIdx].c, 0, 0, coords);
  }
}
//————————————————————————————————————————————————————————————————————

Die Methode UpdateMean aktualisiert den Populationsmittelwert (cB) durch gewichtete Rekombination der besten Individuen. Für jede Koordinate wird ein neuer Durchschnittswert berechnet, der sich aus der Summe der gewichteten Koordinatenwerte der besten Individuen ergibt, wobei die Gewichte im Array „weights“ festgelegt sind.

//+------------------------------------------------------------------+
//| Update the mean using weighted recombination                     |
//+------------------------------------------------------------------+
void C_AO_CMAES::UpdateMean ()
{
  // Weighted recombination: m^(g+1) = Σ w_i * x_{i:λ}^(g+1)
  for (int j = 0; j < coords; j++)
  {
    double meanSum = 0.0;
    for (int i = 0; i < mu; i++)
    {
      int idx = (int)arindex [i];
      meanSum += weights [i] * a [idx].c [j];
    }
    cB [j] = meanSum;
  }
}
//————————————————————————————————————————————————————————————————————

Die Methode ComputeWeights berechnet Gewichte für die gewichtete Rekombination, legt das Gewichtsarray entsprechend der Größe mu an und berechnet log(mu + 0,5) zur Verwendung in der Gewichtsformel. Für jedes i von 0 bis mu-1 weist sie ein Gewicht zu, das der Differenz zwischen log(mu + 0,5) und log(i+1) entspricht; summiert diese Gewichte, dividiert alle Gewichte durch die Summe, sodass die Summe gleich 1 ist, und berechnet die Summe der Quadrate der Gewichte sowie mu_eff – die Effizienz der Verwendung der Gewichte, die für die Abstimmung von Geschwindigkeit und Varianz im Algorithmus wichtig ist.

Diese Methode liefert optimale Gewichte für die Rekombination, wobei der Beitrag der besten Lösungen berücksichtigt wird.

//+------------------------------------------------------------------+
//| Calculate weighted recombination weights                         |
//+------------------------------------------------------------------+
void C_AO_CMAES::ComputeWeights ()
{
  // Allocate the array of weights
  ArrayResize (weights, mu);

  // Pre-calculate log(mu + 0.5)
  double log_mu_plus_half = MathLog (mu + 0.5);

  // Calculate positive weights
  double sum = 0.0;
  for (int i = 0; i < mu; i++)
  {
    weights [i] = log_mu_plus_half - MathLog (i + 1);
    sum += weights [i];
  }

  // Normalize weights
  double sum_weights = 0.0;
  double sum_squares = 0.0;
  for (int i = 0; i < mu; i++)
  {
    weights [i] /= sum;
    sum_weights += weights [i];
    sum_squares += weights [i] * weights [i];
  }

  // Calculate the effective mass of the variance selection
  mu_eff = sum_weights * sum_weights / sum_squares;
}
//————————————————————————————————————————————————————————————————————

Die Methode UpdateDistribution aktualisiert die Verteilungsparameter, nämlich die Kovarianzmatrix (covMatrix) und die Schrittweite (sigma) im CMA-ES-Algorithmus. Wenn genügend Generationen vergangen sind (counteval – eigeneval > eigenInterval), wird die Eigenwertzerlegung berechnet und der aktuelle Mittelwert (cB) im temporären Array „oldMean“ gespeichert. Die Methode ruft „UpdateMean“ auf, um einen neuen Mittelwert zu berechnen, ermittelt die Differenz zwischen dem neuen und dem alten Mittelwert geteilt durch Sigma und führt zudem die Multiplikation von y_w mit C^(-1/2) durch, wobei dieser Vorgang mithilfe der B- und D-Zerlegung in mehrere Schritte unterteilt wird; das Ergebnis wird in „invsqrtC_times_yw“ gespeichert.

Die Methode aktualisiert anschließend den Evolutionspfad für die Schrittweite ps unter Verwendung von invsqrtC_times_yw, ermittelt anhand der ps-Norm und der erwarteten Länge, ob der Fortschritt als „stagnierend“ eingestuft wird, und aktualisiert dann den Evolutionspfad für die pc-Kovarianzmatrix unter Verwendung von y_w und dem Fortschrittsindikator hsig. Die Methode berechnet zunächst die Aktualisierungsmatrix c1a von Rank-One, anschließend die Aktualisierungsmatrix cmu von Rank-μ auf der Grundlage der Differenz zwischen den Einzelwerten und dem alten Mittelwert, geteilt durch Sigma unter Verwendung von „Gewichten“, aktualisiert die Kovarianzmatrix covMatrix anhand der Aktualisierungen von Rank-One- und Rank-μ und passt c1 an, wenn der Fortschritt ins Stocken gerät. Die Funktion „EnforcePositiveDefinite“ wird regelmäßig aufgerufen, um sicherzustellen, dass die Kovarianzmatrix positiv definit ist. Die Sigma-Schrittweite wird auf der Grundlage der PS-Norm aktualisiert und aus Gründen der numerischen Stabilität auf den Bereich zwischen min_sigma und max_sigma begrenzt.

Im Allgemeinen passt „UpdateDistribution“ die Verteilungsparameter (Kovarianzmatrix und Schrittweite) auf der Grundlage des Suchverlaufs (Evolutionspfad) und der Populationsdaten an, um sich an die Zielfunktionslandschaft anzupassen.

//+------------------------------------------------------------------+ 
//| Update distribution parameters                                   |
//+------------------------------------------------------------------+
void C_AO_CMAES::UpdateDistribution ()
{
  // Check the necessity of eigenvalue decomposition
  if (counteval - eigeneval > eigenInterval)
  {
    ComputeEigendecomposition ();
    eigeneval = counteval;
  }

  // Preserve the old average
  double oldMean [];
  ArrayResize (oldMean, coords);
  ArrayCopy (oldMean, cB, 0, 0, coords);

  // Update average
  UpdateMean ();

  // Calculate the mean shift
  double y_w [];
  ArrayResize (y_w, coords);
  for (int j = 0; j < coords; j++)
  {
    y_w [j] = (cB [j] - oldMean [j]) / sigma;
  }

  // Calculate C^(-1/2) * y_w
  // Step 1: B^T * y_w
  ArrayInitialize (temp_vec, 0.0);
  for (int i = 0; i < coords; i++)
  {
    for (int j = 0; j < coords; j++)
    {
      temp_vec [i] += B [j * coords + i] * y_w [j];
    }
  }

  // Step 2: D^(-1) * (B^T * y_w)
  for (int i = 0; i < coords; i++)
  {
    temp_vec [i] /= D [i];
  }

  // Step 3: B * D^(-1) * B^T * y_w
  ArrayInitialize (invsqrtC_times_yw, 0.0);
  for (int i = 0; i < coords; i++)
  {
    for (int j = 0; j < coords; j++)
    {
      invsqrtC_times_yw [i] += B [i * coords + j] * temp_vec [j];
    }
  }

  // Update the evolution path for sigma
  double norm_ps_squared = 0.0;
  for (int i = 0; i < coords; i++)
  {
    ps [i] = (1.0 - cs) * ps [i] + MathSqrt (cs * (2.0 - cs) * mu_eff) * invsqrtC_times_yw [i];
    norm_ps_squared += ps [i] * ps [i];
  }

  // Heaviside function
  double norm_ps = MathSqrt (norm_ps_squared);
  double expected_length = MathSqrt (1.0 - MathPow (1.0 - cs, 2.0 * counteval)) * chiN;
  bool hsig = norm_ps / expected_length < hsig_threshold;

  // Update the evolution path for C
  double delta_hsig = hsig ? 1.0 : 0.0;
  for (int i = 0; i < coords; i++)
  {
    pc [i] = (1.0 - cc) * pc [i] + delta_hsig * MathSqrt (cc * (2.0 - cc) * mu_eff) * y_w [i];
  }

  // Prepare rank-1 update
  double c1a [];
  ArrayResize (c1a, coords * coords);
  for (int i = 0; i < coords; i++)
  {
    for (int j = 0; j <= i; j++)
    {
      c1a [i * coords + j] = c1a [j * coords + i] = pc [i] * pc [j];
    }
  }

  // Prepare rank-μ update
  double cmu [];
  ArrayResize (cmu, coords * coords);
  ArrayInitialize (cmu, 0.0);

  for (int k = 0; k < mu; k++)
  {
    int idx = (int)arindex [k];

    // Calculate y_i = (x_i - m_old) / sigma
    for (int i = 0; i < coords; i++)
    {
      temp_vec [i] = (a [idx].c [i] - oldMean [i]) / sigma;
    }

    // Add the weighted outer product
    for (int i = 0; i < coords; i++)
    {
      for (int j = 0; j <= i; j++)
      {
        double update = weights [k] * temp_vec [i] * temp_vec [j];
        cmu [i * coords + j] += update;
        if (i != j) cmu [j * coords + i] += update;
      }
    }
  }

  // Update C the covariance matrix
  double c1 = learningRateC1;
  double cmu_rate = learningRateCMu;

  // Adjust c1 if hsig is 'false' (progress stalled)
  if (!hsig)
  {
    c1 *= (1.0 - (1.0 - delta_hsig) * cc * (2.0 - cc));
  }

  double one_minus_c1_cmu = 1.0 - c1 - cmu_rate;

  // Update C with rank-1 and rank-μ updates
  for (int i = 0; i < coords; i++)
  {
    for (int j = 0; j <= i; j++)
    {
      covMatrix [i * coords + j] = one_minus_c1_cmu * covMatrix [i * coords + j] +
                                   c1 * c1a [i * coords + j] +
                                   cmu_rate * cmu [i * coords + j];

      // Maintain symmetry
      if (i != j)
      {
        covMatrix [j * coords + i] = covMatrix [i * coords + j];
      }
    }
  }

  // Ensure positive definiteness
  if (counteval % (10 * eigenInterval) == 0)
  {
    EnforcePositiveDefinite ();
  }

  //  Update the sigma step size
  double exponent = (cs / damps) * (norm_ps / chiN - 1.0);
  sigma *= MathExp (exponent);

  // Limit sigma for numerical stability
  double min_sigma = 1e-16;
  double max_eigenvalue = D [0]; // D sorted in descending order
  double max_sigma = 1e4 * MathMax (1.0, MathSqrt (max_eigenvalue));

  if (sigma < min_sigma) sigma = min_sigma;
  else
    if (sigma > max_sigma) sigma = max_sigma;
}
//————————————————————————————————————————————————————————————————————

Die Funktion „ComputeEigendecomposition“ führt die Zerlegung der symmetrischen Kovarianzmatrix „covMatrix“ in Eigenwerte und Eigenvektoren mithilfe der verbesserten Jacobi-Methode durch. Dabei wird eine Kopie der „covMatrix“ erstellt, um die Originaldaten nicht zu verändern. Zunächst wird B als Identitätsmatrix gesetzt, die die Anfangsbasis darstellt. Die Iteration läuft entweder bis zur maximalen Iterationszahl oder bis die von Null verschiedenen Elemente auf den Nebendiagonalen kleiner als „tolerance“ sind. 

Das größte Element außerhalb der Diagonalen finden: Finde das Element mit dem größten Absolutwert außerhalb der Diagonalen, um es für die Drehung auszuwählen. Ist der Maximalwert kleiner als „Toleranz“, wird der Zyklus unterbrochen. Die Methode berechnet den Winkel φ, um die jakobische Rotation durchzuführen, modifiziert die Elemente von C_copy durch Anwendung der Rotation und aktualisiert die Spalten der Matrix B (Eigenvektoren) entsprechend der Rotation. Nach den Iterationen ermittelt die Methode die Quadratwurzel der Diagonalelemente (wobei eine minimale Positivität von 1e-14 gewährleistet ist) und ordnet die Eigenwerte und die zugehörigen Eigenvektoren in absteigender Reihenfolge für die weitere Verwendung. Somit ermöglicht diese Methode die genaue Berechnung der Eigenwerte und Eigenvektoren der Kovarianzmatrix.

//+------------------------------------------------------------------+
//| Calculate the eigenvalue decomposition using the Jacobi method   |
//+------------------------------------------------------------------+
void C_AO_CMAES::ComputeEigendecomposition ()
{
  // Create a copy of the covariance matrix for decomposition
  double C_copy [];
  ArrayResize (C_copy, coords * coords);
  ArrayCopy (C_copy, covMatrix);

  // Initialize B as the identity matrix
  for (int i = 0; i < coords; i++)
  {
    for (int j = 0; j < coords; j++)
    {
      B [i * coords + j] = (i == j) ? 1.0 : 0.0;
    }
  }

  // Improved Jacobi eigenvalue decomposition
  int max_iterations = 10; //50 * coords;
  double tolerance   = 0.01; //1e-14 * coords * coords;

  for (int iter = 0; iter < max_iterations; iter++)
  {
    // Find the largest off-diagonal element
    double max_val = 0.0;
    int p = 0, q = 1;

    for (int i = 0; i < coords - 1; i++)
    {
      for (int j = i + 1; j < coords; j++)
      {
        double val = MathAbs (C_copy [i * coords + j]);
        if (val > max_val)
        {
          max_val = val;
          p = i;
          q = j;
        }
      }
    }

    // Check convergence
    if (max_val < tolerance) break;

    // Calculate the rotation angle
    double app = C_copy [p * coords + p];
    double aqq = C_copy [q * coords + q];
    double apq = C_copy [p * coords + q];

    double phi = 0.5 * MathArctan (2.0 * apq / (aqq - app + 1e-14));
    double c = MathCos (phi);
    double s = MathSin (phi);

    // Update the matrix elements
    double app_new = c * c * app - 2 * c * s * apq + s * s * aqq;
    double aqq_new = s * s * app + 2 * c * s * apq + c * c * aqq;

    C_copy [p * coords + p] = app_new;
    C_copy [q * coords + q] = aqq_new;
    C_copy [p * coords + q] = C_copy [q * coords + p] = 0.0;

    // Update other elements in p and q rows/columns
    for (int i = 0; i < coords; i++)
    {
      if (i != p && i != q)
      {
        double aip = C_copy [i * coords + p];
        double aiq = C_copy [i * coords + q];
        C_copy [i * coords + p] = C_copy [p * coords + i] = c * aip - s * aiq;
        C_copy [i * coords + q] = C_copy [q * coords + i] = s * aip + c * aiq;
      }
    }

    // Update eigenvectors
    for (int i = 0; i < coords; i++)
    {
      double bip = B [i * coords + p];
      double biq = B [i * coords + q];
      B [i * coords + p] = c * bip - s * biq;
      B [i * coords + q] = s * bip + c * biq;
    }
  }

  // Extract eigenvalues and ensure positivity
  double min_eigenvalue = 1e-14;
  for (int i = 0; i < coords; i++)
  {
    D [i] = MathSqrt (MathMax (min_eigenvalue, C_copy [i * coords + i]));
  }

  // Sort eigenvalues and vectors in descending order
  for (int i = 0; i < coords - 1; i++)
  {
    int max_idx = i;
    for (int j = i + 1; j < coords; j++)
    {
      if (D [j] > D [max_idx]) max_idx = j;
    }

    if (max_idx != i)
    {
      // Exchange eigenvalues
      double temp = D [i];
      D [i] = D [max_idx];
      D [max_idx] = temp;

      // Exchange eigenvectors
      for (int k = 0; k < coords; k++)
      {
        temp = B [k * coords + i];
        B [k * coords + i] = B [k * coords + max_idx];
        B [k * coords + max_idx] = temp;
      }
    }
  }
}
//————————————————————————————————————————————————————————————————————

Die Methode „CheckPositiveDefinite“ prüft, ob die Kovarianzmatrix positiv definit ist. Es prüft schnell, ob die Diagonalelemente positiv sind, und vergleicht den kleinsten Eigenwert (aus dem in absteigender Reihenfolge sortierten Array „D“) mit einer kleinen positiven Zahl von der Form 1e-14. Wenn beide Prüfungen erfolgreich sind, gibt die Methode „true “ zurück.

//+------------------------------------------------------------------+
//| Check the positive definiteness of the covariance matrix         |
//+------------------------------------------------------------------+
bool C_AO_CMAES::CheckPositiveDefinite ()
{
  // Quick check: all diagonal elements should be positive
  for (int i = 0; i < coords; i++)
  {
    if (covMatrix [i * coords + i] <= 0) return false;
  }

  // Check if the minimum eigenvalue is positive
  double min_eigenvalue = D [coords - 1]; // D is sorted in descending order
  return min_eigenvalue > 1e-14;
}
//————————————————————————————————————————————————————————————————————

Die Methode „EnforcePositiveDefinite“ stellt die positive Definitheit der Kovarianzmatrix „covMatrix“ sicher. Sie führt mehrere Schritte durch: Es ermittelt das kleinste Element auf der Diagonalen und addiert den fehlenden „Korrekturwert“ zu den Diagonalelementen, sodass das Minimum 1e-10 beträgt; außerdem maximiert es die Symmetrie der Matrix, indem es jedes Element außerhalb der Diagonalen mit seiner Transponierten mittelt.

Ist die Matrix immer noch nicht positiv definit, wird mit „ComputeEigendecomposition“ eine Eigenwertzerlegung durchgeführt, wobei alle Eigenwerte kleiner als √1e-10 durch √1e-10 ersetzt werden, um die Positivität sicherzustellen. Die Methode berechnet dann die covMatrix unter Verwendung der B-Eigenvektoren und der korrigierten D-Eigenwerte neu, um eine korrigierte positiv definite Matrix zu erhalten. Dieser Ansatz stellt sicher, dass die Kovarianzmatrix positiv definit wird und sich für weitere Berechnungen im CMA-ES-Algorithmus eignet.

//+------------------------------------------------------------------+
//| Ensuring positive definiteness of the covariance matrix          |
//+------------------------------------------------------------------+
void C_AO_CMAES::EnforcePositiveDefinite ()
{
  // Method 1: Adding a small value to the diagonal
  double min_diag = 1e308; // A very large number
  for (int i = 0; i < coords; i++)
  {
    if (covMatrix [i * coords + i] < min_diag)
    {
      min_diag = covMatrix [i * coords + i];
    }
  }

  if (min_diag < 1e-10)
  {
    double correction = 1e-10 - min_diag;
    for (int i = 0; i < coords; i++)
    {
      covMatrix [i * coords + i] += correction;
    }
  }

  // Method 2: Ensuring symmetry
  for (int i = 0; i < coords; i++)
  {
    for (int j = i + 1; j < coords; j++)
    {
      double avg = (covMatrix [i * coords + j] + covMatrix [j * coords + i]) * 0.5;
      covMatrix [i * coords + j] = covMatrix [j * coords + i] = avg;
    }
  }

  // If still not positive definite, perform the factorization and fix
  if (!CheckPositiveDefinite ())
  {
    ComputeEigendecomposition ();

    double min_eigenvalue = 1e-10;
    for (int i = 0; i < coords; i++)
    {
      if (D [i] < MathSqrt (min_eigenvalue))
      {
        D [i] = MathSqrt (min_eigenvalue);
      }
    }

    // Reconstruct C = B * D^2 * B^T
    ArrayInitialize (covMatrix, 0.0);
    for (int i = 0; i < coords; i++)
    {
      for (int j = 0; j <= i; j++)
      {
        double sum = 0.0;
        for (int k = 0; k < coords; k++)
        {
          sum += B [i * coords + k] * D [k] * D [k] * B [j * coords + k];
        }
        covMatrix [i * coords + j] = covMatrix [j * coords + i] = sum;
      }
    }
  }
}
//————————————————————————————————————————————————————————————————————


Testergebnisse

Nun können wir uns endlich die Ergebnisse ansehen. Wie sofort erkennbar ist, bewältigt der Algorithmus mittelgroße Probleme sowie – bei bestimmten Einstellungen – Probleme mit geringer Dimension gut und schnell; die schwierigsten, mehrdimensionalen Probleme erfordern jedoch einen unverhältnismäßig hohen Zeitaufwand für die Ausführung, sodass sie praktisch aus den Ergebnissen ausgeschlossen wurden. Warum ist das so? Traditionell stellen Matrixberechnungen in MQL5 ein erhebliches Rechenproblem dar. Bei der manuellen Durchführung von Matrixoperationen zeigt der Algorithmus bei hochdimensionalen Problemen eine unbefriedigende Leistung, was seine praktische Anwendbarkeit erheblich einschränkt. Mit der Einführung integrierter Klassen für die Arbeit mit Matrizen ändert sich die Situation jedoch grundlegend. Es ist nun von entscheidender Bedeutung, für alle rechenintensiven Aufgaben die plattformnative Implementierung von Matrixoperationen zu verwenden.

CMAES|Covariance Matrix Adaptation Evolution Strategy|50.0|25.0|0.01|0.8|1.0|
=============================
5 Hilly's; Func runs: 10000; result: 0.7625797883550075
25 Hilly's; Func runs: 10000; result: 0.7208874560706138
500 Hilly's; Func runs: 10000; result: 0.0
=============================
5 Forest's; Func runs: 10000; result: 0.8205636421348295
25 Forest's; Func runs: 10000; result: 0.7961602627346933
500 Forest's; Func runs: 10000; result: 0.0
=============================
5 Megacity's; Func runs: 10000; result: 0.7584615384615383
25 Megacity's; Func runs: 10000; result: 0.49076923076923074
500 Megacity's; Func runs: 10000; result: 0.0
=============================
All score: 4.34942 (48.33%)

In der Visualisierung ist zu erkennen, dass der Algorithmus sowohl bei „großen Sprüngen“ funktioniert als auch sich auf die Extremwerte der Funktion konzentriert und diese gründlich untersucht.

Hilly

CMA-ES mit der Testfunktion Hilly

Forest

CMA-ES mit der Testfunktion Forest

Megacity

CMA-ES mit der Testfunktion Megacity

Aufgrund der Ergebnisse belegt der CMA-ES-Algorithmus Platz 38 in der Rangliste der leistungsstärksten populationsbasierten Optimierungsalgorithmen.

#
AO
Beschreibung
Hilly
Hilly
Final
Forest
Forest
Final
Megacity (discrete)
Megacity
Final
Final
Result
% of
MAX
10 p (5 F) 50 p (25 F) 1000 p (500 F) 10 p (5 F) 50 p (25 F) 1000 p (500 F) 10 p (5 F) 50 p (25 F) 1000 p (500 F)
1 ANS Suche über die gesamte Nachbarschaft 0.94948 0.84776 0.43857 2.23581 1.00000 0.92334 0.39988 2.32323 0.70923 0.63477 0.23091 1.57491 6.134 68.15
2 CLA Code-Lock-Algorithmus (joo) 0.95345 0.87107 0.37590 2.20042 0.98942 0.91709 0.31642 2.22294 0.79692 0.69385 0.19303 1.68380 6.107 67.86
3 AMOm Optimierung der Tierwanderung M 0.90358 0.84317 0.46284 2.20959 0.99001 0.92436 0.46598 2.38034 0.56769 0.59132 0.23773 1.39675 5.987 66.52
4 (P+O)ES (P+O) Entwicklungsstrategien 0.92256 0.88101 0.40021 2.20379 0.97750 0.87490 0.31945 2.17185 0.67385 0.62985 0.18634 1.49003 5.866 65.17
5 CTA Kometenschweif-Algorithmus (joo) 0.95346 0.86319 0.27770 2.09435 0.99794 0.85740 0.33949 2.19484 0.88769 0.56431 0.10512 1.55712 5.846 64.96
6 TETA Zeitentwicklungs-Reisealgorithmus (joo) 0.91362 0.82349 0.31990 2.05701 0.97096 0.89532 0.29324 2.15952 0.73462 0.68569 0.16021 1.58052 5.797 64.41
7 SDSm stochastische Diffusionssuche M 0.93066 0.85445 0.39476 2.17988 0.99983 0.89244 0.19619 2.08846 0.72333 0.61100 0.10670 1.44103 5.709 63.44
8 BOAm Billard-Optimierungsalgorithmus M 0.95757 0.82599 0.25235 2.03590 1.00000 0.90036 0.30502 2.20538 0.73538 0.52523 0.09563 1.35625 5.598 62.19
9 AAm Algorithmus für das Bogenschießen M 0.91744 0.70876 0.42160 2.04780 0.92527 0.75802 0.35328 2.03657 0.67385 0.55200 0.23738 1.46323 5.548 61.64
10 ESG Entwicklung sozialer Gruppen (joo) 0.99906 0.79654 0.35056 2.14616 1.00000 0.82863 0.13102 1.95965 0.82333 0.55300 0.04725 1.42358 5.529 61.44
11 SIA simuliertes isotropes Tempern (joo) 0.95784 0.84264 0.41465 2.21513 0.98239 0.79586 0.20507 1.98332 0.68667 0.49300 0.09053 1.27020 5.469 60.76
12 BBO biogeografisch basierte Optimierung 0.94912 0.69456 0.35031 1.99399 0.93820 0.67365 0.25682 1.86867 0.74615 0.48277 0.17369 1.40261 5.265 58.50
13 ACS künstliche kooperative Suche 0.75547 0.74744 0.30407 1.80698 1.00000 0.88861 0.22413 2.11274 0.69077 0.48185 0.13322 1.30583 5.226 58.06
14 DA dialektischer Algorithmus 0.86183 0.70033 0.33724 1.89940 0.98163 0.72772 0.28718 1.99653 0.70308 0.45292 0.16367 1.31967 5.216 57.95
15 BHAm Schwarzes-Loch-Algorithmus M 0.75236 0.76675 0.34583 1.86493 0.93593 0.80152 0.27177 2.00923 0.65077 0.51646 0.15472 1.32195 5.196 57.73
16 ASO Optimierung einer anarchischen Gesellschaft 0.84872 0.74646 0.31465 1.90983 0.96148 0.79150 0.23803 1.99101 0.57077 0.54062 0.16614 1.27752 5.178 57.54
17 RFO Optimierung des Royal Flush (joo) 0.83361 0.73742 0.34629 1.91733 0.89424 0.73824 0.24098 1.87346 0.63154 0.50292 0.16421 1.29867 5.089 56.55
18 AOSm Suche nach Atomorbitalen M 0.80232 0.70449 0.31021 1.81702 0.85660 0.69451 0.21996 1.77107 0.74615 0.52862 0.14358 1.41835 5.006 55.63
19 TSEA Algorithmus der Schildkrötenpanzer-Evolution (JOO) 0.96798 0.64480 0.29672 1.90949 0.99449 0.61981 0.22708 1.84139 0.69077 0.42646 0.13598 1.25322 5.004 55.60
20 DE differentielle Evolution 0.95044 0.61674 0.30308 1.87026 0.95317 0.78896 0.16652 1.90865 0.78667 0.36033 0.02953 1.17653 4.955 55.06
21 SRA Algorithmus für erfolgreiche Gastronomen (joo) 0.96883 0.63455 0.29217 1.89555 0.94637 0.55506 0.19124 1.69267 0.74923 0.44031 0.12526 1.31480 4.903 54.48
22 CRO Optimierung chemischer Reaktionen 0.94629 0.66112 0.29853 1.90593 0.87906 0.58422 0.21146 1.67473 0.75846 0.42646 0.12686 1.31178 4.892 54.36
23 BIO Optimierung der Blutvererbung (joo) 0.81568 0.65336 0.30877 1.77781 0.89937 0.65319 0.21760 1.77016 0.67846 0.47631 0.13902 1.29378 4.842 53.80
24 BSA Vogelschwarm-Algorithmus 0.89306 0.64900 0.26250 1.80455 0.92420 0.71121 0.24939 1.88479 0.69385 0.32615 0.10012 1.12012 4.809 53.44
25 HS Harmoniesuche 0.86509 0.68782 0.32527 1.87818 0.99999 0.68002 0.09590 1.77592 0.62000 0.42267 0.05458 1.09725 4.751 52.79
26 SSG Setzlinge säen und wachsen 0.77839 0.64925 0.39543 1.82308 0.85973 0.62467 0.17429 1.65869 0.64667 0.44133 0.10598 1.19398 4.676 51.95
27 BCOm Optimierung der bakteriellen Chemotaxis M 0.75953 0.62268 0.31483 1.69704 0.89378 0.61339 0.22542 1.73259 0.65385 0.42092 0.14435 1.21912 4.649 51.65
28 ABO Optimierung nach afrikanischen Büffeln 0.83337 0.62247 0.29964 1.75548 0.92170 0.58618 0.19723 1.70511 0.61000 0.43154 0.13225 1.17378 4.634 51.49
29 (PO)ES (PO) Evolutionsstrategien 0.79025 0.62647 0.42935 1.84606 0.87616 0.60943 0.19591 1.68151 0.59000 0.37933 0.11322 1.08255 4.610 51.22
30 FBA Fraktal-basierter Algorithmus 0.79000 0.65134 0.28965 1.73099 0.87158 0.56823 0.18877 1.62858 0.61077 0.46062 0.12398 1.19537 4.555 50.61
31 TSm Tabu-Suche M 0.87795 0.61431 0.29104 1.78330 0.92885 0.51844 0.19054 1.63783 0.61077 0.38215 0.12157 1.11449 4.536 50.40
32 BSO Brainstorming-Optimierung 0.93736 0.57616 0.29688 1.81041 0.93131 0.55866 0.23537 1.72534 0.55231 0.29077 0.11914 0.96222 4.498 49.98
33 WOAm Wal-Optimierungsalgorithmus M 0.84521 0.56298 0.26263 1.67081 0.93100 0.52278 0.16365 1.61743 0.66308 0.41138 0.11357 1.18803 4.476 49.74
34 AEFA Algorithmus für künstliche elektrische Felder 0.87700 0.61753 0.25235 1.74688 0.92729 0.72698 0.18064 1.83490 0.66615 0.11631 0.09508 0.87754 4.459 49.55
35 AEO Algorithmus zur Optimierung auf der Grundlage künstlicher Ökosysteme 0.91380 0.46713 0.26470 1.64563 0.90223 0.43705 0.21400 1.55327 0.66154 0.30800 0.28563 1.25517 4.454 49.49
36 CAm Kamel-Algorithmus M 0.78684 0.56042 0.35133 1.69859 0.82772 0.56041 0.24336 1.63149 0.64846 0.33092 0.13418 1.11356 4.444 49.37
37 ACOm Ameisen-Kolonie-Optimierung M 0.88190 0.66127 0.30377 1.84693 0.85873 0.58680 0.15051 1.59604 0.59667 0.37333 0.02472 0.99472 4.438 49.31
38 CMAES Anpassung der Kovarianzmatrix mittels Evolutionsstrategie 0.76258 0.72089 0.00000 1.48347 0.82056 0.79616 0.00000 1.61672 0.75846 0.49077 0.00000 1.24923 4.349 48.33
39 BFO-GA Optimierung der bakteriellen Nahrungssuche - ga 0.89150 0.55111 0.31529 1.75790 0.96982 0.39612 0.06305 1.42899 0.72667 0.27500 0.03525 1.03692 4.224 46.93
40 SOA einfacher Optimierungsalgorithmus 0.91520 0.46976 0.27089 1.65585 0.89675 0.37401 0.16984 1.44060 0.69538 0.28031 0.10852 1.08422 4.181 46.45
41 ABHA Algorithmus für künstliche Bienenstöcke 0.84131 0.54227 0.26304 1.64663 0.87858 0.47779 0.17181 1.52818 0.50923 0.33877 0.10397 0.95197 4.127 45.85
42 ACMO Optimierung atmosphärischer Wolkenmodelle 0.90321 0.48546 0.30403 1.69270 0.80268 0.37857 0.19178 1.37303 0.62308 0.24400 0.10795 0.97503 4.041 44.90
43 ADAMm adaptive Momentenschätzung M 0.88635 0.44766 0.26613 1.60014 0.84497 0.38493 0.16889 1.39880 0.66154 0.27046 0.10594 1.03794 4.037 44.85
44 CGO Optimierung von Chaos-Spielen 0.57256 0.37158 0.32018 1.26432 0.61176 0.61931 0.62161 1.85267 0.37538 0.21923 0.19028 0.78490 3.902 43.35
45 CROm Korallenriff-Optimierung M 0.78512 0.46032 0.25958 1.50502 0.86688 0.35297 0.16267 1.38252 0.63231 0.26738 0.10734 1.00703 3.895 43.27
RW Random Walk 0.48754 0.32159 0.25781 1.06694 0.37554 0.21944 0.15877 0.75375 0.27969 0.14917 0.09847 0.52734 2.348 26.09


Zusammenfassung

Die Fähigkeit von CMA-ES, die Suchstrategie an die lokale Geometrie der Zielfunktion anzupassen, macht es für komplexe, schlecht konditionierte Probleme mit unbekannter Struktur praktisch unverzichtbar. Die dickeren Verteilungsenden der Potenzgesetzverteilung ermöglichen es dem Algorithmus, „große Sprünge“ zu machen, was entscheidend ist, um lokale Optima zu überwinden. Die Rechenkomplexität von O(n²) Speicherplatz und O(n³) Zeit schränkt die Anwendbarkeit des Algorithmus erheblich ein. Bei hochdimensionalen Problemen (n > 100) steht der Ressourcenaufwand in keinem Verhältnis mehr zum erzielten Nutzen. Die Laufzeit bei mehrdimensionalen Funktionen steigt so stark an, dass der Algorithmus für große n praktisch nicht mehr anwendbar ist.

CMA-ES arbeitet mit verrauschten Funktionen, kommt mit diskontinuierlichen Zielfunktionen zurecht und ist auf Landschaften mit mehreren Extrema effektiv. Das Geheimnis dieser Vielseitigkeit liegt in der grundlegenden Philosophie des Algorithmus: Anstatt Annahmen über die Struktur der Funktion zu treffen, erkundet er diese sorgfältig und passt seine Suchstrategie auf der Grundlage gesammelter Erfahrungen an. CMA-ES verändert die Landschaft der evolutionären Berechnung, indem es zeigt, dass biologisch inspirierte Algorithmen auf strengen mathematischen Grundlagen beruhen können und die Anpassung von Algorithmen nicht bloß eine Feinabstimmung von Parametern ist, sondern ein grundlegendes Prinzip zum Erlernen der Struktur eines Problems.

Der Algorithmus eignet sich hervorragend für mittelgroße Probleme; es handelt sich um ein Spezialwerkzeug, das in seiner Nische eine vielversprechende Alternative darstellt. Der Algorithmus verbindet mathematische Eleganz mit praktischer Effizienz, muss jedoch angesichts seiner rechnerischen Grenzen sorgfältig angewendet werden. In solchen Fällen haben die Entwickler der MQL5-Sprache kürzlich schnelle MQL5-Matrixoperationen zur Berechnung von Matrizen eingeführt; in dieser Implementierung des Algorithmus wurden herkömmliche sequenzielle Berechnungen verwendet.

Tabelle

Abbildung 2. Farbskala der Algorithmen nach den entsprechenden Tests

Histogramm

Abbildung 3. Histogramm der Algorithmus-Testergebnisse (Skala von 0 bis 100, je höher, desto besser, wobei 100 das maximal mögliche theoretische Ergebnis ist; im Archiv befindet sich ein Skript zur Berechnung der Bewertungstabelle)

Vor- und Nachteile des CMAES:

Vorteile:

  1. Gute Konvergenz bei Funktionen mittlerer Dimension.

Nachteile:

  1. Streuung der Ergebnisse bei niedrigdimensionalen Funktionen.
  2. Hoher Ressourcenverbrauch bei hochdimensionalen Problemen.

Dem Artikel ist ein Archiv mit den aktuellen Versionen der Algorithmuscodes beigefügt. Der Autor des Artikels übernimmt keine Verantwortung für die absolute Richtigkeit der Beschreibung der kanonischen Algorithmen. An vielen von ihnen wurden Änderungen vorgenommen, um die Suchmöglichkeiten zu verbessern. Die in den Artikeln dargelegten Schlussfolgerungen und Urteile beruhen auf den Ergebnissen der Experimente.


Programme, die in diesem Artikel verwendet werden

# Name Typ Beschreibung
1 #C_AO.mqh
Include
Übergeordnete Klasse von Populationsoptimierungsalgorithmen
2 #C_AO_enum.mqh
Include
Enumeration der Algorithmen zur Populationsoptimierung
3 TestFunctions.mqh
Include
Bibliothek mit Testfunktionen
4
TestStandFunctions.mqh
Include
Bibliothek mit Funktionen für die Testumgebung
5
Utilities.mqh
Include
Bibliothek mit Hilfsfunktionen
6
CalculationTestResults.mqh
Include
Skript zur Berechnung der Ergebnisse in der Vergleichstabelle
7
Testing AOs.mq5
Skript Die einheitliche Testumgebung für alle Algorithmen zur Populationsoptimierung
8
Simple use of population optimization algorithms.mq5
Skript
Ein einfaches Beispiel für die Verwendung von Algorithmen zur Populationsoptimierung ohne Visualisierung
9
Test_AO_CMAES.mq5
Skript CMAES-Testumgebung

Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/18227

Beigefügte Dateien |
CMAES.zip (230.9 KB)
Marktsimulation (Teil 23): Erste Schritte mit SQL (VI) Marktsimulation (Teil 23): Erste Schritte mit SQL (VI)
In diesem Artikel werden wir sehen, wie man eine Datenbank visualisiert und daraus ihre Struktur versteht. Dies geschieht durch die Analyse ihrer internen Struktur. Auch wenn dies auf den ersten Blick unnötig erscheinen mag, ist es durchaus gerechtfertigt, wenn wir wirklich Datenbankadministratoren werden wollen. Schließlich verdienen manche Menschen ihren Lebensunterhalt damit, Datenbanken zu pflegen und zu entwerfen.
Entwicklung eines Expert Advisors für mehrere Währungen (Teil 27): Komponente zur Anzeige von mehrzeiligem Text Entwicklung eines Expert Advisors für mehrere Währungen (Teil 27): Komponente zur Anzeige von mehrzeiligem Text
Wenn Text in einem Chart angezeigt werden soll, können wir die Funktion „Comment()“ verwenden. Aber ihre Möglichkeiten sind recht begrenzt. Daher werden wir in diesem Artikel eine eigene Komponente erstellen – ein Dialogfenster über die gesamte Chartfläche, das mehrzeiligen Text mit flexiblen Schriftarteneinstellungen und Scroll-Unterstützung anzeigen kann.
CFTC-Datenanalyse in Python und Erstellung eines KI-Modells CFTC-Datenanalyse in Python und Erstellung eines KI-Modells
Versuchen wir doch einmal, CFTC-Daten auszuwerten, COT- und TFF-Berichte über Python herunterzuladen, all dies mit den Kursdaten von MetaTrader 5 und einem KI-Modell zu verknüpfen und Prognosen zu erstellen. Was sind COT-Berichte auf dem Devisenmarkt? Wie nutzt man COT- und TFF-Berichte für Prognosen?
CAPM-Indikator für den Forex-Markt CAPM-Indikator für den Forex-Markt
Anpassung des klassischen CAPM-Modells für den Forexmarkt in MQL5. Der Indikator berechnet die erwartete Rendite und die Risikoprämie auf der Grundlage der historischen Volatilität. Die Indikatoren steigen an Hochs und Tiefs an und spiegeln damit die grundlegenden Prinzipien der Preisbildung wider. Praktische Anwendung von Contra-Trend- und Trendfolgestrategien unter Berücksichtigung der Dynamik des Risiko-Ertrags-Verhältnisses in Echtzeit. Der Artikel behandelt mathematische Grundlagen und die technische Umsetzung.