English Русский 中文 Español Português
preview
Exchange-Market-Algorithmus (EMA)

Exchange-Market-Algorithmus (EMA)

MetaTrader 5Handelssysteme |
23 0
Andrey Dik
Andrey Dik


Inhalt

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


Einführung

Im Bereich der numerischen Optimierung werden kontinuierlich neue Algorithmen entwickelt, die darauf abzielen, komplexe Probleme unter Bedingungen von Unsicherheit und Multidimensionalität effektiv zu lösen. Unter diesen nehmen populationsbasierte Metaheuristiken einen besonderen Platz ein. Durch die Simulation natürlicher oder sozialer Prozesse demonstrieren sie beeindruckende Fähigkeiten bei der Suche nach globalen Optima.

Allerdings ist nicht jeder neue Algorithmus in der Lage, wettbewerbsfähige Leistungsniveaus zu erreichen. Dieser Artikel präsentiert eine detaillierte Analyse des Exchange Market Algorithm (EMA), eines Vertreters der oben genannten Klasse von Methoden, die vom Verhalten von Börsenhändlern inspiriert sind. Der Algorithmus simuliert den Aktienhandel, bei dem Marktteilnehmer mit unterschiedlichem Erfolg verschiedene Strategien anwenden, um ihre Gewinne zu maximieren. 


Implementierung des Algorithmus

Der Algorithmus beginnt mit der Erstellung einer Population von Marktteilnehmern (Händlern), die dann in drei gleiche Gruppen unterteilt wird. Jedem Händler wird zufällig eine Startposition im Suchraum zugewiesen, die sein aktuelles Aktienportfolio repräsentiert. Diese Positionen sind gleichmäßig über den gesamten zulässigen Wertebereich verteilt.

Nach einer ersten Bewertung des Erfolgs jedes Händlers (Berechnung des Zielfunktionswerts) wird die gesamte Population in absteigender Reihenfolge des Erfolgs sortiert und in drei Gruppen unterteilt:

  • Die erste Gruppe ist die Elite. Dies sind die erfolgreichsten Marktteilnehmer, die die profitabelsten Positionen gefunden haben. Ein wichtiges Merkmal: Elite-Händler behalten ihre Positionen während der gesamten Handelssitzung unverändert bei und dienen anderen als Beispiel. 
  • Die zweite Gruppe ist die Mittelschicht. Mäßig erfolgreiche Teilnehmer, die versuchen, ihre Position durch Lernen von der Elite zu verbessern, aber auch bereit sind, moderate Risiken einzugehen. 
  • Die dritte Gruppe besteht aus Einsteigern. Die am wenigsten erfolgreichen Teilnehmer, die bereit sind, hohe Risiken einzugehen, um ihre Situation radikal zu verbessern.

Jede Iteration des Algorithmus stellt eine Handelssitzung dar, die aus zwei Phasen besteht, welche unterschiedlichen Marktbedingungen entsprechen.

Phase 1: Ausgeglichener Markt (stabiler Handel) 


Unter stabilen Marktbedingungen verwenden Händler konservative Strategien, die darauf basieren, erfolgreichere Teilnehmer nachzuahmen. 

Verhalten der Mittelschicht. Jeder Händler in der zweiten Gruppe wählt einen zufälligen Mentor aus der Elite aus und verschiebt seine Position allmählich in diese Richtung. Das Ausmaß dieser Verschiebung wird durch den Absorptionskoeffizienten (r1 = 1.5) bestimmt, der mit fortschreitendem Algorithmus adaptiv abnimmt. Die Bewegung ist nicht deterministisch; ein Zufallsmultiplikator wird verwendet, um die Unsicherheit von Marktentscheidungen zu simulieren. 

Verhalten von Einsteigern. Händler in der dritten Gruppe werden sowohl von der Elite als auch von der Mittelschicht beeinflusst. Jeder Einsteiger wählt einen Mentor aus der ersten und zweiten Gruppe, und seine neue Position wird als Kombination von Bewegungen in Richtung beider Mentoren gebildet. Dies spiegelt den Versuch wider, gleichzeitig von den erfolgreichsten Händlern und von denjenigen zu lernen, die ihre Positionen kürzlich verbessert haben.

Phase 2: Der schwankende Markt (volatiler Handel)


Unter volatilen Marktbedingungen verwenden Händler aggressivere Strategien, um neue Möglichkeiten zu erkunden. 

Erkundungsverhalten der Mittelschicht. Händler in der zweiten Gruppe verwenden eine duale Strategie. Mit einer Wahrscheinlichkeit von 50 % übernimmt ein Händler vollständig die Position der global besten Lösung. In anderen Fällen erkunden sie den Bereich um den Schwerpunkt der Elitegruppe und fügen dieser Position kontrolliertes Zufallsrauschen hinzu. Das Rauschniveau wird durch den Risikofaktor (riskAlpha = 0.3) bestimmt und nimmt im Laufe der Zeit ab.

Aggressives Verhalten von Einsteigern. Die dritte Gruppe verwendet die riskantesten Strategien:

  • Mit einer Wahrscheinlichkeit, die dem Risikofaktor (30%) entspricht, vergisst der Händler seine aktuelle Position vollständig und beginnt wieder an einem zufälligen Punkt.
  • In 35% der Fälle wird eine breite Suche um die aktuelle Position mit einem großen Radius durchgeführt.
  • In den übrigen Fällen wird oppositionsbasiertes Lernen verwendet: Der Händler bewegt sich in die entgegengesetzte Richtung zum Zentrum der schlechtesten Entscheidungen, um sich von erfolglosen Bereichen zu entfernen.

Das Hauptmerkmal von EMA ist seine Anpassungsfähigkeit. Im Verlauf der Iterationen wird die Zerfallsrate basierend auf der Sigmoidfunktion berechnet. Dieser Koeffizient gewährleistet einen stufenlosen Übergang von der Exploration (zu Beginn) zur Ausnutzung bereits gefundener vielversprechender Lösungen (am Ende).

Nachdem beide Phasen abgeschlossen sind, erfolgt ein wichtiger Schritt: die Positionen aller Händler außer der Elite werden gemäß den berechneten neuen Werten aktualisiert. Elite-Händler behalten ihre Positionen bei, was die Bewahrung der besten gefundenen Lösungen sicherstellt.

Dann wird eine neue Erfolgsrate (der Wert der Zielfunktion) für alle Händler berechnet, die Population wird erneut sortiert und eine neue global beste Lösung wird bestimmt. Sehen wir uns nun an, wie die Algorithmusformeln im Bild unten aussehen.

EMA-Illustration

Das Bild enthält alle wichtigen Formeln zur Positionsaktualisierung für jede Gruppe in beiden Marktmodi. Basierend auf der obigen Beschreibung und den Formeln können wir nun den EMA-Pseudocode formulieren.

1. Initialisierung

  • Erstelle eine Population von N = 60 Händlern (N ist ein Vielfaches von 3)
  • Lege die Parameter fest: r₁ = 1.5, r₂ = 0.8, α = 0.3 (Risikofaktor)
  • Initialisiere jeden Händler mit einer zufälligen Position
  • Bestimme die Gruppengröße: m = N/3 = 20

2. Hauptschleife

Führe für jede Iteration t = 1, 2, ..., T, aus:

2.1 Ranking

  • Bewerte die Fitness aller Händler
  • Sortiere in absteigender Reihenfolge nach der Fitness
  • Unterteile in Gruppen:
    • G₁ = {1, ..., m} – Elite
    • G₂ = {m+1, ..., 2m} – Mittelschicht
    • G₃ = {2m+1, ..., N} – Einsteiger

2.2 Anpassung der Parameter

  • progress = t / T
  • decay = 1 / (1 + exp(-10(progress - 0.5)))
  • r₁ᵃᵈᵃᵖᵗ = r₁ × (1 - decay × 0.5)
  • r₂ᵃᵈᵃᵖᵗ = r₂ × (1 - decay × 0.3)

2.3 Ausgeglichener Markt

  • G₁: Positionen werden gespeichert
  • G₂: für jedes i ∈ G₂
    • wähle einen zufälligen Anführer k ∈ G₁
    • xᵢ = xᵢ + rand() × r₁ᵃᵈᵃᵖᵗ × (xₖ - xᵢ)
  • G₃: für jedes i ∈ G₃
    • wähle einen Anführer k₁ ∈ G₁ und k₂ ∈ G₂
    • xᵢ = xᵢ + rand() × r₁ᵃᵈᵃᵖᵗ × (xₖ₁ - xᵢ) + rand() × r₂ᵃᵈᵃᵖᵗ × (xₖ₂ - xᵢ)

2.4 Fluktuierender Markt

  • G₂: für jedes i ∈ G₂
    • wenn rand() < 0.5: xᵢ = xᵇᵉˢᵗ
    • andernfalls: xᵢ = center(G₁) + noise × α × (1 - decay × 0.5)
  • G₃: für jedes i ∈ G₃
    • wenn rand() < α: xᵢ = random_position()
    • andernfalls wenn rand() < 0.5 + α/2: xᵢ = xᵢ + big_disturbance
    • andernfalls: xᵢ = 2xᵢ - center(worst) + small_noise

2.5 Update

  • Übernehme die neuen Positionen für G₂ und G₃
  • Aktualisiere die global beste Lösung

3. Rückgabe des Ergebnisses

Rückgabe der besten gefundenen Lösung xᵇᵉˢᵗ

Jetzt können wir die Klasse C_AO_EMA schreiben, die ein Nachfahre der Klasse C_AO sein wird und unseren EMA-Optimierungsalgorithmus implementiert. Hauptkomponenten: popSize ist die Populationsgröße (Anzahl der Agenten), r1, r2 sind Absorptionskoeffizienten für verschiedene Agentengruppen, riskAlpha ist der Risikofaktor. Als Nächstes werden die Parameter für die Einrichtung des Algorithmus initialisiert und Standardwerte zugewiesen.

Funktionen:

  • SetParams() – Aktualisierung der Werte der Algorithmusparameter aus dem params-Array und Durchführung einer Validierungsprüfung dieser Parameter, um sicherzustellen, dass sie innerhalb akzeptabler Bereiche liegen.
  • Init() – Initialisierung des Algorithmus vor der Verwendung, Übernahme der Parameter wie Suchgrenzen (rangeMinP, rangeMaxP), Parameterschritt (rangeStepP) und Anzahl der Epochen (epochsP).
  • Moving() – Bewegung der Agenten im Suchraum und implementiert die Hauptlogik des EMA-Algorithmus zur Aktualisierung der Agentenpositionen.
  • Revision() – überarbeitet die Strategien der Agenten, einschließlich Mechanismen zur Verbesserung und Änderung ihrer aktuellen Positionen.
  • GetDecayRate() – Rückgabe der Zerfallsrate.

Variablen:

  • r1 – Absorptionskoeffizient für eine Gruppe von Agenten. Ein höherer Wert bedeutet, dass Agenten in dieser Gruppe stärker von anderen Agenten oder besseren Lösungen angezogen werden.
  • r2 – Absorptionskoeffizient für eine andere Gruppe von Agenten.
  • riskAlpha – Risikofaktor, wie viel Risiko Agenten bei Entscheidungen ausgesetzt sind.
  • groupSize – Größe jeder der drei Gruppen, in die die Population unterteilt ist (popSize / 3).
  • currentEpoch – Index der aktuellen Algorithmus-Iteration.
  • totalEpochs – Gesamtzahl der vom Algorithmus durchzuführenden Iterationen.
  • tempPop – ein temporäres Array zum Speichern neuer Positionen von Agenten während ihrer Bewegung. Der Typ S_AO_Agent definiert die Struktur eines einzelnen Agenten (Entscheidung).

Die Klasse C_AO_EMA bietet eine Implementierung des EMA-Algorithmus zur numerischen Optimierung. Der Algorithmus unterteilt die Population in Gruppen mit unterschiedlichen Strategien (definiert durch die Koeffizienten r1 und r2) und verwendet einen Risikofaktor (riskAlpha), um zwischen der Erkundung des Suchraums und der Nutzung der gefundenen guten Lösungen abzuwägen. Die Funktionen Moving() und Revision() enthalten die Hauptoptimierungslogik, Init() initialisiert den Algorithmus und SetParams() ermöglicht es uns, die Parameterwerte zu ändern.

//————————————————————————————————————————————————————————————————————
class C_AO_EMA : public C_AO
{
  public: //----------------------------------------------------------
  ~C_AO_EMA () { }
  C_AO_EMA ()
  {
    ao_name = "EMA";
    ao_desc = "Exchange Market Algorithm";
    ao_link = "https://www.mql5.com/en/articles/18605";

    popSize   = 60;     // Population size
    r1        = 1.5;    // Absorption coefficient for group 2
    r2        = 0.8;    // Absorption coefficient for group 3
    riskAlpha = 0.3;    // Risk factor

    ArrayResize (params, 4);

    params [0].name = "popSize";   params [0].val = popSize;
    params [1].name = "r1";        params [1].val = r1;
    params [2].name = "r2";        params [2].val = r2;
    params [3].name = "riskAlpha"; params [3].val = riskAlpha;
  }

  void SetParams ()
  {
    popSize   = (int)params [0].val;
    r1        = params      [1].val;
    r2        = params      [2].val;
    riskAlpha = params      [3].val;

    // Check the parameters validity
    if (popSize < 6) popSize = 6;
    if (popSize % 3 != 0) popSize = ((popSize / 3) + 1) * 3; // Multiple of 3
    if (r1 < 0.0) r1 = 0.0;
    if (r2 < 0.0) r2 = 0.0;
    if (riskAlpha < 0.0) riskAlpha = 0.0;
    if (riskAlpha > 1.0) riskAlpha = 1.0;
  }

  bool Init (const double &rangeMinP  [],
             const double &rangeMaxP  [],
             const double &rangeStepP [],
             const int     epochsP = 0);

  void Moving   ();
  void Revision ();

  //------------------------------------------------------------------
  double r1;         // Absorption coefficient 1
  double r2;         // Absorption coefficient 2
  double riskAlpha;  // Risk factor

  private: //---------------------------------------------------------
  int    groupSize;     // size of each group (popSize/3)
  int    currentEpoch;  // current epoch
  int    totalEpochs;   // total number of epochs

  S_AO_Agent tempPop []; // temporary population for storing new positions

  double GetDecayRate   ();
};
//————————————————————————————————————————————————————————————————————

Die Initialisierungsmethode Init ist dazu vorgesehen, den Algorithmus für die Ausführung vorzubereiten. Sie nimmt Arrays von Minimalwerten, Maximalwerten und Schritten für die Suchparameter sowie die Anzahl der Epochen (Iterationen) entgegen. Zuerst wird die Standard-Initialisierungsfunktion aufgerufen, die das Einrichten der Suchbereiche und das Überprüfen ihrer Gültigkeit übernimmt. Wenn sie false zurückgibt, wird die Initialisierung der Methode beendet.

Als Nächstes wird die Größe jeder Agentengruppe berechnet, indem die Gesamtpopulationsgröße durch 3 geteilt wird. Der Zähler für die aktuelle Epoche wird initialisiert, und die Gesamtzahl der Epochen, für die der Algorithmus ausgelegt ist, wird gespeichert. Danach wird Speicher für eine temporäre Population zugewiesen – ein Array von Agenten, das die gleiche Größe wie die Hauptpopulation hat. In der Schleife wird jeder Agent in diesem temporären Array mit Koordinaten initialisiert, die im coords-Array angegeben sind.

Wenn all diese Schritte erfolgreich abgeschlossen sind, ist der Rückgabewert true, was bedeutet, dass der Algorithmus erfolgreich für die Ausführung vorbereitet wurde.

//————————————————————————————————————————————————————————————————————
//--- Initialization
bool C_AO_EMA::Init (const double &rangeMinP  [],
                     const double &rangeMaxP  [],
                     const double &rangeStepP [],
                     const int epochsP = 0)
{
  if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;

  //------------------------------------------------------------------
  groupSize    = popSize / 3;
  currentEpoch = 0;
  totalEpochs  = epochsP;

  // Initialize the temporary population
  ArrayResize (tempPop, popSize);
  for (int i = 0; i < popSize; i++)
  {
    tempPop [i].Init (coords);
  }

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

Die Moving-Methode ist die Hauptfunktion, die die Entwicklung von Agenten im EMA-Algorithmus implementiert. Sie ist dafür verantwortlich, die Positionen der Agenten im Suchraum zu ändern. Ersteinrichtung (wird nur einmal durchgeführt):

  • Wenn dies der erste Durchlauf des Algorithmus ist, erfolgt eine zufällige Initialisierung der Koordinaten aller Agenten innerhalb der angegebenen Bereiche (rangeMin, rangeMax).
  • Die Koordinaten jedes Agenten werden unter Berücksichtigung des Diskretisierungsschritts (rangeStep) angepasst.
  • Die Variable revision wird auf true gesetzt, damit nachfolgende Aufrufe dieser Methode die Initialisierung nicht wiederholen.

Aktualisierung von Epoche und Zerfallsrate:

  • Der aktuelle Epochenzähler (currentEpoch) wird inkrementiert.
  • Die decayRate wird berechnet, die mit jeder Epoche abnimmt und das adaptive Verhalten des Algorithmus beeinflusst.

Kopieren der aktuellen Population. Die Koordinaten und Werte der Zielfunktion f aller Agenten aus der aktuellen Population a werden in die temporäre Population tempPop kopiert; dies geschieht, damit Änderungen in tempPop a nicht beeinflussen, bis alle Berechnungen in der aktuellen Iteration abgeschlossen sind.

PHASE 1: Ausgeglichener Markt (absorbierende Operatoren). Diese Phase modelliert das Verhalten von Agenten, die Informationen von den besten Agenten absorbieren. Die Population wird in drei Gruppen unterteilt:

  • Gruppe 1 (Elite): Diese Agenten (mit Indizes von 0 bis groupSize - 1) ändern sich in dieser Phase nicht; sie sind die besten Agenten und ihre Positionen bleiben erhalten.
  • Gruppe 2 (absorbierender Operator 1): Agenten dieser Gruppe (mit Indizes von groupSize bis 2*groupSize - 1) aktualisieren ihre Positionen.
    • Für jeden Agenten in dieser Gruppe wird zufällig ein Anführer aus Gruppe 1 ausgewählt.
    • Die Koordinaten des Agenten aus Gruppe 2 werden in Richtung der Koordinaten des ausgewählten Anführers aus Gruppe 1 verschoben. Der Offset hängt vom adaptiven Koeffizienten r1 ab, der unter Berücksichtigung der decayRate abnimmt.
    • Die Koordinaten werden durch Bereich und Schrittweite angepasst.
  • Gruppe 3 (absorbierender Operator 2): Agenten dieser Gruppe (mit Indizes von 2*groupSize bis popSize-1) aktualisieren ebenfalls ihre Positionen, jedoch aktiver.
    • Für jeden Agenten in dieser Gruppe werden zwei Anführer zufällig ausgewählt: einer aus Gruppe 1 und einer aus Gruppe 2.
    • Die Koordinaten des Agenten aus Gruppe 3 werden unter Verwendung desselben adaptiven Koeffizienten r1 und r2 in Richtung beider ausgewählter Anführer verschoben.
    • Die Koordinaten werden durch Bereich und Schrittweite angepasst.

PHASE 2: Fluktuierender Markt (Suchoperatoren). Diese Phase simuliert das explorative Verhalten von Agenten und ermöglicht es ihnen, neue Bereiche im Raum zu durchsuchen.

  • Gruppe 2 (Suchoperator 1 – moderates Risiko):
    • Jede Agentenkoordinate aus dieser Gruppe wird aktualisiert.
    • Es besteht eine 50%ige Chance, dass die Koordinate vollständig durch den Wert von cB ersetzt wird.
    • Andernfalls wird die Koordinate relativ zum Elite-Zentrum (dem Durchschnittswert der Koordinaten der Agenten aus Gruppe 1) unter Hinzufügung von Rauschen verschoben. Das Rauschen hängt von riskAlpha, dem Koordinatenbereich und der decayRate ab.
    • Die Koordinaten werden durch Bereich und Schrittweite angepasst.
  • Gruppe 3 (Suchoperator 2 – hohes Risiko):
    • Jede Agentenkoordinate aus dieser Gruppe wird aktualisiert.
    • Mit der Wahrscheinlichkeit riskAlpha wird die Koordinate innerhalb des Bereichs vollständig zufällig neu initialisiert. Dies simuliert eine sehr riskante, großangelegte Suche.
    • Andernfalls, wenn die zweite Zufallszahl kleiner als 0,5 + riskAlpha / 2.0 ist, erfolgt eine breite Suche: Die Koordinate wird um einen zufälligen Betrag innerhalb eines bestimmten Radius verschoben, der von riskAlpha und decayRate abhängt.
    • Andernfalls wird ein oppositionsbasiertes Lernen angewendet: Die Koordinate wird in die entgegengesetzte Richtung verschoben. Ein wenig zufälliges Rauschen wird hinzugefügt.
    • Die Koordinaten werden durch Bereich und Schrittweite angepasst.

Änderungen kopieren. Schließlich werden, nach allen Berechnungen, die aktualisierten Koordinaten der Agenten aus tempPop (für die Gruppen 2 und 3) zurück in die Hauptpopulation a kopiert. Die Koordinaten der Gruppe 1 bleiben unverändert (sie wurden anfangs nicht zurückkopiert).

Im Allgemeinen implementiert die Methode Moving einen iterativen Prozess, bei dem Agenten in der Population ihre Positionen ändern, indem sie von den besten Agenten lernen und den Suchraum mit unterschiedlichem Risikograd erkunden. 

//————————————————————————————————————————————————————————————————————
//--- Main loop of the algorithm
void C_AO_EMA::Moving ()
{
  // Initial setup
  if (!revision)
  {
    for (int i = 0; i < popSize; i++)
    {
      for (int c = 0; c < coords; c++)
      {
        a [i].c [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]);
        a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
      }
    }

    revision = true;
    return;
  }

  currentEpoch++;
  double decayRate = GetDecayRate ();

  // Copy the current population to the temporary one
  for (int i = 0; i < popSize; i++)
  {
    ArrayCopy (tempPop [i].c, a [i].c, 0, 0, WHOLE_ARRAY);
    tempPop [i].f = a [i].f;
  }

  // PHASE 1: Balanced market (absorbing operators)

  // Group 1 (elite) - does not change
  // Indices: 0 ... groupSize-1

  // Group 2 - absorbing operator 1
  // Indices: groupSize ... 2*groupSize-1

  double adaptiveR1 = r1 * (1.0 - decayRate * 0.5);

  for (int i = groupSize; i < 2 * groupSize; i++)
  {
    // Each agent in group 2 selects a random leader from group 1
    int leaderIdx = u.RNDminusOne (groupSize);

    for (int c = 0; c < coords; c++)
    {
      tempPop [i].c [c] = a [i].c [c] + u.RNDprobab () * adaptiveR1 * (a [leaderIdx].c [c] - a [i].c [c]);
      tempPop [i].c [c] = u.SeInDiSp (tempPop [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
    }
  }

  // Group 3 - absorbing operator 2
  // Indices: 2*groupSize ... popSize-1

  adaptiveR1 = r1 * (1.0 - decayRate * 0.3);

  for (int i = 2 * groupSize; i < popSize; i++)
  {
    // Selection of leaders from groups 1 and 2
    int leader1Idx = u.RNDminusOne (groupSize);
    int leader2Idx = groupSize + u.RNDminusOne (groupSize);

    for (int c = 0; c < coords; c++)
    {
      tempPop [i].c [c] = a [i].c [c] +
                          u.RNDprobab () * adaptiveR1 * (a [leader1Idx].c [c] - a [i].c [c]) +
                          u.RNDprobab () * adaptiveR1 * (a [leader2Idx].c [c] - a [i].c [c]);

      tempPop [i].c [c] = u.SeInDiSp (tempPop [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
    }
  }

  // PHASE 2: Fluctuating market (search operators)

  // Group 2 - search operator 1 (moderate risk)
  for (int i = groupSize; i < 2 * groupSize; i++)
  {
    for (int c = 0; c < coords; c++)
    {
      double range = rangeMax [c] - rangeMin [c];

      if (u.RNDprobab () < 0.5)
      {
        tempPop [i].c [c] = cB [c];// tempPop [i].c [c] + delta;
      }
      else
      {
        // Search around the center of the elite
        double eliteCenter = 0.0;
        for (int j = 0; j < groupSize; j++)
        {
          eliteCenter += a [j].c [c];
        }
        eliteCenter /= (double)groupSize;

        double noise = riskAlpha * range * u.RNDfromCI (-0.5, 0.5) * (1.0 - decayRate * 0.5);
        tempPop [i].c [c] = eliteCenter + noise;
      }

      // Check boundaries
      tempPop [i].c [c] = u.SeInDiSp (tempPop [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
    }
  }

  // Group 3 - search operator 2 (high risk)
  for (int i = 2 * groupSize; i < popSize; i++)
  {
    for (int c = 0; c < coords; c++)
    {
      double range = rangeMax [c] - rangeMin [c];

      if (u.RNDprobab () < riskAlpha)
      {
        // Full reinitialization
        tempPop [i].c [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]);
      }
      else
        if (u.RNDprobab () < 0.5 + riskAlpha / 2.0)
        {
          // Broad search
          double searchRadius = 2.0 * riskAlpha * range * (1.0 - decayRate * 0.3);
          double delta = u.RNDfromCI (-searchRadius, searchRadius);

          tempPop [i].c [c] = tempPop [i].c [c] + delta;
        }
        else
        {
          // Opposition-based training
          double worstCenter = 0.0;
          int worstCount = groupSize / 2;
          for (int j = popSize - worstCount; j < popSize; j++)
          {
            worstCenter += a [j].c [c];
          }
          worstCenter /= (double)worstCount;

          // Moving in the opposite direction from the worst ones
          tempPop [i].c [c] = 2.0 * tempPop [i].c [c] - worstCenter;

          // Add a little noise
          double noise = riskAlpha * range * u.RNDfromCI (-0.1, 0.1);
          tempPop [i].c [c] += noise;
        }

      // Check boundaries
      tempPop [i].c [c] = u.SeInDiSp (tempPop [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
    }
  }

  // Copying from the temporary population to the main one (except group 1)
  for (int i = groupSize; i < popSize; i++)
  {
    ArrayCopy (a [i].c, tempPop [i].c, 0, 0, WHOLE_ARRAY);
  }
}
//————————————————————————————————————————————————————————————————————

Die Methode GetDecayRate wird verwendet, um die Abklingrate zu berechnen, die steuert, in welchem Maße sich das Algorithmusverhalten während der Ausführung ändert. Sie prüft zuerst, ob die Gesamtzahl der totalEpochs 0 oder negativ ist, und gibt dann null zurück, um eine Division durch null zu vermeiden und einen sicheren Betrieb zu gewährleisten. Als Nächstes berechnet sie den Fortschritt der Suche als Anteil vergangener Epochen relativ zur Gesamtzahl von progress. Der Wert von progress steigt von 0 auf 1, während der Algorithmus fortschreitet.

Unter Verwendung von progress gibt die Methode einen Wert basierend auf der Sigmoidfunktion in exponentieller Form zurück. Diese Funktion bietet einen glatten Übergang des Dämpfungsfaktors von 0 auf 1, sodass der Wert zu Beginn der Suche (wenn progress nahe 0 ist) nahe 0 liegt und am Ende (wenn progress sich 1 nähert) bei 1 liegt. Dieser nichtlineare Übergang hilft dabei, das Gleichgewicht zwischen der Erkundung des Raums und der Ausnutzung bereits gefundener Lösungen auszugleichen.

Insbesondere wird eine Sigmoidfunktion der Form: 1 / (1 + exp(-10 * (progress - 0.5))) verwendet. Diese Form ergibt einen steileren Übergang nahe dem Mittelpunkt des Optimierungsprozesses progress ≈ 0.5, was zu einer dynamischeren Änderung des Koeffizienten während des Algorithmusbetriebs beiträgt.

//————————————————————————————————————————————————————————————————————
//--- Get the decay rate
double C_AO_EMA::GetDecayRate ()
{
  if (totalEpochs <= 0) return 0.0;

  // Non-linear attenuation for better exploitation/exploration balance
  double progress = (double)currentEpoch / (double)totalEpochs;

  // Use the sigmoid function for a smooth transition
  return 1.0 / (1.0 + MathExp (-10.0 * (progress - 0.5)));
}
//————————————————————————————————————————————————————————————————————

Die Methode Revision im Kontext des EMA-Algorithmus ist für die Aktualisierung der besten gefundenen Lösungen (globales Optimum) bei jeder Iteration verantwortlich. Ein temporäres statisches Array aT vom Typ S_AO_Agent wird erstellt. Statisch bedeutet, dass dieses Array erstellt wird, sobald die Funktion zum ersten Mal aufgerufen wird, und seinen Zustand zwischen den Aufrufen beibehält. Mit ArrayResize(aT, popSize) wird das temporäre Array auf die Populationsgröße angepasst, sodass es popSize (die Populationsgröße) an Agenten aufnehmen kann. Dieses Array wird zum Sortieren verwendet. Die Sortierfunktion wird vom Hilfsobjekt u aufgerufen. Die Funktion sortiert die Hauptpopulation der a-Agenten nach ihrer Fitness (dem Wert der Zielfunktion f). 

Das ist wichtig: Nach dieser Operation ist das Array a so sortiert, dass der Agent mit dem besten Wert der Zielfunktion f an erster Stelle (Index 0) steht. Bei Minimierungsalgorithmen ist dies der Agent mit dem kleinsten f, bei Maximierungsalgorithmen derjenige mit dem größten f. Die Koordinaten des besten Agenten der aktuellen Population (nach dem Sortieren nun an Position a[0] lokalisiert) werden in das cB-Array kopiert, das die Koordinaten der bisher vom Algorithmus gefundenen besten Lösung speichert. Der Zielfunktionswert des besten Agenten wird der Variablen fB zugewiesen. Diese Variable speichert den Wert der Zielfunktion, der den besten cB-Koordinaten entspricht.

//————————————————————————————————————————————————————————————————————
//--- Update the best solutions
void C_AO_EMA::Revision ()
{
  static S_AO_Agent aT []; ArrayResize (aT, popSize);
  u.Sorting (a, aT, popSize);
  ArrayCopy (cB, a [0].c, 0, 0, WHOLE_ARRAY);
  fB = a [0].f;
}
//————————————————————————————————————————————————————————————————————


Testergebnisse

Die Testergebnisse sind bei weitem nicht perfekt, aber der Algorithmus funktioniert und verfügt über gewisse Suchfähigkeiten.

EMA|Exchange Market Algorithm|60.0|1.5|0.8|0.3|
=============================
5 Hilly's; Func runs: 10000; result: 0.6706604188712635
25 Hilly's; Func runs: 10000; result: 0.42759923501764946
500 Hilly's; Func runs: 10000; result: 0.252217676777693
=============================
5 Forest's; Func runs: 10000; result: 0.7419215403847332
25 Forest's; Func runs: 10000; result: 0.38137087707323236
500 Forest's; Func runs: 10000; result: 0.19454127467011006
=============================
5 Megacity's; Func runs: 10000; result: 0.38769230769230767
25 Megacity's; Func runs: 10000; result: 0.21323076923076928
500 Megacity's; Func runs: 10000; result: 0.09672307692307769
=============================
All score: 3.36596 (37.40%)

Die Visualisierung zeigt eine Streuung in den Ergebnissen für niedrigdimensionale Funktionen.

Hilly

EMA mit der Testfunktion Hilly

Forest

EMA mit der Testfunktion Forest

Megacity

EMA mit der Testfunktion Megacity

Nach dem Test wird der EMA-Algorithmus nur zu Referenzzwecken in unsere Rangliste der Populationsoptimierungsalgorithmen aufgenommen.

# 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 Tiermigration 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 Zeit-Evolutions-Reise-Algorithmus (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 Glühen (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 Algorithmus für schwarze Löcher 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 Anarchische Gesellschaftsoptimierung 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 atomaren Orbitalen 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 Schildkrötenpanzer-Evolutionsalgorithmus (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 BSA Backtracking-Suchalgorithmus 0.97309 0.54534 0.29098 1.80941 0.99999 0.58543 0.21747 1.80289 0.84769 0.36953 0.12978 1.34700 4.959 55.10
21 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
22 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
23 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
24 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
25 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
26 DEA Algorithmus zur Echoortung bei Delfinen 0.75995 0.67572 0.34171 1.77738 0.89582 0.64223 0.23941 1.77746 0.61538 0.44031 0.15115 1.20684 4.762 52.91
27 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
28 SSG Setzen, 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
29 BCOm Optimierung mit 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
30 ABO Optimierung des afrikanischen Büffels 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
31 (PO)ES (PO) Entwicklungsstrategien 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
32 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
33 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
34 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
35 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
36 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
37 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
38 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
39 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
40 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
41 BFO-GA Optimierung der bakteriellen Futtersuche – 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
42 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
43 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
44 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
45 ADAMm adaptive Momentabschä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
EMA Exchange-Market-Algorithmus (EMA) 0.67066 0.42759 0.25221 1.35046 0.74192 0.38137 0.19454 1.31783 0.38769 0.21323 0.09672 0.69764 3.366 37.40
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

Der vorgestellte EMA-Algorithmus verfügt zwar über grundlegende Suchfunktionen, zeigt jedoch keine ausreichende Leistung, um in die Top 45 der Rangliste der Populationsoptimierungsalgorithmen aufgenommen zu werden. Seine monolithische Struktur und das Fehlen expliziter Mechanismen zur Überwindung der bekannten Probleme beim Feststecken in lokalen Extrema deuten auf eine Reihe potenzieller Mängel hin. 

Ohne signifikante Verbesserungen seiner Funktionalität und seiner adaptiven Verhaltenssteuerung wird EMA ein grundlegender Algorithmus mit begrenztem Potenzial zur Lösung komplexer Optimierungsprobleme bleiben, was sein Fehlen in den Top-Rankings der Metaheuristiken erklärt. Die im Algorithmus vorgestellten Ideen sind jedoch recht interessant und vielversprechend für unseren Ideenpool zur weiteren Entwicklung.

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 von EMA:

Vorteile:

  1. Einfache Umsetzung

Nachteile:

  1. Geringe Konvergenzgenauigkeit

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.


Im Artikel verwendete Programme

# 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_EMA.mq5
Skript EMA-Testumgebung

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

Beigefügte Dateien |
EMA.zip (242.9 KB)
Die Übertragung der Trading-Signale in einem universalen Expert Advisor. Die Übertragung der Trading-Signale in einem universalen Expert Advisor.
In diesem Artikel wurden die verschiedenen Möglichkeiten beschrieben, um die Trading-Signale von einem Signalmodul des universalen EAs zum Steuermodul der Positionen und Orders zu übertragen. Es wurden die seriellen und parallelen Interfaces betrachtet.
Neuronale Netze im Trading: Hierarchische Entdeckung von Skills für adaptives Agentenverhalten (HiSSD) Neuronale Netze im Trading: Hierarchische Entdeckung von Skills für adaptives Agentenverhalten (HiSSD)
In diesem Artikel untersuchen wir das HiSSD-Framework, das hierarchisches Lernen und Multi-Agenten-Ansätze kombiniert, um adaptive Systeme zu schaffen. Wir untersuchen im Detail, wie diese innovative Methodik dabei hilft, verborgene Muster in Finanzmärkten aufzudecken und Handelsstrategien in dezentralen Umgebungen zu optimieren.
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.
Saisonalitätsindikator nach Stunden, Wochentagen und Tagen des Monats Saisonalitätsindikator nach Stunden, Wochentagen und Tagen des Monats
Der Artikel erklärt, wie man ein nützliches Werkzeug zur Analyse wiederkehrender Kursmuster an den Finanzmärkten entwickelt – nach Kalendertag des Monats (1-31), Wochentag (Montag-Sonntag) oder Stunde des Tages (0-23). Der Indikator analysiert historische Daten, berechnet die durchschnittliche Rendite für jeden Zeitraum und zeigt die Ergebnisse als Histogramm mit einer Prognose an. Er enthält anpassbare Parameter: Saisonalitätstyp, Anzahl der analysierten Bars, Anzeige als Prozentsatz oder absolute Werte sowie Chartfarben.