English 日本語
preview
Automatisieren von Handelsstrategien in MQL5 (Teil 20): Multi-Symbol-Strategie mit CCI und AO

Automatisieren von Handelsstrategien in MQL5 (Teil 20): Multi-Symbol-Strategie mit CCI und AO

MetaTrader 5Handel |
195 0
Allan Munene Mutiiria
Allan Munene Mutiiria

Einführung

In unserem vorangegangenen Artikel (Teil 19) haben wir die Strategie Envelopes Trend Bounce Scalping untersucht und uns dabei auf die Handelsausführung und das Risikomanagement konzentriert, um ihre Automatisierung in MetaQuotes Language 5 (MQL5) zu vervollständigen. In Teil 20 stellen wir eine Multi-Symbol-Handelsstrategie vor, die den Commodity Channel Index (CCI) und den Awesome Oszillator (AO) nutzt, um eine Trendumkehr bei mehreren Währungspaaren zu erfassen. Wir werden die folgenden Themen behandeln:

  1. Strategiefahrplan und Architektur
  2. Implementation in MQL5
  3. Backtest und Optimierung
  4. Schlussfolgerung

Am Ende haben Sie ein robustes MQL5-Handelssystem für den Multi-Symbol-Handel, bereit für die Optimierung und den Einsatz - legen Sie los!


Strategiefahrplan und Architektur

In Teil 19 haben wir die Strategie Envelopes Trend Bounce Scalping entwickelt, die sich auf die Handelsausführung und das Risikomanagement konzentriert, um auf Signale zu reagieren, die durch Preisinteraktionen mit dem Envelopes-Indikator erzeugt und durch Trendfilter bestätigt werden. In Teil 20 gehen wir nun zu einer Multi-Symbol-Handelsstrategie über, die den Commodity Channel Index (CCI) und den Awesome Oscillator (AO) verwendet, um Trendumkehrungen bei mehreren Währungspaaren auf zwei Zeitrahmen (M5 für Signale und H1 für die Trendbestätigung) zu erkennen. Unsere Roadmap für diesen Artikel konzentriert sich auf die Entwicklung eines skalierbaren Systems, das Signale effizient verarbeitet, Handelsgeschäfte ausführt und Risiken über mehrere Symbole hinweg verwaltet.

Unser Architekturplan betont Modularität und Robustheit und verwendet eine klassenbasierte Struktur in MQL5, um die Komponenten der Strategie zu organisieren. Unser Ziel ist es, eine gemeinsame Klasse zu schaffen, die die Berechnung von Indikatoren (CCI und AO), die Generierung von Signalen auf der Grundlage vordefinierter Schwellenwerte und die Ausführung von Handelsgeschäfte mit Stop-Loss- und Take-Profit-Einstellungen übernimmt und gleichzeitig sicherstellt, dass keine offenen Aufträge für ein Symbol existieren, bevor neue Handelsgeschäfte platziert werden. Dieses Design beinhaltet Sicherheitsvorkehrungen wie Spread-Kontrollen und unterstützt den Handel auf neuen Bars oder Tick-by-Tick, was Flexibilität für verschiedene Marktbedingungen bietet und letztendlich ein kohärentes Multi-Symbol-Handelssystem liefert. Kurz und bündig: Das wollen wir erreichen.

STRATEGIEPLAN


Implementation in MQL5

Um das Programm in MQL5 zu erstellen, öffnen wir den MetaEditor, gehen zum Navigator, suchen den Ordner der Indikatoren, klicken auf die Registerkarte „Neu“ und folgen den Anweisungen, um die Datei zu erstellen. Sobald dies geschehen ist, werden wir in der Codierungsumgebung damit beginnen, einige Strukturen und Klassen zu deklarieren, die wir verwenden werden, da wir einen objektorientierten Programmieransatz (OOP) anwenden wollen.

//+------------------------------------------------------------------+
//|                                          MultiSymbolCCIAO_EA.mq5 |
//|                           Copyright 2025, Allan Munene Mutiiria. |
//|                                   https://t.me/Forex_Algo_Trader |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, Allan Munene Mutiiria."
#property link      "https://t.me/Forex_Algo_Trader"
#property version   "1.00"
#property strict
#property description "Multi-symbol trading strategy using CCI and AO indicators"

#include <Trade/Trade.mqh> //--- Include the Trade library for trading operations

//+------------------------------------------------------------------+
//| Input Parameters Structure                                       | //--- Define a structure for trading parameters
//+------------------------------------------------------------------+
struct TradingParameters {
   string            symbols;                  //--- Store comma-separated symbol list
   int               per_signal_cci;           //--- Set period for CCI signal
   int               per_trend_cci;            //--- Set period for CCI trend
   ENUM_APPLIED_PRICE price_cci;               //--- Specify applied price for CCI
   int               cci_signal_buy_value;     //--- Define CCI buy signal threshold
   int               cci_signal_sell_value;    //--- Define CCI sell signal threshold
   ENUM_TIMEFRAMES   tf_signal;                //--- Set timeframe for signal
   ENUM_TIMEFRAMES   tf_trend;                 //--- Set timeframe for trend
   bool              use_ao_for_trend;         //--- Enable AO for trend confirmation
   int               take;                     //--- Set take-profit in points
   int               stop;                     //--- Set stop-loss in points
   double            lots;                     //--- Specify trade lot size
   int               slip;                     //--- Set maximum slippage
   int               max_spread;               //--- Define maximum allowed spread
   int               magic;                    //--- Set magic number for trades
   bool              trade_anytime;            //--- Allow trading at any time
   string            comment;                  //--- Store trade comment
   int               tester_max_balance;       //--- Set tester balance limit
   bool              debug_mode;               //--- Enable debug mode
};

//+------------------------------------------------------------------+
//| Symbol Data Structure                                            | //--- Define a structure for symbol-specific data
//+------------------------------------------------------------------+
struct SymbolData {
   string            name;                     //--- Store symbol name
   datetime          last_bar_time;            //--- Track last bar timestamp
   int               cci_signal_handle;        //--- Hold CCI signal indicator handle
   int               cci_trend_handle;         //--- Hold CCI trend indicator handle
   int               ao_signal_handle;         //--- Hold AO signal indicator handle
   int               ao_trend_handle;          //--- Hold AO trend indicator handle
   double            cci_signal_data[];        //--- Store CCI signal data
   double            cci_trend_data[];         //--- Store CCI trend data
   double            ao_signal_data[];         //--- Store AO signal data
   double            ao_trend_data[];          //--- Store AO trend data
};

Wir beginnen mit der Implementierung der Multi-Symbol-Handelsstrategie CCI und AO in MQL5, indem wir die grundlegenden Komponenten für eine robuste Handelsausführung einrichten. Die Bibliothek „Trade.mqh“ ermöglicht Handelsoperationen über die Klasse „CTrade“, die Methoden zum Öffnen und Schließen von Positionen bereitstellt. Durch diese Einbeziehung wird sichergestellt, dass wir Zugang zu den wesentlichen Handelsfunktionen haben, die für die Ausführung von Kauf- und Verkaufsaufträgen über mehrere Symbole hinweg erforderlich sind. 

Als Nächstes erstellen wir die Struktur der „TradingParameters“, um alle Eingabeparameter für die Strategie zu organisieren. Diese Struktur enthält wichtige Variablen wie „symbols“ für die kommagetrennte Liste der Währungspaare, die Periodenlängen „per_signal_cci“ und „per_trend_cci“ für den CCI und „price_cci“ für den angewandten Preistyp. Wir definieren auch „cci_signal_buy_value“ und „cci_signal_sell_value“, um Signalschwellen festzulegen, „tf_signal“ und „tf_trend“ für Zeitrahmen und Risikomanagement-Variablen wie „take“, „stop“, „lots“ und „max_spread“. Darüber hinaus sorgt „magic“ für eine eindeutige Identifizierung des Handels, „trade_anytime“ steuert das Timing des Handels und „debug_mode“ ermöglicht die Diagnoseprotokollierung und bietet eine zentrale Konfiguration für die Strategie.

Schließlich richten wir die Struktur „SymbolData“ ein, um Daten für jedes Symbol im Handelssystem zu verwalten. Diese Struktur enthält „name“ für den Symbolidentifikator, „last_bar_time“, um den Zeitstempel des letzten Balkens zu verfolgen, und Handles wie „cci_signal_handle“ und „ao_trend_handle“ für CCI- und AO-Indikatoren sowohl für Signal- als auch für Trend-Zeitrahmen. Außerdem werden Arrays wie „cci_signal_data“ und „ao_trend_data“ zum Speichern von Indikatorwerten verwendet, was eine effiziente Datenverwaltung für die Verarbeitung mehrerer Symbole ermöglicht. Diese Strukturen bilden die Grundlage für ein modulares und skalierbares Handelssystem. Als Nächstes müssen wir eine Handelsklasse für die Steuerlogik deklarieren.

//+------------------------------------------------------------------+
//| Trading Strategy Class                                           | //--- Implement a class for trading strategy logic
//+------------------------------------------------------------------+
class CTradingStrategy {
private:
   CTrade            m_trade;                  //--- Initialize trade object for trading operations
   TradingParameters m_params;                 //--- Store trading parameters
   SymbolData        m_symbols[];              //--- Store array of symbol data
   int               m_array_size;             //--- Track number of symbols
   datetime          m_last_day;               //--- Store last day timestamp
   bool              m_is_new_day;             //--- Indicate new day detection
   int               m_candle_shift;           //--- Set candle shift for signal calculation
   const int         CCI_TREND_BUY_VALUE;      //--- Define constant for CCI trend buy threshold
   const int         CCI_TREND_SELL_VALUE;     //--- Define constant for CCI trend sell threshold
}

Hier implementieren wir die Kernlogik der Strategie, indem wir die Klasse „CTradingStrategy“ erstellen, die alle Handelsfunktionen in einer modularen und organisierten Weise kapselt. Wir definieren private Mitgliedsvariablen, um den Zustand und die Operationen der Strategie zu verwalten, beginnend mit „m_trade“, einer Instanz der Klasse „CTrade“ aus der Trade-Bibliothek, um Aufgaben der Handelsausführung wie das Öffnen und Schließen von Positionen zu handhaben. Als Nächstes fügen wir „m_params“ ein, eine Instanz der Struktur „TradingParameters“, um alle Konfigurationseinstellungen wie Symbollisten, Indikatorperioden und Risikoparameter zu speichern und einen zentralen Zugriff auf nutzerdefinierte Eingaben zu gewährleisten.

Wir deklarieren auch „m_symbols“, ein Array der Struktur „SymbolData“, um Daten für jedes Symbol zu speichern, einschließlich Indikator-Handles und Datenpuffer, was die Verarbeitung mehrerer Symbole erleichtert. Um die Anzahl der Symbole zu verfolgen, verwenden wir „m_array_size“, während „m_last_day“ und „m_is_new_day“ tägliche Zeitstempel-Updates zur Erkennung neuer Handelstage verwalten. Darüber hinaus legen wir „m_candle_shift“ fest, um zu steuern, ob Signale auf der Grundlage des „trade_anytime“-Parameters auf dem aktuellen oder dem vorherigen Balken erzeugt werden. Schließlich definieren wir die Konstanten „CCI_TREND_BUY_VALUE“ und „CCI_TREND_SELL_VALUE“, um feste CCI-Schwellenwerte für die Trendbestätigung festzulegen und eine konsistente Signallogik in der gesamten Strategie sicherzustellen. Wir können nun weitere Methoden unter dem privaten Zugriffsmodifikator wie unten für die Nützlichkeit aufnehmen.

void PrintDebug(string text) {              //--- Define method to print debug messages
   if(m_params.debug_mode && !MQLInfoInteger(MQL_OPTIMIZATION)) { //--- Check debug mode and optimization status
      Print(text);                          //--- Output debug message
   }
}

void PrintMessage(string text) {            //--- Define method to print informational messages
   if(!MQLInfoInteger(MQL_OPTIMIZATION)) {  //--- Check if not in optimization mode
      Print(text);                          //--- Output message
   }
}

void PrepareSymbolsList() {                 //--- Define method to prepare symbol list
   string symbols_array[];                  //--- Initialize temporary array for symbols
   ushort sep = StringGetCharacter(",", 0); //--- Get comma separator character
   m_array_size = StringSplit(m_params.symbols, sep, symbols_array); //--- Split symbols string into array
   ArrayResize(m_symbols, m_array_size);    //--- Resize symbol data array
   for(int i = 0; i < m_array_size; i++) {  //--- Iterate through symbols
      m_symbols[i].name = symbols_array[i]; //--- Set symbol name
      m_symbols[i].last_bar_time = 0;       //--- Initialize last bar time
      SymbolSelect(m_symbols[i].name, true); //--- Ensure symbol is in market watch
   }
}

Wir implementieren Hilfsmethoden innerhalb der Klasse „CTradingStrategy“, um Debugging und Symbolvorbereitung für die Multi-Symbol CCI- und AO-Strategie zu handhaben. Wir erstellen die Funktion „PrintDebug“, um Diagnosemeldungen auszugeben, wenn „m_params.debug_mode“ aktiviert ist und sich der EA nicht im Optimierungsmodus befindet, und verwenden „Print“, um den Parameter „text“ für die Fehlersuche zu protokollieren. In ähnlicher Weise definieren wir die Funktion „PrintMessage“, um Informationsmeldungen zu protokollieren. Dabei stellen wir sicher, dass die Ausgabe nur außerhalb des Optimierungsmodus erfolgt, indem wir „MQLInfoInteger (MQL_OPTIMIZATION)“ prüfen und „Print“ für die Eingabe „text“ verwenden.

Außerdem entwickeln wir die Funktion „PrepareSymbolsList“, um das Symboldatenfeld zu initialisieren. Wir deklarieren ein temporäres „symbols_array“, um die aufgeteilten Symbole zu speichern, verwenden StringGetCharacter, um das Komma-Trennzeichen für „sep“ zu erhalten, und wenden StringSplit an, um „m_params.symbols“ in „symbols_array“ zu parsen. Wir ändern die Größe von „m_symbols“ mit ArrayResize auf der Grundlage von „m_array_size“ und iterieren, um jedes „m_symbols[i].name“ auf ein Symbol zu setzen, initialisieren „m_symbols[i].last_bar_time“ auf Null und rufen SymbolSelect auf, um sicherzustellen, dass jedes Symbol in der Marktbeobachtung für den Handel verfügbar ist. Als Nächstes müssen wir die Indikatorwerte initialisieren und aktualisieren.

bool InitializeIndicators(int index) {      //--- Define method to initialize indicators
   m_symbols[index].cci_signal_handle = iCCI(m_symbols[index].name, m_params.tf_signal, m_params.per_signal_cci, m_params.price_cci); //--- Create CCI signal indicator
   if(m_symbols[index].cci_signal_handle == INVALID_HANDLE) { //--- Check for invalid handle
      Print("INITIALIZATION OF CCI SIGNAL FAILED: ", m_symbols[index].name); //--- Log error
      return false;                         //--- Return failure
   }
   m_symbols[index].cci_trend_handle = iCCI(m_symbols[index].name, m_params.tf_trend, m_params.per_trend_cci, m_params.price_cci); //--- Create CCI trend indicator
   if(m_symbols[index].cci_trend_handle == INVALID_HANDLE) { //--- Check for invalid handle
      Print("INITIALIZATION OF CCI TREND FAILED: ", m_symbols[index].name); //--- Log error
      return false;                         //--- Return failure
   }
   m_symbols[index].ao_signal_handle = iAO(m_symbols[index].name, m_params.tf_signal); //--- Create AO signal indicator
   if(m_symbols[index].ao_signal_handle == INVALID_HANDLE) { //--- Check for invalid handle
      Print("INITIALIZATION OF AO SIGNAL FAILED: ", m_symbols[index].name); //--- Log error
      return false;                         //--- Return failure
   }
   m_symbols[index].ao_trend_handle = iAO(m_symbols[index].name, m_params.tf_trend); //--- Create AO trend indicator
   if(m_symbols[index].ao_trend_handle == INVALID_HANDLE) { //--- Check for invalid handle
      Print("INITIALIZATION OF AO TREND FAILED: ", m_symbols[index].name); //--- Log error
      return false;                         //--- Return failure
   }
   ArraySetAsSeries(m_symbols[index].cci_signal_data, true); //--- Set CCI signal data as series
   ArraySetAsSeries(m_symbols[index].cci_trend_data, true); //--- Set CCI trend data as series
   ArraySetAsSeries(m_symbols[index].ao_signal_data, true); //--- Set AO signal data as series
   ArraySetAsSeries(m_symbols[index].ao_trend_data, true); //--- Set AO trend data as series
   return true;                             //--- Return success
}
   
bool UpdateIndicatorData(int index) {       //--- Define method to update indicator data
   if(CopyBuffer(m_symbols[index].cci_signal_handle, 0, 0, 3, m_symbols[index].cci_signal_data) < 3) { //--- Copy CCI signal data
      Print("UNABLE TO COPY CCI SIGNAL DATA: ", m_symbols[index].name); //--- Log error
      return false;                         //--- Return failure
   }
   if(CopyBuffer(m_symbols[index].cci_trend_handle, 0, 0, 3, m_symbols[index].cci_trend_data) < 3) { //--- Copy CCI trend data
      Print("UNABLE TO COPY CCI TREND DATA: ", m_symbols[index].name); //--- Log error
      return false;                         //--- Return failure
   }
   if(CopyBuffer(m_symbols[index].ao_signal_handle, 0, 0, 3, m_symbols[index].ao_signal_data) < 3) { //--- Copy AO signal data
      Print("UNABLE TO COPY AO SIGNAL DATA: ", m_symbols[index].name); //--- Log error
      return false;                         //--- Return failure
   }
   if(CopyBuffer(m_symbols[index].ao_trend_handle, 0, 0, 3, m_symbols[index].ao_trend_data) < 3) { //--- Copy AO trend data
      Print("UNABLE TO COPY AO TREND DATA: ", m_symbols[index].name); //--- Log error
      return false;                         //--- Return failure
   }
   return true;                             //--- Return success
}

Hier implementieren wir die Einrichtung und Aktualisierung von Indikatoren in der Klasse „CTradingStrategy“. Wir erstellen die Funktion „InitializeIndicators“, um „m_symbols[index].cci_signal_handle“ und „m_symbols[index].cci_trend_handle“ mit der Funktion iCCI zu setzen, und „m_symbols[index].m_symbols[index].ao_signal_handle“ und „m_symbols[index].ao_trend_handle“ unter Verwendung der Funktion iAO für ein Symbol an „index“, wobei Fehler mit „Print“ protokolliert werden, wenn die Handles INVALID_HANDLE sind. Wir konfigurieren Datenarrays als Zeitreihen mit ArraySetAsSeries.

Wir entwickeln auch die Funktion „UpdateIndicatorData“, um drei Datenpunkte in „m_symbols[index].cci_signal_data“, „m_symbols[index].cci_trend_data“, „m_symbols[index].m_symbols[index].ao_signal_data“ und „m_symbols[index].ao_trend_data“ mit der Funktion CopyBuffer, wobei Fehler mit „Print“ protokolliert werden, wenn nicht genügend Daten abgerufen werden. Als Nächstes müssen wir eine Zählung der Aufträge durchführen, damit wir den Schwellenwert der Position verfolgen können.

int CountOrders(string symbol, int magic, ENUM_POSITION_TYPE type) { //--- Define method to count orders
   int count = 0;                           //--- Initialize order counter
   for(int i = PositionsTotal() - 1; i >= 0; i--) { //--- Iterate through positions
      ulong ticket = PositionGetTicket(i);   //--- Get position ticket
      if(PositionSelectByTicket(ticket)) {   //--- Select position by ticket
         if(PositionGetInteger(POSITION_MAGIC) == magic && //--- Check magic number
            PositionGetString(POSITION_SYMBOL) == symbol && //--- Check symbol
            PositionGetInteger(POSITION_TYPE) == type) { //--- Check position type
            count++;                        //--- Increment counter
         }
      }
   }
   return count;                            //--- Return order count
}

long OpenOrder(string symbol, ENUM_ORDER_TYPE type, double price, double sl, double tp, double lots, int magic, string comment) { //--- Define method to open orders
   long ticket = m_trade.PositionOpen(symbol, type, lots, price, sl, tp, comment); //--- Execute position open
   if(ticket < 0) {                         //--- Check for order failure
      PrintMessage(StringFormat("Info - OrderSend %s %d_%s_%.5f error %.5f_%.5f_%.5f_#%d", //--- Log error
         comment, type, symbol, price, price, sl, tp, GetLastError()));
   } else {                                 //--- Handle successful order
      PrintMessage(StringFormat("Info - OrderSend done. Comment:%s, Type:%d, Sym:%s, Price:%.5f, SL:%.5f, TP:%.5f", //--- Log success
         comment, type, symbol, price, sl, tp));
   }
   return ticket;                           //--- Return order ticket
}

Hier implementieren wir Handelsmanagementfunktionen innerhalb der Klasse „CTradingStrategy“, um die Zählung und Ausführung der Aufträge zu handhaben. Wir erstellen die Funktion „CountOrders“, um die offenen Positionen für ein bestimmtes „Symbol“ und eine „magische“ Zahl vom Typ ENUM_POSITION_TYPE zu zählen. Wir initialisieren „count“ auf Null, iterieren durch die Positionen mit „PositionsTotal“ und PositionGetTicket und verwenden PositionSelectByTicket, um zu prüfen, ob „PositionGetInteger(POSITION_MAGIC)“, „PositionGetString(POSITION_SYMBOL)“ und „PositionGetInteger(POSITION_TYPE)“ mit den Eingaben übereinstimmen, wobei „count“ bei Übereinstimmungen erhöht wird, bevor es zurückgegeben wird.

Wir entwickeln auch die Funktion „OpenOrder“ zur Ausführung von Handelsgeschäften für ein bestimmtes „Symbol“ und einen „Typ“ mit den Parametern „Preis“, „sl“ (Stop-Loss), „tp“ (Take-Profit), „Lots“, „Magic“ und „Comment“. Wir rufen „m_trade.PositionOpen“ auf, um die Position zu öffnen, und speichern das Ergebnis in „ticket“. Wir verwenden „PrintMessage“ mit StringFormat, um Fehler zu protokollieren, wenn „ticket“ negativ ist, einschließlich GetLastError, oder um Erfolgsdetails zu protokollieren, wenn der Auftrag erteilt wurde, und geben „ticket“ zur Nachverfolgung zurück. Damit können wir eine öffentliche Klasse definieren, von der aus wir die Mitglieder initialisieren können.

public:
   CTradingStrategy() : CCI_TREND_BUY_VALUE(-114), CCI_TREND_SELL_VALUE(134) { //--- Initialize constructor with constants
      m_last_day = 0;                          //--- Set initial last day
      m_is_new_day = true;                     //--- Set new day flag
      m_array_size = 0;                        //--- Set initial array size
   }
   
   bool Init() {                               //--- Define initialization method
      m_params.symbols = "EURUSDm,GBPUSDm,AUDUSDm"; //--- Set default symbols
      m_params.per_signal_cci = 20;            //--- Set CCI signal period
      m_params.per_trend_cci = 24;             //--- Set CCI trend period
      m_params.price_cci = PRICE_TYPICAL;      //--- Set CCI applied price
      m_params.cci_signal_buy_value = -90;     //--- Set CCI buy signal threshold
      m_params.cci_signal_sell_value = 130;    //--- Set CCI sell signal threshold
      m_params.tf_signal = PERIOD_M5;          //--- Set signal timeframe
      m_params.tf_trend = PERIOD_H1;           //--- Set trend timeframe
      m_params.use_ao_for_trend = false;       //--- Disable AO trend by default
      m_params.take = 200;                     //--- Set take-profit
      m_params.stop = 300;                     //--- Set stop-loss
      m_params.lots = 0.01;                    //--- Set lot size
      m_params.slip = 5;                       //--- Set slippage
      m_params.max_spread = 20;                //--- Set maximum spread
      m_params.magic = 123456789;              //--- Set magic number
      m_params.trade_anytime = false;          //--- Disable trade anytime
      m_params.comment = "EA_AO_BP";           //--- Set trade comment
      m_params.tester_max_balance = 0;         //--- Set tester balance limit
      m_params.debug_mode = false;             //--- Disable debug mode
      
      m_candle_shift = m_params.trade_anytime ? 0 : 1; //--- Set candle shift based on trade mode
      m_trade.SetExpertMagicNumber(m_params.magic); //--- Set magic number for trade object
      
      PrepareSymbolsList();                    //--- Prepare symbol list
      for(int i = 0; i < m_array_size; i++) {  //--- Iterate through symbols
         if(!InitializeIndicators(i)) {         //--- Initialize indicators
            return false;                      //--- Return failure on error
         }
      }
      PrintMessage("Current Spread on " + Symbol() + ": " + //--- Log current spread
         IntegerToString((int)SymbolInfoInteger(Symbol(), SYMBOL_SPREAD)));
      return true;                             //--- Return success
   }

Im Modifikator für den öffentlichen Zugang implementieren wir die Initialisierungslogik. Wir definieren den Konstruktor „CTradingStrategy“, um „CCI_TREND_BUY_VALUE“ und „CCI_TREND_SELL_VALUE“ zu initialisieren als Konstanten auf -114 bzw. 134 gesetzt und „m_last_day“ auf Null, „m_is_new_day“ auf true und „m_array_size“ auf Null für die anfängliche Statusverwaltung gesetzt.

Wir erstellen auch die Funktion „Init“, um die Parameter und Ressourcen der Strategie zu konfigurieren. Wir weisen den „m_params“-Mitgliedern Standardwerte zu, einschließlich „m_params.symbols“ für Währungspaare, „m_params.per_signal_cci“ und „m_params.per_trend_cci“ für CCI-Perioden, „m_params.price_cci“ als „PRICE_TYPICAL“, und Schwellenwerte wie „m_params.cci_signal_buy_value“ und „m_params.cci_signal_sell_value“.

Wir setzen „m_params.tf_signal“ auf PERIOD_M5, „m_params.tf_trend“ auf „PERIOD_H1“, und Risikoparameter wie „m_params.take“, „m_params.stop“ und „m_params.lots“. Wir konfigurieren „m_candle_shift“ auf der Grundlage von „m_params.trade_anytime“, rufen „m_trade.SetExpertMagicNumber“ mit „m_params.magic“ auf und rufen „PrepareSymbolsList“ auf, um Symbole einzurichten. Wir iterieren durch „m_array_size“, um „InitializeIndicators“ für jedes Symbol aufzurufen, wobei bei einem Fehlschlag „false“ zurückgegeben wird, und protokollieren die aktuelle Spanne mit „PrintMessage“, bevor wir bei Erfolg „true“ zurückgeben.

Nachdem wir alles initialisiert haben, können wir die Ereignisbehandlung OnTick immer noch innerhalb unserer Klasse definieren, sodass wir die Methode nur innerhalb des eigentlichen Ereignishandlers aufrufen können. Deshalb muss sie hier veröffentlicht werden. Sie können ihn auch als virtuell einstellen, aber für den Moment wollen wir es einfach halten.

void OnTick() {                             //--- Define tick handling method
   datetime new_day = iTime(Symbol(), PERIOD_D1, 0); //--- Get current day timestamp
   m_is_new_day = (m_last_day != new_day);  //--- Check for new day
   if(m_is_new_day) {                       //--- Handle new day
      m_last_day = new_day;                 //--- Update last day
   }
   
   for(int i = 0; i < m_array_size; i++) {  //--- Iterate through symbols
      bool is_new_bar = false;              //--- Initialize new bar flag
      bool buy_signal = false, sell_signal = false; //--- Initialize signal flags
      bool buy_trend = false, sell_trend = false; //--- Initialize trend flags
      
      datetime new_time = iTime(m_symbols[i].name, m_params.tf_signal, 0); //--- Get current bar time
      if(!m_params.trade_anytime && m_symbols[i].last_bar_time != new_time) { //--- Check for new bar
         is_new_bar = true;                 //--- Set new bar flag
         m_symbols[i].last_bar_time = new_time; //--- Update last bar time
      }
      
      if(!UpdateIndicatorData(i)) continue; //--- Update indicators, skip on failure
      
      double ask = SymbolInfoDouble(m_symbols[i].name, SYMBOL_ASK); //--- Get ask price
      double bid = SymbolInfoDouble(m_symbols[i].name, SYMBOL_BID); //--- Get bid price
      double point = SymbolInfoDouble(m_symbols[i].name, SYMBOL_POINT); //--- Get point value
      long spread = SymbolInfoInteger(Symbol(), SYMBOL_SPREAD); //--- Get current spread
      
      int total_orders = CountOrders(m_symbols[i].name, m_params.magic, POSITION_TYPE_BUY) + //--- Count buy orders
                        CountOrders(m_symbols[i].name, m_params.magic, POSITION_TYPE_SELL); //--- Count sell orders
      
      // Generate signals
      buy_signal = m_symbols[i].cci_signal_data[m_candle_shift+1] < m_params.cci_signal_buy_value && //--- Check CCI buy signal condition
                  m_symbols[i].cci_signal_data[m_candle_shift] > m_params.cci_signal_buy_value && //--- Confirm CCI buy signal
                  m_symbols[i].ao_signal_data[m_candle_shift+1] < m_symbols[i].ao_signal_data[m_candle_shift]; //--- Confirm AO buy signal
                  
      sell_signal = m_symbols[i].cci_signal_data[m_candle_shift+1] > m_params.cci_signal_sell_value && //--- Check CCI sell signal condition
                   m_symbols[i].cci_signal_data[m_candle_shift] < m_params.cci_signal_sell_value && //--- Confirm CCI sell signal
                   m_symbols[i].ao_signal_data[m_candle_shift+1] > m_symbols[i].ao_signal_data[m_candle_shift]; //--- Confirm AO sell signal
      
      buy_trend = m_symbols[i].cci_trend_data[m_candle_shift+1] < m_symbols[i].cci_signal_data[m_candle_shift] && //--- Check CCI trend buy condition
                 m_symbols[i].cci_trend_data[m_candle_shift] > CCI_TREND_BUY_VALUE && //--- Confirm CCI trend buy threshold
                 m_symbols[i].cci_trend_data[m_candle_shift] < CCI_TREND_SELL_VALUE && //--- Confirm CCI trend sell threshold
                 (!m_params.use_ao_for_trend || //--- Check AO trend condition
                  (m_symbols[i].ao_trend_data[m_candle_shift+1] < m_symbols[i].ao_trend_data[m_candle_shift])); //--- Confirm AO trend buy
                  
      sell_trend = m_symbols[i].cci_trend_data[m_candle_shift+1] > m_symbols[i].cci_signal_data[m_candle_shift] && //--- Check CCI trend sell condition
                  m_symbols[i].cci_trend_data[m_candle_shift] > CCI_TREND_BUY_VALUE && //--- Confirm CCI trend buy threshold
                  m_symbols[i].cci_trend_data[m_candle_shift] < CCI_TREND_SELL_VALUE && //--- Confirm CCI trend sell threshold
                  (!m_params.use_ao_for_trend || //--- Check AO trend condition
                   (m_symbols[i].ao_trend_data[m_candle_shift+1] > m_symbols[i].ao_trend_data[m_candle_shift])); //--- Confirm AO trend sell
      
      // Execute trades
      if(spread < m_params.max_spread && total_orders == 0 && //--- Check spread and open orders
         (m_params.trade_anytime || is_new_bar)) { //--- Check trade timing
         if(buy_signal && buy_trend) {         //--- Check buy conditions
            double sl = m_params.stop == 0 ? 0 : ask - m_params.stop * point; //--- Calculate stop-loss
            double tp = m_params.take == 0 ? 0 : ask + m_params.take * point; //--- Calculate take-profit
            OpenOrder(m_symbols[i].name, ORDER_TYPE_BUY, ask, sl, tp, m_params.lots, //--- Open buy order
                     m_params.magic, "Open BUY " + m_params.comment);
         }
         if(sell_signal && sell_trend) {       //--- Check sell conditions
            double sl = m_params.stop == 0 ? 0 : bid + m_params.stop * point; //--- Calculate stop-loss
            double tp = m_params.take == 0 ? 0 : bid - m_params.take * point; //--- Calculate take-profit
            OpenOrder(m_symbols[i].name, ORDER_TYPE_SELL, bid, sl, tp, m_params.lots, //--- Open sell order
                     m_params.magic, "Open SELL " + m_params.comment);
         }
      }
      
      // Debug output
      if((m_params.trade_anytime || is_new_bar) && (buy_signal || sell_signal)) { //--- Check debug conditions
         PrintDebug(StringFormat("Debug - IsNewBar: %b - candle_shift: %d - buy_signal: %b - " //--- Log debug information
                               "sell_signal: %b - buy_trend: %b - sell_trend: %b",
                               is_new_bar, m_candle_shift, buy_signal, sell_signal, 
                               buy_trend, sell_trend));
      }
   }
}

Hier implementieren wir die Handelslogik für die Multi-Symbol CCI- und AO-Strategie, indem wir die Funktion OnTick in der Klasse „CTradingStrategy“ erstellen. Wir rufen den Zeitstempel des aktuellen Tages mit iTime in „new_day“ ab und aktualisieren „m_is_new_day“ und „m_last_day“, um tägliche Änderungen zu verfolgen. Für jedes Symbol in „m_array_size“ werden die Flags „is_new_bar“, „buy_signal“, „sell_signal“, „buy_trend“ und „sell_trend“ initialisiert und „iTime“ verwendet, um neue Bars zu erkennen und „m_symbols[i].last_bar_time“, wenn „m_params.trade_anytime“ falsch ist.

Wir rufen „UpdateIndicatorData“ auf, um die Indikatoren zu aktualisieren, und holen „ask“, „bid“, „point“ und „spread“ mit SymbolInfoDouble und SymbolInfoInteger ab. Wir berechnen „total_orders“ mit „CountOrders“ für Kauf- und Verkaufspositionen. Wir setzen „buy_signal“, indem wir „m_symbols[i].cci_signal_data“ mit „m_params.cci_signal_buy_value“ und „m_symbols[i].ao_signal_data“ zur Bestätigung vergleichen, und „sell_signal“ mit „m_params.cci_signal_sell_value“. Wir ermitteln „buy_trend“ und „sell_trend“ anhand von „m_symbols[i].cci_trend_data“, „CCI_TREND_BUY_VALUE“, „CCI_TREND_SELL_VALUE“ und optional „m_symbols[i].ao_trend_data“.

Wenn „spread“ unter „m_params.max_spread“ liegt, „total_orders“ Null ist und der Handel erlaubt ist, berechnen wir „sl“ und „tp“ mit „m_params.stop“ und „m_params.take“ und rufen dann „OpenOrder“ für Kauf- oder Verkaufsgeschäfte auf. Wir protokollieren Signalzustände mit „PrintDebug“ und StringFormat, wenn das Debugging ausgelöst wird, um eine effektive Handelsüberwachung zu gewährleisten. Um die Positionen zu schließen, implementieren wir diese Funktion unten.

bool CloseAllTrades() {                     //--- Define method to close all trades
   for(int i = PositionsTotal() - 1; i >= 0; i--) { //--- Iterate through positions
      ulong ticket = PositionGetTicket(i);   //--- Get position ticket
      if(PositionSelectByTicket(ticket) &&   //--- Select position
         PositionGetInteger(POSITION_MAGIC) == m_params.magic) { //--- Check magic number
         ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); //--- Get position type
         if(type == POSITION_TYPE_BUY || type == POSITION_TYPE_SELL) { //--- Check position type
            m_trade.PositionClose(ticket);   //--- Close position
            PrintMessage("Position close " + IntegerToString(ticket)); //--- Log closure
         }
      }
   }
   for(int i = OrdersTotal() - 1; i >= 0; i--) { //--- Iterate through orders
      ulong ticket = OrderGetTicket(i);      //--- Get order ticket
      if(OrderSelect(ticket) && OrderGetInteger(ORDER_MAGIC) == m_params.magic) { //--- Select order and check magic
         ENUM_ORDER_TYPE type = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE); //--- Get order type
         if(type >= ORDER_TYPE_BUY_STOP && type <= ORDER_TYPE_SELL_LIMIT) { //--- Check order type
            m_trade.OrderDelete(ticket);   //--- Delete order
            PrintMessage("Order delete " + IntegerToString(ticket)); //--- Log deletion
         }
      }
   }
   return true;                             //--- Return success
}

Hier implementieren wir die Funktionalität zur Schließung von Handelsgeschäften für die Multi-Symbol CCI- und AO-Strategie, indem wir die Funktion „CloseAllTrades“ innerhalb der Klasse „CTradingStrategy“ erstellen. Wir iterieren durch alle offenen Positionen mit PositionsTotal und rufen das Ticket jeder Position mit PositionGetTicket in „ticket“ ab. Für jede durch „PositionSelectByTicket“ ausgewählte Position wird überprüft, ob „PositionGetInteger(POSITION_MAGIC)“ mit „m_params.magic“ übereinstimmt, und wenn „PositionGetInteger(POSITION_TYPE)“ „POSITION_TYPE_BUY“ oder „POSITION_TYPE_SELL“ ist, rufen wir „m_trade.PositionClose“ auf, um sie zu schließen und protokollieren die Aktion mit „PrintMessage“ und IntegerToString.

Außerdem durchlaufen wir mit OrdersTotal eine Schleife durch die ausstehenden Bestellungen und holen uns mit „OrderGetTicket“ in „ticket“ das Ticket für jede Bestellung. Wenn OrderSelect erfolgreich ist und „OrderGetInteger(ORDER_MAGIC)“ gleich „m_params.magic“ ist, prüfen wir, ob OrderGetInteger (ORDER_TYPE) zwischen „ORDER_TYPE_BUY_STOP“ und „ORDER_TYPE_SELL_LIMIT“ liegt, und verwenden dann „m_trade.OrderDelete“, um den Auftrag zu löschen und ihn mit „PrintMessage“ und „IntegerToString“ zu protokollieren. Wir geben „true“ zurück, um die erfolgreiche Schließung aller relevanten Handelsgeschäfte und Aufträge anzuzeigen. Und das ist alles! Jetzt müssen wir nur noch die Klassen verwenden.

//+------------------------------------------------------------------+
//| Global Variables and Functions                                   | //--- Define global variables and functions
//+------------------------------------------------------------------+
CTradingStrategy g_strategy;                   //--- Initialize global strategy object

//+------------------------------------------------------------------+
//| Expert Advisor Functions                                         | //--- Define EA core functions
//+------------------------------------------------------------------+
int OnInit() {                                 //--- Define initialization function
   return g_strategy.Init() ? INIT_SUCCEEDED : INIT_FAILED; //--- Initialize strategy and return status
}

Wir schließen die Implementierung der Multi-Symbol CCI- und AO-Strategie ab, indem wir globale und Kernfunktionen des Expert Advisors definieren. Wir deklarieren „g_strategy“ als eine globale Instanz der Klasse „CTradingStrategy“, um alle Handelsoperationen über Symbole hinweg zu verwalten und einen zentralen Zugriff auf die Logik und den Status der Strategie während des gesamten Lebenszyklus des EA zu gewährleisten.

Wir erstellen auch die Funktion „OnInit“, um die Initialisierung des EAs zu handhaben. Wir rufen die Funktion „Init“ von „g_strategy“ auf, um Parameter, Symbole und Indikatoren einzurichten, und geben im Erfolgsfall INIT_SUCCEEDED oder im Fehlerfall „INIT_FAILED“ zurück, um den korrekten Initialisierungsstatus für die MetaTrader 5-Plattform sicherzustellen. Bei der Initialisierung haben wir folgendes Ergebnis.

INIT DES EA

Aus dem Bild geht hervor, dass wir alle Daten für die 3 ausgewählten Symbole haben. Jetzt können wir die Ereignisbehandlung von OnTick aufrufen, um die schwere Arbeit zu erledigen, und das war's dann auch schon.

//+------------------------------------------------------------------+
//| Expert Advisor Functions                                         | //--- Define EA core functions
//+------------------------------------------------------------------+
int OnInit() {                                                         //--- Define initialization function
   return g_strategy.Init() ? INIT_SUCCEEDED : INIT_FAILED;            //--- Initialize strategy and return status
}

//+------------------------------------------------------------------+

Wir rufen einfach die Funktion „Init“ des globalen Objekts „g_strategy“, einer Instanz der Klasse „CTradingStrategy“, auf, um Handelsparameter, Symbollisten und Indikatoren zu initialisieren. Wir geben INIT_SUCCEEDED zurück, wenn die Initialisierung erfolgreich war, oder „INIT_FAILED“, wenn ein Fehler aufgetreten ist. Damit wird sichergestellt, dass die MetaTrader 5-Plattform den entsprechenden Status erhält, um die Ausführung fortzusetzen oder anzuhalten. Nach der Kompilierung erhalten wir folgendes Ergebnis.

ENDERGEBNIS DES HANDELS

Aus dem Bild ist ersichtlich, dass wir in der Lage sind, auf der Grundlage bestätigter Signale für die jeweiligen Symbole Handelsgeschäfte zu eröffnen und diese individuell zu verwalten. Bleiben nur noch die Backtests des Programms, und das wird im nächsten Abschnitt behandelt.


Backtest und Optimierung

Nach einem gründlichen Backtest haben wir die folgenden Ergebnisse.

Backtest-Grafik:

GRAFIK

Bericht des Backtest:

BERICHT


Schlussfolgerung

Abschließend haben wir ein MQL5-Programm entwickelt, das die Multi-Symbol-Strategie CCI und AO automatisiert und Handelsgeschäfte über mehrere Währungspaare unter Verwendung der Klasse „CTradingStrategy“ für die Signalerzeugung mit CCI- und AO-Indikatoren und ein robustes Handelsmanagement über die Bibliothek „CTrade“ ausführt. Mit modularen Komponenten sowie Risikokontrollen wie Spread-Validierung und Stop-Loss-Einstellungen bietet dieses System einen skalierbaren Rahmen, den Sie durch die Optimierung von Parametern oder die Integration zusätzlicher Filter anpassen können.

Haftungsausschluss: Dieser Artikel ist nur für Bildungszwecke gedacht. Der Handel ist mit erheblichen finanziellen Risiken verbunden, und die Volatilität der Märkte kann zu Verlusten führen. Gründliche Backtests und sorgfältiges Risikomanagement sind entscheidend, bevor Sie dieses Programm auf den Live-Märkten einsetzen.

Indem Sie die vorgestellten Fähigkeiten und Konzepte nutzen, können Sie dieses Multi-Symbol-Handelssystem verfeinern oder seine Architektur anpassen, um neue Strategien zu erstellen und so Ihr Know-how im algorithmischen MQL5-Handel zu erweitern.

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

Beigefügte Dateien |
MQL5-Assistenz-Techniken, die Sie kennen sollten (Teil 69): Verwendung der Muster von SAR und RVI MQL5-Assistenz-Techniken, die Sie kennen sollten (Teil 69): Verwendung der Muster von SAR und RVI
Der Parabolic-SAR (SAR) und der Relative Vigour Index (RVI) sind ein weiteres Paar von Indikatoren, die in Verbindung mit einem MQL5 Expert Advisor verwendet werden können. Auch dieses Indikatorpaar ist, wie die anderen, die wir in der Vergangenheit behandelt haben, komplementär, da der SAR den Trend definiert, während der RVI das Momentum überprüft. Wie üblich verwenden wir den MQL5-Assistenten, um das Potenzial dieser Indikatorenkombination zu ermitteln und zu testen.
Entwicklung des Price Action Analysis Toolkit (Teil 28): Werkzeug für den Ausbruch aus der Eröffnungsspanne Entwicklung des Price Action Analysis Toolkit (Teil 28): Werkzeug für den Ausbruch aus der Eröffnungsspanne
Zu Beginn einer jeden Handelssitzung wird die Tendenz des Marktes oft erst deutlich, wenn die Kurse sich jenseits der Eröffnungsspanne bewegen. In diesem Artikel zeigen wir Ihnen, wie Sie einen MQL5 Expert Advisor erstellen, der automatisch Ausbrüche aus der Eröffnungsspanne erkennt und analysiert und Ihnen zeitnahe, datengestützte Signale für sichere Intraday-Einstiege liefert.
Entwicklung des Price Action Analysis Toolkit (Teil 30): Commodity Channel Index (CCI), Zero Line EA Entwicklung des Price Action Analysis Toolkit (Teil 30): Commodity Channel Index (CCI), Zero Line EA
Die Automatisierung der Preisaktionsanalyse ist der Weg in die Zukunft. In diesem Artikel verwenden wir den Dual CCI-Indikator, die Nulllinien-Kreuzungsstrategie, den EMA und die Kursentwicklung, um ein Tool zu entwickeln, das Handelssignale generiert und Stop-Loss- (SL) und Take-Profit-Levels (TP) unter Verwendung der ATR festlegt. Bitte lesen Sie diesen Artikel, um zu erfahren, wie wir bei der Entwicklung des „CCI Zero Line EA“ vorgehen.
MetaTrader 5 Machine Learning Blueprint (Teil 1): Datenlecks und Zeitstempelfehler MetaTrader 5 Machine Learning Blueprint (Teil 1): Datenlecks und Zeitstempelfehler
Bevor wir überhaupt damit beginnen können, ML für unseren Handel auf dem MetaTrader 5 zu nutzen, müssen wir uns mit einem der am meisten übersehenen Fallstricke befassen - dem Datenleck. In diesem Artikel wird erläutert, wie Datenlecks, insbesondere die Falle von MetaTrader 5-Zeitstempel, die Leistung unseres Modells verzerren und zu unzuverlässigen Handelssignalen führen können. Indem wir uns mit den Mechanismen dieses Problems befassen und Strategien zu seiner Vermeidung vorstellen, ebnen wir den Weg für den Aufbau robuster Modelle für maschinelles Lernen, die zuverlässige Vorhersagen in Live-Handelsumgebungen liefern.