English Русский Español 日本語 Português
preview
Entwicklung eines Expertenberaters für mehrere Währungen (Teil 2): Übergang zu virtuellen Positionen von Handelsstrategien

Entwicklung eines Expertenberaters für mehrere Währungen (Teil 2): Übergang zu virtuellen Positionen von Handelsstrategien

MetaTrader 5Handel | 18 Juni 2024, 12:13
112 0
Yuriy Bykov
Yuriy Bykov

Einführung

Im letzten Artikel haben wir mit der Entwicklung eines Multiwährungs-EAs begonnen, der gleichzeitig mit verschiedenen Handelsstrategien arbeitet. In der ersten Phase gab es nur zwei verschiedene Strategien. Sie stellten die Umsetzung derselben Handelsidee dar und arbeiteten mit demselben Handelsinstrument (Symbol) und Chart-Zeitrahmen (Periode). Sie unterschieden sich nur in den numerischen Werten der Parameter voneinander.

Außerdem haben wir die optimale Größe der offenen Positionen auf der Grundlage des gewünschten maximalen Drawdown-Niveaus (10 % der Einlage) bestimmt. Wir haben dies für jede Strategie separat getan. Als wir die beiden Strategien miteinander kombinierten, mussten wir die Größe der eröffneten Positionen reduzieren, um das vorgegebene Drawdown-Niveau zu halten. Bei zwei Strategien war der Rückgang gering. Was aber, wenn wir Dutzende oder Hunderte von Strategieinstanzen kombinieren wollen? Es kann durchaus vorkommen, dass wir bei einigen Strategien die Positionsgröße auf einen Wert reduzieren müssen, der unter der vom Broker erlaubten Mindestgröße für offene Positionen liegt. In diesem Fall sind diese Strategien einfach nicht in der Lage, am Handel teilzunehmen. Wie können wir sie zum Funktionieren bringen?

Zu diesem Zweck nehmen wir den Strategien das Recht, selbstständig Positionen zu eröffnen und schwebende Aufträge zu erteilen. Die Strategien müssen nur noch virtuell handeln, d. h. sich merken, auf welchen Niveaus Positionen einer bestimmten Größe eröffnet werden sollten, und auf Anfrage melden, welches Volumen jetzt eröffnet werden sollte. Wir eröffnen echte Marktpositionen erst, nachdem wir alle Strategien geprüft und das erforderliche Gesamtvolumen berechnet haben, wobei wir die Skalierung berücksichtigen, um einen bestimmten Drawdown aufrechtzuerhalten.

Uns geht es jetzt nur darum, die Eignung dieses Ansatzes zu prüfen, nicht aber die Effizienz seiner Umsetzung. Daher werden wir im Rahmen dieses Artikels versuchen, zumindest eine funktionierende Implementierung dieses Ansatzes zu entwickeln, die uns später dabei helfen wird, einen aus architektonischer Sicht schöneren Ansatz zu entwickeln, da wir bereits wissen, wie wir Fehler vermeiden können.

Versuchen wir, dies umzusetzen.


Rückblick auf frühere Errungenschaften

Wir haben die EA-Klasse CAdvisor entwickelt, die das Array der Handelsstrategie-Instanzen (genauer gesagt, Zeiger auf Instanzen) speichert. Dies ermöglicht es, eine Instanz eines Expert Advisors im Hauptprogramm zu erstellen und ihm mehrere Instanzen von Strategieklassen hinzuzufügen. Da das Array Zeiger auf Objekte der Basisklasse CStrategy speichert, kann es Zeiger auf Objekte aller von CStrategy geerbten Nachfolgeklassen speichern. In unserem Fall haben wir eine abgeleitete Klasse CSimpleVolumesStrategy erstellt, deren zwei Objekte zu diesem Array im EA hinzugefügt wurden.

Wir sollten uns auf geeignete Namen einigen, um die Präsentation zu erleichtern:

  • Der EA ist unsere endgültige mq5-Datei, die nach der Kompilierung eine ausführbare ex5-Datei liefert, die im Prüfprogramm und im Terminal ausgeführt werden kann.
  • Der EA ist das im Programm deklarierte Objekt der Klasse CAdvisor. Wir werden nur eine EA-Instanz in einem Programm verwenden.
  • Die Strategie ist ein Objekt einer Unterklasse, die von der Basisklasse CStrategy geerbt wurde. 

Erinnern wir uns auch daran, dass ein Zeiger auf ein Objekt (Strategie oder eine andere Klasse) eine Information über den Speicherplatz eines zuvor erstellten Objekts ist (vereinfacht). So kann vermieden werden, dass dasselbe Objekt an einem anderen Speicherort neu erstellt wird, wenn es an Funktionen übergeben oder neue Variablen oder Array-Elemente zugewiesen werden.

Aus diesem Grund werden im Expert Advisor Zeiger auf Strategieobjekte in einem Array gespeichert, sodass beim Füllen dieses Arrays keine Kopien von Strategieobjekten erstellt werden. Wenn wir dann auf die Elemente des Strategie-Arrays zugreifen, greifen wir auf die ursprünglichen Strategieobjekte zu.

Die Arbeiten der EA umfassten die folgenden Phasen:

  • Der EA wurde im Speicherbereich „static“ erstellt.
  • Bei der Initialisierung des Programms wurden zwei Strategien im dynamischen Speicher angelegt und Zeiger auf sie im EA gespeichert.
  • Während das Programm lief, rief der EA nacheinander jede Strategie auf, um die erforderlichen Handelsaktionen durch Aufruf der Methode CStrategy::Tick() durchzuführen.
  • Bei der Deinitialisierung des Programms löschte der EA Strategieobjekte aus dem dynamischen Speicher.

Bevor wir beginnen, müssen wir noch einige kleinere Korrekturen am EA und der EA-Klasse vornehmen. In der EA werden wir dafür sorgen, dass der Experte im dynamischen Speicherbereich erstellt wird.

CAdvisor     expert;          // EA object
CAdvisor     *expert;         // Pointer to the EA object

int OnInit() {
   expert = new CAdvisor();   // Create EA object
   
   // The rest of the code from OnInit() ...
}

In der EA-Klasse werden wir einen Destruktor erstellen - eine Funktion, die automatisch aufgerufen wird, wenn das EA-Objekt aus dem Speicher gelöscht wird. Der Destruktor übernimmt die Operationen zum Entfernen von Strategieobjekten der Methode CAdvisor::Deinit() aus dem dynamischen Speicher. Wir brauchen diese Methode jetzt nicht. Streichen wir sie. Wir werden auch die Klassenvariable m_strategiesCount entfernen, die die Anzahl der Strategien speichert. Bei Bedarf können wir ArraySize() verwenden.

class CAdvisor {
protected:
   CStrategy         *m_strategies[];  // Array of trading strategies
public:
   ~CAdvisor();                        // Destructor

   // ...
};

//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
void CAdvisor::~CAdvisor() {
// Delete all strategy objects
   for(int i = 0; i < ArraySize(m_strategies); i++) {
      delete m_strategies[i];
   }
}

Wir ersetzen in der Funktion OnDeinit() die Methode CAdvisor::Deinit() durch das Entfernen des EA-Objekts.

void OnDeinit(const int reason) {
   expert.Deinit();
   delete expert;
}


Strategieplan

Wenn Handelsstrategien nicht mehr in der Lage sind, selbst Marktpositionen zu eröffnen, dann 

  • benötigen wir Objekte, die Informationen über virtuelle Positionen von Strategien speichern;
  • brauchen wir Objekte, die Informationen über virtuelle Positionen in reale Marktpositionen umwandeln können.

Objekte für virtuelle Positionen sollten fester Bestandteil der Strategie sein, und es sollte mehrere davon geben. Nennen wir also die erste neue Klasse CVirtualOrder und fügen wir das Array dieser Objekte der Strategieklasse CStrategy hinzu. CStrategy erhält außerdem eine Eigenschaft, die eine Anzeige der Änderungen der offenen virtuellen Positionen speichert, sowie Methoden zum Abrufen und Setzen ihres Wertes. Diese Eigenschaft bestimmt, in welchem von zwei Zuständen sich die Strategie gerade befindet:

  • keine Änderungen - das gesamte offene virtuelle Volumen wurde für den Markt freigegeben;
  • es gibt Änderungen - das virtuelle Volumen entspricht nicht dem Marktvolumen, daher ist es notwendig, das Volumen der realen Marktpositionen anzupassen.
Für den Moment scheinen diese beiden Zustände ausreichend zu sein, sodass wir uns auf dieses Modell beschränken werden.

Da nun jemand anderes für die Eröffnung echter Positionen zuständig ist, kann die Magic-Nummer m_magic aus der Basisklasse der Strategie entfernt werden. In Zukunft werden wir die grundlegendste Klasse von Strategien noch weiter bereinigen, doch vorerst beschränken wir uns auf eine Teilbereinigung.

Damit sieht die Basisstrategieklasse nun wie folgt aus.

#include "VirtualOrder.mqh"

//+------------------------------------------------------------------+
//| Base class of the trading strategy                               |
//+------------------------------------------------------------------+
class CStrategy {
protected:
   string            m_symbol;         // Symbol (trading instrument)
   ENUM_TIMEFRAMES   m_timeframe;      // Chart period (timeframe)
   double            m_fixedLot;       // Size of opened positions (fixed)

   CVirtualOrder     m_orders[];       // Array of virtual positions (orders)
   int               m_ordersTotal;    // Total number of open positions and orders
   double            m_volumeTotal;    // Total volume of open positions and orders

   bool              m_isChanged;      // Sign of changes in open virtual positions
   void              CountOrders();    // Calculate the number and volumes of open positions and orders

public:
   // Constructor
   CStrategy(string p_symbol = "",
             ENUM_TIMEFRAMES p_timeframe = PERIOD_CURRENT,
             double p_fixedLot = 0.01);

   virtual void      Tick();           // Main method - handling OnTick events
   virtual double    Volume();         // Total volume of virtual positions
   virtual string    Symbol();         // Strategy symbol (only one for a single strategy so far)
   virtual bool      IsChanged();      // Are there any changes in open virtual positions?
   virtual void      ResetChanges();   // Reset the sign of changes in open virtual positions
};

Wir können bereits die Methoden Symbol(), IsChanged() und ResetChanges() implementieren.

//+------------------------------------------------------------------+
//| Strategy symbol                                                  |
//+------------------------------------------------------------------+
string CStrategy::Symbol() {
   return m_symbol;
}

//+------------------------------------------------------------------+
//| Are there any changes to open virtual positions?                 |
//+------------------------------------------------------------------+
bool CStrategy::IsChanged() {
   return m_isChanged;
}

//+------------------------------------------------------------------+
//| Reset the flag for changes in virtual positions                  |
//+------------------------------------------------------------------+
void CStrategy::ResetChanges() {
   m_isChanged = false;
}

Die übrigen Methoden ( Tick(), Volume() und CountOrders()) werden wir später entweder in den Nachkommen der Basisklasse oder in der Klasse selbst implementieren.

Die zweite neue Klasse, deren Objekte daran beteiligt sein werden, virtuelle Positionen von Strategien auf den Markt zu bringen, wird CReceiver heißen. Um zu funktionieren, sollte dieses Objekt Zugang zu allen EA-Strategien haben, um herauszufinden, welche Symbole und welches Volumen für die Eröffnung echter Positionen verwendet werden sollten. Ein solches Objekt reicht für einen EA aus. Das CReceiver-Objekt sollte eine Magische-Nummer haben, die für geöffnete Marktpositionen gesetzt wird.

#include "Strategy.mqh"

//+------------------------------------------------------------------+
//| Base class for converting open volumes into market positions     |
//+------------------------------------------------------------------+
class CReceiver {
protected:
   CStrategy         *m_strategies[];  // Array of strategies
   ulong             m_magic;          // Magic

public:
   CReceiver(ulong p_magic = 0);                // Constructor
   virtual void      Add(CStrategy *strategy);  // Adding strategy
   virtual bool      Correct();                 // Adjustment of open volumes
};

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CReceiver::CReceiver(ulong p_magic) : m_magic(p_magic) {
   ArrayResize(m_strategies, 0, 128);
}

//+------------------------------------------------------------------+
//| Add strategy                                                     |
//+------------------------------------------------------------------+
void CReceiver::Add(CStrategy *strategy) {
   APPEND(m_strategies, strategy);
}

//+------------------------------------------------------------------+
//| Adjust open volumes                                              |
//+------------------------------------------------------------------+
bool CReceiver::Correct() {
   return true;
}

Diese Basisklasse enthält keine Implementierung eines speziellen Mechanismus zur Volumenregelung. Daher werden wir in der Lage sein, verschiedene Implementierungen der Anpassung in verschiedenen Nachkommen dieser Klasse vorzunehmen. Das Klassenobjekt kann als ein einfacher Anfang für die Strategien dienen, die vorerst selbst Marktpositionen eröffnen. Wir benötigen dies, um den Anpassungsmechanismus zu debuggen: Es wird notwendig sein, zu vergleichen, welche Positionen vom EA eröffnet werden, bei denen die Strategien selbst realen Handel betreiben, und welche Positionen vom EA eröffnet werden, bei denen die Strategien nur virtuellen Handel betreiben.

Daher werden wir zwei EAs vorbereiten, in denen die Strategien aus dem vorherigen Artikel selbst realen Handel betreiben.

Im ersten EA wird die Strategie in einer Instanz sein. In seinen Parametern können wir die Parameter dieser einzelnen Strategieinstanz für ihre Optimierung angeben.

Der zweite EA enthält mehrere Instanzen von Handelsstrategien mit vordefinierten Parametern, die aus der ersten EA-Optimierung resultieren.


EA zur Optimierung der Strategieparameter

Beim letzten Mal haben wir die Strategieparameter optimiert, indem wir die Strategieimplementierung nicht in Form des Klassenobjekts CStrategy verwendet haben. Aber jetzt haben wir bereits eine fertige Klasse CSimpleVolumesStrategy, also erstellen wir ein separates Programm, in dem der EA eine einzelne Instanz dieser Strategie enthalten wird. Wir werden diese Klasse etwas anders nennen, um zu betonen, dass die Strategie selbst Marktpositionen eröffnet: Statt CSimpleVolumesStrategy verwenden wir CSimpleVolumesMarketStrategy und speichern sie in der Datei SimpleVolumesMarketStrategy.mqh im aktuellen Ordner.

In der EA-Datei werden wir die Strategieparameter aus den EA-Eingabevariablen laden und eine Instanz der Strategie zum EA-Objekt hinzufügen. Wir erhalten einen EA, mit dem wir die Strategieparameter optimieren können. 

#include "Advisor.mqh"
#include "SimpleVolumesMarketStrategy.mqh"
#include "VolumeReceiver.mqh"

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
input string      symbol_              = "EURGBP";    // Trading instrument (symbol)
input ENUM_TIMEFRAMES
timeframe_           = PERIOD_H1;   // Chart period

input group "===  Opening signal parameters"
input int         signalPeriod_        = 130;   // Number of candles for volume averaging
input double      signalDeviation_     = 0.9;   // Relative deviation from the average to open the first order 
input double      signaAddlDeviation_  = 1.4;   // Relative deviation from the average for opening the second and subsequent orders

input group "===  Pending order parameters"
input int         openDistance_        = 0;     // Distance from price to pending order
input double      stopLevel_           = 2000;  // Stop Loss (in points)
input double      takeLevel_           = 475;   // Take Profit (in points)
input int         ordersExpiration_    = 6000;  // Pending order expiration time (in minutes)

input group "===  Money management parameters"
input int         maxCountOfOrders_    = 3;     // Maximum number of simultaneously open orders
input double      fixedLot_            = 0.01;  // Single order volume

input group "===  EA parameters"
input ulong       magic_              = 27181; // Magic

CAdvisor     *expert;         // Pointer to the EA object

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
   expert = new CAdvisor();
   expert.Add(new CSimpleVolumesMarketStrategy(
                 magic_, symbol_, timeframe_,
                 fixedLot_,
                 signalPeriod_, signalDeviation_, signaAddlDeviation_,
                 openDistance_, stopLevel_, takeLevel_, ordersExpiration_,
                 maxCountOfOrders_)
             );       // Add one strategy instance

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
   expert.Tick();
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
   delete expert;
}

Speichern wir sie im aktuellen Ordner der Datei SimpleVolumesMarketExpertSingle.mq5.

Lassen Sie uns nun die Handelsstrategie ein wenig verkomplizieren, um die Umsetzung der Aufgabe zu vereinfachen. Es wird für uns einfacher sein, eine Strategie auf den virtuellen Handel zu übertragen, die Marktpositionen anstelle von schwebenden Aufträgen verwendet. Die aktuelle Version der Strategie funktioniert nur mit schwebenden Aufträgen. Fügen wir der Strategie openDistance_ eine Parameterwertanalyse hinzu. Liegt er über Null, eröffnet die Strategie die schwebenden Aufträge BUY_STOP und SELL_STOP. Liegt er unter Null, so eröffnet die Strategie die schwebenden Aufträge BUY_LIMIT und SELL_LIMIT. Ist der Wert gleich Null, werden Marktpositionen eröffnet. 

Dazu müssen wir lediglich Änderungen am Code der Methoden CSimpleVolumesMarketStrategy::OpenBuyOrder() undCSimpleVolumesMarketStrategy::OpenSellOrder() vornehmen.

void CSimpleVolumesMarketStrategy::OpenBuyOrder() {
// Previous code in the method ...

// Order volume
   double lot = m_fixedLot;

// Set a pending order
   bool res = false;
   if(openDistance_ > 0) {
      res = trade.BuyStop(lot, ...);
   } else if(openDistance_ < 0) {
      res = trade.BuyLimit(lot, ...);
   } else {
      res = trade.Buy(lot, ...);
   }

   if(!res) {
      Print("Error opening order");
   }
}

Eine weitere notwendige Änderung, die an der Strategie vorgenommen werden muss, besteht darin, den Initialisierungscode von der Methode Init() in den Strategiekonstruktor zu verschieben. Dies ist notwendig, da der EA nun nicht mehr die Strategieinitialisierungsmethode aufruft, da er davon ausgeht, dass sich sein Code innerhalb des Strategiekonstruktors befindet.

Lassen Sie uns einen neuen EA kompilieren und ihn so einstellen, dass er auf H1 mit drei Symbolen optimiert: EURGBP, GBPUSD und EURUSD.

Abb. 1. Testergebnisse mit den Parametern [EURGBP, H1, 17, 1,7, 0,5, 0, 16500, 100, 52000, 3, 0,01].

Abb. 1. Testergebnisse mit den Parametern [EURGBP, H1, 17, 1.7, 0.5, 0, 16500, 100, 52000, 3, 0.01].

Wählen wir aus den Optimierungsergebnissen mehrere gute Optionen für Parameter aus (z. B. drei Optionen für jedes Symbol) und erstellen einen zweiten EA, in dem neun Instanzen der Strategie mit den ausgewählten Parametern erstellt werden. Für jeden Fall berechnen wir die optimale Größe der offenen Positionen, bei der der Drawdown einer Strategie 10 % nicht überschreitet. Die Berechnungsmethode wurde im vorigen Artikel beschrieben.

Um Veränderungen in der EA-Leistung zu demonstrieren, werden wir die Möglichkeit schaffen, die einzubeziehenden Strategien festzulegen. Zu diesem Zweck werden zunächst alle Strategieinstanzen in das Array aus neun Elementen eingefügt. Fügen wir die Eingabe startIndex_ hinzu, die den Anfangsindex im Strategie-Array festlegt, von dem aus die Strategien zu arbeiten beginnen. Der Parameter totalStrategies_ bestimmt, wie viele sequentielle Strategien aus dem Array ab startIndex_ gestartet werden sollen. Am Ende der Initialisierung fügen wir dem EA-Objekt die entsprechenden Strategien aus dem Array hinzu.

#include "Advisor.mqh"
#include "SimpleVolumesMarketStrategy.mqh"

input int startIndex_      = 0;        // Starting index
input int totalStrategies_ = 1;        // Number of strategies
input double depoPart_     = 1.0;      // Part of the deposit for one strategy
input ulong  magic_        = 27182;    // Magic

CAdvisor     *expert;                  // EA object

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
// Check if the parameters are correct
   if(startIndex_ < 0 || startIndex_ + totalStrategies_ > 9) {
      return INIT_PARAMETERS_INCORRECT;
   }

// Create and fill the array of strategy instances
   CStrategy *strategies[9];
   strategies[0] = new CSimpleVolumesMarketStrategy(
      magic_ + 0, "EURGBP", PERIOD_H1,
      NormalizeDouble(0.01 / 0.16 * depoPart_, 2),
      13, 0.3, 1.0, 0, 10500, 465, 1000, 3);
   strategies[1] = new CSimpleVolumesMarketStrategy(
      magic_ + 1, "EURGBP", PERIOD_H1,
      NormalizeDouble(0.01 / 0.09 * depoPart_, 2),
      17, 1.7, 0.5, 0, 16500, 220, 1000, 3);
   strategies[2] = new CSimpleVolumesMarketStrategy(
      magic_ + 2, "EURGBP", PERIOD_H1,
      NormalizeDouble(0.01 / 0.16 * depoPart_, 2),
      51, 0.5, 1.1, 0, 19500, 370, 22000, 3);
   strategies[3] = new CSimpleVolumesMarketStrategy(
      magic_ + 3, "GBPUSD", PERIOD_H1,
      NormalizeDouble(0.01 / 0.25 * depoPart_, 2),
      80, 1.1, 0.2, 0, 6000, 1190, 1000, 3);
   strategies[4] = new CSimpleVolumesMarketStrategy(
      magic_ + 4, "GBPUSD", PERIOD_H1,
      NormalizeDouble(0.01 / 0.09 * depoPart_, 2),
      128, 2.0, 0.9, 0, 2000, 1170, 1000, 3);
   strategies[5] = new CSimpleVolumesMarketStrategy(
      magic_ + 5, "GBPUSD", PERIOD_H1,
      NormalizeDouble(0.01 / 0.14 * depoPart_, 2),
      13, 1.5, 0.8, 0, 2500, 1375, 1000, 3);
   strategies[6] = new CSimpleVolumesMarketStrategy(
      magic_ + 6, "EURUSD", PERIOD_H1,
      NormalizeDouble(0.01 / 0.23 * depoPart_, 2),
      24, 0.1, 0.3, 0, 7500, 2400, 24000, 3);
   strategies[7] = new CSimpleVolumesMarketStrategy(
      magic_ + 7, "EURUSD", PERIOD_H1,
      NormalizeDouble(0.01 / 0.20 * depoPart_, 2),
      18, 0.2, 0.4, 0, 19500, 1480, 6000, 3);
   strategies[8] = new CSimpleVolumesMarketStrategy(
      magic_ + 8, "EURUSD", PERIOD_H1,
      NormalizeDouble(0.01 / 0.22 * depoPart_, 2),
      128, 0.7, 0.3, 0, 3000, 170, 42000, 3);

   expert = new CAdvisor();

// Add the necessary strategies to the EA
   for(int i = startIndex_; i < startIndex_ + totalStrategies_; i++) {
      expert.Add(strategies[i]);
   }

   return(INIT_SUCCEEDED);
}

void OnTick() {
   expert.Tick();
}

void OnDeinit(const int reason) {
   delete expert;
}

Auf diese Weise können wir die Optimierung des Anfangsindexes der Strategien im Strategie-Array nutzen, um Ergebnisse für jede Strategieinstanz zu erhalten. Starten wir ihn mit einer Ersteinlage von 100.000 USD und erhalten die folgenden Ergebnisse.


Abb. 2. Ergebnisse der Einzelläufe von neun Strategieinstanzen

Es ist klar, dass der Drawdown etwa 1 % der Ersteinlage beträgt, d. h. etwa 1.000 USD, wie wir bei der Auswahl der optimalen Größe der zu eröffnenden Positionen geplant haben. Die durchschnittliche Sharpe Ratio beträgt 1,3.

Schalten wir nun alle Instanzen ein und wählen wir den entsprechenden Multiplikator depoPart_, um einen Drawdown von 1000 USD zu erhalten. Wenn depoPart_ = 0,38 ist, bleibt der Drawdown innerhalb akzeptabler Grenzen.


Abb. 3. Ergebnisse der Prüfung des gleichzeitigen Betriebs von neun Strategieinstanzen

Vergleicht man die Ergebnisse der Arbeit einzelner Exemplare von Strategien und die Ergebnisse der gleichzeitigen Arbeit aller Exemplare, so kann man feststellen, dass wir bei gleichem Drawdown eine Steigerung des Gewinns um etwa das Dreifache sowie eine Erhöhung der Sharpe Ratio von 1,3 auf 2,84 erhalten haben.

Konzentrieren wir uns nun auf die Hauptaufgabe.


Klasse der virtuellen Positionen (Aufträge)

Erstellen wir also die versprochene Klasse CVirtualOrder und fügen wir ihr Felder hinzu, um alle Eigenschaften der offenen Positionen zu speichern.

class CVirtualOrder {
private:
//--- Order (position) properties
   ulong             m_id;          // Unique ID 

   string            m_symbol;      // Symbol
   double            m_lot;         // Volume
   ENUM_ORDER_TYPE   m_type;        // Type
   double            m_openPrice;   // Open price
   double            m_stopLoss;    // StopLoss level
   double            m_takeProfit;  // TakeProfit level
   string            m_comment;     // Comment

   datetime          m_openTime;    // Open time

//--- Closed order (position) properties
   double            m_closePrice;  // Close price
   datetime          m_closeTime;   // Close time
   string            m_closeReason; // Closure reason

   double            m_point;       // Point value

   bool              m_isStopLoss;  // StopLoss activation property
   bool              m_isTakeProfit;// TakeProfit activation property
};

Jede virtuelle Position sollte eine eindeutige ID haben. Fügen wir also die statische Klassenvariable s_count hinzu, um die Anzahl aller im Programm erstellten Positionsobjekte zu zählen. Wenn ein neues Positionsobjekt erstellt wird, wird dieser Zähler um 1 erhöht und dieser Wert wird zu einer eindeutigen Positionsnummer. Wir setzen den Anfangswert von s_count auf 0.

Wir benötigen auch das Objekt der Klasse CSymbolInfo für Preisinformationen. Wir machen es auch zu einem statischen Mitglied der Klasse. 

class CVirtualOrder {
private:
   static int        s_count;
   static
   CSymbolInfo       s_symbolInfo;

//--- Order (position) properties ...

};

int               CVirtualOrder::s_count = 0;
CSymbolInfo       CVirtualOrder::s_symbolInfo;

Es ist erwähnenswert, dass das Erstellen eines virtuellen Positionsobjekts und das „Öffnen“ einer virtuellen Position unterschiedliche Vorgänge sind. Das Positionsobjekt kann im Voraus erstellt werden und auf den Moment warten, in dem die Strategie eine virtuelle Position eröffnen möchte. In diesem Moment werden die Positionseigenschaften mit den aktuellen Werten von Symbol, Volumen, Eröffnungskurs und anderen gefüllt. Wenn die Strategie beschließt, eine Position zu schließen, speichert das Objekt die Werte für das Schließen: Preis, Zeit und Grund des Schließens. Beim nächsten Vorgang der Eröffnung einer virtuellen Position können wir dieselbe Instanz des Objekts verwenden, indem wir seine Schließeigenschaften löschen und es erneut mit neuen Werten für Symbol, Volumen, Eröffnungskurs und andere füllen.

Fügen wir Methoden zu dieser Klasse hinzu. Wir benötigen öffentliche Methoden zum Öffnen und Schließen einer virtuellen Position und einen Konstruktor. Nützlich sind auch die Methoden, die den Status der Position (ist sie offen und in welche Richtung?) und ihre wichtigsten Eigenschaften - Volumen und aktueller Gewinn - überprüfen.

class CVirtualOrder {
//--- Previous code...

public:
                     CVirtualOrder();  // Constructor
                     
//--- Methods for checking the order (position) status
   bool              IsOpen();         // Is the order open?
   bool              IsMarketOrder();  // Is this a market position?
   bool              IsBuyOrder();     // Is this an open BUY position?
   bool              IsSellOrder();    // Is this an open SELL position?
  
//--- Methods for obtaining order (position) properties
   double            Volume();         // Volume with direction 
   double            Profit();         // Current profit 

//--- Methods for handling orders (positions)
   bool              Open(string symbol,
                          ENUM_ORDER_TYPE type,
                          double lot,
                          double sl = 0,
                          double tp = 0,
                          string comment = "",
                          bool inPoints = true);   // Opening an order (position)
   bool              Close();                      // Closing an order (position)
};

Die Implementierung einiger dieser Methoden ist sehr einfach und kurz, sodass sie z. B. direkt in der Klassendeklaration platziert werden kann:

class CVirtualOrder : public CObject {
// ...

//--- Methods for checking the order (position) status
   bool              IsOpen() {        // Is the order open?
      return(this.m_openTime > 0 && this.m_closeTime == 0);
   };
   bool              IsMarketOrder() { // Is this a market position?
      return IsOpen() && (m_type == ORDER_TYPE_BUY || m_type == ORDER_TYPE_SELL);
   }
   
// ...
};

Der Konstruktor weist allen Eigenschaften der virtuellen Position mit Ausnahme einer - einer eindeutigen ID - über die Initialisierungsliste leere (im Sinne von offensichtlich ungültigen) Werte zu. Wie bereits erwähnt, wird der ID im Konstruktor ein Wert zugewiesen, der sich aus dem Wert des Zählers für zuvor erstellte Klassenobjekte ergibt. Dieser Wert bleibt während der gesamten EA-Operation erhalten. Vor der Zuweisung wird der Zähler der erstellten Objekte erhöht.

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CVirtualOrder::CVirtualOrder() :
// Initialization list
   m_id(++s_count),  // New ID = object counter + 1
   m_symbol(""),
   m_lot(0),
   m_type(-1),
   m_openPrice(0),
   m_stopLoss(0),
   m_takeProfit(0),
   m_openTime(0),
   m_comment(""),
   m_closePrice(0),
   m_closeTime(0),
   m_closeReason(""),
   m_point(0) {
}

Bevor wir die Methoden der Klasse CVirtualOrder weiter implementieren, sollten wir einen Blick in die Zukunft werfen und überlegen, wie wir die Klasse verwenden werden. Wir haben Strategieobjekte, die jetzt echte Marktpositionen eröffnen. Außerdem kennen wir die maximale Anzahl der offenen Marktpositionen (die in den Strategieparametern festgelegt ist). Wir wollen zu virtuellen Positionen übergehen. Dann wird auch ihre Zahl begrenzt sein. Das bedeutet, dass wir in einer Strategie ein Array von virtuellen Positionsobjekten erstellen können, das wir bei der Initialisierung der Strategie mit der erforderlichen Anzahl von virtuellen Positionen füllen und dann nur mit diesem Array arbeiten.

Wenn die Bedingungen für die Eröffnung einer neuen Position eintreten, nehmen wir eine virtuelle Position, die noch nicht eröffnet wurde, aus dem Array und machen sie zu einer offenen Position. Wenn die Bedingungen für eine Zwangsschließung von Positionen eintreten, wandeln wir diese in geschlossene Positionen um.

Solange es offene virtuelle Positionen gibt, muss jede Strategie diese Objekte bei jedem Tick auf die gleiche Weise verarbeiten: Sie geht alles der Reihe nach durch, prüft, ob das StopLoss- oder TakeProfit-Niveau erreicht wurde, und wenn ja, schließt sie die Position. Diese Gleichheit ermöglicht es uns, die Implementierung der Behandlung offener virtueller Verhaltenspositionen in ihre eigene Klasse zu übertragen und nur die entsprechende Methode aus der Strategie aufzurufen.

Die Klasse CVirtualOrder erhält die Methode Tick(), die die Bedingungen für das Schließen einer Position prüft, und wenn diese erfüllt sind, wird die Position in den geschlossenen Zustand überführt. Wenn sich der Zustand der Position ändert, gibt die Methode true zurück.

Fügen wir auch die statische Methode Tick() hinzu, die mehrere virtuelle Positionsobjekte gleichzeitig verarbeitet. Sie akzeptiert einen Link zu einem Array solcher Objekte als Parameter. Die Methode Tick() wird für jedes Array-Objekt aufgerufen. Wenn mindestens eine virtuelle Position geschlossen ist, wird schließlich „true“ zurückgegeben.

class CVirtualOrder {
private:
   //...
public:
   //...
   
//--- Methods for handling orders (positions)
   bool              Open(string symbol,
                          ENUM_ORDER_TYPE type,
                          double lot,
                          double sl = 0,
                          double tp = 0,
                          string comment = "",
                          bool inPoints = false
                         );      // Open order (position)

   bool              Tick();     // Handle tick for an order (position)
   bool              Close();    // Close an order (position)

   static bool       Tick(CVirtualOrder &orders[]);   // Handle a tick for the array of virtual orders
};               

//...

//+------------------------------------------------------------------+
//| Handle a tick of a single virtual order (position)               |
//+------------------------------------------------------------------+
bool CVirtualOrder::Tick() {
   if(IsMarketOrder()) {  // If this is a market virtual position
      if(CheckClose()) {  // Check if SL or TP levels have been reached
         Close();         // Close when reached
         return true;     // Return the fact that there are changes in open positions
      }
   }

   return false;
}

//+------------------------------------------------------------------+
//| Handle a tick for the array of virtual orders (positions)        |
//+------------------------------------------------------------------+
bool CVirtualOrder::Tick(CVirtualOrder &orders[]) {
   bool isChanged = false;                      // We assume that there will be no changes
   for(int i = 0; i < ArraySize(orders); i++) { // For all orders (positions)
      isChanged |= orders[i].Tick();            // Check and close if necessary
   }
   return isChanged;
}
//+------------------------------------------------------------------+

Speichern wir diesen Code in der Datei VirtualOrder.mqh des aktuellen Ordners.


Verbesserung der einfachen Handelsstrategieklasse

Nun können wir zur Handelsstrategieklasse zurückkehren und Änderungen vornehmen, die es uns ermöglichen, mit virtuellen Positionen zu arbeiten. Wie wir bereits vereinbart haben, verfügen wir in der Basisklasse CStrategy bereits über das Array m_orders[] zur Speicherung virtueller Positionsobjekte. Daher ist sie auch in der Klasse CSimpleVolumesStrategy verfügbar. Die betreffende Strategie hat den Parameter m_maxCountOfOrders, der die maximale Anzahl der gleichzeitig offenen Positionen festlegt. Dann setzen wir die Größe des Arrays der virtuellen Positionen im Konstruktor gleich diesem Parameter.

Als Nächstes müssen wir nur die Eröffnung von realen Positionen in den Methoden OpenBuyOrder() und OpenSellOrder() durch die Eröffnung von virtuellen Positionen ersetzen. Es gibt derzeit nichts, womit wir die Eröffnung echter schwebender Aufträge ersetzen könnten, also werden wir diese Vorgänge einfach auskommentieren.

//+------------------------------------------------------------------+
//| Open BUY order                                                   |
//+------------------------------------------------------------------+
void CSimpleVolumesStrategy::OpenBuyOrder() {
// ...
   
   if(m_openDistance > 0) {
      /* // Set BUY STOP pending order
         res = trade.BuyStop(lot, ...);  */
   } else if(m_openDistance < 0) {
      /* // Set BUY LIMIT pending order
         res = trade.BuyLimit(lot, ...); */
   } else {
      // Open a virtual BUY position
      for(int i = 0; i < m_maxCountOfOrders; i++) {   // Iterate through all virtual positions
         if(!m_orders[i].IsOpen()) {                  // If we find one that is not open, then open it
            res = m_orders[i].Open(m_symbol, ORDER_TYPE_BUY, m_fixedLot,
                                   NormalizeDouble(sl, digits),
                                   NormalizeDouble(tp, digits));
            break;                                    // and exit
         }
      }
   } 

   ... 
}

//+------------------------------------------------------------------+
//| Open SELL order                                                  |
//+------------------------------------------------------------------+
void CSimpleVolumesStrategy::OpenSellOrder() {
// ...
   
   if(m_openDistance > 0) {
      /* // Set SELL STOP pending order
      res = trade.SellStop(lot, ...);          */
   } else if(m_openDistance < 0) {
      /* // Set SELL LIMIT pending order
      res = trade.SellLimit(lot, ...);         */
   } else {
      // Open a virtual SELL position
      for(int i = 0; i < m_maxCountOfOrders; i++) {   // Iterate through all virtual positions
         if(!m_orders[i].IsOpen()) {                  // If we find one that is not open, then open it
            res = m_orders[i].Open(m_symbol, ORDER_TYPE_SELL, m_fixedLot,
                                   NormalizeDouble(sl, digits),
                                   NormalizeDouble(tp, digits));
            break;                                    // and exit
         }
      }
   }

   ... 
}

Speichern wir die Änderungen in der Datei SimpleVolumesStrategy.mqh des aktuellen Ordners.


Erstellung einer Klasse zur Umwandlung von offenen Volumina in Marktpositionen

Wir haben bereits eine Basisklasse für Objekte zur Umwandlung von offenen Volumina in Marktpositionen erstellt, die bisher nichts anderes tut, als das Array der verwendeten Strategien zu füllen. Nun müssen wir eine abgeleitete Klasse schreiben, die eine spezifische Implementierung der Platzierung von Positionen auf dem Markt enthält. Lassen Sie uns die Klasse CVolumeReceiver erstellen. Wir müssen eine ganze Menge Code hinzufügen, um die Methode Correct() zu implementieren. Wir werden sie in mehrere geschützte Klassenmethoden aufteilen.

#include "Receiver.mqh"

//+------------------------------------------------------------------+
//| Class for converting open volumes into market positions          |
//+------------------------------------------------------------------+
class CVolumeReceiver : public CReceiver {
protected:
   bool              m_isNetting;      // Is this a netting account?
   string            m_symbols[];      // Array of used symbols

   double            m_minMargin;      // Minimum margin for opening

   CPositionInfo     m_position;
   CSymbolInfo       m_symbolInfo;
   CTrade            m_trade;

   // Filling the array of open market volumes by symbols 
   void              FillSymbolVolumes(double &oldVolumes[]);

   // Correction of open volumes using the array of volumes 
   virtual bool      Correct(double &symbolVolumes[]);

   // Volume correction for this symbol
   bool              CorrectPosition(string symbol, double oldVolume, double diffVolume);

   // Auxiliary methods
   bool              ClearOpen(string symbol, double diffVolume);
   bool              AddBuy(string symbol, double volume);
   bool              AddSell(string symbol, double volume);

   bool              CloseBuyPartial(string symbol, double volume);
   bool              CloseSellPartial(string symbol, double volume);
   bool              CloseHedgingPartial(string symbol, double volume, ENUM_POSITION_TYPE type);
   bool              CloseFull(string symbol = "");

   bool              FreeMarginCheck(string symbol, double volume, ENUM_ORDER_TYPE type);

public:
   CVolumeReceiver(ulong p_magic, double p_minMargin = 100);   // Constructor
   virtual void      Add(CStrategy *strategy) override;        // Add strategy
   virtual bool      Correct() override;                       // Adjustment of open volumes
};

Der allgemeine Algorithmus für die Methode der Korrektur des offenen Volumens lautet wie folgt:

  • Gehen wir für jedes verwendete Symbol alle Strategien durch und berechnen Sie das gesamte offene Volumen für jedes verwendete Symbol. Das resultierende newVolumes-Array wird an die nächste überladene Methode Correct() übergeben
    //+------------------------------------------------------------------+
    //| Adjustment of open volumes                                       |
    //+------------------------------------------------------------------+
    bool CVolumeReceiver::Correct() {
       int symbolsTotal = ArraySize(m_symbols);
       double newVolumes[];
    
       ArrayResize(newVolumes, symbolsTotal);
       ArrayInitialize(newVolumes, 0);
    
       for(int j = 0; j < symbolsTotal; j++) {  // For each used symbol        
          for(int i = 0; i < ArraySize(m_strategies); i++) { // Iterate through all strategies
             if(m_strategies[i].Symbol() == m_symbols[j]) {  // If the strategy uses this symbol
                newVolumes[j] += m_strategies[i].Volume();   // Add its open volume
             }
          }
       }
       // Call correction of open volumes using the array of volumes
       return Correct(newVolumes);
    }
    
  • Wir legen für jedes Symbol fest, wie stark das Volumen der offenen Positionen für das Symbol verändert werden soll. Ggf. rufen wir die Volumenkorrekturmethode für dieses Symbol auf:
    //+------------------------------------------------------------------+
    //| Adjusting open volumes using the array of volumes                |
    //+------------------------------------------------------------------+
    bool CVolumeReceiver::Correct(double &newVolumes[]) {
       // ...
       bool res = true;
    
       // For each symbol
       for(int j = 0; j < ArraySize(m_symbols); j++) {
          // ...
          // Define how much the volume of open positions for the symbol should be changed
          double oldVolume = oldVolumes[j];
          double newVolume = newVolumes[j];
          
          // ...
          double diffVolume = newVolume - oldVolume;
          
          // If there is a need to adjust the volume for a given symbol, then do that
          if(MathAbs(diffVolume) > 0.001) {
             res = res && CorrectPosition(m_symbols[j], oldVolume, diffVolume);
          }
       }
    
       return res;
    }
    
  • Für ein Symbol bestimmen wir, welche Art von Handelsvorgang wir durchführen müssen (hinzufügen, schließen und wieder öffnen), basierend auf den Werten des vorherigen offenen Volumens und der gewünschten Änderung, und rufen die entsprechende Hilfsmethode auf:
    //+------------------------------------------------------------------+
    //| Adjust volume by the symbol                                      |
    //+------------------------------------------------------------------+
    bool CVolumeReceiver::CorrectPosition(string symbol, double oldVolume, double diffVolume) {
       bool res = false;
    
       // ...
    
       double volume = MathAbs(diffVolume);
    
       if(oldVolume > 0) { // Have BUY position
          if(diffVolume > 0) { // New BUY position
             res = AddBuy(symbol, volume);
          } else if(diffVolume < 0) { // New SELL position
             if(volume < oldVolume) {
                res = CloseBuyPartial(symbol, volume);
             } else {
                res = CloseFull(symbol);
    
                if(res && volume > oldVolume) {
                   res = AddSell(symbol, volume - oldVolume);
                }
             }
          }
       } else if(oldVolume < 0) { // Have SELL position
          if(diffVolume < 0) { // New SELL position
             res = AddSell(symbol, volume);
          } else if(diffVolume > 0) { // New BUY position
             if(volume < -oldVolume) {
                res = CloseSellPartial(symbol, volume);
             } else {
                res = CloseFull(symbol);
    
                if(res && volume > -oldVolume) {
                   res = AddBuy(symbol, volume + oldVolume);
                }
             }
          }
       } else { // No old position
          res = ClearOpen(symbol, diffVolume);
       }
    
       return res;
    }
    

Wir speichern den Code in der Datei VolumeReceiver.mqh des aktuellen Ordners.


EA mit einer Strategie und virtuellen Positionen

Erstellen wir einen EA, der eine Instanz einer Handelsstrategie mit virtuellen Positionen verwendet, basierend auf der Datei SimpleVolumesMarketExpertSingle.mq5. Wir müssen die erforderlichen Dateien einbinden, wenn wir den EA-Konstruktor aufrufen, ihm das neue Objekt der Klasse CVolumeReceiver übergeben und die Klasse der erstellten Strategie ersetzen.

#include "Advisor.mqh"
#include "SimpleVolumesStrategy.mqh"
#include "VolumeReceiver.mqh"

// Input parameters...

CAdvisor     *expert;         // Pointer to the EA object

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
   expert = new CAdvisor(new CVolumeReceiver(magic_));
   expert.Add(new CSimpleVolumesStrategy(
                         symbol_, timeframe_,
                         fixedLot_,
                         signalPeriod_, signalDeviation_, signaAddlDeviation_,
                         openDistance_, stopLevel_, takeLevel_, ordersExpiration_,
                         maxCountOfOrders_)
                     );       // Add one strategy instance

   return(INIT_SUCCEEDED);
}

void OnTick() {
   expert.Tick();
}

void OnDeinit(const int reason) {
   delete expert;
}

Wir speichern diesen Code in der Datei SimpleVolumesExpertSingle.mq5 im aktuellen Ordner.


Vergleich zwischen realem und virtuellem Handel

Starten wir EAs mit der gleichen Strategie über einen kurzen Zeitraum, mit den gleichen Strategieparametern, aber unterschiedlichen Arten der Eröffnung realer Positionen - direkt und über virtuelle Positionen. Speichern wir diese Ergebnisse in Berichten und sehen uns die Liste der von beiden EAs getätigten Geschäfte an.

Abb. 4. Von den beiden EAs ausgeführten Handel (mit und ohne virtuelle Positionen)

Um die Breite zu verringern, wurden Spalten, die in allen Zeilen die gleichen Werte aufweisen, wie das Symbol (immer EURGBP), das Volumen (immer 0,01) und andere, aus den Tabellen entfernt. Wie wir sehen, werden die ersten Positionen in beiden Fällen zum gleichen Preis und zu den gleichen Zeitpunkten eröffnet. Wenn wir eine offene Position SELL haben (2018.03.02 15:46:47 sell in) und eine BUY öffnen (2018.03.06 13:56:04 buy in), schließt der EA, der über virtuelle Positionen arbeitet, einfach die vorherige Position SELL (2018.03.06 13:56:04 buy out). Das Gesamtergebnis verbesserte sich dadurch, da der erste EA weiterhin Swaps für offene Positionen in verschiedenen Richtungen zahlte, während dies beim zweiten EA nicht der Fall war.

EA mit mehreren Strategien und virtuellen Positionen

Lassen Sie uns ähnliche Manipulationen mit dem EA aus der Datei SimpleVolumesMarketExpert.mq5 durchführen. Wir werden die erforderlichen Dateien beifügen. Wenn wir den EA-Konstruktor aufrufen, übergeben wir ihm das neue Objekt der Klasse CVolumeReceiver und ersetzen die Klasse der erstellten Strategien. Speichern Sie das Ergebnis in der Datei SimpleVolumesExpert.mq5 und sehen Sie sich die Ergebnisse an.

Abb. 5. Ergebnisse der EA-Arbeit mit neun Strategieinstanzen und virtuellen Positionen

Vergleicht man diese Ergebnisse mit denen eines ähnlichen EA, der keine virtuellen Positionen verwendet, kann man bei einigen Indikatoren eine Verbesserung feststellen: Der Gewinn hat sich leicht erhöht und der Drawdown hat sich verringert, die Sharpe Ratio und der Gewinnfaktor haben sich ebenfalls erhöht.


Schlussfolgerung

Wir haben einen weiteren Schritt zur Erreichung unseres Ziels gemacht. Durch die Umstellung auf virtuelle Positionsstrategien haben wir die Möglichkeit verbessert, dass eine große Anzahl von Handelsstrategien zusammenarbeiten kann, ohne sich gegenseitig zu behindern. Dies ermöglicht es uns auch, eine niedrigere Mindesteinlage für den Handel zu verwenden, als wenn wir jede Instanz der Strategie unabhängig voneinander handeln würden. Ein weiterer netter Bonus wird die Möglichkeit sein, an Netting-Konten zu arbeiten.

Aber es sind noch viele weitere Schritte zu unternehmen. So wurden bisher nur Strategien implementiert, die Marktpositionen eröffnen, aber keine schwebenden Aufträge. Auch Fragen der Geldverwaltung werden für die Zukunft aufgeschoben. Zurzeit handeln wir mit einem festen Volumen und wählen die optimale Positionsgröße manuell aus. Strategien, die auf mehreren Symbolen gleichzeitig arbeiten sollen (die nicht in einfachere Einzelsymbolstrategien aufgeteilt werden können), können diese Operationsstruktur nicht verwenden.

Bleiben Sie dran für weitere Updates.


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

Wie Sie mit der Erfüllung von Händleraufträgen im Freelance-Service Geld verdienen können Wie Sie mit der Erfüllung von Händleraufträgen im Freelance-Service Geld verdienen können
MQL5 Freelance ist ein Online-Dienst, bei dem Entwickler für die Erstellung von Handelsanwendungen für Händler als Kunden bezahlt werden. Der Dienst existiert seit 2010 sehr erfolgreich und hat bis heute über 100.000 Projekte im Gesamtwert von 7 Millionen Dollar abgeschlossen. Wie wir sehen, geht es hier um eine beträchtliche Menge Geld.
Algorithmen zur Optimierung mit Populationen: Evolution sozialer Gruppen (ESG) Algorithmen zur Optimierung mit Populationen: Evolution sozialer Gruppen (ESG)
Wir werden das Prinzip des Aufbaus von Algorithmen mit mehreren Populationen besprechen. Als Beispiel für diese Art von Algorithmus werden wir uns den neuen nutzerdefinierten Algorithmus - Evolution of Social Groups (ESG) - ansehen. Wir werden die grundlegenden Konzepte, die Mechanismen der Populationsinteraktion und die Vorteile dieses Algorithmus analysieren und seine Leistung bei Optimierungsproblemen untersuchen.
Algorithmen zur Optimierung mit Populationen: Künstliche multisoziale Suchobjekte (MSO) Algorithmen zur Optimierung mit Populationen: Künstliche multisoziale Suchobjekte (MSO)
Dies ist eine Fortsetzung des vorangegangenen Artikels, der sich mit dem Konzept der sozialen Gruppen befasst. In dem Artikel wird die Entwicklung sozialer Gruppen anhand von Bewegungs- und Gedächtnisalgorithmen untersucht. Die Ergebnisse werden dazu beitragen, die Entwicklung sozialer Systeme zu verstehen und sie bei der Optimierung und Suche nach Lösungen anzuwenden.
Entwicklung eines Replay Systems (Teil 38): Den Weg ebnen (II) Entwicklung eines Replay Systems (Teil 38): Den Weg ebnen (II)
Viele Menschen, die sich für MQL5-Programmierer halten, verfügen nicht über die Grundkenntnisse, die ich in diesem Artikel erläutern werde. Viele Menschen halten MQL5 für ein begrenztes Werkzeug, aber der eigentliche Grund ist, dass sie nicht über die erforderlichen Kenntnisse verfügen. Wenn Sie also etwas nicht wissen, brauchen Sie sich dafür nicht zu schämen. Es ist besser, sich dafür zu schämen, nicht zu fragen. MetaTrader 5 einfach dazu zu zwingen, die Indikatorduplikation zu deaktivieren, gewährleistet in keiner Weise eine Zwei-Wege-Kommunikation zwischen dem Indikator und dem Expert Advisor. Davon sind wir noch weit entfernt, aber die Tatsache, dass sich der Indikator auf dem Chart nicht dupliziert, stimmt uns zuversichtlich.