Automatisieren von Handelsstrategien in MQL5 (Teil 20): Multi-Symbol-Strategie mit CCI und AO
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:
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.

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.

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.

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:

Bericht des Backtest:

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
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.
MQL5-Assistenz-Techniken, die Sie kennen sollten (Teil 69): Verwendung der Muster von SAR und RVI
Entwicklung des Price Action Analysis Toolkit (Teil 28): Werkzeug für den Ausbruch aus der Eröffnungsspanne
Entwicklung des Price Action Analysis Toolkit (Teil 30): Commodity Channel Index (CCI), Zero Line EA
MetaTrader 5 Machine Learning Blueprint (Teil 1): Datenlecks und Zeitstempelfehler
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.