English Русский 中文 Español 日本語 Português
preview
Chaos-Optimierungsalgorithmus (COA)

Chaos-Optimierungsalgorithmus (COA)

MetaTrader 5Beispiele |
21 0
Andrey Dik
Andrey Dik


Inhalt

  1. Einführung
  2. Implementierung des Algorithmus


Einführung

Bei modernen rechnergestützten Problemstellungen, insbesondere auf den Finanzmärkten und im algorithmischen Handel, spielen effiziente Optimierungsmethoden eine entscheidende Rolle. Unter den zahlreichen globalen Optimierungsalgorithmen nehmen von Naturphänomenen inspirierte Ansätze einen besonderen Platz ein. Der Chaos-Optimierungs-Algorithmus (COA) ist eine solche innovative Lösung, die Chaostheorie und globale Optimierungsmethoden kombiniert.

In diesem Artikel stelle ich einen verbesserten chaotischen Optimierungsalgorithmus vor, der grundlegende Eigenschaften chaotischer Systeme ausnutzt – Ergodizität, Empfindlichkeit gegenüber Anfangsbedingungen und quasi-stochastisches Verhalten. Der Algorithmus verwendet verschiedene chaotische Abbildungen, um den Suchraum effizient zu erkunden und lokale Optima zu vermeiden, ein Problem, das bei Optimierungsmethoden häufig auftritt.

Eine Besonderheit des vorgeschlagenen Algorithmus ist, dass er die chaotische Suche mit der gewichteten Gradientenmethode und adaptiven Mechanismen kombiniert, die eine dynamische Anpassung der Suchparameter ermöglichen. Durch die Verwendung verschiedener Arten von chaotischen Abbildungen – die logistische Abbildung, die Sinusabbildung und die Tent-Abbildung – zeigt der Algorithmus eine gute Widerstandsfähigkeit gegen Stagnation und die Fähigkeit, globale Optima in komplexen multimodalen Funktionen zu finden. Wie üblich werden wir den gesamten Algorithmus analysieren und dann seine Fähigkeiten mit unseren inzwischen standardisierten Testfunktionen testen. 


Implementierung des Algorithmus

Der Algorithmus besteht aus drei Hauptphasen:

  • Chaotische Suche mit der ersten Carrier-Wave, bei der zunächst die chaotischen Anfangsvariablen initialisiert, anschließend ihre Folgewerten über eine logistische Abbildung erzeugt, auf den Optimierungsbereich projiziert und die beste gefundene Lösung gespeichert werden;
  • Suche entlang der Richtung des gewichteten Gradienten;
  • chaotische Suche mit einer zweiten Carrier-Wave, bei der eine lokale Suche um die beste Lösung herum durchgeführt wird, mit einem fraktalen Ansatz zur Implementierung der Schrittweite. 

In der nachstehenden Abbildung habe ich versucht, das Wesen des chaotischen Optimierungsalgorithmus zu veranschaulichen, dessen Hauptidee darin besteht, das Chaos als nützliches Optimierungsinstrument und nicht als Zufallsprozess zu nutzen. Das globale Optimum (hellgelb leuchtend in der Mitte) ist das Ziel, das wir finden wollen. Die blau leuchtenden Partikel sind Suchagenten, die sich entlang chaotischer Trajektorien bewegen. Diese Bahnen werden durch leuchtende Linien dargestellt, die die nichtlineare Natur der Bewegung zeigen.

Die Abbildung veranschaulicht die Schlüsseleigenschaften chaotischen Verhaltens: Determinismus (Trajektorien sind glatt, nicht zufällig), Ergodizität (Partikel erkunden den gesamten Raum), Empfindlichkeit gegenüber Anfangsbedingungen (verschiedene Partikel bewegen sich auf unterschiedlichen Bahnen). Die Suchdynamik wird durch ein Leuchten unterschiedlicher Intensität dargestellt, das die „Energie“ der Suche in verschiedenen Bereichen visualisiert, wobei konzentrische Kreise um das Optimum Anziehungsbereiche symbolisieren und Unschärfen sowie Farbverläufe die Kontinuität des Suchraums vermitteln. Die wichtigsten Phasen des Algorithmus:

  • breite Suche vom Zentrum weg (entfernte Teilchen),
  • schrittweise Annäherung an vielversprechende Gebiete (mittlere Trajektorien),
  • lokale Suche in der Nähe des Optimums (Partikel in der Nähe des Zentrums).

Das sich daraus ergebende Bild kann als ein „Porträt“ der chaotischen Optimierung betrachtet werden, in dem das Chaos nicht als Unordnung, sondern als kontrollierter Prozess der Erkundung des Lösungsraums dargestellt wird.

Chaos_1

Abbildung 1. Visualisierung der chaotischen Optimierung

Das nachstehende visuelle Diagramm des Algorithmus spiegelt drei Hauptphasen wider:

  1. Die erste Carrier-Wave-Suche (blauer Block) verwendet eine chaotische Abbildung für die globale Suche und transformiert die chaotischen Variablen in den Suchraum,
  2. Die gewichtete Gradientensuche (orangefarbener Block) ist ein gewichtetes Gradientenverfahren, bei dem auch Nebenbedingungen über Gewichtsverhältnisse berücksichtigt werden,
  3. Zweite Carrier-Wave-Suche (violetter Block) – lokale Suche nach der besten Lösung und adaptive Abstimmung des Parameters α.           

Chaos

 Abbildung 2. Ablaufschema des chaotischen Optimierungsalgorithmus

Das Diagramm zeigt die grundlegende dreistufige Struktur des COA-Algorithmus (CHAOS). In meiner Implementierung (und davon gab es mehrere) entschied ich mich für eine fortgeschrittenere und praktischere Version des Algorithmus, erweiterte die Agentenstruktur, fügte einen Bewegungszähler, einen Stagnationszähler und chaotische Variablen für jede Dimension hinzu. Es war auch wichtig, die chaotischen Abbildungen zu variieren; die Autoren verwendeten ausschließlich die logistische Abbildung, während meine Version Sinus- und Tent-Abbildungen hinzugefügt hat. Diese Implementierung beinhaltet die automatische Anpassung der Strafparameter, die Anpassung der Suchparameter, je nach Erfolg mit Trägheitsanpassung. Zusätzlich wurde eine komplexere Initialisierung unter Verwendung des Latin Hypercube hinzugefügt.

Beginnen wir mit dem Schreiben des Pseudocodes für den Algorithmus:

Initialisierung des Algorithmus

  1. Einstellung der Algorithmusparameter:
    • Populationsgröße (popSize)
    • Anzahl der Iterationen der ersten Phase (S1)
    • Anzahl der Iterationen der zweiten Phase (S2)
    • Strafparameter (sigma)
    • Anpassungsquote (t3)
    • kleine Konstante für Gewichtsverhältnisse (eps)
    • Trägheitsparameter (Trägheit)
    • Parameter für den sozialen Einfluss (socialFactor)
    • Mutationswahrscheinlichkeit (mutationRate)
  2. Initialisierung der Agenten:
    • Für jeden Agenten in der Population:
      • chaotische Variablen (Gamma) mit verschiedenen Anfangswerten initialisieren
      • Geschwindigkeitsvektoren zurücksetzen (Geschwindigkeit)
      • den Stagnationszähler auf 0 setzen
  3. Initialisierung der Suchparameter (alpha):
    • Anpassung der Parameter je nach Größe des Suchraums
    • alpha[c] = 0,1 * (rangeMax[c] – rangeMin[c]) / sqrt(coords)

Phase 1: Ausgangspopulation mit chaotischer Verteilung

  1. Erstellung einer Ausgangspopulation mithilfe eines Latin Hypercube:
    • Werte für jede Dimension erzeugen
    • die Werte mischen, um eine gleichmäßige Verteilung sicherzustellen
    • die Werte auf den Variablenbereich abbilden.
  2. Es werden verschiedene Strategien zur Initialisierung der Agenten verwendet:
    • für das erste Quartal der Agenten: gleichmäßige Verteilung
    • für das zweite Quartal: Clusterbildung um mehrere Punkte
    • für das dritte Quartal: zufällige Positionen mit einer Verschiebung zu den Rändern
    • für das letzte Quartal: chaotische Positionen mit verschiedenen Abbildungen

Phase 2: Chaotische Suche mit der ersten Carrier-Wave

  1. Für jede Iteration bis zu S1:
    • Für jeden Agenten in der Population:
      • wenn die Mutationswahrscheinlichkeit ausgelöst wird, die Mutation anwenden
      • ansonsten für jede Koordinate:
        • Aktualisiere die chaotischen Variablen über die gewählte Abbildung (logistische, Sinus- oder Tent-Abbildung).
        • Bestimme die Strategie (globale oder lokale Suche) auf der Grundlage der Optimierungsphase.
        • Für die globale Suche: verwende chaotische Werte zur Bestimmung der neuen Position.
        • für die lokale Suche: kombiniere die Anziehungskraft auf die besten Lösungen mit chaotischer Störung.
        • Aktualisiere die Geschwindigkeit unter Berücksichtigung der Trägheit.
        • Berücksichtige die Geschwindigkeits- und Positionsgrenzen.
        • Prüfe auf Verstöße gegen Beschränkungen und nimm Korrekturen vor.
    • Bewertung und Aktualisierung:
      • Berechne die Straffunktion für jeden Agenten.
      • Aktualisiere die besten persönlichen und globalen Lösungen.
      • Aktualisiere dynamisch die Strafparameter.
      • Passe den Suchparameter (Alpha) je nach Erfolg an.

Phase 3: Chaotische Suche mit der zweiten Carrier-Wave

  1. Für jede Iteration von S1 bis S1+S2:
    • Prüfe auf Konvergenz
    • für jeden Agenten in der Population:
      • Wenn Konvergenz festgestellt wird, wende eine zufällige Mutation bei einigen Agenten an.
      • ansonsten für jede Koordinate:
        • die chaotische Variable aktualisieren
        • Schränke den Suchradius adaptiv ein.
        • Wähle einen Basispunkt aus, wobei die besten Lösungen Vorrang haben.
        • Füge chaotische Störungen und Lévy-Rauschen hinzu, um zufällige weite Sprünge zu ermöglichen.
        • Aktualisierung von Geschwindigkeit und Position mit Trägheit
        • Positionsbeschränkungen anwenden
    • Bewertung und Aktualisierung:
      • Berechne die Straffunktion für jeden Agenten.
      • Aktualisiere die besten persönlichen und globalen Lösungen.
      • Aktualisiere die Geschichte der besten globalen Lösung, um die Konvergenz zu bestimmen.
      • Setze festsitzende Agenten bei Bedarf zurück.

Hilfsfunktionen

  1. Chaotische Abbildungen:
    • LogisticMap(x): x[n+1] = rx[n](1-x[n]) mit Validierung
    • SineMap(x): x[n+1] = sin(π*x[n]) mit Normierung
    • TentMap(x): x[n+1] = μ*min(x[n], 1-x[n]) mit Validierung
    • SelectChaosMap(x, type): Auswahl einer Abbildung anhand des Typs
  2. Umgang mit Beschränkungen und Sanktionen:
    • CalculateConstraintValue(agent, coord): Berechnung der Verletzung einer Nebenbedingung
    • CalculateConstraintGradient(agent, coord): Berechnung des Gradienten der Nebenbedingung.
    • CalculateWeightedGradient(agent, coord): Berechnung des gewichteten Gradienten
    • CalculatePenaltyFunction(agent): Berechnung des Wertes der Straffunktion
  3. Umgang mit Stagnation und Konvergenz:
    • IsConverged(): Überprüfung der Konvergenz anhand der Historie der besten Lösungen
    • ResetStagnatingAgents(): Zurücksetzen von stagnierenden Agenten.
    • ApplyMutation(agent): Anwendung verschiedener Arten von Mutationen
    • UpdateSigma(): Dynamische Aktualisierung des Strafparameters
    • UpdateBestHistory(newBest): Aktualisierung der Historie der besten Werte

Nun können wir mit der Beschreibung der Implementierung des COA-Algorithmus (CHAOS) fortfahren. In diesem Artikel werden wir alle wichtigen Implementierungsmethoden untersuchen, und im nächsten Artikel werden wir direkt zu den Tests und den Leistungsergebnissen des Algorithmus übergehen. Implementieren wir die Struktur S_COA_Agent und die Strukturfelder:

  • gamma [] – Satz chaotischer Variablen des Pseudo-Zufallstyps, die verwendet werden, um Elemente des Zufalls und der Vielfalt in das Verhalten des Agenten einzuführen,
  • velocity [] – Array von Geschwindigkeiten, erlaubt es dem Agenten, sich dynamischer durch den Raum zu bewegen und dabei die Trägheit zu berücksichtigen,
  • stagnationCounter – Zähler, der sich erhöht, wenn der Agent keine Verbesserung seiner Lösung zeigt; er hilft bei der Implementierung von Mechanismen zum Neustart von Strategien im Falle einer Stagnation.

Mit der Methode Init() werden die Anfangswerte von Arrays erstellt und festgelegt. Für gamma[] wird eine gleichmäßige Verteilung zwischen 0,1 und 0,9 verwendet, um Abwechslung in die Anfangsbedingungen der chaotischen Variablen zu bringen. Die Geschwindigkeiten in velocity [] beginnen bei Null, der Stagnationszähler wird auf Null gesetzt.

//——————————————————————————————————————————————————————————————————————————————
// Improved agent structure with additional fields
struct S_COA_Agent
{
    double gamma    [];       // chaotic variables
    double velocity [];       // movement velocity (to increase inertia)
    int    stagnationCounter; // stagnation counter

    void Init (int coords)
    {
      ArrayResize (gamma,    coords);
      ArrayResize (velocity, coords);

      // Uniform distribution of gamma values
      for (int i = 0; i < coords; i++)
      {
        // Use different initial values for better variety
        gamma [i] = 0.1 + 0.8 * (i % coords) / (double)MathMax (1, coords - 1);

        // Initialize velocity with zeros
        velocity [i] = 0.0;
      }

      stagnationCounter = 0;
    }
};
//——————————————————————————————————————————————————————————————————————————————

Die Klasse C_AO_COA_chaos ist von der Basisklasse C_AO abgeleitet und ist eine Implementierung des COA(CHAOS)-Algorithmus. Sie enthält die für den Betrieb erforderlichen Methoden und Parameter sowie zusätzliche Funktionen zur Steuerung des Verhaltens der Agenten, die auf den Konzepten der chaotischen Suche basieren. Komponenten der Klasse:

  • SetParams () – Methode zur Einstellung der Algorithmusparameter,
  • Init () – Initialisierungsmethode, die Bereiche und Parameter für den Betrieb des Algorithmus annimmt,
  • Moving () – Methode, die für das Verschieben von Agenten im Lösungsraum zuständig ist,
  • Revision () – Methode zur Überprüfung bzw. Korrektur der Agentenpositionen.
Parameter des Algorithmus:
  • S1, S2 – Anzahl der Iterationen in zwei Phasen des Algorithmus.
  • sigma, t3, eps, inertia, socialFactor, mutationRate – Parameter, die das Verhalten der Agenten und den Algorithmus als Ganzes beeinflussen.
  • alpha [] – Array von Parametern, die für die Suche verwendet werden.
  • agent [] – Array von Agenten, aus denen die Population des Algorithmus besteht.
Geschützte Methoden:
  • Methoden zur Berechnung von Gradienten, Nebenbedingungen und Straffunktionen sowie zur Überprüfung der Durchführbarkeit von Lösungen (IsFeasible),
  • Methoden für chaotische Abbildungen (LogisticMap, SineMap, TentMap, SelectChaosMap).
Private Felder der Klasse:
  • Variablen, die Informationen über die aktuelle Epoche (epochs, epochNow) und den dynamischen Strafterm (currentSigma) speichern,
  • globalBestHistory [] – Array zur Speicherung der global besten Werte über mehrere Iterationen,
  • Index der Historie (historyIndex) zur Verfolgung der Position im Array der besten Werte,
  • Die Methoden zur Verwaltung der Agentenpopulation (InitialPopulation), zur Durchführung der verschiedenen Suchphasen (FirstCarrierWaveSearch, SecondCarrierWaveSearch), zur Mutation von Agenten (ApplyMutation), zur Aktualisierung des Strafterms (UpdateSigma) und zur Überprüfung der Konvergenz (IsConverged) sowie zum Zurücksetzen stagnierender Agenten (ResetStagnatingAgents).

    Die Klasse C_AO_COA_chaos ist also eine komplexe Komponente eines Optimierungssystems, das Agenten zur Lösungsfindung einsetzt. Es integriert die Parameter, Methoden und Logik, die zur Steuerung von Agenten innerhalb eines Algorithmus benötigt werden, einschließlich deterministischer und chaotischer Strategien. 

    //——————————————————————————————————————————————————————————————————————————————
    class C_AO_COA_chaos : public C_AO
    {
      public: //--------------------------------------------------------------------
      ~C_AO_COA_chaos () { }
      C_AO_COA_chaos ()
      {
        ao_name = "COA(CHAOS)";
        ao_desc = "Chaos Optimization Algorithm";
        ao_link = "https://www.mql5.com/de/articles/16729";
    
        // Internal parameters (not externally configurable)
        inertia      = 0.7;
        socialFactor = 1.5;
        mutationRate = 0.05;
    
        // Default parameters
        popSize = 50;
        S1      = 30;
        S2      = 20;
        sigma   = 2.0;
        t3      = 1.2;
        eps     = 0.0001;
    
        // Initialize the parameter array for the C_AO interface
        ArrayResize (params, 6);
    
        params [0].name = "popSize"; params [0].val = popSize;
        params [1].name = "S1";      params [1].val = S1;
        params [2].name = "S2";      params [2].val = S2;
        params [3].name = "sigma";   params [3].val = sigma;
        params [4].name = "t3";      params [4].val = t3;
        params [5].name = "eps";     params [5].val = eps;
      }
    
      void SetParams ()
      {
        // Update internal parameters from the params array
        popSize = (int)params [0].val;
        S1      = (int)params [1].val;
        S2      = (int)params [2].val;
        sigma   = params      [3].val;
        t3      = params      [4].val;
        eps     = params      [5].val;
      }
    
      bool Init (const double &rangeMinP  [], // minimum search range
                 const double &rangeMaxP  [], // maximum search range
                 const double &rangeStepP [], // search step
                 const int     epochsP = 0);  // number of epochs
    
      void Moving   ();
      void Revision ();
    
      //----------------------------------------------------------------------------
      // External algorithm parameters
      int    S1;             // first phase iterations
      int    S2;             // second phase iterations
      double sigma;          // penalty parameter
      double t3;             // alpha correction ratio
      double eps;            // small number for weighting ratios
    
      // Internal algorithm parameters
      double inertia;        // inertia parameter for movement (internal)
      double socialFactor;   // social influence parameter (internal) 
      double mutationRate;   // mutation probability (internal) 
    
      S_COA_Agent agent [];  // array of agents
    
      private: //-------------------------------------------------------------------
      int    epochNow;
      double currentSigma;           // Dynamic penalty parameter
      double alpha             [];   // search parameters
      double globalBestHistory [10]; // History of global best solution values
      int    historyIndex;
    
      // Auxiliary methods
      double CalculateWeightedGradient   (int agentIdx, int coordIdx);
      double CalculateConstraintValue    (int agentIdx, int coordIdx);
      double CalculateConstraintGradient (int agentIdx, int coordIdx);
      double CalculatePenaltyFunction    (int agentIdx);
    
      // Method for checking the solution feasibility
      bool IsFeasible              (int agentIdx);
    
      // Chaotic maps
      double LogisticMap           (double x);
      double SineMap               (double x);
      double TentMap               (double x);
      double SelectChaosMap        (double x, int type);
    
      void InitialPopulation       ();
      void FirstCarrierWaveSearch  ();
      void SecondCarrierWaveSearch ();
      void ApplyMutation           (int agentIdx);
      void UpdateSigma             ();
      void UpdateBestHistory       (double newBest);
      bool IsConverged             ();
      void ResetStagnatingAgents   ();
    };
    //——————————————————————————————————————————————————————————————————————————————
    

    Die Init-Methode der Klasse C_AO_COA ist für die anfängliche Einrichtung und Vorbereitung des Algorithmus für den Betrieb zuständig. Sie führt mehrere wichtige Aufgaben aus: Zunächst führt sie eine grundlegende Initialisierung mit der Methode StandardInit() durch, die Bereiche, Schritte und andere Parameter einrichtet. Wenn dies nicht gelingt, schlägt die Initialisierung fehl.

    Als Nächstes werden die Parameter für die Anzahl der Epochen, die aktuelle Epoche (epochNow) und den Strafterm (currentSigma) festgelegt. Eine Historie der besten Lösungen wird initialisiert, um den Fortschritt zu verfolgen. Die Größe der Arrays der zu suchenden Mindest- und Höchstwertbereiche wird überprüft. Wenn die Größen nicht übereinstimmen oder nicht angegeben sind, wird die Initialisierung abgebrochen.

    Anschließend werden Arrays initialisiert, in denen Agenten, Alpha-Verhältnisse und die besten gefundenen Lösungen gespeichert werden. Jeder Agent erhält eine Ausgangsposition, die auf verschiedenen Strategien basiert:

    • Ein Teil der Agenten verteilt sich gleichmäßig über den gesamten Bereich,
    • Eine andere Gruppierung um mehrere Punkte innerhalb eines Bereichs.
    • Andere werden nach dem Zufallsprinzip unter Berücksichtigung der Grenzen positioniert,
    • Die übrigen verwenden chaotische Abbildungsfunktionen, um Ausgangslösungen zu erhalten.

    Die Init-Methode der Klasse C_AO_COA_chaos setzt die Anfangsparameter und Arrays, die für die Suche nach der optimalen Lösung erforderlich sind. Der Prozess umfasst die Überprüfung der Korrektheit der Eingabedaten, die Einrichtung von Suchbereichen, die Initialisierung eines Arrays von Agenten mit verschiedenen Startpositionsstrategien und die Festlegung der Werte globaler Variablen wie der besten gefundenen Lösung. Während der Ausführung des Verfahrens werden die für den weiteren iterativen Optimierungsprozess notwendigen Datenstrukturen angelegt und Parameter gesetzt, die das Verhalten der Agenten und des Algorithmus insgesamt regeln.

    //——————————————————————————————————————————————————————————————————————————————
    bool C_AO_COA_chaos::Init (const double &rangeMinP  [],
                         const double &rangeMaxP  [],
                         const double &rangeStepP [],
                         const int     epochsP = 0)
    {
      if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;
    
      //----------------------------------------------------------------------------
      epochNow     = 0;
      currentSigma = sigma;
      historyIndex = 0;
    
      // Initialize the history of best values
      for (int i = 0; i < 10; i++) globalBestHistory [i] = -DBL_MAX;
    
      // Check and initialize the main arrays
      int arraySize = ArraySize (rangeMinP);
      if (arraySize <= 0 || arraySize != ArraySize (rangeMaxP) || arraySize != ArraySize (rangeStepP))
      {
        return false;
      }
    
      ArrayResize (agent, popSize);
      ArrayResize (alpha, coords);
    
      // Adaptive alpha initialization depending on the search range
      for (int c = 0; c < coords; c++)
      {
        // alpha depends on the size of the search space
        double range = rangeMax [c] - rangeMin [c];
        alpha [c] = 0.1 * range / MathSqrt (MathMax (1.0, (double)coords));
      }
    
      // Initialize of agents with various strategies
      for (int i = 0; i < popSize; i++)
      {
        agent [i].Init (coords);
    
        for (int c = 0; c < coords; c++)
        {
          double position;
    
          // Different initialization strategies
          if (i < popSize / 4)
          {
            // Uniform distribution in space
            position = rangeMin [c] + (i * (rangeMax [c] - rangeMin [c])) / MathMax (1, popSize / 4);
          }
          else
            if (i < popSize / 2)
            {
              // Clustering around multiple points
              int cluster = (i - popSize / 4) % 3;
              double clusterCenter = rangeMin [c] + (cluster + 1) * (rangeMax [c] - rangeMin [c]) / 4.0;
              position = clusterCenter + u.RNDfromCI (-0.1, 0.1) * (rangeMax [c] - rangeMin [c]);
            }
            else
              if (i < 3 * popSize / 4)
              {
                // Random positions with an offset towards the boundaries
                double r = u.RNDprobab ();
                if (r < 0.5) position = rangeMin [c] + 0.2 * r * (rangeMax [c] - rangeMin [c]);
                else position = rangeMax [c] - 0.2 * (1.0 - r) * (rangeMax [c] - rangeMin [c]);
              }
              else
              {
                // Chaotic positions using different maps
                int mapType = i % 3;
                double chaosValue = SelectChaosMap (agent [i].gamma [c], mapType);
                position = rangeMin [c] + chaosValue * (rangeMax [c] - rangeMin [c]);
              }
    
          a [i].cB [c] = u.SeInDiSp (position, rangeMin [c], rangeMax [c], rangeStep [c]);
        }
      }
    
      return true;
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    Die Methode LogisticMap implementiert die logistische Abbildung, die zur Erzeugung chaotischer Sequenzen verwendet wird. Diese Funktion wird in dem Algorithmus verwendet, um Zufälligkeit und Abwechslung in die Lösungssuche zu bringen. Die Hauptidee der Methode ist die Berechnung eines neuen Zustandswertes auf der Grundlage des aktuellen Zustands unter Verwendung der logistischen Abbildungsgleichung mit einem Parameter, der leicht variiert wird, um das chaotische Verhalten zu verbessern.

    Vor der Berechnung wird der Eingabewert auf Gültigkeit und Bereich geprüft; stimmt er nicht überein, wird er durch eine Zufallszahl innerhalb des angegebenen Bereichs ersetzt. Nach der Berechnung des neuen Wertes wird dieser ebenfalls auf einen akzeptablen Bereich überprüft und gegebenenfalls durch eine Zufallszahl ersetzt, um die Stabilität der Funktion zu gewährleisten. Dadurch stellt die interne Logik sicher, dass der nächste Zustand generiert wird, ohne dass die Grenzen überschritten werden.

    //——————————————————————————————————————————————————————————————————————————————
    // Improved chaotic maps
    double C_AO_COA_chaos::LogisticMap (double x)
    {
      // Protection against incorrect inputs
      if (x < 0.0 || x > 1.0 || MathIsValidNumber (x) == false)
      {
        x = 0.2 + 0.6 * u.RNDprobab ();
      }
    
      // x(n+1) = r*x(n)*(1-x(n))
      double r = 3.9 + 0.1 * u.RNDprobab (); // Slightly randomized parameter to avoid loops
      double result = r * x * (1.0 - x);
    
      // Additional validation
      if (result < 0.0 || result > 1.0 || MathIsValidNumber (result) == false)
      {
        result = 0.2 + 0.6 * u.RNDprobab ();
      }
    
      return result;
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    Die Methode SineMap implementiert eine chaotische Abbildung auf der Grundlage der Sinusfunktion. Sie nimmt den aktuellen Zustand, prüft seine Gültigkeit und ersetzt ihn durch einen Zufallswert in diesem Bereich, wenn er falsch ist oder außerhalb des Bereichs [0, 1] liegt. Anschließend wird ein neuer Wert mithilfe der Sinusfunktion berechnet, normalisiert, sodass er wieder im Bereich [0, 1] liegt, und eine weitere Prüfung durchgeführt.

    Liegt der endgültige Wert außerhalb der Grenzen oder ist er ungültig, wird er erneut durch eine Zufallszahl im Bereich [0,2, 0,8] ersetzt. Als Ergebnis liefert die Methode einen neuen Zustand, der auf der Grundlage des aktuellen Zustands mithilfe der chaotischen Abbildung ermittelt wird.

    //——————————————————————————————————————————————————————————————————————————————
    double C_AO_COA_chaos::SineMap (double x)
    {
      // Protection against incorrect inputs
      if (x < 0.0 || x > 1.0 || MathIsValidNumber (x) == false)
      {
        x = 0.2 + 0.6 * u.RNDprobab ();
      }
    
      // x(n+1) = sin(π*x(n))
      double result = MathSin (M_PI * x);
    
      // Normalize the result to the range [0, 1]
      result = (result + 1.0) / 2.0;
    
      // Additional validation
      if (result < 0.0 || result > 1.0 || MathIsValidNumber (result) == false)
      {
        result = 0.2 + 0.6 * u.RNDprobab ();
      }
    
      return result;
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    Die TentMap-Methode implementiert eine Tent-Abbildung zur Erzeugung einer chaotischen Sequenz. Die Methode nimmt einen Eingabewert „x“, der zwischen 0 und 1 liegen sollte, prüft „x“ auf Gültigkeit und ersetzt ihn gegebenenfalls durch einen Zufallswert innerhalb des zulässigen Bereichs. Anschließend wird unter Verwendung eines „mu“-Parameters nahe 2 ein neuer Wert auf der Grundlage einer für die „Tent“-Abbildung charakteristischen stückweisen linearen Funktion berechnet.

    Nach der Berechnung wird eine weitere Prüfung durchgeführt, um sicherzustellen, dass der Wert gültig ist, und, falls erforderlich, wird er mit einer Zufallszahl normalisiert. Die Methode gibt dann einen neuen, zufällig generierten Wert zurück.

    //——————————————————————————————————————————————————————————————————————————————
    double C_AO_COA_chaos::TentMap (double x)
    {
      // Protection against incorrect inputs
      if (x < 0.0 || x > 1.0 || MathIsValidNumber (x) == false)
      {
        x = 0.2 + 0.6 * u.RNDprobab ();
      }
    
      // Tent map: x(n+1) = μ*min(x(n), 1-x(n))
      double mu = 1.99; // Parameter close to 2 for chaotic behavior
      double result;
    
      if (x <= 0.5) result = mu * x;
      else result = mu * (1.0 - x);
    
      // Additional validation
      if (result < 0.0 || result > 1.0 || MathIsValidNumber (result) == false)
      {
        result = 0.2 + 0.6 * u.RNDprobab ();
      }
    
      return result;
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    Die Methode SelectChaosMap dient der Auswahl und Anwendung einer chaotischen Abbildungsfunktion in Abhängigkeit vom angegebenen Typ. Sie nimmt einen „x“-Wert und einen „type“-Parameter an, der die spezifische Art der chaotischen Abbildung angibt. Die Grundidee der Methode besteht darin, den Rest der Division des Typs durch 3 zu verwenden, um die Abbildungsoption zu bestimmen, die eine zyklische Auswahl zwischen drei verschiedenen chaotischen Abbildungen bietet: logistische, Sinus- oder Tent-Abbildung. Je nach Ergebnis wird die entsprechende Funktion aufgerufen, die den Eingabewert von „x“ unter Verwendung der gewählten chaotischen Dynamik in einen neuen Wert umwandelt.

    Fällt der Typ aus irgendeinem Grund nicht in den erwarteten Bereich (0, 1, 2), wird standardmäßig die logistische Abbildung angewendet. Jede dieser Abbildungen simuliert chaotisches Verhalten und wird verwendet, um verschiedene und unvorhersehbare Zahlen als Teil der Optimierung zu erzeugen.

    //——————————————————————————————————————————————————————————————————————————————
    double C_AO_COA_chaos::SelectChaosMap (double x, int type)
    {
      // Select a chaotic map based on type
      switch (type % 3)
      {
        case 0:
          return LogisticMap (x);
        case 1:
          return SineMap (x);
        case 2:
          return TentMap (x);
        default:
          return LogisticMap (x);
      }
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    Die InitialPopulation-Methode dient der Initialisierung der Ausgangspopulation des Optimierungsalgorithmus unter Verwendung der Latin Hypercube Sampling (LHS)-Technik. LHS ist ein geschichtetes Stichprobenverfahren, das im Vergleich zu Zufallsstichproben eine gleichmäßigere Abdeckung des mehrdimensionalen Suchraums ermöglicht und dadurch die Qualität der Ausgangspopulation verbessert.

    Die Methode beginnt mit der Deklaration von Arrays für die Latin-Hypercube-Werte und temporäre Werte, die bei der Generierung helfen sollen. Die Methode versucht, Speicher für die erforderlichen Arrays zuzuweisen, und wenn die Speicherzuweisung fehlschlägt, wird ein Backup-Szenario durchgeführt, bei dem die anfängliche Population zufällig erstellt wird. Dadurch wird sichergestellt, dass das Programm nicht wegen Speichermangels abstürzt.

    Die Methode erzeugt dann Werte für den Latin Hypercube. Für jede Koordinate wird eine geordnete Reihe von Werten erstellt, die dann nach dem Zufallsprinzip gemischt wird. Die gemischten Werte werden dem Hypercube-Array zugewiesen. Die Werte des Latin Hypercubes werden in Koordinaten von Individuen im Suchraum umgewandelt. Die Berechnung wird unter Verwendung der angegebenen Bereiche durchgeführt, und die erhaltenen Werte werden auf den erforderlichen Bereich und Schritt begrenzt.

    Am Ende setzt die Methode ein Kennzeichen, das anzeigt, dass die Ausgangspopulation geändert oder neu erstellt wurde. Der Vorteil dieses Ansatzes besteht darin, dass er eine diversifiziertere Ausgangspopulation schafft.

    //——————————————————————————————————————————————————————————————————————————————
    void C_AO_COA_chaos::InitialPopulation ()
    {
      // Create Latin Hypercube for the initial population
      double latinCube []; // One-dimensional array for storing hypercube values
      double tempValues []; // Temporary array for storing and shuffling values
    
      ArrayResize (latinCube, popSize * coords);
      ArrayResize (tempValues, popSize);
    
      // Generate a Latin hypercube
      for (int c = 0; c < coords; c++)
      {
        // Create ordered values
        for (int i = 0; i < popSize; i++)
        {
          tempValues [i] = (double)i / popSize;
        }
    
        // Shuffle the values
        for (int i = popSize - 1; i > 0; i--)
        {
          int j = (int)(u.RNDprobab () * (i + 1));
          if (j < popSize)
          {
            double temp = tempValues [i];
            tempValues [i] = tempValues [j];
            tempValues [j] = temp;
          }
        }
    
        // Assign the mixed values
        for (int i = 0; i < popSize; i++)
        {
          latinCube [i * coords + c] = tempValues [i];
        }
      }
    
      // Convert the Latin hypercube values to coordinates
      for (int i = 0; i < popSize; i++)
      {
        for (int c = 0; c < coords; c++)
        {
          double x = rangeMin [c] + latinCube [i * coords + c] * (rangeMax [c] - rangeMin [c]);
          a [i].c [c] = u.SeInDiSp (x, rangeMin [c], rangeMax [c], rangeStep [c]);
        }
      }
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    Die FirstCarrierWaveSearch-Methode implementiert eine Suchphase in den Algorithmus, die ein Gleichgewicht zwischen der globalen Erkundung des Raums und der lokalen Ausnutzung bekannter guter Lösungen herstellen soll. Seine Hauptaufgabe besteht darin, die Positionen und Geschwindigkeiten der Agenten zu aktualisieren, um weiterhin mögliche Lösungen zu finden und zu verbessern. Zu Beginn der Methode wird ein Verhältnis definiert, das den Grad der Erkundung in der aktuellen Suchepoche steuert. Dieses Verhältnis nimmt mit fortschreitenden Epochen quadratisch ab, was eine allmähliche Verlagerung des Schwerpunkts von globalen Suchvorgängen auf lokale Verbesserungen gewährleistet. Dann wird für jeden Agenten in der Population ein Mutationstest durchgeführt – mit einer gewissen Wahrscheinlichkeit wird eine Mutation durchgeführt, um die Vielfalt der Lösungen zu erhöhen. Anschließend wird für jede Suchrichtung (Koordinaten):

    • der Typ der chaotischen Abbildung ausgewählt, der zur Erzeugung neuer potenzieller Lösungen verwendet wird,
    • die Suchstrategie festgelegt: entweder global oder lokal.

    Bei der globalen Suche aktualisiert der Agent seine Position mithilfe einer chaotischen Komponente, und die Geschwindigkeit wird angepasst, um die Trägheit und die Bewegungsrichtung zu berücksichtigen. Bei der lokalen Suche konzentriert sich der Agent auf die besten gefundenen Lösungen und zieht diese mit einer kleinen Zufallsvariation an, um Schleifenbildung zu vermeiden. In beiden Fällen ist die Geschwindigkeit begrenzt, um zu vermeiden, dass die Grenzen des Suchraums zu weit überschritten werden. Die Positionen der Agenten werden unter Berücksichtigung der Nebenbedingungen des Suchraums aktualisiert, und wenn nötig, werden Korrekturen vorgenommen, wenn Verletzungen der Nebenbedingungen festgestellt werden. In diesem Fall werden die Positionen angepasst und die Geschwindigkeiten reduziert, um die nachfolgenden Schritte zu glätten.

    //——————————————————————————————————————————————————————————————————————————————
    void C_AO_COA_chaos::FirstCarrierWaveSearch ()
    {
      // Adaptive balance between exploration and exploitation
      double globalPhase = (double)epochNow / S1;
      double explorationRate = 1.0 - globalPhase * globalPhase; // Quadratic decrease
    
      // For each agent
      for (int i = 0; i < popSize; i++)
      {
        // Apply mutations with some probability to increase diversity
        if (u.RNDprobab () < mutationRate * (1.0 + explorationRate))
        {
          ApplyMutation (i);
          continue;
        }
    
        for (int c = 0; c < coords; c++)
        {
          // Select a chaotic map with uniform distribution
          int mapType = ((i + c + epochNow) % 3);
    
          // Safely check access to the gamma array
          if (c < ArraySize (agent [i].gamma))
          {
            agent [i].gamma [c] = SelectChaosMap (agent [i].gamma [c], mapType);
          }
          else
          {
            continue; // Skip if the index is invalid
          }
    
          // Determine the relationship between global and local search
          double strategy = u.RNDprobab ();
          double x;
    
          if (strategy < explorationRate)
          {
            // Global search with a chaotic component
            x = rangeMin [c] + agent [i].gamma [c] * (rangeMax [c] - rangeMin [c]);
    
            // Add a velocity component to maintain the movement direction
            agent [i].velocity [c] = inertia * agent [i].velocity [c] +
                                     (1.0 - inertia) * (x - a [i].c [c]);
          }
          else
          {
            // Local search around the best solutions
            double personalAttraction = u.RNDprobab ();
            double globalAttraction = u.RNDprobab ();
    
            // Balanced attraction to the best solutions
            double attractionTerm = //personalAttraction * (agent [i].cPrev [c] - a [i].c [c]) +
                                    personalAttraction * (a [i].cB [c] - a [i].c [c]) +
                                    socialFactor * globalAttraction * (cB [c] - a [i].c [c]);
    
            // Chaotic disturbance to prevent being stuck
            double chaosRange = alpha [c] * explorationRate;
            double chaosTerm = chaosRange * (2.0 * agent [i].gamma [c] - 1.0);
    
            // Update velocity with inertia
            agent [i].velocity [c] = inertia * agent [i].velocity [c] +
                                     (1.0 - inertia) * (attractionTerm + chaosTerm);
          }
    
          // Limit the velocity to prevent too large steps
          double maxVelocity = 0.1 * (rangeMax [c] - rangeMin [c]);
          if (MathAbs (agent [i].velocity [c]) > maxVelocity)
          {
            agent [i].velocity [c] = maxVelocity * (agent [i].velocity [c] > 0 ? 1.0 : -1.0);
          }
    
          // Apply the velocity to the position
          x = a [i].c [c] + agent [i].velocity [c];
    
          // Apply search space restrictions
          a [i].c [c] = u.SeInDiSp (x, rangeMin [c], rangeMax [c], rangeStep [c]);
    
          // Check the constraints and apply a smooth correction
          double violation = CalculateConstraintValue (i, c);
          if (violation > eps)
          {
            double gradient = CalculateWeightedGradient (i, c);
            double correction = -gradient * violation * (1.0 - globalPhase);
            a [i].c [c] = u.SeInDiSp (a [i].c [c] + correction, rangeMin [c], rangeMax [c], rangeStep [c]);
    
            // Reset the velocity when correcting violations
            agent [i].velocity [c] *= 0.5;
          }
        }
      }
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    Die Methode SecondCarrierWaveSearch ist eine Optimierungsstufe, die sich nach der ersten Suche entwickelt und darauf abzielt, die gefundenen Lösungen zu vertiefen und zu verfeinern. Das Hauptziel dieser Methode ist die Verbesserung der im vorherigen Schritt erzielten Ergebnisse durch die Anwendung ausgefeilterer Suchstrategien und die Anpassung der Parameter.

    Die Methode beginnt mit der Berechnung eines Parameters, der die lokale Suchphase widerspiegelt, die sich mit der Zeit intensiviert. Dadurch kann der Algorithmus schrittweise von einer breiten Suche zu einer detaillierteren und präziseren Erkundung des Bereichs bereits bekannter Lösungen übergehen. Zu Beginn der Methode wird geprüft, ob die Konvergenz erreicht ist. Wenn der Algorithmus einen stabilen Zustand erreicht hat, werden einige Agenten einer Mutation unterzogen, um die Vielfalt der Lösungen zu erhöhen und lokale Extreme zu vermeiden.

    Für jeden Agenten wird eine sequentielle Suche nach neuen Lösungen in seinem Raum durchgeführt. Es werden chaotische Abbildungen definiert, die dazu beitragen, Elemente des Zufalls einzuführen. Der Suchparameter wird kleiner, je näher wir der optimalen Lösung kommen. Dies ermöglicht einen engeren Fokus bei der Suche in der Nähe der derzeit besten Lösungen. Bei der Aktualisierung der einzelnen Positionen werden die bisherigen Leistungen der Agenten berücksichtigt. Es wird ein Basispunkt bestimmt, der entweder das absolute globale Optimum oder die bisherigen persönlichen Leistungen des Agenten sein kann, was dazu beiträgt, sowohl die individuellen als auch die allgemeinen Ergebnisse der gesamten Population zu berücksichtigen.

    Bei der Positionsaktualisierung werden sowohl chaotische Verzerrungen als auch Zufallsrauschen (z. B. Lévy-Rauschen) verwendet, was ein Element der Zufälligkeit hinzufügt und die Suche nach neuen, potenziell besseren Lösungen erleichtert. Die Methode berücksichtigt die Trägheit bei der Aktualisierung der Agentengeschwindigkeiten, was sanftere Änderungen ermöglicht und aggressive Bewegungen verhindert. Infolgedessen werden die aktualisierten Positionen durch die festgelegten Grenzen begrenzt, wodurch sichergestellt wird, dass die Problembedingungen erfüllt sind.

    Die Methode SecondCarrierWaveSearch zielt auf eine genauere und tiefgreifendere Optimierung bestehender Lösungen ab.

    //——————————————————————————————————————————————————————————————————————————————
    void C_AO_COA_chaos::SecondCarrierWaveSearch ()
    {
      // Refining local search with adaptive parameters
      double localPhase = (double)(epochNow - S1) / S2;
      double intensificationRate = localPhase * localPhase; // Quadratic increase in intensification
    
      // Check the algorithm convergence
      bool isConverged = IsConverged ();
    
      // For each agent
      for (int i = 0; i < popSize; i++)
      {
        // If convergence is detected, add a random mutation to some agents
        if (isConverged && i % 3 == 0)
        {
          ApplyMutation (i);
          continue;
        }
    
        for (int c = 0; c < coords; c++)
        {
          // Select a chaotic map with uniform distribution
          int mapType = ((i * c + epochNow) % 3);
          agent [i].gamma [c] = SelectChaosMap (agent [i].gamma [c], mapType);
    
          // Adaptive search radius with narrowing towards the end of optimization
          double adaptiveAlpha = alpha [c] * (1.0 - 0.8 * intensificationRate);
    
          // Select a base point with priority to the best solutions
          double basePoint;
          if (a [i].f > a [i].fB)
          {
            basePoint = a [i].c [c];  // The current position is better
          }
          else
          {
            double r = u.RNDprobab ();
    
            if (r < 0.7 * (1.0 + intensificationRate)) // Increase attraction to the global best
            {
              basePoint = cB [c];  // Global best
            }
            else
            {
              basePoint = a [i].cB [c];  // Personal best
            }
          }
    
          // Local search with a chaotic component
          double chaosOffset = adaptiveAlpha * (2.0 * agent [i].gamma [c] - 1.0);
    
          // Add Levy noise for random long jumps (heavy tailed distribution) 
          double levyNoise = 0.0;
          if (u.RNDprobab () < 0.1 * (1.0 - intensificationRate))
          {
            // Simplified approximation of Levy noise
            double u1 = u.RNDprobab ();
            double u2 = u.RNDprobab ();
    
            if (u2 > 0.01) // Protection against division by very small numbers
            {
              levyNoise = 0.01 * u1 / MathPow (u2, 0.5) * adaptiveAlpha * (rangeMax [c] - rangeMin [c]);
            }
          }
    
          // Update the velocity with inertia
          agent [i].velocity [c] = inertia * (1.0 - 0.5 * intensificationRate) * agent [i].velocity [c] +
                                   (1.0 - inertia) * (chaosOffset + levyNoise);
    
          // Apply the velocity to the position
          double x = basePoint + agent [i].velocity [c];
    
          // Limit the position
          a [i].c [c] = u.SeInDiSp (x, rangeMin [c], rangeMax [c], rangeStep [c]);
        }
      }
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    Im nächsten Artikel werden wir die übrigen Methoden des Algorithmus weiter untersuchen, Tests durchführen und die Ergebnisse zusammenfassen. 

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

    Beigefügte Dateien |
    COAfCHAOSp.zip (203.55 KB)
    Winkelanalyse von Preisbewegungen: Ein hybrides Modell zur Prognose von Finanzmärkten Winkelanalyse von Preisbewegungen: Ein hybrides Modell zur Prognose von Finanzmärkten
    Was ist die Winkelanalyse der Finanzmärkte? Wie kann man mithilfe der Winkel von Preisbewegung und maschinellem Lernen genaue Prognosen mit einer Genauigkeit von 67 % erstellen? Wie kann man ein Regressions- und Klassifikationsmodell mit Winkelmerkmalen kombinieren und einen funktionierenden Algorithmus erhalten? Was hat Gann damit zu tun? Warum sind Winkel der Preisbewegung ein guter Indikator für maschinelles Lernen?
    Anwendung des L1-Trendfilters in MetaTrader 5 Anwendung des L1-Trendfilters in MetaTrader 5
    Dieser Artikel befasst sich mit der praktischen Anwendung des L1-Trendfilters im MetaTrader 5, wobei sowohl die mathematischen Grundlagen als auch die Verwendung in MQL5-Programmen behandelt werden. Der L1-Filter ermöglicht die Extraktion stückweise linearer Trends, die die wesentliche Marktstruktur erhalten und gleichzeitig das Kursrauschen reduzieren. Die Studie analysiert die Skalierung der Parameter, das Verhalten der Trendschätzung und die Integration der Methode in algorithmische Handelsstrategien. Experimentelle Ergebnisse zeigen, wie der L1-Trendfilter die Signalstabilität, das Handels-Timing und die allgemeine Robustheit von Handelssystemen verbessern kann.
    Eine alternative Log-datei mit der Verwendung der HTML und CSS Eine alternative Log-datei mit der Verwendung der HTML und CSS
    In diesem Artikel werden wir eine sehr einfache, aber leistungsfähige Bibliothek zur Erstellung der HTML-Dateien schreiben, dabei lernen wir auch, wie man eine ihre Darstellung einstellen kann (nach seinem Geschmack) und sehen wir, wie man es leicht in seinem Expert Advisor oder Skript hinzufügen oder verwenden kann.
    Neuronale Netze im Trading: Anomalieerkennung im Frequenzbereich (CATCH) Neuronale Netze im Trading: Anomalieerkennung im Frequenzbereich (CATCH)
    Das CATCH-Framework kombiniert Fourier-Transformation und Frequenz-Patching, um Marktanomalien genau zu erkennen, die mit herkömmlichen Methoden kaum oder gar nicht erkannt werden können. Im Folgenden untersuchen wir, wie dieser Ansatz verborgene Muster in Finanzdaten aufdeckt.