Expert Advisor Universal: Integração com os Módulos de Sinais Padrão do MetaTrader (parte 7)

Vasiliy Sokolov | 12 setembro, 2016


Tabela de conteúdos


Introdução

Nos artigos anteriores, discutimos os mecanismos que tornam o processo de criação dos algoritmos de negociação eficientes e simples - em particular, criamos o algoritmo CStrategy. O projeto está sendo desenvolvido há mais de seis meses. E durante este tempo, novos módulos foram adicionados ao CStrategy tornando o processo de negociação mais eficiente e seguro em termos de execução técnica das operações de negociação. No entanto, ainda não se obteve uma característica importante no motor. Apesar do CStrategy ser uma aplicação estritamente orientada ao objeto, ele ainda mantém uma "característica própria". A abordagem orientada a objetos postula a abertura e a modularidade do código. Sendo assim, a base do código deve ser baseada em classes gerais comuns. Especialmente, quando se trata do modelo de negociação e do modelo de formação do sinal. A lógica da negociação CStrategy é baseada no módulo de negociação padrão CTrade, porém ocorrem alguns impasses com o banco de dados de sinais. Simplificando, o CStrategy não contém qualquer módulo responsável pela geração sinais de negociação. Com as versões anteriores, todos os usuários tinham que reescrever desde o princípio a lógica do Expert Advisor, mesmo com a disponibilidade do sinal necessário no pacote padrão do MetaTrader 5. Portanto, a versão de mecanismos de estratégia foi atualizada para que a mesma possa trabalhar com o banco de dados padrão dos sinais do MetaTrader 5 . Neste artigo, irei explicar como integrar o CStrategy com um dos módulos padrão de sinal, assim como demonstarei como criar a sua própria estratégia usando exclusivamente os algoritmos prontos.

 

Uma visão geral das classes empregadas pelo gerador de estratégias

Um conjunto de várias classes que estão incluídas no pacote MetaTrader 5 padrão são utilizados para a geração de estratégias automatizadas usando o Assistente MQL. As classes estão disponíveis como arquivos mqh, localizados em subpastas apropriadas do diretório MQL5\Include. Estas classes (ou módulos) podem ser convencionalmente divididas em várias categorias. Aqui estão elas.

  • Classes básicas para a organização de dados (CObject, CArrayDouble, CArrayObj e outros). Todos os outros módulos para negociação são construídos com base nessas classes.
  • Classes de acesso dos buffers do indicador (CDoubleBuffer, CIndicatorBuffer). Por conseguinte, eles são utilizados para trabalhar com indicadores.
  • Classes de indicadores e classes TimeSeries baseadas na classe comum CSeries .
  • A classe básica CBaseExpert e CExpert são derivadas dela. Todos os módulos auxiliares são baseados na CBaseExpert - por exemplo, o módulo de cálculo do capital e o módulo de controle do trailing stop. A CExpert é a base de todos os Expert Advisors customizados.
  • Módulos de sinal com base na CExpertSignal, que por sua vez se baseia na CErtBase. Os módulos de sinais geram negociações de sinais de Compra e Venda. Utilizam-se as classes dos indicadores, no qual se baseiam os sinais.
  • O módulo de negociação CExpertTrade. É baseado na classe CTrade e fornece acesso à execução de operações de negociação.

O diagrama abaixo mostra o esquema geral de herança vertical das classes usadas no processo de geração automática das estratégias:

 

Fig. 1. A herança de classes padrão do gerador de estratégias

A figura mostra apenas as classes básicas e algumas derivadas. O esquema não apresenta todos os indicadores herdados da CIndicators. Trailing separado, gerenciamento de dinheiro e gerenciamento de sinais não estão incluídos no esquema. Em vez disso, apenas as relações básicas são descritas. Um dos grupos existentes e interessantes para nós: as classes de sinal CExpertSignal e suas classes filhas. Na Figura 1, o grupo é realçada por uma linha tracejada verde.

Além das ligações verticais, as classes formam um complexo sistema de inclusão (ligações horizontais). Por exemplo, os módulos de sinal usam ativamente as classes do indicador que, por sua vez, utilizam buffers para indicadores. Vários conjuntos estão contidos em outro. Por exemplo, os módulos de gestão de dinheiro são, ao mesmo tempo experts de negociação (pelo menos ao nível da CExpertBase), porém estes módulos não têm nada a ver com o Expert Advisor.

Como regra geral, as cadeias de inicialização complexas são necessárias a fim de criar os objetos dessas classes. Por exemplo, para criar um sinal do objeto, tal como CSignalMacd, devemos inicializar o sinal e os indicadores adequados sobre os quais se baseiam, bem como esta inicialização necessita das séries temporais (classes filhas da CPriceSeries) para o seu funcionamento. Se um objeto pode ser inicializado por outros mais complexos, o mesmo procedimento deverá ser realizado (comoas timeseries). Assim, a questão de inicialização é uma das partes mais complicadas da biblioteca descrita.

Vamos analisar o seguinte exemplo. Suponha que temos de inicializar o módulo CMacdSignal dentro de sua classe no momento da criação do objeto correspondente desta classe. O módulo do código de sinal de inicialização seria o seguinte:

-//+-----------------------------------------------------------------+
//| Inicialização do módulo de sinal CSignalMacd                     |
-//+-----------------------------------------------------------------+
CStrategyMACD::CStrategyMACD(void)
{
   CSymbolInfo* info = new CSymbolInfo();                // Criando um objeto que representa o símbolo de negociação da estratégia
   info.Name(Symbol());                                  // Inicialização do objeto que representa o símbolo de negociação da estratégia
   m_signal_ma.Init(info, Period(), 10);                 // Inicialização do módulo de sinal pelo símbolo de negociação e timeframe
   m_signal_ma.InitIndicators(GetPointer(m_indicators)); // Criando oa indicadores requeridos no módulo de sinal com base na lista vazia de indicadores m_indicators
   m_signal_ma.EveryTick(true);                          // Modo de teste
   m_signal_ma.Magic(ExpertMagic());                     // Número mágico
   m_signal_ma.PatternsUsage(8);                         // Máscara padrão 
   m_open.Create(Symbol(), Period());                    // Inicialização das timeseries de preços Abertos
   m_high.Create(Symbol(), Period());                    // Inicialização das timeseries de preços de Alta
   m_low.Create(Symbol(), Period());                     // Inicialização das timeseries de preços de Baixa
   m_close.Create(Symbol(), Period());                   // Inicialização das timeseries de preços Fechados
   m_signal_ma.SetPriceSeries(GetPointer(m_open),        // Inicializaçao do módulo de sinais pelos objetos das timeseries
                              GetPointer(m_high),
                              GetPointer(m_low),
                              GetPointer(m_close));
}

Observe que os problemas de inicialização não são relevantes aos usuários do gerador de estratégia automatizada. Todas as cadeias de inicializações são criadas automaticamente no gerador de estratégia, tudo que o usuário precisa fazer é começar a usar o Expert Advisor. A situação é diferente para aqueles que irão utilizar este conjunto de classes para criar suas próprias soluções. Neste caso, será necessário realizar toda a cadeia de inicialização.

 

Uma visão geral do módulo de sinal, o conceito de um padrão

Como já foi mencionado, os módulos de sinal são baseados na classe comum CExpertSignal que, por sua vez, se baseia no CExpertBase. Cada módulo padrão de sinais é de fato uma classe com funções que buscam por um ou mais padrões - condições lógicas especiais que determinam o momento para comprar ou vender. Por exemplo, se a Média Móvel rápida atravessa uma lenta de cima para baixo, ela forma um padrão de compra. Cada indicador pode formar uma série de condições para entrar comprado e vendido. Um exemplo simples é o indicador MACD. Tanto a divergência do indicador, quanto um cruzamento simples da linha de sinal com o principal indicador do histograma podem ser interpretados como um sinal. Ambas condições de negociação representam diferentes padrões. É possível a entrada no mercado após esses eventos ocorrerem uma ou mais vezes. É importante notar que os padrões de compra e venda são diferentes e, geralmente, representam condições opostas. Do ponto de vista do gerador de estratégia automatizada, um sinal é um processador que possui um ou mais padrões quando há um indicador comum. Por exemplo, um sinal com base no MACD pode detectar a presença de vários padrões no mercado. O módulo padrão de sinais com base no MACD contém cinco padrões de compra e de venda:

  • Reversão - o oscilador retorna para cima (compra) ou para baixo (venda)
  • A intersecção das linhas principais e de sinal
  • Cruzando o nível zero
  • Divergência
  • Divergência Dupla

A descrição detalhada dos padrões está disponível nos arquivos de Ajuda nos terminais, por isso não será discutidos os detalhes. Outros módulos de sinal contém padrões diferentes e diferentes números deles. Normalmente, cada sinal engloba uma média de três padrões de compra e de venda. Um sinal pode envolver no máximo 32 padrões numa direção e 32 no oposto (este é o tamanho do campo do bit da variável integrativa que armazena a máscara dos padrões utilizados).

O sinal pode identificar padrões, assim como pode sugerir alguma recomendação como um inteiro. É pressuposto que este número mostre a força do sinal: quanto maior o número, maior será a intensidade do sinal. É possível ajustar a intensidade de sinal para cada um dos padrões usando o grupo especial de métodos Pattern_x(valor int), onde x é o índice do padrão. Por exemplo, será descrito o seguinte código de configuração do sinal:

-//+-----------------------------------------------------------------+
//| Inicialização do módulo de sinal CSignalMacd                     |
-//+-----------------------------------------------------------------+
CStrategyMACD::CStrategyMACD(void)
{
   m_signal_ma.Pattern_0(0);
   m_signal_ma.Pattern_1(0);
   m_signal_ma.Pattern_2(0);
   m_signal_ma.Pattern_3(100);
   m_signal_ma.Pattern_4(0);
   m_signal_ma.Pattern_5(0);
}

Neste caso, o módulo de sinal CSignalMacd só irá devolver um valor quando a Divergência é formada, ou seja, o padrão do indicador MACD, onde o primeiro vale analisado do oscilador é menor do que o anterior e o preço do vale correspondente é mais profundo do que o anterior (é uma definição para compra). Todos os outros padrões serão ignorados.

Recomendações da negociação são devolvidas por dois métodos independentes LongCondition e ShortCondition. O primeiro retorna uma recomendação de compra, o segundo - para venda. Vamos nos aprofundar num deles, a fim de entender como eles funcionam. Aqui está o LongCondition:

-//+-----------------------------------------------------------------+
//| Uma indicação de que o preço vai crescer.                        |
-//+-----------------------------------------------------------------+
int CSignalMACD::LongCondition(void)
  {
   int result=0;
   int idx   =StartIndex();
//--- Verificando a direção da linha principal
   double diff = DiffMain(idx);
   if(diff>0.0)
     {
      //--- a linha principal é direcionada para cima, isto é uma confirmação de que o preço pode subir
      if(IS_PATTERN_USAGE(0))
         result=m_pattern_0;      // confirmação de sinal é o número 0
      //--- Se o padrão 1 é usado, a reversão da linha principal é pesquisada
      if(IS_PATTERN_USAGE(1) && DiffMain(idx+1)<0.0)
         result=m_pattern_1;      // confirmação de sinal é o 1
       //--- Se o padrão 2 é usado, a interseção da linha principal com um sinal é procurada
      if(IS_PATTERN_USAGE(2) && State(idx)>0.0 && State(idx+1)<0.0)
         result=m_pattern_2;      // confirmação de sinal é o número 2
      //--- Se o padrão 3 é utilizado, o cruzamento da linha principal com o nível zero é pesquisada 
      if(IS_PATTERN_USAGE(3) && Main(idx)>0.0 && Main(idx+1)<0.0)
         result=m_pattern_3;      // confirmação de sinal é o número 3
      //--- Se os padrões 4 e 5 são usados e a linha principal está abaixo do nível zero e está aumentando, então a divergência é procurada 
      if((IS_PATTERN_USAGE(4) || IS_PATTERN_USAGE(5)) && Main(idx)<0.0)
        {
         //--- Uma análise estendida do estado do oscilador 
         ExtState(idx);
         //--- Se o padrão 4 é usado, é esperado obter um sinal de divergência 
         if(IS_PATTERN_USAGE(4) && CompareMaps(1,1)) // 0000 0001b
            result=m_pattern_4;   // confirmação de sinal é o número 4
         //--- Se o padrão 5 é usado, um sinal de Divergência Dupla é esperado 
         if(IS_PATTERN_USAGE(5) && CompareMaps(0x11,2)) // 0001 0001b
            return(m_pattern_5);  //confirmação de sinal é o número 2 5
        }
     }
//--- Retornando o resultado
   return(result);
  }

 O método funciona com a macro detecção de padrões, IS_PATTERN_USAGE, que se baseia numa máscara de bits:

//--- Verifique se o padrão de mercado é utilizado
#define IS_PATTERN_USAGE(p)          ((m_patterns_usage&(((int)1)<<p))!=0)

Se o padrão é usado e se as condições correspondentes são preenchidas, então o resultado recomendado será igual ao peso padrão correspondente, que por sua vez, é definido pelo usuário através dos métodos de grupo Pattern_x. Entretanto, se você utilizar vários padrões, não será possível descobrir quais deles foram acionados, porque a checagem não tem operadores de interrupção. A força de recomendações, neste caso, será igual ao peso do padrão que foi determinado por último.

O número do padrão que você deseja utilizar deve ser colocado numa máscara especial de bit. Por exemplo, se você quiser usar padrão No 3, o quarto bit da variável de 32 bits deve ser igual a 1 (note que a indexação de padrões começa com zero, de modo que o quarto dígito é usado para o terceiro padrão, enquanto o primeiro dígito é utilizado para o padrão zero). Se convertermos o número 1000 em binário para um número decimal, obtemos o número 8. Esse é o número que você precisa transpor para o método PatternsUsage. Os padrões podem ser combinados. Por exemplo, para usar o padrão No 3 juntamente com o padrão No 2, é necessário criar um campo de bits, no qual o quarto e o terceiro dígito seriam igual a um: 1100. O mesmo valor no formato decimal é 12.

 

Um módulo de sinais. Uso primário

Nós já aprendemos o suficiente para começar a usar o módulo de sinais.  Vamos experimentar na base da estratégia. Para este fim, criamos uma classe experimental especial, a estratégia CSignalSamples.

-//+-----------------------------------------------------------------+
//|                                                EventListener.mqh |
//|           Copyright 2016, Vasiliy Sokolov, St-Petersburg, Russia |
//|                                https://www.mql5.com/en/users/c-4 |
-//+-----------------------------------------------------------------+
#property copyright "Copyright 2016, Vasiliy Sokolov."
#property link      "https://www.mql5.com/en/users/c-4"
#include <Strategy\Strategy.mqh>
#include <Expert\Signal\SignalMACD.mqh>
-//+-----------------------------------------------------------------+
//| A estratégia recebe eventos e os exibe no terminal.              |
-//+-----------------------------------------------------------------+
class CSignalSamples : public CStrategy
{
private:
   CSignalMACD       m_signal_macd;
   CSymbolInfo       m_info;
   CiOpen            m_open;
   CiHigh            m_high;
   CiLow             m_low;
   CiClose           m_close;
   CIndicators       m_indicators;
public:
                     CSignalSamples(void);
   virtual void      OnEvent(const MarketEvent& event);                     
};
-//+-----------------------------------------------------------------+
//| Inicialização do módulo de sinal CSignalMacd                     |
-//+-----------------------------------------------------------------+
CSignalSamples::CSignalSamples(void)
{
   m_signal_macd.Pattern_0(0);
   m_signal_macd.Pattern_1(0);
   m_signal_macd.Pattern_2(0);
   m_signal_macd.Pattern_3(100);
   m_signal_macd.Pattern_4(0);
   m_signal_macd.Pattern_5(0);
   m_info.Name(Symbol());                                  // Inicialização do objeto que representa o símbolo de negociação da estratégia
   m_signal_macd.Init(GetPointer(m_info), Period(), 10);   // Inicialização do módulo de sinal pelo símbolo de negociação e timeframe
   m_signal_macd.InitIndicators(GetPointer(m_indicators)); //criando indicadores requeridos no módulo de sinal com base na lista vazia de indicadores m_indicators
   m_signal_macd.EveryTick(true);                          // Modo de teste
   m_signal_macd.Magic(ExpertMagic());                     // Número mágico
   m_signal_macd.PatternsUsage(8);                         // Máscara padrão
   m_open.Create(Symbol(), Period());                      // Inicialização das timeseries de abertura (Open) dos preços Abertos
   m_high.Create(Symbol(), Period());                      // Inicialização das timeseries da Máxima (High) dos preços 
   m_low.Create(Symbol(), Period());                       // Inicialização das timeseries da Mínima (Low) dos preços Baixos
   m_close.Create(Symbol(), Period());                     // Inicialização das timeseries do Fechamento (Close) dos preços 
   m_signal_macd.SetPriceSeries(GetPointer(m_open),        // Inicialização do módulo de sinal pelos objetos das timeseries
                              GetPointer(m_high),
                              GetPointer(m_low),
                              GetPointer(m_close));
                              
}
-//+-----------------------------------------------------------------+
//| Comprando.                                                       |
-//+-----------------------------------------------------------------+
void CSignalSamples::OnEvent(const MarketEvent &event)
{
   if(event.type != MARKET_EVENT_BAR_OPEN)
      return;
   m_indicators.Refresh();
   m_signal_macd.SetDirection();
   int power_sell = m_signal_macd.ShortCondition();
   int power_buy = m_signal_macd.LongCondition();
   if(power_buy != 0 || power_sell != 0)
      printf("PowerSell: " + (string)power_sell + " PowerBuy: " + (string)power_buy);
}
-//+-----------------------------------------------------------------+


As versões mais recentes do motor CStrategy contém um novo evento OnEvent, que é representado pelo método OnEvent, sempre é chamado quando ocorre um evento. Ao contrário dos métodos mais habituais: BuyInit, SellInit e BuySupport; o evento OnEvent é chamado independentemente do modo da estratégia de negociação e do cronograma de negociação. Assim, o OnEvent permite acessar o fluxo de eventos a partir da estratégia, mantendo a rigososidade do modelo de evento. É muito conveniente usar o OnEvent para cálculos ou ações ordinárias, os quais não estão relacionados a uma compra ou venda com direção específica.

A maioria do código é dedicado à inicialização do módulo de sinais de negociação. Como um exemplo, foi utilizado um módulo com base no indicador MACD: CSignalMACD. Apenas o padrão Nº 3 é utilizado no módulo. A este módulo é atribuido peso 100 e um método apropriado PatternsUsage com o valor igual a 8 é usado. Com a finalidade de executar esta estratégia, é necessário preparar um carregador de estratégia ou um módulo executável mq5. Seu conteúdo:

-//+-----------------------------------------------------------------+
//|                                                       Agent.mq5  |
//|           Copyright 2016, Vasiliy Sokolov, St-Petersburg, Russia |
//|                                https://www.mql5.com/en/users/c-4 |
-//+-----------------------------------------------------------------+
#property copyright "Copyright 2016, Vasiliy Sokolov."
#property link      "https://www.mql5.com/en/users/c-4"
#property version   "1.00"
#include <Strategy\StrategiesList.mqh>
#include <Strategy\Samples\SignalSamples.mqh>
CStrategyList Manager;
-//+-----------------------------------------------------------------+
//| Função de inicialização do Expert                                |
-//+-----------------------------------------------------------------+
int OnInit() 
{
   CSignalSamples* signal = new CSignalSamples();
   signal.ExpertMagic(2918);
   signal.ExpertName("MQL Signal Samples");
   signal.Timeframe(Period());
   if(!Manager.AddStrategy(signal))
      delete signal;
   return(INIT_SUCCEEDED);
}
-//+-----------------------------------------------------------------+
//| Função tick do Expert                                            |
-//+-----------------------------------------------------------------+
void OnTick()
{
  Manager.OnTick();
}
-//+-----------------------------------------------------------------+
//| Função OnChartEvent                                              |
-//+-----------------------------------------------------------------+
void OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
{
   Manager.OnChartEvent(id, lparam, dparam, sparam);
}

Consiste da inicialização da estratégia na função OnInit e manipulação de novos ticks em OnTick. Após executar o código resultante no Testador de Estratégia no modo visualização, você vai ver notificações sobre os sinais recebidos:

2016.06.20 16:34:31.697 tester agent shutdown finished
2016.06.20 16:34:31.642 shutdown tester machine
2016.06.20 16:34:31.599 tester agent shutdown started
2016.06.20 16:34:31.381 log file "Z:\MetaTrader 5\Tester\Agent-127.0.0.1-3000\logs\20160620.log" written
2016.06.20 16:34:31.381 325 Mb memory used including 28 Mb of history data, 64 Mb of tick data
2016.06.20 16:34:31.381 EURUSD,M1: 51350 ticks (12935 bars) generated in 0:00:00.780 (total bars in history 476937, total time 0:00:00.843)
2016.06.20 16:34:31.376 final balance 100000.00 USD
2016.06.20 16:34:31.373 2016.04.14 22:12:00   PowerSell: 100 PowerBuy: 0
2016.06.20 16:34:31.373 2016.04.14 22:01:00   PowerSell: 0 PowerBuy: 100
2016.06.20 16:34:31.373 2016.04.14 21:24:00   PowerSell: 100 PowerBuy: 0
2016.06.20 16:34:31.373 2016.04.14 20:54:00   PowerSell: 0 PowerBuy: 100
2016.06.20 16:34:31.373 2016.04.14 20:50:00   PowerSell: 100 PowerBuy: 0
2016.06.20 16:34:31.373 2016.04.14 20:18:00   PowerSell: 0 PowerBuy: 100
2016.06.20 16:34:31.373 2016.04.14 20:14:00   PowerSell: 100 PowerBuy: 0
2016.06.20 16:34:31.373 2016.04.14 20:13:00   PowerSell: 100 PowerBuy: 0
2016.06.20 16:34:31.373 2016.04.14 20:07:00   PowerSell: 0 PowerBuy: 100
2016.06.20 16:34:31.372 2016.04.14 19:48:00   PowerSell: 100 PowerBuy: 0
2016.06.20 16:34:31.372 2016.04.14 18:48:00   PowerSell: 0 PowerBuy: 100
...
...

Essa notificação indica que os sinais solicitados foram recebidos com sucesso e podemos continuar a integrar o módulo de sinal em nossa estratégia.

 

Elaborando uma estratégia primária baseada no CSignalMACD

Agora é hora de escrever uma estratégia totalmente funcional com base no módulo de sinal CSignalMACD. O primeiro padrão que vamos utilizar é o padrão do cruzamento da linha de sinal com o oscilador. Vamos usar a documentação. Abra MQL5 Referencia -> Biblioteca Padrão -> Classes de Estratégias de Negociação -> Módulos de Sinais de Negociação -> Os Sinais do Oscilador MACD. Encontre o segundo padrão "Cruzamento da linha principal e de sinal". Aqui está sua descrição:

  • Comprando: "Cruzamento da linha principal e do sinal"- a linha principal está acima da linha de sinal na barra analisada e a mesma barra está abaixo da barra anterior.

  • Fig 2. O oscilador cruza a linha de sinal de baixo para cima

  • Vendendo: "Cruzamento da linha principal e do sinal" - a linha principal está abaixo da linha de sinal na barra analisada e a mesma barra está acima da barra anterior.

Fig. 3. O oscilador cruza a linha de sinal de cima para baixo

Precisamos definir o número do padrão que corresponde a essa descrição. O cabeçalho da classe CSignalMACD vai nos ajudar:

-//+-----------------------------------------------------------------+
//| Classe CSignalMACD.                                              |
//| Objetivo: Classe geradora de sinais de negociação baseada no     |
//|          oscilador 'Moving Average Convergence/Divergence'.      |
//| É derivada da classe CExpertSignal.                              |
-//+-----------------------------------------------------------------+
class CSignalMACD : public CExpertSignal
  {
protected:
   CiMACD            m_MACD;           // Objeto do oscilador
   //--- parâmetros ajustados
   int               m_period_fast;    // Parâmetro do oscilador "período EMA rápida" 
   int               m_period_slow;    // Parâmetro do oscilador "período EMA lenta" 
   int               m_period_signal;  // Parâmetro do oscilador "diferença média de período" 
   ENUM_APPLIED_PRICE m_applied;       // Parâmetro do oscilador "séries de preço" 
   //--- "Pesos" de modelos do mercado (0-100)
   int               m_pattern_0;      // padrão 0 "movimentos do oscilador na direção necessária"
   int               m_pattern_1;      // padrão 1 "oscilador reverte para a direção pretendida"
   int               m_pattern_2;      // padrão 2 "cruzamento da linha principal e de sinal"
   int               m_pattern_3;      // padrão 3 "cruzamento da linha principal e do nível zero"
   int               m_pattern_4;      // padrão 4 "divergência do oscilador e do preço"
   int               m_pattern_5;      // padrão 5 "divergência dupla do oscilador e do preço"
   //--- variáveis
   double            m_extr_osc[10];   // Um array de valores extremos do oscilador 
   double            m_extr_pr[10];    // Um array de valores de preços extremos adequados 
   int               m_extr_pos[10];   // Array de deslocamento extremo (em barras) 
   uint              m_extr_map;       // Resultando bitmap do oscilador e dos preços extremos 
...
  }

A partir dos comentários no código, podemos ver que o tipo de padrão é do n° 2.

Agora que sabemos o número do padrão, é preciso configurar corretamente o sinal. Em primeiro lugar, a fim de evitar confusões, não serão utilizados outros padrões. Para este propósito, defina a máscara padrão igual a 4 (100 em forma binária). Uma vez que apenas usará um padrão, não precisamos saber a intensidade do sinal (para não configurar a força dos padrões) - ou existe um sinal, ou não existem sinais. Vamos verificar o sinal na abertura de uma nova barra, portanto devemos indicá-la chamando o método de sinal apropriado EveryTick com a flag falsa. A configuração deve ser implementada no construtor de estratégia. Em seguida, prossiga para a programação da lógica de negociação. Vamos substituir os métodos InitBuy, SupportBuy, InitSell e SupportSell. Vamos nomear a estratégia COnSignalMACD: o prefixo On indica que a estratégia é baseada no módulo padrão de sinais. O código de estratégia é dado abaixo:

-//+-----------------------------------------------------------------+
//|                                                EventListener.mqh |
//|           Copyright 2016, Vasiliy Sokolov, St-Petersburg, Russia |
//|                                https://www.mql5.com/en/users/c-4 |
-//+-----------------------------------------------------------------+
#property copyright "Copyright 2016, Vasiliy Sokolov."
#property link      "https://www.mql5.com/en/users/c-4"
#include <Strategy\Strategy.mqh>
#include <Expert\Signal\SignalMACD.mqh>
-//+-----------------------------------------------------------------+
//| A estratégia recebe eventos e os exibe no terminal.              |
-//+-----------------------------------------------------------------+
class COnSignalMACD : public CStrategy
{
private:
   CSignalMACD       m_signal_macd;
   CSymbolInfo       m_info;
   CiOpen            m_open;
   CiHigh            m_high;
   CiLow             m_low;
   CiClose           m_close;
   CIndicators       m_indicators;
public:
                     COnSignalMACD(void);
   virtual void      InitBuy(const MarketEvent &event);
   virtual void      InitSell(const MarketEvent &event);
   virtual void      SupportBuy(const MarketEvent& event, CPosition* pos);
   virtual void      SupportSell(const MarketEvent& event, CPosition* pos);
};
-//+-----------------------------------------------------------------+
//| Inicialização do módulo de sinal CSignalMacd                     |
-//+-----------------------------------------------------------------+
COnSignalMACD::COnSignalMACD(void)
{
   m_info.Name(Symbol());                                  // Inicialização do objeto que representa o símbolo de negociação da estratégia
   m_signal_macd.Init(GetPointer(m_info), Period(), 10);   // Inicialização do módulo de sinal pelo símbolo de negociação e timeframe
   m_signal_macd.InitIndicators(GetPointer(m_indicators)); // classificação de avaliação exigida no módulo de sinal com base na lista vazia m_indicators
   m_signal_macd.EveryTick(false);                         // Modo de teste
   m_signal_macd.Magic(ExpertMagic());                     // Número mágico
   m_signal_macd.PatternsUsage(4);                         // Máscara padrão
   m_open.Create(Symbol(), Period());                      // Inicialização das timeseries de abertura (Open) dos preços Abertos
   m_high.Create(Symbol(), Period());                      // Inicialização das timeseries da Máxima (High) dos preços 
   m_low.Create(Symbol(), Period());                       // Inicialização das timeseries da Mínima (Low) dos preços Baixos
   m_close.Create(Symbol(), Period());                     // Inicialização das timeseries do Fechamento (Close) dos preços 
   m_signal_macd.SetPriceSeries(GetPointer(m_open),        // Inicialização do módulo de sinal pelos objetos das timeseries
                              GetPointer(m_high),
                              GetPointer(m_low),
                              GetPointer(m_close));
}
-//+-----------------------------------------------------------------+
//| Comprando.                                                       |
-//+-----------------------------------------------------------------+
void COnSignalMACD::InitBuy(const MarketEvent &event)
{
   if(event.type != MARKET_EVENT_BAR_OPEN)
      return;
   m_indicators.Refresh();
   m_signal_macd.SetDirection();
   int power_buy = m_signal_macd.LongCondition();
   if(power_buy != 0)
      Trade.Buy(1.0);
}
-//+-----------------------------------------------------------------+
//| Fechando compras                                                 |
-//+-----------------------------------------------------------------+
void COnSignalMACD::SupportBuy(const MarketEvent &event, CPosition* pos)
{
   if(event.type != MARKET_EVENT_BAR_OPEN)
      return;
   m_indicators.Refresh();
   m_signal_macd.SetDirection();
   int power_sell = m_signal_macd.ShortCondition();
   //printf("Power sell: " + (string)power_sell);
   if(power_sell != 0)
      pos.CloseAtMarket();
}
-//+-----------------------------------------------------------------+
//| Vendendo.                                                        |
-//+-----------------------------------------------------------------+
void COnSignalMACD::InitSell(const MarketEvent &event)
{
   if(event.type != MARKET_EVENT_BAR_OPEN)
      return;
   m_indicators.Refresh();
   m_signal_macd.SetDirection();
   int power_sell = m_signal_macd.ShortCondition();
   if(power_sell != 0)
      Trade.Sell(1.0);
}
-//+-----------------------------------------------------------------+
//| Fechando vendas                                                  |
-//+-----------------------------------------------------------------+
void COnSignalMACD::SupportSell(const MarketEvent &event, CPosition* pos)
{
   if(event.type != MARKET_EVENT_BAR_OPEN)
      return;
   m_indicators.Refresh();
   m_signal_macd.SetDirection();
   int power_buy = m_signal_macd.LongCondition();
   if(power_buy != 0)
      pos.CloseAtMarket();
}
-//+-----------------------------------------------------------------+


As posições de compra e venda são abertas com base nos sinais descritos na referência. As posições existentes são fechadas por sinais opostos. Assim, se existe uma condição para abrir uma posição comprada, a posição vendida aberta anteriormente será fechada, e vice-versa.

O resultado de negociação pode ser visto no testador de estratégia. Parte do histórico de testes é destaque na figura abaixo:

 

Fig. 4. Abrindo as negociações na intersecção do histograma MACD e na linha de sinal

De acordo com o modo de teste, negociações são abertas sobre um sinal recebido na barra anterior. A figura mostra que na barra seguinte, na interseção do histograma MACD e da linha de sinal, uma posição comprada ou vendida é aberta e a posição anterior é fechada.

 

Um Adaptador de Sinais

Descobrimos que, antes de começar a trabalhar com o sinal, você precisará configurá-lo. Os sinais são configurados utilizando sinais complexos, que por sua vez, também precisam ser configuradom antes de prosseguir ao próximo sinal. Sinais diferentes requerem diferentes objetos para a operação. Por exemplo, alguns sinais só exigem a especificação de timeseries básicas, enquanto em outros sinais, você precisa especificar o recipiente de indicadores e dados de preços adicionais, como o tick ou volume real. Tudo isto complica o uso dos sinais a nível de usuário, já que o mesmo precisa saber o sistema interno do sinal e os dados que são necessários para o seu bom funcionamento.

Para evitar estas dificuldades, um adaptador de classes especial foi introduzido. A classe é chamada CSignalAdapter e está localizada no diretório comum do projeto CStrategy. O adaptador possui uma interface simples. Ele permite a criação de um sinal recebendo as flags que formam um padrão de compra e venda. Para criar um sinal, precisamos transmitir os parâmetros do mesmo para um método especial, o CSignalAdapter::CreateSignal. Os parâmetros de sinal estão contidos na estrutura especial MqlSignalParams. Aqui está a definição dessa estrutura:

//+--------------------------------------------------------------------+
//| Parâmetros do sinal                                                |
//+--------------------------------------------------------------------+
struct MqlSignalParams
{
public:
   string            symbol;           // Símbolo
   ENUM_TIMEFRAMES   period;           // Período do gráfico
   ENUM_SIGNAL_TYPE  signal_type;      // Tipo do sinal
   int               index_pattern;    // Índice padrão
   int               magic;            // Número mágico do EA
   double            point;            // Número de pontos
   bool              every_tick;       // Modo de teste "Cada tick" 
   void operator=(MqlSignalParams& params);
};
//+--------------------------------------------------------------------+
//| O operador de cópia é usado, já que a estratégia utiliza strings   |
//+--------------------------------------------------------------------+
void MqlSignalParams::operator=(MqlSignalParams& params)
{
   symbol = params.symbol;
   period = params.period;
   signal_type = params.signal_type;
   usage_pattern = params.usage_pattern;
   magic = params.magic;
   point = params.point;
   every_tick = params.every_tick;
}

A estrutura contém tipos básicos que definem as seguintes características de sinal:

  • Simbolo;
  • Timeframe;
  • Tipo de sinal;
  • Número mágico do Expert Advisor;
  • Uma flag que indica o modo de teste de "Cada tick";
  • Filtragem do preço;
  • Padrão de sinal utilizado.

Aqui estão mais alguns detalhes sobre o index_pattern. Ao contrário do módulo de sinais, ele não aceita a máscara padrão, somente recebe o índice de uma delas Assim, cada adaptador de sinal pode usar apenas um padrão de sinal selecionado. O valor do index_pattern deve situar-se dentro de 1 a 31 e deve ser igual ao número real de padrões de sinal usados.

Além dos parâmetros de base, por utilizar um tipo de string, a estrutura contém uma cópia do operador. Por conta disto, não é possível copiar automaticamente uma estrutura para outra. Após determinar os parâmetros necessários e preenchimento com a estrutura apropriada, o usuário pode chamar o método CSignalAdapter::CreateSignal e receber uma instância do sinal criado a partir deste método como resposta. A instância recebida pode ser configurada levando em conta as características específicas do sinal correspondente.

A lista abaixo mostra um método de configuração do sinal de CSignalMACD usando o CSignalAdapter:

-//+-----------------------------------------------------------------+
//|                                                EventListener.mqh |
//|           Copyright 2016, Vasiliy Sokolov, St-Petersburg, Russia |
//|                                https://www.mql5.com/en/users/c-4 |
-//+-----------------------------------------------------------------+
#property copyright "Copyright 2016, Vasiliy Sokolov."
#property link      "https://www.mql5.com/en/users/c-4"
#include <Strategy\Strategy.mqh>
#include <Strategy\SignalAdapter.mqh>

-//+-----------------------------------------------------------------+
//| A estratégia recebe eventos e os exibe no terminal.              |
-//+-----------------------------------------------------------------+
class CAdapterMACD : public CStrategy
{
private:
   CSignalAdapter    m_signal;
   MqlSignalParams   m_params;
public:
                     CAdapterMACD(void);
   virtual void      InitBuy(const MarketEvent &event);
   virtual void      InitSell(const MarketEvent &event);
   virtual void      SupportBuy(const MarketEvent& event, CPosition* pos);
   virtual void      SupportSell(const MarketEvent& event, CPosition* pos);
};
-//+-----------------------------------------------------------------+
//| Configurando o Adaptador                                         |
-//+-----------------------------------------------------------------+
CAdapterMACD::CAdapterMACD(void)
{
   m_params.symbol = Symbol();
   m_params.period = Period();
   m_params.every_tick = false;
   m_params.signal_type = SIGNAL_MACD;
   m_params.magic = 1234;
   m_params.point = 1.0;
   m_params.usage_pattern = 2;
   CSignalMACD* macd = m_signal.CreateSignal(m_params);
   macd.PeriodFast(15);
   macd.PeriodSlow(32);
   macd.PeriodSignal(6);
}

Os parâmetros também devem ser configurados no adaptador. No entanto, ao contrário da primeira versão do Expert Advisor, todos os parâmetros são triviais, ou seja, são tipos básicos. Além disso, não há nenhuma necessidade de criar ou controlar outros objetos complexos, tais como timeseries e indicadores. Tudo isto será feito pelo adaptador. É por isso que seu uso simplifica muito o trabalho com os sinais.  

Note que após criarmos o sinal, continuamos a configurá-lo, definindo o nosso próprio período do indicador MACD (15, 32, 6). Isso é fácil de fazer, porque o método CreateSignal retornou o objeto correspondente.

Uma vez que o sinal está devidamente configurado, você pode começar a usá-lo. Métodos simples BuySignal e ShortSignal são utilizados para esta finalidade. Aqui temos a continuação de classe da estratégia:

-//+-----------------------------------------------------------------+
//| Comprando.                                                       |
-//+-----------------------------------------------------------------+
void CAdapterMACD::InitBuy(const MarketEvent &event)
{
   if(event.type != MARKET_EVENT_BAR_OPEN)
      return;
   if(m_signal.LongSignal())
      Trade.Buy(1.0);
}
-//+-----------------------------------------------------------------+
//| Fechando compras                                                 |
-//+-----------------------------------------------------------------+
void CAdapterMACD::SupportBuy(const MarketEvent &event, CPosition* pos)
{
   if(event.type != MARKET_EVENT_BAR_OPEN)
      return;
   if(m_signal.ShortSignal())
      pos.CloseAtMarket();
}
-//+-----------------------------------------------------------------+
//| Vendendo                                                        |
-//+-----------------------------------------------------------------+
void CAdapterMACD::InitSell(const MarketEvent &event)
{
   if(event.type != MARKET_EVENT_BAR_OPEN)
      return;
   if(m_signal.ShortSignal())
      Trade.Sell(1.0);
}
-//+-----------------------------------------------------------------+
//| Fechando vendas                                                  |
-//+-----------------------------------------------------------------+
void CAdapterMACD::SupportSell(const MarketEvent &event, CPosition* pos)
{
   if(event.type != MARKET_EVENT_BAR_OPEN)
      return;
   if(m_signal.LongSignal())
      pos.CloseAtMarket();
}

A lógica acima faz o mesmo que o exemplo anterior: ele abre posições vendidas e comprada na interseção do histograma MACD e sua linha de sinal. No entanto, como você pode ver, o código está mais enxuto agora. Ao contrário da primeira versão do código de sinal, agora não existe necessidade de definir a direção do sinal ou atualizar sempre os valores dos indicadores. Também não é necessário adicionar objetos auxiliares ao código da estratégia. Todas essas ações são executadas pelo adaptador.

 

Combinando diversos sinais num código de estratégia de negociação

Podemos utilizar diferentes padrões e sinais para entrada e saída do mercado? A resposta é sim. Temos pleno acesso ao sistema de sinal, para que possamos utilizar mais de um padrão. Com a finalidade de evitar confusão no padrão, o adaptador de sinal permite definir apenas um padrão para uso. Entretanto, o número de tais adaptadores é ilimitado. Neste caso, cada padrão é representado por um adaptador separado e por um sinal, mesmo se todos os padrões são baseados somente num sinal. Obviamente, em termos de recursos, é um pouco menos eficiente do que o disponível na biblioteca padrão, porém tem seus benefícios.

Vamos escrever um exemplo de uma estratégia que pode receber diferentes sinais de entrada e saída. A estratégia irá utilizar os padrões do indicador RSI com base nas áreas sobrecompradas e sobrevendidas para as entradas. O segundo padrão - usando o Accelerator Oscillator (AC), proposto por Bill Williams, para as saídas. Aqui estão as regras da estratégia de forma mais detalhada.

Comprando: reversão atrás do nível sobrevendido - o oscilador é direcionado para cima e seu valor na barra analisada está atrás do nível sobrevendido (valor padrão é 30).

 

Fig. 5. Condições de entrada da posição comprada 

 

Vendendo: reversão atrás do nível sobrecomprado - o oscilador é direcionado para baixo e seu valor na barra analisada está atrás do nível sobrecomprado (valor padrão é 70).

 

Fig. 6. Condições de entrada da posição vendida

Fechando Compra: O valor do indicador AC é superior a 0, e ele cai em relação as duas barras anteriores.


Fig. 7. Condições de saída de posição comprada 

Fechando Venda: O valor do indicador de AC é inferior a 0, e cresce em relação as duas barras anteriores.

 

Fig. 8. Condições de saída de uma posição vendida

A saída de uma posição comprada é realizada com base no padrão do sinal AC, o RSI é usado para entrar em posição vendida e vice-versa, você sairá da posição vendida usando o padrão do sinal AC e o RSI é usado para entrar em posição comprada.

A lógica implemetada pelo Expert Advisor está disponível abaixo:

-//+-----------------------------------------------------------------+
//|                                                EventListener.mqh |
//|           Copyright 2016, Vasiliy Sokolov, St-Petersburg, Russia |
//|                                https://www.mql5.com/en/users/c-4 |
-//+-----------------------------------------------------------------+
#property copyright "Copyright 2016, Vasiliy Sokolov."
#property link      "https://www.mql5.com/ru/users/c-4"
#include <Strategy\Strategy.mqh>
#include <Strategy\SignalAdapter.mqh>
input int RSI_Period = 14; // Período RSI
-//+-----------------------------------------------------------------+
//| A estratégia recebe eventos e os exibe no terminal.              |
-//+-----------------------------------------------------------------+
class COnSignal_RSI_AC : public CStrategy
{
private:
   CSignalAdapter    m_adapter_rsi;
   CSignalAdapter    m_adapter_ac;
public:
                     COnSignal_RSI_AC(void);
   virtual void      InitBuy(const MarketEvent &event);
   virtual void      InitSell(const MarketEvent &event);
   virtual void      SupportBuy(const MarketEvent& event, CPosition* pos);
   virtual void      SupportSell(const MarketEvent& event, CPosition* pos);
};
-//+-----------------------------------------------------------------+
//| Inicialização do módulo de sinal CSignalMacd                     |
-//+-----------------------------------------------------------------+
COnSignal_RSI_AC::COnSignal_RSI_AC(void)
{
   MqlSignalParams params;
   params.every_tick = false;
   params.magic = 32910;
   params.point = 10.0;
   params.symbol = Symbol();
   params.period = Period();
   params.usage_pattern = 2;
   params.signal_type = SIGNAL_AC;
   CSignalAC* ac = m_adapter_ac.CreateSignal(params);
   params.usage_pattern = 1;
   params.magic = 32911;
   params.signal_type = SIGNAL_RSI;
   CSignalRSI* rsi = m_adapter_rsi.CreateSignal(params);
   rsi.PeriodRSI(RSI_Period);
}
-//+-----------------------------------------------------------------+
//| Comprando.                                                       |
-//+-----------------------------------------------------------------+
void COnSignal_RSI_AC::InitBuy(const MarketEvent &event)
{
   if(event.type != MARKET_EVENT_BAR_OPEN)
      return;
   if(positions.open_buy > 0)
      return;
   if(m_adapter_rsi.LongSignal())
      Trade.Buy(1.0);
}
-//+-----------------------------------------------------------------+
//| Fechando compras                                                 |
-//+-----------------------------------------------------------------+
void COnSignal_RSI_AC::SupportBuy(const MarketEvent &event, CPosition* pos)
{
   if(event.type != MARKET_EVENT_BAR_OPEN)
      return;
   if(m_adapter_ac.ShortSignal())
      pos.CloseAtMarket();
}
-//+-----------------------------------------------------------------+
//| Vendendo.                                                        |
-//+-----------------------------------------------------------------+
void COnSignal_RSI_AC::InitSell(const MarketEvent &event)
{
   if(event.type != MARKET_EVENT_BAR_OPEN)
      return;
   if(positions.open_sell > 0)
      return;
   if(m_adapter_rsi.ShortSignal())
      Trade.Sell(1.0);
}
-//+-----------------------------------------------------------------+
//| Fechando vendas                                                  |
-//+-----------------------------------------------------------------+
void COnSignal_RSI_AC::SupportSell(const MarketEvent &event, CPosition* pos)
{
   if(event.type != MARKET_EVENT_BAR_OPEN)
      return;
   if(m_adapter_ac.LongSignal())
      pos.CloseAtMarket();
}
-//+-----------------------------------------------------------------+


Note que o Expert Advisor tem um parâmetro externo que permite a determinação do período RSI Isto é realizado no construtor de estratégia através de um acesso direto ao sinal.

O resultado da operação da estratégia é mostrado no gráfico abaixo:

 

Fig. 8. O resultado da estratégia 

Como pode ser visto a partir do gráfico, o EA utiliza dois indicadores - RSI e AC. O EA entra no mercado quando o RSI começa a oscilar dentro de suas áreas compradas e vendidas. Estas áreas estão circulados em vermelho. O EA sai quando o indicador AC forma três linhas da mesma cor. Para uma saída de posição comprada, as linhas devem ser vermelhas e devem estar acima do nível zero. Para uma saída de posição vendida, as linhas devem ser verde e devem estar abaixo do nível zero. Tais momentos são mostrados nas caixas em azul.

O gráfico mostra que a lógica do EA é processada corretamente. As regras de negociação dessa Expert Advisor não são triviais. No entanto, a própria estratégia não é longa. Estes são os benefícios do reuso do código.

 

Conclusão

Temos considerado um método de integrar a biblioteca padrão de sinais para o motor de negociação CStrategy. Através desta integração, o CStrategy é uma ferramenta muito útil para a criação de estratégias personalizadas com base nos padrões de sinais. Além disso, qualquer escrita de sinal no gerador automatizado de estratégia MetaTrader, automaticamente se torna disponível no motor de negociação CStrategy.

A possibilidade de usar sinais de negociação padrão na CStrategy pode economizar significativamente o tempo de desenvolvimento da estratégia. Não é mais necessário escrever os seus próprios indicadores ou algoritmos de detecção padrão, se eles já estiverem disponíveis no módulo padrão de sinais. Além disso, o novo recurso reduz significativamente a complexidade do processo de desenvolvimento estratégico, enquanto a definição dos padrões complexos, tais como a divergência ou a dupla divergência, agora são realizados por soluções já definidas.