English Русский 中文 Deutsch 日本語
preview
Automatização de estratégias de negociação em MQL5 (Parte 20): estratégia multissímbolo usando CCI e AO

Automatização de estratégias de negociação em MQL5 (Parte 20): estratégia multissímbolo usando CCI e AO

MetaTrader 5Negociação |
29 0
Allan Munene Mutiiria
Allan Munene Mutiiria

Introdução

No artigo anterior (Parte 19), analisamos uma estratégia de scalping em pullback baseada no indicador Envelopes, mais especificamente a execução de operações e a gestão de risco, concluindo sua automatização em MQL5. Na parte 20, apresentaremos uma estratégia de negociação multissímbolo que usa o Commodity Channel Index (CCI) e o Awesome Oscillator (AO) para detectar reversões de tendência em vários pares de moedas. Abordaremos os seguintes tópicos:

  1. Roteiro estratégico e arquitetura
  2. Implementação com MQL5
  3. Teste em dados históricos e otimização
  4. Conclusão

Ao final, você terá um sistema de negociação robusto em MQL5 para negociação multissímbolo, pronto para otimização e implantação.


Roteiro estratégico e arquitetura

Na Parte 19, criamos estratégias de scalping em correção baseadas em envelopes (Envelopes Trend Bounce Scalping Strategy), com foco específico na execução de operações e na gestão de risco para detectar sinais de negociação usando a interação do preço com as bandas do indicador Envelopes, confirmada por filtros de tendência. Agora, na parte 20, passamos para uma estratégia de negociação multissímbolo que usa o Índice de Canal de Commodities (Commodity Channel Index, CCI) e o Awesome Oscillator (AO) para identificar reversões de tendência em vários pares de moedas em dois timeframes (M5 para sinais e H1 para confirmação da tendência). O plano do artigo é desenvolver um sistema escalável que processe sinais com eficiência, execute operações e gerencie riscos em vários símbolos.

A arquitetura prioriza modularidade e confiabilidade, usando uma estrutura de classes em MQL5 para estruturar os componentes da estratégia. Nosso objetivo é criar uma classe geral responsável por calcular os indicadores CCI e AO, gerar sinais com base em valores-limite predefinidos e executar operações com configurações de stop loss e take profit, garantindo também que não haja ordens abertas para o símbolo em questão antes do envio de novas ordens. O projeto inclui mecanismos de proteção, como a verificação do spread, e oferece suporte à negociação em novas barras ou com dados tick a tick, proporcionando flexibilidade para diferentes condições de mercado e resultando, por fim, em um sistema de negociação multissímbolo coeso. Abaixo, o esquema mostra o que pretendemos alcançar.

PLANO DA ESTRATÉGIA


Implementação com MQL5

Para criar o programa em MQL5, abra o MetaEditor, acesse o "Navegador", selecione a pasta Indicators, clique em "Criar" e siga as instruções para criar o arquivo. Depois de criar o arquivo no ambiente de desenvolvimento, começaremos declarando algumas estruturas e classes que usaremos, pois queremos aplicar uma abordagem orientada a objetos (Object Oriented Programming, OOP).

//+------------------------------------------------------------------+
//|                                          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
};

Começaremos a implementação da estratégia de negociação multissímbolo baseada em CCI e AO em MQL5 configurando os componentes básicos para uma execução confiável de operações. Adicionaremos a biblioteca Trade.mqh para viabilizar operações de negociação por meio da classe CTrade, que fornece métodos para abrir e fechar posições. Essa inclusão garante acesso às principais funções de negociação necessárias para executar ordens de compra e venda para vários símbolos. 

Em seguida, criamos a estrutura TradingParameters para agrupar todos os parâmetros de entrada da estratégia. Essa estrutura contém variáveis importantes, como symbols para a lista de pares de moedas separados por vírgulas, per_signal_cci e per_trend_cci para os períodos do indicador CCI, além de price_cci para o tipo de preço aplicado. Também definimos as variáveis cci_signal_buy_value e cci_signal_sell_value para configurar os valores-limite dos sinais, tf_signal e tf_trend para os timeframes, além de variáveis de gestão de risco, como take, stop, lots e max_spread. Além disso, magic permite identificar exclusivamente as operações, trade_anytime controla o momento de sua execução, e debug_mode habilita o log diagnóstico, mantendo a configuração da estratégia centralizada.

Por fim, criamos a estrutura SymbolData para gerenciar os dados de cada símbolo no sistema de negociação. Essa estrutura contém o campo name para o identificador do símbolo, o campo last_bar_time para acompanhar o timestamp da última barra, além de identificadores, ou handles, como cci_signal_handle e ao_trend_handle para os indicadores CCI e AO tanto nos timeframes de sinal quanto nos de tendência. Também incluímos arrays como cci_signal_data e ao_trend_data para armazenar os valores dos indicadores, o que permite uma gestão eficiente dos dados ao processar múltiplos símbolos. Essas estruturas estabelecem a base para um sistema de negociação modular e escalável. Na próxima etapa, precisamos declarar a classe de negociação para a lógica de controle.

//+------------------------------------------------------------------+
//| 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
}

Aqui, implementamos a lógica principal da estratégia criando a classe CTradingStrategy, que reúne toda a funcionalidade de negociação de maneira modular e bem estruturada. Definimos variáveis-membro privadas para gerenciar o estado e as operações da estratégia, começando por m_trade, uma instância da classe CTrade da biblioteca Trade, responsável por tratar tarefas de execução, como abertura e fechamento de posições, como abertura e fechamento de posições. Em seguida, adicionamos m_params, uma instância da estrutura TradingParameters, para armazenar todos os parâmetros de configuração, como listas de símbolos, períodos dos indicadores e parâmetros de risco, garantindo acesso centralizado aos dados de entrada definidos pelo usuário.

Também declaramos m_symbols, um array da estrutura SymbolData, para armazenar os dados de cada símbolo, incluindo identificadores (handles) dos indicadores e buffers de dados, o que facilita o processamento de múltiplos símbolos. Para acompanhar a quantidade de símbolos, usamos m_array_size, enquanto os parâmetros m_last_day e m_is_new_day são usados para controlar a atualização diária dos timestamps para determinar novos dias de negociação. Além disso, definimos o parâmetro m_candle_shift, que determina se os sinais são gerados na barra atual ou na barra anterior, dependendo do parâmetro trade_anytime. Por fim, definimos as constantes CCI_TREND_BUY_VALUE e CCI_TREND_SELL_VALUE para configurar valores-limite fixos do CCI para confirmação da tendência, garantindo uma lógica de sinais consistente em toda a estratégia. Agora, com o modificador de acesso privado, podemos adicionar outros métodos, como mostrado abaixo, para ampliar sua utilidade.

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
   }
}

A classe CTradingStrategy implementa métodos auxiliares para depuração e preparação dos símbolos para a estratégia multissímbolo baseada em CCI e AO. Criaremos a função PrintDebug para exibir mensagens diagnósticas quando m_params.debug_mode estiver habilitado e o EA não estiver em modo de otimização, usando Print para registrar o parâmetro text durante a depuração. De forma semelhante, definiremos a função PrintMessage para registrar mensagens informativas, garantindo a exibição das informações apenas fora do modo de otimização, após verificar MQLInfoInteger(MQL_OPTIMIZATION) e do uso de Print para o parâmetro de entrada text.

Introduziremos a função PrepareSymbolsList para inicializar o array de dados dos símbolos. Declararemos o array temporário symbols_array para armazenar os símbolos resultantes da separação, usaremos StringGetCharacter para obter a vírgula como separador em sep e aplicaremos StringSplit para converter m_params.symbols em symbols_array. Redimensionamos m_symbols usando ArrayResize com base em m_array_size e iteramos para atribuir o símbolo correspondente a cada m_symbolsi].name, inicializamos m_symbols[i].last_bar_time com zero e chamamos [SymbolSelect para garantir que cada símbolo esteja selecionado na Observação do Mercado para a execução de operações. Em seguida, precisamos inicializar e atualizar os valores dos indicadores.

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
}

Aqui, implementamos a configuração e a atualização dos indicadores na classe CTradingStrategy. Criaremos a função InitializeIndicators para definir m_symbolsindex].cci_signal_handle e m_symbols[index].cci_trend_handle usando a função [iCCI, bem como m_symbolsindex].ao_signal_handle e m_symbols[index].ao_trend_handle usando a função [iAO para o símbolo no índice index, exibindo erros com Print se os handles forem INVALID_HANDLE. Configuraremos os arrays de dados como séries temporais com ArraySetAsSeries.

Introduziremos a função UpdateIndicatorData para copiar três pontos de dados para m_symbolsindex].cci_signal_data, m_symbols[index].cci_trend_data, m_symbols[index].ao_signal_data e m_symbols[index].ao_trend_data usando a função [CopyBuffer, exibindo erros com Print se forem recebidos dados insuficientes. Em seguida, precisamos contar o número de ordens para acompanhar o limite de posições.

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
}

Aqui, implementamos as funções de gerenciamento de operações dentro da classe CTradingStrategy para tratar a contagem e a execução de ordens. Criaremos a função CountOrders para contar posições abertas de acordo com os valores informados de symbol, magic e o tipo ENUM_POSITION_TYPE. Inicializamos count com zero, percorremos as posições usando PositionsTotal e PositionGetTicket, e também usamos PositionSelectByTicket para garantir que PositionGetInteger(POSITION_MAGIC), PositionGetString(POSITION_SYMBOL) e PositionGetInteger(POSITION_TYPE) correspondam aos valores de entrada, incrementando count quando houver correspondência antes de retornar o resultado.

Introduzimos a função OpenOrder para executar operações com os valores especificados de symbol e type, com os parâmetros price, sl (stop loss), tp (take profit), lots, magic e comment. Chamaremos m_trade.PositionOpen para abrir a posição, armazenaremos o resultado em ticket e usaremos PrintMessage com StringFormat para exibir erros se ticket for negativo, incluindo GetLastError, ou uma mensagem de colocação bem-sucedida da ordem com o valor de ticket para acompanhamento. Depois disso, podemos definir a seção pública da classe, onde inicializaremos os membros.

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
   }

Implementamos a lógica de inicialização na seção pública da classe. Definiremos o construtor CTradingStrategy para inicializar CCI_TREND_BUY_VALUE e CCI_TREND_SELL_VALUE com os valores constantes -114 e 134, respectivamente, respectivamente; definiremos m_last_day como zero, m_is_new_day como true e m_array_size como zero para gerenciar o estado inicial.

Também criamos a função Init para configurar os parâmetros e recursos da estratégia. Atribuímos valores padrão aos membros de m_params, incluindo m_params.symbols para os pares de moedas, m_params.per_signal_cci e m_params.per_trend_cci para os períodos do CCI, m_params.price_cci como PRICE_TYPICAL, além de valores-limite, como m_params.cci_signal_buy_value e m_params.cci_signal_sell_value.

Definimos m_params.tf_signal como PERIOD_M5, m_params.tf_trend como PERIOD_H1, além de parâmetros de risco, como m_params.take, m_params.stop e m_params.lots. Configuramos m_candle_shift com base em m_params.trade_anytime, chamamos o método m_trade.SetExpertMagicNumber com o parâmetro m_params.magic e usamos o método PrepareSymbolsList para configurar os símbolos. Percorremos os índices até m_array_size para chamar InitializeIndicators para cada símbolo para chamar InitializeIndicators para cada símbolo, retornando false em caso de falha, e registramos o spread atual com a função PrintMessage antes de retornar true quando tudo for concluído com sucesso.

Por fim, após inicializar tudo o que é necessário, podemos definir o manipulador de eventos OnTick, ainda dentro da nossa classe, para que o método seja chamado apenas dentro do próprio manipulador de eventos. Portanto, aqui ele deve ser público. Também podemos defini-lo como virtual, mas, por enquanto, manteremos uma abordagem simples.

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));
      }
   }
}

Aqui, implementamos a lógica de negociação da estratégia multissímbolo baseada em CCI e AO, criando a função OnTick na classe CTradingStrategy. Obtemos o timestamp do dia atual com iTime em new_day e atualizamos m_is_new_day e m_last_day para acompanhar as mudanças diárias. Para cada símbolo do array m_symbols, inicializamos os sinalizadores is_new_bar, buy_signal, sell_signal, buy_trend e sell_trend is_new_bar, buy_signal, sell_signal, buy_trend e sell_trend, e também usamos iTime para detectar novas barras, atualizando m_symbols[i].last_bar_time se m_params.trade_anytime for false.

Chamamos UpdateIndicatorData para atualizar os indicadores, prosseguindo para o próximo símbolo em caso de falha, e obtemos os valores de ask, bid, point e spread com SymbolInfoDouble e SymbolInfoInteger. Calculamos total_orders usando CountOrders para posições de compra e venda. Definimos buy_signal comparando m_symbols[i].cci_signal_data com m_params.cci_signal_buy_value e usando m_symbols[i].ao_signal_data para confirmação; já sell_signal é definido pela comparação com m_params.cci_signal_sell_value. Determinamos buy_trend e sell_trend usando m_symbols[i].cci_trend_data, CCI_TREND_BUY_VALUE, CCI_TREND_SELL_VALUE e, se necessário, m_symbols[i].ao_trend_data.

Se spread for menor que m_params.max_spread, total_orders for igual a zero e a negociação estiver liberada, calculamos sl e tp com base em m_params.stop e m_params.take e, em seguida, chamamos a função OpenOrder para executar operações de compra ou venda. Registramos os estados dos sinais com a função PrintDebug e StringFormat quando a depuração está habilitada, o que permite monitorar a negociação com eficiência. Para fechar posições, implementamos a função a seguir.

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
}

Aqui, implementamos a lógica de fechamento de operações para a estratégia multissímbolo baseada em CCI e AO, criando a função CloseAllTrades dentro da classe CTradingStrategy. Percorremos todas as posições abertas com PositionsTotal e obtemos o ticket de cada posição com PositionGetTicket em ticket. Para cada posição selecionada com PositionSelectByTicket, verificamos se PositionGetInteger(POSITION_MAGIC) corresponde a m_params.magic. Se PositionGetInteger(POSITION_TYPE) for igual a POSITION_TYPE_BUY ou POSITION_TYPE_SELL, chamamos m_trade.PositionClose para fechar a posição e registramos a ação com PrintMessage e IntegerToString.

Percorremos as ordens pendentes com OrdersTotal, obtendo o ticket de cada ordem com OrderGetTicket em ticket. Se OrderSelect for Se OrderSelect selecionar a ordem com sucesso e OrderGetInteger(ORDER_MAGIC) for igual a m_params.magic, verificamos se OrderGetInteger(ORDER_TYPE) está entre ORDER_TYPE_BUY_STOP e ORDER_TYPE_SELL_LIMIT; em seguida, usamos m_trade.OrderDelete para excluir a ordem e registramos isso com PrintMessage e IntegerToString. Retornamos true, indicando o fechamento bem-sucedido de todas as operações e ordens correspondentes. É isso! Agora só precisamos usar essas classes.

//+------------------------------------------------------------------+
//| 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
}

Concluímos a implementação da estratégia multissímbolo baseada em CCI e AO definindo as funções globais e principais do EA. Declaramos g_strategy como uma instância global da classe CTradingStrategy para gerenciar todas as operações de negociação para diferentes símbolos, mantendo acesso centralizado à lógica e ao estado da estratégia durante todo o ciclo de vida do EA.

Também criamos a função OnInit para tratar a inicialização do EA. Chamamos a função Init da classe g_strategy para configurar parâmetros, símbolos e indicadores e retornamos INIT_SUCCEEDED em caso de sucesso ou INIT_FAILED em caso de erro, garantindo o status correto de inicialização para a plataforma MetaTrader 5. Na inicialização, obtemos o seguinte.

INICIALIZAÇÃO DO EA

Como podemos ver, temos todos os dados dos três símbolos selecionados. Agora podemos passar para o manipulador de eventos OnTick, que assumirá a lógica principal.

//+------------------------------------------------------------------+
//| 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
}

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

Para inicializar os parâmetros de negociação, as listas de símbolos e os indicadores, basta chamar a função Init do objeto global g_strategy, que é uma instância da classe CTradingStrategy. Retornamos INIT_SUCCEEDED se a inicialização for concluída com sucesso ou INIT_FAILED em caso de erro. Dessa forma, a plataforma MetaTrader 5 recebe o status apropriado para continuar ou interromper a execução do EA. Após a compilação, obtemos o seguinte.

RESULTADO FINAL DAS OPERAÇÕES

Na imagem, vemos que podemos abrir operações com base em sinais confirmados para os símbolos correspondentes e gerenciá-las individualmente. Resta apenas testar o programa em dados históricos.


Teste em dados históricos e otimização

Após um teste cuidadoso em dados históricos, obtivemos os seguintes resultados.

Gráfico do teste em dados históricos:

Gráfico

Relatório do teste em dados históricos:

RELATÓRIO


Conclusão

Desenvolvemos um programa em MQL5 que automatiza a estratégia multissímbolo com CCI e AO, executa operações em múltiplos pares de moedas com o uso da classe CTradingStrategy para gerar sinais com base nos indicadores CCI e AO e implementa uma gestão robusta de operações por meio da biblioteca CTrade. Graças aos componentes modulares e aos mecanismos de controle de risco, como a verificação do spread e a configuração do stop loss, esse sistema oferece uma estrutura escalável que pode ser ajustada alterando parâmetros ou integrando filtros adicionais ou da integração de filtros adicionais.

Aviso: O artigo destina-se exclusivamente a fins educacionais. A negociação envolve riscos financeiros significativos, e a volatilidade do mercado pode causar perdas. Testes cuidadosos em dados históricos e uma gestão de risco criteriosa são fundamentais antes de implantar este programa antes de implantar este programa no mercado real.

Usando as ideias apresentadas, você pode aprimorar este sistema de negociação multissímbolo ou adaptar sua arquitetura para criar novas estratégias, aprimorando seus conhecimentos em negociação algorítmica em MQL5 em negociação algorítmica em MQL5.

Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/18604

Arquivos anexados |
Caminhe em novos trilhos: Personalize indicadores no MQL5 Caminhe em novos trilhos: Personalize indicadores no MQL5
Vou agora listar todas as possibilidades novas e recursos do novo terminal e linguagem. Elas são várias, e algumas novidades valem a discussão em um artigo separado. Além disso, não há códigos aqui escritos com programação orientada ao objeto, é um tópico muito importante para ser simplesmente mencionado em um contexto como vantagens adicionais para os desenvolvedores. Neste artigo vamos considerar os indicadores, sua estrutura, desenho, tipos e seus detalhes de programação em comparação com o MQL4. Espero que este artigo seja útil tanto para desenvolvedores iniciantes quanto para experientes, talvez alguns deles encontrem algo novo.
Envio de mensagens de MQL5 para o Discord, criação de um bot Discord-MetaTrader 5 Envio de mensagens de MQL5 para o Discord, criação de um bot Discord-MetaTrader 5
Assim como o Telegram, o Discord é capaz de receber informações e mensagens em formato JSON usando suas APIs de comunicação. Neste artigo, veremos como usar a API do Discord para enviar sinais de trading e atualizações do MetaTrader 5 para sua comunidade de trading no Discord.
Está chegando o novo MetaTrader 5 e MQL5 Está chegando o novo MetaTrader 5 e MQL5
Esta é apenas uma breve resenha do MetaTrader 5. Eu não posso descrever todos os novos recursos do sistema por um período tão curto de tempo - os testes começaram em 09.09.2009. Esta é uma data simbólica, e tenho certeza que será um número de sorte. Alguns dias passaram-se desde que eu obtive a versão beta do terminal MetaTrader 5 e MQL5. Eu ainda não consegui testar todos os seus recursos, mas já estou impressionado.
Criação de classes Python para trading no MetaTrader 5, análogas às apresentadas em MQL5 Criação de classes Python para trading no MetaTrader 5, análogas às apresentadas em MQL5
O pacote Python MetaTrader 5 oferece uma maneira simples de criar aplicativos de trading para a plataforma MetaTrader 5 na linguagem Python. Embora seja um módulo poderoso e útil, ele não é tão simples quanto a linguagem de programação MQL5 quando se trata de desenvolver soluções para trading algorítmico. Neste artigo, criaremos classes para trading análogas às oferecidas pela linguagem MQL5, a fim de criar uma sintaxe semelhante e tornar o desenvolvimento de robôs de trading em Python tão simples quanto em MQL5.