English Русский 中文 Español Deutsch 日本語
preview
Símbolos personalizados: Fundamentos práticos

Símbolos personalizados: Fundamentos práticos

MetaTrader 5Testador | 30 dezembro 2020, 09:24
2 690 0
Stanislav Korotky
Stanislav Korotky

A MetaTrader 5 apresenta a capacidade de criar símbolos personalizados com suas próprias cotações e ticks. Eles podem ser acessados tanto da interface da plataforma e no nível do programa, via API MQL. Os símbolos personalizados são exibidos em gráficos padrão, permitindo a aplicação de indicadores, marcação com objetos e até mesmo a criação de estratégias de negociação baseadas nesses símbolos.

Os símbolos personalizados podem usar cotações de símbolos reais fornecidas por corretores ou recursos externos como fontes de dados. Neste artigo, nós analisaremos vários métodos populares para a transformação de símbolos que fornecem ferramentas analíticas adicionais para os traders:

  • gráfico de volume e intervalo iguais
  • gráficos de tick
  • mudança do tempo de cotações com a conversão de formas de velas
  • Renko

Além disso, nós desenvolveremos um mecanismo para adaptar os Expert Advisors para negociar um símbolo real que está associado a um símbolo personalizado derivado cujo gráfico está sendo executado o EA.

Neste artigo, os gráficos do símbolo de origem (padrão) usam um fundo preto e os gráficos com os símbolos personalizados usam um fundo branco.

Gráficos de volume/intervalo iguais (Equivolume)

Um gráfico de equivolume é um gráfico de barras baseado no princípio de igualdade do volume entre as barras. Em um gráfico regular, cada nova barra é formada em intervalos especificados, que correspondem ao tamanho do intervalo de tempo. Em um gráfico de equivolume, cada barra é considerada formada quando a soma dos ticks ou volumes reais atinge um valor predefinido. Depois disso, o programa começa a calcular o valor da próxima barra. Obviamente, os movimentos de preços também são controlados quando os volumes são calculados e, portanto, você recebe os quatro valores de preço usuais no gráfico: Abertura, Máxima, Mínima e Fechamento.

Embora o eixo horizontal no gráfico de equivolume ainda se baseia em tempo, os horários de cada barra são arbitrários e dependem da volatilidade (número ou tamanho das negociações) em cada tempo gráfico. Muitos traders consideram este método de formação de barra para fornecer uma descrição mais adequada de um mercado dinâmico em comparação com um valor constante do tempo gráfico.

Infelizmente, nem a MetaTrader 4 nem a MetaTrader 5 fornecem gráficos de volume equivalentes prontos para uso. Eles devem ser gerados de uma maneira especial.

Na MetaTrader 4, isso pode ser feito usando os gráficos offline. Este método foi descrito no artigo Mapeamento de Equivolumes Revisitado.

O mesmo algoritmo pode ser implementado na MetaTrader 5 usando os símbolos personalizados. Para simplificar a tarefa, vamos utilizar um Expert Advisor, que não realiza negociações, do artigo especificado e adaptá-lo à MQL API MetaTrader 5.

O arquivo original EqualVolumeBars.mq4 foi renomeado para EqualVolumeBars.mq5 e foi ligeiramente modificado. Em particular, as palavras-chave 'extern', que descrevem os parâmetros de entrada foram substituídas por 'input'. Em vez de dois parâmetros, StartYear e StartMonth, é utilizado um parâmetro, o StartDate. O CustomPeriod que foi usado na MetaTrader 4 para definir um tempo gráfico não padrão não é necessário agora e, portanto, foi excluído.

Preste atenção que os volumes da MetaTrader 4 são todos volumes de ticks, ou seja, eles representam o número de ticks (mudanças de preço) em uma barra. A ideia original era processar as barras M1 (com os seus volumes de ticks) ou um arquivo csv externo com os ticks fornecido por outra corretora, para contar os ticks recebidos por unidade de tempo e formar uma nova barra de equivolume assim que o número especificado de ticks fosse alcançado. As barras foram gravadas em um arquivo hst que pode ser aberto na MetaTrader 4 como um gráfico offline.

A parte do código relacionada à leitura de um arquivo csv e à gravação de um arquivo hst não é necessária na MetaTrader 5. Em vez disso, nós podemos ler o histórico real de ticks e formar as barras usando a API de símbolos personalizados. Além disso, a MetaTrader 5 permite que as corretoras forneçam volumes e ticks reais (para instrumentos de bolsa, mas geralmente não estão disponíveis para instrumentos de forex). Se este modo estiver ativado, as barras de equivolume podem ser construídas não pelo número de ticks, mas por volumes reais.

O parâmetro de entrada FromM1 determina se o EA processará as barras M1 ('true' por padrão) ou o histórico de ticks ('false'). Ao iniciar o processamento do tick, não selecione um passado muito distante como o início, pois isso pode exigir muito tempo e espaço em disco. Se você já trabalhou com o histórico de ticks, então, você entende as capacidades do seu PC e os recursos disponíveis.

As barras de intervalos iguais são plotadas de maneira semelhante. No entanto, uma nova barra é aberta quando o preço ultrapassa um determinado número de pontos. Observe que essas barras estão disponíveis apenas no modo tick (FromM1 == false).

Tipo do gráfico — EqualTickVolumes, EqualRealVolumes, RangeBars — é definido pelo parâmetro de entrada WorkMode.

A maneira mais conveniente de trabalhar com os símbolos personalizados é usar a Biblioteca de símbolos (de fxsaber) Ele pode ser conectado a um Expert Advisor usando a diretiva #include:

  #include <Symbol.mqh>

Agora, nós podemos criar um símbolo personalizado com base no símbolo do gráfico atual. Isto se faz do seguinte modo:

  if(!SymbolSelect(symbolName, true))
  {
    const SYMBOL Symb(symbolName);
    Symb.CloneProperties(_Symbol);
    
    if(!SymbolSelect(symbolName, true))
    {
      Alert("Can't select symbol:", symbolName, " err:", GetLastError());
      return INIT_FAILED;
    }
  }

onde symbolName é uma string com o nome do símbolo personalizado.

Este fragmento de inicialização e muitas outras tarefas auxiliares relacionadas ao gerenciamento do símbolo personalizado (em particular, redefinir um histórico existente, abrir um gráfico com um novo símbolo personalizado) serão realizadas em todos os programas de maneira semelhante. Você pode ver os códigos-fonte relevantes no anexo abaixo. Eu vou omiti-los neste artigo, pois são de importância secundária.

Quando uma nova barra de equivolume aparece ou a atual muda, a função WriteToFile é chamada. Esta função é implementada chamando a CustomRatesUpdate na MetaTrader 5:

  void WriteToFile(datetime t, double o, double l, double h, double c, long v, long m = 0)
  {
    MqlRates r[1];
    
    r[0].time = t;
    r[0].open = o;
    r[0].low = l;
    r[0].high = h;
    r[0].close = c;
    r[0].tick_volume = v;
    r[0].spread = 0;
    r[0].real_volume = m;
    
    int code = CustomRatesUpdate(symbolName, r);
    if(code < 1)
    {
      Print("CustomRatesUpdate failed: ", GetLastError());
    }
  }

Surpreendentemente, o ciclo de barras M1 (modo FromM1 = true) é quase o mesmo que na versão em MQL4, o que significa que simplesmente adaptando a função WriteToFile, nós podemos receber um código em MQL5 funcional para as barras M1. A única parte que precisa ser alterada é a geração de ticks em RefreshWindow. Na MetaTrader 4, isso era feito enviando mensagens do Windows para emular os ticks em um gráfico offline. A MetaTrader 5 usa a função CustomTicksAdd:

  void RefreshWindow(const datetime t)
  {
    MqlTick ta[1];
    SymbolInfoTick(_Symbol, ta[0]);
    ta[0].time = t;
    ta[0].time_msc = ta[0].time * 1000;
    if(CustomTicksAdd(symbolName, ta) == -1)
    {
      Print("CustomTicksAdd failed:", GetLastError(), " ", (long) ta[0].time);
      ArrayPrint(ta);
    }
  }

A geração de ticks chama o evento OnTick nos gráficos dos símbolos personalizados, permitindo que os Expert Advisors em execução nesses gráficos possam negociar. No entanto, essa tecnologia requer algumas ações extras, que nós consideraremos mais tarde.

O modo em que as barras de equivolume são geradas a partir do histórico de ticks (FromM1 = false) é um pouco mais complicado. Isso requer a leitura de ticks reais usando as funções padrão CopyTicks/CopyTicksRange. Toda essa funcionalidade é implementada na classe TicksBuffer.

  #define TICKS_ARRAY 10000
  
  class TicksBuffer
  {
    private:
      MqlTick array[];
      int tick;
    
    public:
      bool fill(ulong &cursor, const bool history = false)
      {
        int size = history ? CopyTicks(_Symbol, array, COPY_TICKS_ALL, cursor, TICKS_ARRAY) : CopyTicksRange(_Symbol, array, COPY_TICKS_ALL, cursor);
        if(size == -1)
        {
          Print("CopyTicks failed: ", GetLastError());
          return false;
        }
        else if(size == 0)
        {
          if(history) Print("End of CopyTicks at ", (datetime)(cursor / 1000));
          return false;
        }
        
        cursor = array[size - 1].time_msc + 1;
        tick = 0;
      
        return true;
      }
      
      bool read(MqlTick &t)
      {
        if(tick < ArraySize(array))
        {
          t = array[tick++];
          return true;
        }
        return false;
      }
  };

Os ticks são solicitados no método 'fill' em fragmentos de TICKS_ARRAY e, em seguida, são adicionados ao 'array' do qual são lidos pelo método 'read' um por um. O método implementa o algoritmo para trabalhar com o histórico de ticks semelhante ao das barras de histórico M1 (o código-fonte completo é fornecido no anexo).

    TicksBuffer tb;
    
    while(tb.fill(cursor, true) && !IsStopped())
    {
      MqlTick t;
      while(tb.read(t))
      {
        ...
        // New or first bar
        if(IsNewBar() || now_volume < 1)
        {
          WriteToFile(...);
        }
      }
    }

Cada vez que o Expert Advisor é iniciado, ele apaga o histórico existente do símbolo personalizado especificado, se existir, usando a função Reset. Se necessário, este comportamento pode ser melhorado salvando o histórico e continuando a gerar barras na posição onde a geração anterior terminou.

Você pode comparar o código-fonte de EqualVolumeBars.mq4 e do EqualVolumeBars.mq5 resultante.

Vamos ver como o novo Expert Advisor funciona. Este é o gráfico EURUSD H1 no qual o EA está sendo executado:

O Expert Advisor EqualVolumeBars no gráfico EURUSD H1 na MetaTrader 5

O Expert Advisor EqualVolumeBars no gráfico EURUSD H1 na MetaTrader 5

Abaixo está o gráfico de equivolume criado pelo EA, no qual cada barra consiste em 1000 ticks.

Gráfico de equivolume EURUSD com 1000 ticks por barra, gerado pelo EqualVolumeBars EA na MetaTrader 5

Gráfico de equivolume EURUSD com 1000 ticks por barra, gerado pelo EqualVolumeBars EA na MetaTrader 5

Observe que os volumes de ticks de todas as barras são iguais, exceto para a última, que ainda está sendo formada (a contagem de ticks continua).

Vamos verificar outro modo de operação - gráfico de igual amplitude. Abaixo está um gráfico que consiste em barras com uma oscilação de 100 pontos.

Gráfico de amplitude igual EURUSD com 100 pontos por barra, gerado pelo EA EqualVolumeBars na MetaTrader 5

Gráfico de amplitude igual EURUSD com 100 pontos por barra, gerado pelo EA EqualVolumeBars na MetaTrader 5

Além disso, o EA permite usar o modo de volume real para instrumentos de bolsa:

O gráfico original LKOH com o volume real de 10,000 por barra, gerado pelo EA EqualVolumeBars na MetaTrader 5

O gráfico de equivolume LKOH com o volume real de 10,000 por barra, gerado pelo EA EqualVolumeBars na MetaTrader 5

O gráfico LKOH original (a) e o equivolume (b) com o volume real de 10,000 por barra, gerado pelo EA EqualVolumeBars na MetaTrader 5

O período do símbolo no qual o EA está sendo executado não é importante, já que as barras M1 ou o histórico de ticks são sempre usados para cálculos.

O período dos gráficos de símbolos personalizados deve ser igual a M1 (o menor tempo gráfico disponível na plataforma). Assim, o tempo das barras, geralmente corresponde aproximadamente aos seus momentos de formação. No entanto, durante fortes movimentos do mercado, quando o número de ticks ou o tamanho dos volumes formam várias barras por minuto, o tempo das barras estará à frente do tempo atual. Quando o mercado se acalmar, os horários das barras de equivolume voltarão ao normal. Essa limitação da plataforma provavelmente não é particularmente crítica para barras de volume igual ou intervalo igual, uma vez que a própria ideia de tais gráficos é desvinculá-los do tempo absoluto.

Gráfico de ticks

O gráfico de ticks na MetaTrader 5 está disponível na janela Observação do Mercado. Por algum motivo, sua implementação difere dos gráficos regulares. Ele exibe um número limitado de ticks (pelo que eu sei, até 2000), é pequeno e não pode ser expandido para tela inteira e não possui todos os recursos que normalmente são fornecidos pelos gráficos padrão, como a capacidade de usar indicadores, objetos e Expert Advisors.

Gráfico de ticks na janela Observação do Mercado da MetaTrader 5

Gráfico de ticks na janela Observação do Mercado da MetaTrader 5

Então, por que as ferramentas de análise padrão não são suportadas para ticks, enquanto a MetaTrader 5 apresenta suporte nativo para ticks reais e oferece recursos de negociação de alta frequência (HFT)? Alguns traders consideram os ticks uma entidade muito pequena ou mesmo ruído. Outros traders tentam gerar lucros a partir de ticks. Portanto, pode ser útil exibir os ticks em um gráfico padrão com a capacidade de escalar, aplicar modelos e eventos para usar Expert Advisors. Isso pode ser implementado usando a funcionalidade do símbolo personalizado.

Novamente, nós podemos usar as funções da API MQL já conhecidas, como CopyTicks e CustomRatesUpdate. Usando-as, nós podemos implementar facilmente um Expert Advisor, que não realiza negociações, mas que gera um símbolo personalizado com base no símbolo do gráfico atual. Aqui, cada barra M1 no histórico do símbolo personalizado é um tick separado. Um exemplo desse código-fonte está anexado a seguir, no arquivo Ticks2Bars.mq5. Por exemplo, se você executar o Expert Advisor no gráfico EURUSD (qualquer tempo gráfico), ele criará o símbolo EURUSD_ticks.

As entradas do EA são as seguintes:

  • Limit — o número de barras (ticks) criadas após o lançamento, o padrão é 1000; se você definir 0, a geração de ticks online começará, sem um histórico anterior.
  • Reset — a opção redefine o histórico de ticks/barras anteriores após o lançamento, definido como true por padrão.
  • LoopBack — a opção ativa um modo de buffer circular no qual os ticks (com índices superiores ao Limit) são empurrados para fora do array interno de "cotações" conforme novos ticks são adicionados no início, de modo que o gráfico sempre tenha barras de Limite; definido como true por padrão; quando LoopBack está habilitado, o parâmetro Limit deve ser maior que 0; quando LoopBack está desabilitado, o array é constantemente expandido e o número de barras no gráfico aumenta.
  • EmulateTicks — a opção emula eventos da chegada de novos ticks para um símbolo personalizado que é um recurso importante para as chamadas do Expert Advisor, definido como true por padrão.
  • RenderBars — método de exibição de barra/tick: OHLC ou HighLow; OHLC por padrão; neste modo, um tick é uma barra completa com um corpo (máxima = ask, mínima = bid, último (se disponível) = fechamento; se último = 0, então abertura e fechamento são iguais a um dos valores da máxima ou mínima apropriada dependendo da direção do movimento do preço desde o tick anterior; no modo HighLow, os ticks são mostrados como barras de pinos em que máxima = ask, mínima = bid, abertura = fechamento = (ask + bid) / 2.

As principais operações são realizadas pela função 'apply':

  bool apply(const datetime cursor, const MqlTick &t, MqlRates &r)
  {
    static MqlTick p;
    
    // eliminate strange things
    if(t.ask == 0 || t.bid == 0 || t.ask < t.bid) return false;
    
    r.high = t.ask;
    r.low = t.bid;
    
    if(t.last != 0)
    {
      if(RenderBars == OHLC)
      {
        if(t.last > p.last)
        {
          r.open = r.low;
          r.close = r.high;
        }
        else
        {
          r.open = r.high;
          r.close = r.low;
        }
      }
      else
      {
        r.open = r.close = (r.high + r.low) / 2;
      }
      
      if(t.last < t.bid) r.low = t.last;
      if(t.last > t.ask) r.high = t.last;
      r.close = t.last;
    }
    else
    {
      if(RenderBars == OHLC)
      {
        if((t.ask + t.bid) / 2 > (p.ask + p.bid) / 2)
        {
          r.open = r.low;
          r.close = r.high;
        }
        else
        {
          r.open = r.high;
          r.close = r.low;
        }
      }
      else
      {
        r.open = r.close = (r.high + r.low) / 2;
      }
    }
    
    r.time = cursor;
    r.spread = (int)((t.ask - t.bid)/_Point);
    r.tick_volume = 1;
    r.real_volume = (long)t.volume;
  
    p = t;
    return true;
  }

Nesta função, os campos da estrutura MqlTick para o 'cursor' do momento atual são portados para os campos da estrutura MqlRates que é então escrita no histórico.

A figura abaixo mostra um gráfico de barras de tick de um símbolo personalizado (um gráfico de tick padrão é mostrado para comparação):

Gráfico de ticks EURUSD funcional na MetaTrader 5

Gráfico de ticks EURUSD funcional na MetaTrader 5

Este é o gráfico onde nós podemos usar indicadores, objetos ou EAs para automatizar a análise e negociação dos ticks.

Observe que os horários das barras no gráfico são fictícios. Se o modo LoopBack estiver habilitado, a última barra sempre tem o horário atual com precisão de um minuto, e as barras anteriores estão distantes com um passo de 1 minuto cada (que é o período gráfico mínimo na MetaTrader 5). Se o modo LoopBack estiver desabilitado, os horários das barras aumentam em 1 minuto, começando com o horário de inicialização do EA, de modo que todas as barras acima do limite inicial estão em um futuro virtual.

No entanto, a barra M1 mais à direita corresponde ao tick mais recente e ao preço atual de fechamento ou último (last). Isso permite negociar nesses gráficos usando Expert Advisors, tanto online quanto no testador. Para trabalhar online, o EA precisa de uma pequena modificação, porque ele deve ser capaz de negociar o símbolo XY original do gráfico de símbolos "XY_ticks" (símbolos personalizados existem apenas no terminal e não são conhecidos no servidor). No exemplo acima, "EURUSD_ticks" deve ser substituído por "EURUSD" em todas as ordens de negociação.

Se um Expert Advisor receber os sinais de negociação dos indicadores, então pode ser o suficiente para criar suas instâncias no gráfico personalizado, e executar este EA no gráfico do símbolo que iremos negociar. Mas esse método nem sempre é aplicável. Outro método de adaptação de Expert Advisors para negociação de símbolos personalizados será descrito mais adiante.

Algumas dificuldades em trabalhar com gráficos de ticks está relacionada ao fato de que eles são atualizados muito rapidamente. Devido a isso, é quase impossível analisar e marcar os gráficos manualmente - tudo deve ser automatizado usando indicadores ou scripts.

A abordagem apresentada com "cotações de tick" permite o teste de estratégias de escalpelamento sem acumulação de dados especiais em buffers internos e cálculo de sinais com base nos buffers, já que podemos simplesmente usar indicadores ou objetos usuais.

Deslocamento Temporal e a Metamorfose das Velas

Muitos traders utilizam padrões de velas como um sinal principal ou adicional. Esse método é visualmente informativo, mas tem uma desvantagem importante.

Os padrões de velas descrevem uma geometria predefinida de uma sequência de barras. Todas as barras são formadas conforme o preço se altera ao longo do tempo. No entanto, o tempo é inerentemente contínuo, embora os gráficos apresentem o tempo dividido artificialmente em segmentos que correspondem as barras e alinhados a um determinado fuso horário (escolhido pela corretora). Por exemplo, se você deslocar um gráfico de barras H1 por alguns minutos (digamos, 15 minutos), a geometria das barras provavelmente mudará. Como resultado, os padrões existentes anteriormente podem desaparecer completamente e novos serão formados em outros lugares. No entanto, a ação do preço é a mesma.

Se você visualizar alguns padrões de velas populares, poderá ver facilmente que eles são formados por movimentos de preços semelhantes, e a diferença em sua aparência é causada pelo horário de início do cálculo da barra. Por exemplo, se você deslocar o eixo de tempo em meia barra, um "martelo" pode ser transformado em um padrão de "perfuração" e um "enforcado" pode se transformar em uma "nuvem negra". Dependendo do valor do deslocamento e das mudanças locais de preços, o padrão também pode se transformar em "engolfo" de baixa ou alta. Se você mudar para um tempo gráfico inferior, todos os padrões acima podem se tornar uma "estrela da manhã" ou uma "estrela da tarde".

Em outras palavras, cada padrão é uma função desde o início do cálculo do tempo e da escala. Em particular, ao mudar o ponto de partida, nós podemos detectar uma figura e interceptar todas as outras que lhe são equivalentes.

Os padrões de reversão (que são muito populares entre os traders, pois permitem determinar o início de um movimento) podem ser apresentados da seguinte forma simplificada:

Reversões de preço e estrutura das velas equivalentes

Reversões de preço e estrutura das velas equivalentes

A figura mostra uma apresentação esquemática de uma reversão para cima e uma reversão para baixo, cada uma delas com duas variantes de configuração da barra. Nós já vimos que pode haver mais padrões com significados semelhantes. Não seria razoável rastrear todos eles. Uma solução mais conveniente seria a capacidade de determinar os padrões de velas para qualquer ponto inicial.

Além disso, a transformação do tempo pode representar um deslocamento aceitável em relação ao GMT para algum outro fuso horário, o que significa teoricamente que esses novos padrões de vela também deveriam funcionar nesta nova área, como os formados em nosso fuso horário. Como os servidores de negociação estão localizados em todo o mundo, em todos os fusos horários, em algum lugar, os traders podem ver sinais absolutamente diferentes. Cada sinal é valioso.

Nós podemos chegar à conclusão de que os fãs de padrões de velas devem levar em consideração sua variabilidade dependendo do ponto de partida. É aqui que os símbolos personalizados são úteis.

Um sinal personalizado baseado no símbolo de trabalho permite a construção de barras e ticks com horários deslocados segundo um valor especificado tanto para o futuro quanto para o passado. Este valor de deslocamento pode ser interpretado como parte de uma barra de qualquer tempo gráfico selecionado. Os preços e o movimento não mudam, mas isso ainda pode fornecer um resultado interessante. Em primeiro lugar, ele fornece a detecção dos padrões de velas que passariam despercebidos sem tal mudança. Em segundo lugar, nós podemos ver uma barra incompleta à frente.

Por exemplo, se você mudar as cotações para 5 minutos à frente, as barras em um gráfico M15 serão abertas e fechadas um terço antes do gráfico de origem (no 10º, 25º, 40º, 55º minuto de uma hora). Se o deslocamento for insignificante, os padrões nos gráficos original e personalizado serão quase idênticos, mas os sinais (calculados por barras, incluindo os sinais de indicadores) do gráfico personalizado virão antes.

A criação de tal símbolo personalizado com deslocamento temporal é implementado no Expert Advisor TimeShift.mq5.

O valor de deslocamento é especificado no parâmetro de deslocamento de origem (em segundos). O EA funciona usando os ticks, permitindo calcular no início o histórico de cotações transformadas, a partir da data especificada no parâmetro Start. Em seguida, os ticks são processados de forma online se o modo de geração de evento da OnTick estiver habilitado, para o qual o parâmetro EmulateTicks é fornecido (true por padrão).

A conversão de tempo é realizada de forma simples, que é para o histórico de ticks e para os ticks online: por exemplo, a função 'adicionar' é usada no último caso:

  ulong lastTick;
  
  void add()
  {
    MqlTick array[];
    int size = CopyTicksRange(_Symbol, array, COPY_TICKS_ALL, lastTick + 1, LONG_MAX);
    if(size > 0)
    {
      lastTick = array[size - 1].time_msc;
      for(int i = 0; i < size; i++)
      {
        array[i].time += Shift;
        array[i].time_msc += Shift * 1000;
      }
      if(CustomTicksAdd(symbolName, array) == -1)
      {
        Print("Tick error: ", GetLastError());
      }
    }
  }
  
  void OnTick(void)
  {
    ...
    if(EmulateTicks)
    {
      add();
    }
  }

Os gráficos EURUSD H1 de origem e modificado são exibidos abaixo.

Gráfico EURUSD H1 com o Expert Advisor TimeShift

Gráfico EURUSD H1 com o Expert Advisor TimeShift

Após uma mudança de meia barra (30 minutos), a imagem muda.

O gráfico personalizado EURUSD H1 com um deslocamento de meia hora

O gráfico personalizado EURUSD H1 com um deslocamento de meia hora

Aqueles que estão bem familiarizados com os padrões de velas, definitivamente notarão muitas diferenças, incluindo novos sinais que não estavam presentes no gráfico original. Assim, ao aplicar um indicador de vela comum em um gráfico de símbolo personalizado, podemos receber o dobro de alertas e oportunidades de negociação. O processo pode até ser automatizado usando os Expert Advisors, mas aqui precisamos ensinar o EA a negociar um símbolo real de um gráfico de símbolo personalizado. Esta tarefa será considerada no final do artigo. Agora, vamos considerar outro tipo de gráfico personalizado, que provavelmente é o mais popular - Renko.

Renko

O Expert Advisor não operacional RenkoTicks.mq5 será usado para implementar os gráficos Renko. O EA gera o renko como cotações de símbolo personalizadas enquanto processa os ticks reais (disponível na MetaTrader 5 de sua corretora). As cotações (barras) do símbolo original e o período do gráfico de trabalho na qual o RenkoTicks é colocado são irrelevantes.

Ao gerar o renko, uma alternativa aos símbolos personalizados poderia ser um indicador ou um desenho (usando objetos ou uma tela), mas em ambos os casos seria impossível usar indicadores ou scripts nos pseudo-gráficos resultantes.

Todas as barras do renko são formadas no período M1. Isso é feito intencionalmente, pois às vezes as barras de renko podem ser formadas muito rapidamente uma após a outra, por exemplo, durante períodos de alta volatilidade, e o tempo entre as barras deve ser o mais curto possível. Um minuto é a distância mínima suportada na MetaTrader 5. É por isso que o gráfico renko deve sempre ter o tempo gráfico M1. Não faz sentido mudar o gráfico do renko para outro tempo gráfico. O tempo de início de cada barra de 1 minuto corresponde ao tempo em que a formação da barra de renko apropriada começa. O tempo de conclusão da barra de um minuto é "falso" e você deve verificar o início da próxima barra de 1 minuto.

Infelizmente, às vezes várias barras de renko devem ser formadas em um minuto. Como a MetaTrader 5 não permite isso, o EA gera barras como sequências de barras M1 adjacentes, aumentando artificialmente a sua contagem a cada minuto. Como resultado, o horário formal das barras de renko pode não coincidir com o horário real (pode estar antes dele). Por exemplo, com um tamanho de renko de 100 pips, um movimento de 300 pips ocorrido às 12:00:00 que levou 10 segundos teria criado barras de renko às 12:00:00, 12:00:05, 12:00:10. Em vez disso, o EA gerará barras às 12:00, 12:01 e 12:02.

Quando isso acontece no histórico de cotações, o seguinte problema pode surgir: as barras renko transferidas do passado se sobreporão a outras barras formadas a partir das barras posteriores do gráfico original. Suponha que outro movimento de 100 pontos aconteça às 12h02 e então precisamos gerar uma barra de renko com tempo de abertura 12h02, mas dessa vez ela já está ocupada! Para resolver esses conflitos, o Expert Advisor tem um modo especial com um aumento forçado do próximo tempo da barra formada em 1 minuto, se a contagem necessária já estiver ocupada. Este modo é definido pelo parâmetro SkipOverflows que é definido como false por padrão (as barras não se sobrepõem, em vez disso, elas se movem para o futuro, se necessário). Se SkipOverflows for true, as barras com tempos sobrepostos se sobrescreverão e o renko resultante não será totalmente correto.

Observe que tal situação com um movimento forte e a geração de várias barras "à frente" é possível em tempo real - neste caso, as barras realmente se formarão no futuro! Em nosso exemplo, as barras do renko com horários de abertura de 12:00, 12:01, 12:02 existirão às 12:00:10! Isso deve ser levado em consideração na análise e na negociação.

Existem algumas maneiras de resolver esse problema, por exemplo, aumentar o tamanho da barra do renko. No entanto, tem uma desvantagem óbvia - isso diminuirá a precisão do renko, ou seja, ele registrará movimentos de cotação mais bruscos e gerará menos barras. Outra forma possível é embalar (deslocar para a esquerda) as barras mais antigas, mas isso requer o redesenho de indicadores ou objetos.

Devido aos recursos específicos da plataforma, o EA gera ticks fictícios com um tempo igual ao tempo de abertura da última barra renko. Seu único propósito é lançar o manipulador da OnTick em um Expert Advisor de negociação. Se os ticks fossem traduzidos do símbolo original para um símbolo personalizado sem alterações, isso estragaria a própria estrutura do renko. Novamente, nós podemos tomar o movimento forte como exemplo e tentar enviar para um gráfico renko um tick com uma hora real às 12:00:00. Mas o tempo deste tick não corresponderá a última barra 0 (atual), mas a barra 2 com o horário de abertura às 12:00. Como resultado, esse tick estragará a barra do renko das 12:00 (que está no histórico) ou produzirá um erro. O Renko pode ser prejudicado por uma situação oposta, quando o movimento é muito lento. Se as cotações ficarem na faixa de uma barra durante muito tempo, a barra do renko permanecerá com o mesmo tempo de abertura, enquanto os novos ticks podem ter um horário que supere em mais de um minuto a barra 0 do renko. Se tais ticks forem enviados para o gráfico renko, isso formará barras fantasmas no "futuro".

Observe que os ticks do histórico do renko são formados em um estilo minimalista, 1 tick por caixa. Ao trabalhar online, todos os ticks são enviados para o renko.

Semelhante a outros símbolos personalizados, esta abordagem nos permite usar quaisquer indicadores, scripts e objetos nos gráficos renko, bem como negociar usando Expert Advisors.

Parâmetros Principais

  • RenkoBoxSize — tamanho da barra do renko em pontos, 100 por padrão.
  • ShowWicks — pavio mostrando a bandeira, true por padrão.
  • EmulateOnLineChart — flag do envio do tick, true por padrão.
  • OutputSymbolName — nome do símbolo personalizado para o renko gerado, uma string vazia por padrão — o nome é formado como "Symbol_T_Type_Size", onde Symbol é o símbolo de trabalho atual, T é o sinal do modo de ticks, Type — "r" (renko) com pavio ou "b" (tijolo) sem pavio, Tamanho — RenkoBoxSize; exemplo: "EURUSD_T_r100".
  • Reset — flag para o recálculo de todo o gráfico renko, definido como false por padrão. Se você definir como true, é recomendável aguardar o resultado e defini-lo novamente como false para evitar o recálculo a cada reinicialização do terminal. Este modo é útil quando a geração de barras de renko falhar em alguma posição. Normalmente, a opção está sempre desabilitada, pois o EA pode continuar o cálculo a partir da última barra do renko disponível.
  • StartFrom, StopAt — início e fim do período do histórico; zeros são usados por padrão, o que significa que todo o histórico disponível será usado. Durante o primeiro uso do EA, é recomendado definir StartFrom para um passado recente, a fim de avaliar a velocidade do sistema quando ele gera as barras do renko por ticks reais.
  • SkipOverflows — flag para processar conflitos de sobreposição de caixas; definido como falso por padrão, o que significa que o novo tempo da barra será forçado a aumentar em 1 minuto, se o cálculo necessário já estiver ocupado pela caixa anterior.
  • CloseTimeMode — se true, as caixas são formadas inteiramente no momento do fechamento (um evento de "tick" por caixa); false por padrão.

A classe Renko lida com o fluxo de tick e cria novas barras de renko em sua base. Seus principais componentes são indicados no seguinte pseudocódigo:

  class Renko
  {
    protected:
      bool incrementTime(const datetime time);
      void doWriteStruct(const datetime dtTime, const double dOpen, const double dHigh, const double dLow, const double dClose, const double dVol, const double dRealVol, const int spread);
      
    public:
      datetime checkEnding();
      void continueFrom(const datetime time);
      void doReset();
  
      void onTick(const MqlTick &t);
      void updateChartWindow(const double bid = 0, const double ask = 0);
  };

Os métodos protegidos incrementTime e doWriteStruct, respectivamente, mudam para a próxima amostra M1 livre, mais próxima do tempo especificado, para a próxima caixa de renko e gravam a própria barra usando a chamada CustomRatesUpdate. Os primeiros três métodos na parte pública são responsáveis por inicializar o algoritmo na inicialização. O Expert Advisor pode verificar a existência de cotações Renko anteriores (isso é feito pelo método checkEnding, que retorna a data e hora do término do histórico) e, dependendo se existem ou não, o EA continua a partir do momento especificado usando o método continueFrom (restaurando os valores das variáveis internas), ou usa doReset para lidar com ticks de um estado "vazio".

O método onTick é chamado a cada tick (tanto no histórico quanto online) e, se necessário, forma uma barra de renko usando a doWriteStruct (eu usei um algoritmo do famoso EA RenkoLiveChart.mq4 com algumas correções). Se a emulação do tick for especificada nas configurações do EA, a updateChartWindow é adicionalmente chamada. Os códigos-fonte completos estão anexados abaixo.

A classe TickProvider é responsável pela "entrega" dos ticks para o objeto Renko:

  class TickProvider
  {
    public:
      virtual bool hasNext() = 0;
      virtual void getTick(MqlTick &t) = 0;
  
      bool read(Renko &r)
      {
        while(hasNext() && !IsStopped())
        {
          MqlTick t;
          getTick(t);
          r.onTick(t);
        }
        
        return IsStopped();
      }
  };

Ela é abstrata, pois declara uma interface comum para leitura/recebimento dos ticks de duas fontes diferentes: o histórico de ticks do símbolo base no EA e a fila de eventos OnTick ao trabalhar online. O método 'read' é um loop de ticks universal que usa os métodos virtuais hasNext() e getTick().

O histórico de ticks é lido na classe HistoryTickProvider de uma maneira familiar: ele usa a CopyTicksRange e o buffer intermediário MqlTick array[], no qual os ticks são solicitados por dia:

  class HistoryTickProvider : public TickProvider
  {
    private:
      datetime start;
      datetime stop;
      ulong length;     // in seconds
      MqlTick array[];
      int size;
      int cursor;
      
      int numberOfDays;
      int daysCount;
      
    protected:
      void fillArray()
      {
        cursor = 0;
        do
        {
          size = CopyTicksRange(_Symbol, array, COPY_TICKS_ALL, start * 1000, MathMin(start + length, stop) * 1000);
          Comment("Processing: ", DoubleToString(daysCount * 100.0 / (numberOfDays + 1), 0), "% ", TTSM(start));
          if(size == -1)
          {
            Print("CopyTicksRange failed: ", GetLastError());
          }
          else
          {
            if(size > 0 && array[0].time_msc < start * 1000) // prevent older than requested data returned
            {
              start = stop;
              size = 0;
            }
            else
            {
              start = (datetime)MathMin(start + length, stop);
              if(size > 0) daysCount++;
            }
          }
        }
        while(size == 0 && start < stop);
      }
    
    public:
      HistoryTickProvider(const datetime from, const long secs, const datetime to = 0): start(from), stop(to), length(secs), cursor(0), size(0)
      {
        if(stop == 0) stop = TimeCurrent();
        numberOfDays = (int)((stop - start) / DAY_LONG);
        daysCount = 0;
        fillArray();
      }
  
      bool hasNext() override
      {
        return cursor < size;
      }
  
      void getTick(MqlTick &t) override
      {
        if(cursor < size)
        {
          t = array[cursor++];
          if(cursor == size)
          {
            fillArray();
          }
        }
      }
  };

A classe de provedor de ticks online CurrentTickProvider:

  class CurrentTickProvider : public TickProvider
  {
    private:
      bool ready;
      
    public:
      bool hasNext() override
      {
        ready = !ready;
        return ready;
      }
      
      void getTick(MqlTick &t) override
      {
        SymbolInfoTick(_Symbol, t);
      }
  };

A parte principal do processamento de ticks, de maneira breve, é assim:

  const long DAY_LONG = 60 * 60 * 24;
  bool _FirstRun = true;
  
  Renko renko;
  CurrentTickProvider online;
  
  void OnTick(void)
  {
    if(_FirstRun)
    {
      // find existing renko tail to supersede StartFrom
      const datetime trap = renko.checkEnding();
      if(trap > TimeCurrent())
      {
        Print("Symbol/Timeframe data not ready...");
        return;
      }
      if((trap == 0) || Reset) renko.doReset();
      else renko.continueFrom(trap);
  
      HistoryTickProvider htp((trap == 0 || Reset) ? StartFrom : trap, DAY_LONG, StopAt);
      
      const bool interrupted = htp.read(renko);
      _FirstRun = false;
      
      if(!interrupted)
      {
        Comment("RenkoChart (" + (string)RenkoBoxSize + "pt): open ", _SymbolName, " / ", renko.getBoxCount(), " bars");
      }
      else
      {
        Print("Interrupted. Custom symbol data is inconsistent - please, reset or delete");
      }
    }
    else if(StopAt == 0) // process online if not stopped explicitly
    {
      online.read(renko);
    }
  }

Na primeira inicialização, busca-se o fim do histórico do renko, o objeto HistoryTickProvider é criado com o horário de início StartFrom ou do histórico (se encontrado) e, em seguida, todos os ticks são lidos. Todos os tiques adicionais são processados de maneira online através do objeto CurrentTickProvider (ele é criado no contexto global, assim como o objeto Renko).

Vamos gerar um gráfico renko baseado no EURUSD com um tamanho de barra de 100 pips a partir de 2019. Para fazer isso, executamos o EA no gráfico EURUSD H1 com as configurações padrão, exceto para StartFrom. O intervalo de tempo importa apenas quando o EA é reiniciado com o histórico de renko disponível - neste caso, o recálculo do renko começará com um recuo para o tempo da barra em que o último bloco de renko cai.

Por exemplo, para o EURUSD H1 original:

Gráfico EURUSD H1 com o EA RenkoTicks

Gráfico EURUSD H1 com o EA RenkoTicks

obteremos o seguinte gráfico:

Gráfico renko EURUSD com tamanho de bloco de 100 pontos

Gráfico renko EURUSD com tamanho de bloco de 100 pontos

Para maior clareza visual, eu adicionei duas MAs.

Agora que nós recebemos as cotações para o símbolo renko, é hora de desenvolver um EA de teste para negociação.

Um Expert Advisor de negociação com base em uma interseção de duas MAs

Vamos usar uma das estratégias de negociação mais simples, que é a interseção de duas médias móveis. A imagem anterior demonstra a ideia. Quando a MA rápida (vermelha) cruzar a MA lenta (azul) para cima ou para baixo, abrimos Compra ou Venda, respectivamente. Este é um sistema de reversão.

Seria difícil criar um Expert Advisor do zero, mas a MetaTrader 5 oferece o Assistente MQL que pode gerar Expert Advisors com base em uma biblioteca de classes padrão (fornecida com a plataforma). Ele é muito conveniente para os traders que não estão familiarizados com programação. A estrutura de código resultante é comum para um grande número de robôs e, portanto, é uma boa ideia usá-la para a tarefa principal - para adaptar robôs para negociação de símbolos personalizados. Os Expert Advisors criados sem uma biblioteca padrão também podem ser adaptados de acordo com o mesmo método, mas como sua criação pode diferir muito, o programador terá que fornecer as alterações apropriadas, se necessário (em geral, programadores experientes podem adaptar quaisquer outros EAs usando nosso exemplo).

Curiosamente, a biblioteca padrão não tem sinal de interseção de duas MAs, embora seja uma das estratégias mais populares (pelo menos é a mais popular ao aprender negociação algorítmica). E, portanto, nós precisamos escrever o módulo de sinal apropriado. Vamos chamá-lo de Signal2MACross.mqh. Abaixo está o código que atende às regras exigidas para o sinal a ser usado com o Assistente MQL.

Ele começa com um "cabeçalho" — um comentário especial com a descrição do sinal no formato apropriado, que o torna acessível a partir do MetaEditor:

  //--- wizard description start
  //+------------------------------------------------------------------+
  //| Description of the class                                         |
  //| Title=Signals of 2 MAs crosses                                   |
  //| Type=SignalAdvanced                                              |
  //| Name=2MA Cross                                                   |
  //| ShortName=2MACross                                               |
  //| Class=Signal2MACross                                             |
  //| Page=signal_2mac                                                 |
  //| Parameter=SlowPeriod,int,11,Slow MA period                       |
  //| Parameter=FastPeriod,int,7,Fast Ma period                        |
  //| Parameter=MAMethod,ENUM_MA_METHOD,MODE_LWMA,Method of averaging  |
  //| Parameter=MAPrice,ENUM_APPLIED_PRICE,PRICE_OPEN,Price type       |
  //| Parameter=Shift,int,0,Shift                                      |
  //+------------------------------------------------------------------+
  //--- wizard description end

O nome da classe (line Class) deve corresponder ao nome de uma classe real no seguinte código em MQL. O sinal tem 5 parâmetros típicos para duas MAs: 2 períodos (rápido e lento), método de cálculo da média, tipo de preço e deslocamento.

A classe foi herdada da CExpertSignal. Ela contém duas instâncias de objetos de indicadores CiMA, variáveis com parâmetros de trabalho, métodos de definição de parâmetros (os nomes dos métodos devem corresponder aos nomes no cabeçalho). Além disso, a classe redefiniu os métodos virtuais que são chamados durante a inicialização do indicador, bem como ao verificar as configurações e determinar os sinais de Compra e Venda.

  class Signal2MACross : public CExpertSignal
  {
    protected:
      CiMA              m_maSlow;         // object-indicator
      CiMA              m_maFast;         // object-indicator
      
      // adjustable parameters
      int               m_slow;
      int               m_fast;
      ENUM_MA_METHOD    m_method;
      ENUM_APPLIED_PRICE m_type;
      int               m_shift;
      
      // "weights" of market models (0-100)
      int               m_pattern_0;      // model 0 "fast MA crosses slow MA"
  
    public:
                        Signal2MACross(void);
                       ~Signal2MACross(void);
                       
      // parameters setters
      void              SlowPeriod(int value) { m_slow = value; }
      void              FastPeriod(int value) { m_fast = value; }
      void              MAMethod(ENUM_MA_METHOD value) { m_method = value; }
      void              MAPrice(ENUM_APPLIED_PRICE value) { m_type = value; }
      void              Shift(int value) { m_shift = value; }
      
      // adjusting "weights" of market models
      void              Pattern_0(int value) { m_pattern_0 = value; }
      
      // verification of settings
      virtual bool      ValidationSettings(void);
      
      // creating the indicator and timeseries
      virtual bool      InitIndicators(CIndicators *indicators);
      
      // checking if the market models are formed
      virtual int       LongCondition(void);
      virtual int       ShortCondition(void);
  
    protected:
      // initialization of the indicators
      bool              InitMAs(CIndicators *indicators);
      
      // getting data
      double            FastMA(int ind) { return(m_maFast.Main(ind)); }
      double            SlowMA(int ind) { return(m_maSlow.Main(ind)); }
  };

A classe descreve a única estratégia (padrão ou modelo): quando a MA rápida cruzar a lento, uma Compra (para cruzar para cima) ou uma Venda (para cruzar para baixo) é inicializada. O peso do modelo é igual a 100 por padrão.

  Signal2MACross::Signal2MACross(void) : m_slow(11), m_fast(7), m_method(MODE_LWMA), m_type(PRICE_OPEN), m_shift(0), m_pattern_0(100)
  {
  }

As condições de abertura de posição são determinadas pelos dois métodos a seguir (estritamente falando, o código não verifica a interseção, mas o posicionamento de uma MA em relação à outra, enquanto o efeito será o mesmo para o sistema que está sempre no mercado, no entanto o código é mais simples):

  int Signal2MACross::LongCondition(void)
  {
    const int idx = StartIndex();
    
    if(FastMA(idx) > SlowMA(idx))
    {
      return m_pattern_0;
    }
    return 0;
  }
  
  int Signal2MACross::ShortCondition(void)
  {
    const int idx = StartIndex();
  
    if(FastMA(idx) < SlowMA(idx))
    {
      return m_pattern_0;
    }
    return 0;
  }

A função StartIndex é definida na classe pai. Como você pode ver no código, o índice é o número da barra para o qual o sinal é analisado. Se cada operação baseada em tick for selecionada nas configurações do EA (Expert_EveryTick = true, veja mais adiante), então o índice inicial é igual a 0; caso contrário (ou seja, opera por barras fechadas), o índice é 1.

Salve o arquivo Signal2MACross.mqh na pasta MQL5/Include/Expert/Signal/MySignals e reinicie o MetaEditor (se estiver em execução) para selecionar o novo módulo no Assistente MQL.

Agora nós podemos gerar um Expert Advisor com base em nosso sinal. Selecione 'Arquivo' -> 'Novo' no menu e abra a caixa de diálogo do Assistente. Em seguida, siga as etapas abaixo:

  1. selecione "Expert Advisor (gerar)"
  2. defina o nome do EA, por exemplo Experts\Examples\MA2Cross
  3. adicione o sinal "Signals of 2 MAs crosses"
  4. selecione "Trailing stop não usado"
  5. selecione "Negociação com volume fixo"

Como resultado, você receberá o EA com o seguinte código:

  #include <Expert\Expert.mqh>
  #include <Expert\Signal\MySignals\Signal2MACross.mqh>
  #include <Expert\Trailing\TrailingNone.mqh>
  #include <Expert\Money\MoneyFixedLot.mqh>
  
  //+------------------------------------------------------------------+
  //| Inputs                                                           |
  //+------------------------------------------------------------------+
  // inputs for expert
  input string             Expert_Title              = "MA2Cross";  // Document name
  ulong                    Expert_MagicNumber        = 7623;
  bool                     Expert_EveryTick          = false;
  // inputs for main signal
  input int                Signal_ThresholdOpen      = 10;          // Signal threshold value to open [0...100]
  input int                Signal_ThresholdClose     = 10;          // Signal threshold value to close [0...100]
  input double             Signal_PriceLevel         = 0.0;         // Price level to execute a deal
  input double             Signal_StopLevel          = 0.0;         // Stop Loss level (in points)
  input double             Signal_TakeLevel          = 0.0;         // Take Profit level (in points)
  input int                Signal_Expiration         = 0;           // Expiration of pending orders (in bars)
  input int                Signal_2MACross_SlowPeriod = 11;         // 2MA Cross(11,7,MODE_LWMA,...) Slow MA period
  input int                Signal_2MACross_FastPeriod = 7;          // 2MA Cross(11,7,MODE_LWMA,...) Fast Ma period
  input ENUM_MA_METHOD     Signal_2MACross_MAMethod  = MODE_LWMA;   // 2MA Cross(11,7,MODE_LWMA,...) Method of averaging
  input ENUM_APPLIED_PRICE Signal_2MACross_MAPrice   = PRICE_OPEN;  // 2MA Cross(11,7,MODE_LWMA,...) Price type
  input int                Signal_2MACross_Shift     = 0;           // 2MA Cross(11,7,MODE_LWMA,...) Shift
  input double             Signal_2MACross_Weight    = 1.0;         // 2MA Cross(11,7,MODE_LWMA,...) Weight [0...1.0]
  // inputs for money
  input double             Money_FixLot_Percent      = 10.0;        // Percent
  input double             Money_FixLot_Lots         = 0.1;         // Fixed volume
  
  //+------------------------------------------------------------------+
  //| Global expert object                                             |
  //+------------------------------------------------------------------+
  CExpert ExtExpert;
  
  //+------------------------------------------------------------------+
  //| Initialization function of the expert                            |
  //+------------------------------------------------------------------+
  int OnInit()
  {
    // Initializing expert
    if(!ExtExpert.Init(Symbol(), Period(), Expert_EveryTick, Expert_MagicNumber))
    {
      printf(__FUNCTION__ + ": error initializing expert");
      ExtExpert.Deinit();
      return(INIT_FAILED);
    }
    // Creating signal
    CExpertSignal *signal = new CExpertSignal;
    if(signal == NULL)
    {
      printf(__FUNCTION__ + ": error creating signal");
      ExtExpert.Deinit();
      return(INIT_FAILED);
    }
    
    ExtExpert.InitSignal(signal);
    signal.ThresholdOpen(Signal_ThresholdOpen);
    signal.ThresholdClose(Signal_ThresholdClose);
    signal.PriceLevel(Signal_PriceLevel);
    signal.StopLevel(Signal_StopLevel);
    signal.TakeLevel(Signal_TakeLevel);
    signal.Expiration(Signal_Expiration);
    
    // Creating filter Signal2MACross
    Signal2MACross *filter0 = new Signal2MACross;
    if(filter0 == NULL)
    {
      printf(__FUNCTION__ + ": error creating filter0");
      ExtExpert.Deinit();
      return(INIT_FAILED);
    }
    signal.AddFilter(filter0);
    
    // Set filter parameters
    filter0.SlowPeriod(Signal_2MACross_SlowPeriod);
    filter0.FastPeriod(Signal_2MACross_FastPeriod);
    filter0.MAMethod(Signal_2MACross_MAMethod);
    filter0.MAPrice(Signal_2MACross_MAPrice);
    filter0.Shift(Signal_2MACross_Shift);
    filter0.Weight(Signal_2MACross_Weight);
  
    ...
    
    // Check all trading objects parameters
    if(!ExtExpert.ValidationSettings())
    {
      ExtExpert.Deinit();
      return(INIT_FAILED);
    }
    
    // Tuning of all necessary indicators
    if(!ExtExpert.InitIndicators())
    {
      printf(__FUNCTION__ + ": error initializing indicators");
      ExtExpert.Deinit();
      return(INIT_FAILED);
    }
  
    return(INIT_SUCCEEDED);
  }

O código completo de MA2Cross.mq5 está anexado ao artigo. Tudo está pronto para compilação, teste no Testador de Estratégia e até otimização em qualquer símbolo, incluindo o nosso renko personalizado. Como nós estamos interessados exatamente no renko, nós precisamos explicar alguns momentos.

Cada bloco renko, em sua forma "retangular", não existe até que ele seja completamente formado pelo movimento do preço. Quando o próximo bloco aparece, nós não sabemos nem o seu preço de fechamento e nem o seu preço de abertura, porque existem duas direções opostas possíveis: para cima e para baixo. Quando o bloco finalmente fecha, é o preço de fechamento que é decisivo e mais característico. É por isso que o valor padrão do parâmetro Signal_2MACross_MAPrice no EA é alterado para PRICE_CLOSE - não é recomendado alterá-lo. Você pode experimentar outros tipos de preços, mas a ideia do renko não é apenas desvincular do tempo, mas também remover pequenas flutuações de preço, o que é conseguido quantizando de acordo com o tamanho do tijolo.

Observe que a barra 0 do renko está sempre incompleta (na maioria dos casos é uma vela sem corpo, não um retângulo), por isso usamos o sinal da barra 1. Para este propósito, nós definimos o valor do parâmetro Expert_EveryTick igual a false.

Geramos o renko personalizado baseado no EURUSD com tamanho de bloco de 100 pontos. Como resultado, nós obtemos o símbolo EURUSD_T_r100. Selecione ele no testador. Certifique-se de definir o tempo gráfico M1.

Vamos ver como o Expert Advisor se comporta neste símbolo para o período 2019-2020 (primeiro semestre), por exemplo, com períodos padrão de 7 e 11 (outras combinações podem ser verificadas independentemente usando a otimização).

O resultado da estratégia MA2CrossCustom no gráfico renko de 100 pontos derivado do EURUSD

O resultado da estratégia MA2CrossCustom no gráfico renko de 100 pontos derivado do EURUSD

Para comparar o símbolo personalizado com um símbolo real, eu forneço aqui um relatório do EA MA2CrossCustom que é semelhante ao MA2Cross com um parâmetro WorkSymbol vazio. Na próxima seção, nós consideraremos como obter o MA2CrossCustom de MA2Cross.

Como pode ser visto na tabela de negociações, os negócios são executados a preços que são múltiplos do tamanho do bloco: os preços de venda coincidem completamente e os preços de compra diferem pelo tamanho do spread (nosso gerador de renko salva para cada barra o valor máximo do spread registrado durante sua formação, se desejar, é possível alterar este comportamento no código-fonte). O Renko é construído pelo tipo de preço usado no gráfico de origem. No nosso caso, ele é o bid. O último preço será usado para instrumentos de bolsa.

Tabela de negócios ao negociar com o símbolo renko personalizado baseado no EURUSD

Tabela de negócios ao negociar com o símbolo renko personalizado baseado no EURUSD

Agora, o resultado parece bom demais para ser verdade. Na verdade, existem muitas nuances ocultas.

A negociação de símbolos Renko no testador afeta a precisão dos resultados em qualquer modo: por preço de abertura, M1 OHLC e por ticks.

O preço de abertura da barra de um renko padrão nem sempre é alcançado no momento em que a barra é marcada, mas em muitos casos é alcançado mais tarde (porque o preço "sobe" e desce por um tempo dentro do tamanho do renko e pode eventualmente mudar de direção, formando uma barra de reversão). O tempo de marcação da barra é o tempo de conclusão da barra anterior.

O preço de fechamento não corresponde ao horário de fechamento, pois a barra de renko é a barra M1, ou seja, tem duração fixa de 1 minuto.

É possível gerar um renko não padrão, no qual as barras são marcadas com a hora de conclusão, não a hora de início. Então, o preço de fechamento corresponderá ao horário de fechamento. No entanto, o horário de abertura será 1 minuto antes do horário de fechamento e, portanto, não corresponderá ao preço de abertura real (é o preço de fechamento mais/menos o tamanho do renko).

A análise do Renko deve ser realizada pelas barras formadas, mas seu preço característico é o preço de fechamento, mas durante a operação barra a barra, o testador fornece apenas o preço de abertura para a (última) barra atual (não há modo por preços de fechamento). Aqui, os preços de abertura da barra são preditores por definição. Se nós usarmos os sinais das barras fechadas (geralmente a partir da 1ª), as negociações serão executadas de qualquer maneira ao preço atual da barra 0. Mesmo se nós usarmos o modo de tick, o testador gera ticks para o renko de acordo com as regras comuns, usando pontos de referência com base na configuração de cada barra. O testador não leva em consideração a estrutura e o comportamento específico das cotações do renko (que estamos tentando emular visualmente com as barras M1). Se nós hipoteticamente imaginarmos uma formação única de uma barra inteira, ela ainda terá um corpo - e para tais barras o testador gera ticks a partir do preço de abertura. Se nós definirmos o volume do tick da barra igual a um, a barra perderá a configuração (se tornará um rótulo do preço igual ao OHLC).

Assim, todos os métodos de construção do renko terão artefatos de execução de ordem ao testar um símbolo renko personalizado.

Em outras palavras, por causa da própria estrutura do renko, nós temos um graal de teste nos símbolos do renko porque ele aponta para o futuro em um passo igual ao tamanho da barra do renko.

É por isso que é necessário testar o sistema de negociação não em uma barra renko separada, mas combinado com a execução de ordens de negociação em um símbolo real.

O Renko fornece análises e "timing" - quando entrar no mercado.

Até agora, nós testamos apenas a capacidade do EA de negociar com um símbolo personalizado. Isso define uma limitação na aplicação do EA apenas no testador. Para tornar o EA universal, ou seja, ser capaz de negociar o símbolo original online durante a execução do gráfico renko, nós precisamos adicionar algumas coisas. Isso também ajudará a resolver o problema com resultados otimistas demais.

Adaptação de Expert Advisors para negociação em gráficos de símbolos personalizados

Um símbolo personalizado é conhecido apenas pela plataforma do cliente e não existe no servidor de negociação. Obviamente, um Expert Advisor ajustado em um gráfico de símbolo personalizado deve gerar todas as ordens de negociação para o símbolo original (no qual o símbolo personalizado é baseado). Como a solução mais simples para este problema, o EA pode ser executado no gráfico de símbolo original, mas recebe sinais (por exemplo, de indicadores) de um símbolo personalizado. No entanto, muitos traders preferem ver o quadro completo. Além disso, as modificações seletivas de código podem produzir erros. É desejável minimizar as edições no código-fonte.

Infelizmente, o nome do símbolo original e do renko criado com base nele não podem ser vinculados por meio da própria plataforma. Uma solução conveniente seria ter um campo de string "origin" ou "parent" nas propriedades do símbolo personalizado, no qual nós poderíamos escrever o nome do símbolo real. Ele estaria vazio por padrão. Mas, quando preenchida, a plataforma substituiria automaticamente o símbolo em todas as ordens de negociação e solicitações do histórico. Como esse mecanismo não existe na plataforma, nós teremos que implementá-lo nós mesmos. Os nomes da fonte e do símbolo personalizado serão definidos usando os parâmetros. As propriedades do símbolo personalizado têm um campo com um significado adequado - SYMBOL_BASIS. Mas como não podemos garantir que os geradores arbitrários de símbolos personalizados (quaisquer programas MQL) preencherão o parâmetro corretamente ou o usarão exatamente para esse propósito, nós precisamos fornecer outra solução.

Para esse propósito, eu desenvolvi a classe CustomOrder (veja a CustomOrder.mqh anexada abaixo). Ela contém os métodos wrapper para todas as funções da API MQL relacionadas ao envio de ordens de negociação e solicitações de histórico, que contêm um parâmetro de string com o nome do símbolo do instrumento. Esses métodos substituem um símbolo personalizado pelo símbolo de trabalho atual ou vice-versa. Todas as outras funções da API não requerem o "hook". O código é exibido abaixo.

  class CustomOrder
  {
    private:
      static string workSymbol;
      
      static void replaceRequest(MqlTradeRequest &request)
      {
        if(request.symbol == _Symbol && workSymbol != NULL)
        {
          request.symbol = workSymbol;
          if(request.type == ORDER_TYPE_BUY
          || request.type == ORDER_TYPE_SELL)
          {
            if(request.price == SymbolInfoDouble(_Symbol, SYMBOL_ASK)) request.price = SymbolInfoDouble(workSymbol, SYMBOL_ASK);
            if(request.price == SymbolInfoDouble(_Symbol, SYMBOL_BID)) request.price = SymbolInfoDouble(workSymbol, SYMBOL_BID);
          }
        }
      }
      
    public:
      static void setReplacementSymbol(const string replacementSymbol)
      {
        workSymbol = replacementSymbol;
      }
      
      static bool OrderSend(MqlTradeRequest &request, MqlTradeResult &result)
      {
        replaceRequest(request);
        return ::OrderSend(request, result);
      }
      
      static bool OrderCalcProfit(ENUM_ORDER_TYPE action, string symbol, double volume, double price_open, double price_close, double &profit)
      {
        if(symbol == _Symbol && workSymbol != NULL)
        {
          symbol = workSymbol;
        }
        return ::OrderCalcProfit(action, symbol, volume, price_open, price_close, profit);
      }
      
      static string PositionGetString(ENUM_POSITION_PROPERTY_STRING property_id)
      {
        const string result = ::PositionGetString(property_id);
        if(property_id == POSITION_SYMBOL && result == workSymbol) return _Symbol;
        return result;
      }
      
      static string OrderGetString(ENUM_ORDER_PROPERTY_STRING property_id)
      {
        const string result = ::OrderGetString(property_id);
        if(property_id == ORDER_SYMBOL && result == workSymbol) return _Symbol;
        return result;
      }
      
      static string HistoryOrderGetString(ulong ticket_number, ENUM_ORDER_PROPERTY_STRING property_id)
      {
        const string result = ::HistoryOrderGetString(ticket_number, property_id);
        if(property_id == ORDER_SYMBOL && result == workSymbol) return _Symbol;
        return result;
      }
      
      static string HistoryDealGetString(ulong ticket_number, ENUM_DEAL_PROPERTY_STRING property_id)
      {
        const string result = ::HistoryDealGetString(ticket_number, property_id);
        if(property_id == DEAL_SYMBOL && result == workSymbol) return _Symbol;
        return result;
      }
      
      static bool PositionSelect(string symbol)
      {
        if(symbol == _Symbol && workSymbol != NULL) return ::PositionSelect(workSymbol);
        return ::PositionSelect(symbol);
      }
      
      static string PositionGetSymbol(int index)
      {
        const string result = ::PositionGetSymbol(index);
        if(result == workSymbol) return _Symbol;
        return result;
      }
      ...
  };
  
  static string CustomOrder::workSymbol = NULL;

Para minimizar as edições no código-fonte, as seguintes macros são usadas (para todos os métodos):

  bool CustomOrderSend(const MqlTradeRequest &request, MqlTradeResult &result)
  {
    return CustomOrder::OrderSend((MqlTradeRequest)request, result);
  }
  
  #define OrderSend CustomOrderSend

Eles permitem o redirecionamento automático de todas as chamadas de função API padrão para os métodos da classe CustomOrder. Para este propósito, incluímos a CustomOrder.mqh no EA e definimos o símbolo de trabalho:

  #include <CustomOrder.mqh>
  #include <Expert\Expert.mqh>
  ...
  input string WorkSymbol = "";
  
  int OnInit()
  {
    if(WorkSymbol != "")
    {
      CustomOrder::setReplacementSymbol(WorkSymbol);
      
      // force a chart for the work symbol to open (in visual mode only)
      MqlRates rates[1];
      CopyRates(WorkSymbol, PERIOD_H1, 0, 1, rates);
    }
    ...
  }

É importante que a diretiva #include <CustomOrder.mqh> venha primeiro, antes de todas as outras. Assim, ela afetará todos os códigos-fonte, incluindo as bibliotecas padrão conectadas. Se o caractere curinga não for especificado, a CustomOrder.mqh conectada não terá efeito no EA e transferirá o controle para as funções padrão da API.

O EA MA2Cross modificado foi renomeado para MA2CrossCustom.mq5.

Agora nós podemos definir o WorkSymbol para EURUSD, deixando todas as outras configurações iguais, e iniciar o teste. Agora, o EA realmente negocia o EURUSD, embora seja executado no gráfico de símbolos do renko.

O resultado da estratégia MA2CrossCustom no gráfico renko de 100 pontos ao negociar o símbolo real EURUSD

O resultado da estratégia MA2CrossCustom no gráfico renko de 100 pontos ao negociar o símbolo real EURUSD

Desta vez, o resultado está mais próximo da realidade.

Nas negociações em EURUSD, os preços diferem dos preços de fechamento da barra renko de forma mais significativa. Isso ocorre porque as barras de renko são sempre marcadas por um minuto começando (esta é uma limitação do tempo gráfico M1 na plataforma), mas o preço cruza a fronteira da barra em momentos arbitrários dentro de um minuto. Uma vez que o Expert Advisor opera no gráfico em um modo barra a barra (não deve ser confundido com o modo do testador), o surgimento do sinal é "movido" para a abertura de uma barra de minutos do EURUSD, quando o preço geralmente é diferente. Em média, o erro é a expectativa matemática do intervalo de barras de minutos por operação.

As negociações realizadas no EURUSD pelo Expert Advisor a partir de um gráfico Renko com base no EURUSD

As negociações realizadas no EURUSD pelo Expert Advisor a partir de um gráfico Renko com base no EURUSD

Para eliminar as discrepâncias, o EA teria que processar todos os ticks, mas já descobrimos que a lógica da geração de ticks no testador difere daquela da formação de renko: em particular, o preço de abertura das barras de reversão sempre tem um gap igual a um bloco do renko relativo ao fechamento da barra anterior.

Este problema não existe na negociação online.

Vamos verificar a funcionalidade do CustomOrder usando outro Expert Advisor, escrito sem usar a biblioteca padrão. Nós usaremos para isso o EA ExprBot do artigo sobre Cálculo de expressões matemáticas - ele também explora a estratégia de interseção de duas MAs e realiza a operação de negociação usando a biblioteca MT4Orders. O Expert Advisor ExprBotCustom.mq5 modificado está anexado a seguir, junto com os arquivos de cabeçalho necessários (pasta ExpresSParserS).

Seguem abaixo os resultados no intervalo 2019-2020 (primeiro semestre), com as mesmas configurações (períodos 7/11, média tipo LWMA, preços de FECHAMENTO na barra 1).

O resultado da estratégia ExprBotCustom no gráfico renko de 100 pontos com base no EURUSD

O resultado da estratégia ExprBotCustom no gráfico renko de 100 pontos com base no EURUSD

O resultado da estratégia ExprBotCustom no gráfico renko de 100 pontos ao negociar o símbolo real EURUSD

O resultado da estratégia ExprBotCustom no gráfico renko de 100 pontos ao negociar o símbolo real EURUSD

Esses resultados são muito semelhantes aos obtidos com o EA MA2CrossCustom.

Nós podemos concluir que a abordagem proposta resolve o problema. No entanto, a implementação atual de CustomOrder é apenas um mínimo básico. Melhorias podem ser necessárias dependendo da estratégia de negociação e das especificações do símbolo de trabalho.

Conclusão

Nós consideramos várias maneiras de gerar símbolos personalizados usando as cotações de símbolos de trabalho fornecidas pela corretora. Algoritmos especiais de generalização e acumulação de dados permitem ver cotações usuais de um ângulo diferente e construir sistemas de negociação avançados com base nisso.

Obviamente, os símbolos personalizados oferecem muito mais possibilidades. As aplicações potenciais dessa tecnologia são muito mais amplas. Por exemplo, nós podemos usar símbolos sintéticos, volume delta e fontes de dados de terceiros. A transformação do programa descrito permite o uso dessas possibilidades nos gráficos padrão da MetaTrader 5, no testador de estratégia e no modo online.

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/8226

Arquivos anexados |
MQL5CUST.zip (61.87 KB)
Otimização paralela pelo método de enxame de partículas (Particle Swarm Optimization) Otimização paralela pelo método de enxame de partículas (Particle Swarm Optimization)
Este artigo descreve uma forma de otimização rápida por meio do método de enxame de partículas e apresenta uma implementação em MQL pronta para ser utilizada tanto no modo thread único dentro do EA quanto no modo multi-thread paralelo com complemento que executado nos agentes locais do testador.
Trabalhando com séries temporais na biblioteca DoEasy (Parte 52): natureza multiplataforma de indicadores padrão multiperíodos multissímbolos de buffer único Trabalhando com séries temporais na biblioteca DoEasy (Parte 52): natureza multiplataforma de indicadores padrão multiperíodos multissímbolos de buffer único
Neste artigo, consideraremos a criação de um indicador padrão Accumulation/Distribution multissímbolo multiperíodo. Para que os programas escritos para a plataforma MetaTrader 4 desatualizada baseada nesta biblioteca funcionem normalmente ao mudar para o MetaTrader 5, iremos modificar ligeiramente as classes da biblioteca, a nível de indicadores.
Negociação Forex e sua matemática básica Negociação Forex e sua matemática básica
O objetivo do artigo consiste em descrever as principais características da negociação forex da forma mais simples e rápida possível, compartilhando verdades simples com iniciantes. Aqui tentaremos responder às perguntas mais interessantes no ambiente de negociação, bem como escrever um indicador simples.
Trabalhando com séries temporais na biblioteca DoEasy (Parte 51): indicadores padrão multiperíodos multissímbolos compostos Trabalhando com séries temporais na biblioteca DoEasy (Parte 51): indicadores padrão multiperíodos multissímbolos compostos
Neste artigo, vamos completar o desenvolvimento de objetos para indicadores padrão multissímbolos multiperíodos. Usando o indicador padrão Ichimoku Kinko Hyo como exemplo, analisaremos a criação de indicadores personalizados complexos que têm buffers desenhados auxiliares para exibir dados num gráfico.