English Русский 中文 Español Deutsch 日本語
Como escrever ZigZags rápidos que não são redesenhados

Como escrever ZigZags rápidos que não são redesenhados

MetaTrader 4Exemplos | 8 fevereiro 2016, 11:59
524 0
Candid
Candid

Introdução

Entre todas as possibilidades de algoritmos de gráficos de Zigzag, podemos distinguir uma classe que o autor chama de "Zigzags com troca ao romper o nível de lentidão". Essa classe, completa ou em partes, inclui a maior parte dos Zigzags existentes. O nome da classe, de fato, representa um modelo de algoritmo. Para fazer um indicador disso, é suficiente apenas adicionar a função que detectaria o nível de lentidão. A diversidade de algoritmos de tal função é limitada somente pela imaginação do autor do futuro Zigzag.

Abordagem geral

Antes de tudo, vamos tentar formular a abordagem geral para escrever um indicador. Assim:

- A função start() de qualquer indicador (assim como qualquer EA) representa uma função de callback, isto é, uma função a ser chamada para processar um evento específico. Especificamente, para processar um tick.

- O objeto de escrita de um indicador é, como regra, o cálculo de uma ou de várias características do mercado. Junto com as quantidades auxiliares necessárias para os cálculos, elas formam o conjunto principal de variáveis de um dado indicador. Vamos definir o estado do indicador como um conjunto de valores daquelas variáveis principais a um tempo específico. Com base nessa definição, podemos afirmar o seguinte:

  • Ao calcular os novos valores de variáveis a um novo tick, a função start() calcula o novo estado do indicador.
  • Assim, de fato, a função start() é um operador que transfere o indicador de um estado para outro.

- Nesses termos, o processo de escrita de um indicador se reduz para determinar um conjunto de quantidades descrevendo seu estado (variáveis do estado) e para escrever um operador que transferiria o indicador em um novo estado na chegada de um novo tick. A inicialização das variáveis do estado se torna uma parte essencial do algoritmo de indicador. Mostraremos como tudo isso pode ser feito no exemplo de ZigZags de um certo tipo.

Quais ZigZags estão em questão

Como foi dito acima, neste artigo, estamos interessados na troca de ZigZags no rompimento do nível de lentidão. O que é um "nível de lentidão"? Vamos presumir que queremos escrever um ZigZag para o qual o pico seja fixo quando o preço se mover do pico para os pontos H. Fixar um pico significa trocar a direção de um segmento ZigZag para a direção oposta. Fixamos o mínimo e agora vamos para um segmento superior. Vamos introduzir uma variável para o preço máximo de um segmento superior incompleto, TempMax. Fixaremos esse máximo (e trocaremos a direção), se o preço romper no nível de:

SwitchLevel = TempMax - H *Point .

Se o tempo máximo for atualizado antes da troca, teremos que calcular o novo valor de SwitchLevel. Assim, SwitchLevel seguirá o tempo máximo, sendo H pontos atrás dele.

A situação será absolutamente sistemática para um segmento inferior: SwitchLevel agora seguirá o tempo mínimo (TempMin ), sendo os mesmos H pontos atrás dele. Mas, agora, temos:

SwitchLevel = TempMin + H *Point .

De fato, acabamos de descrever o algoritmo para o cálculo do nível de lentidão para o ZigZag que criaremos. Obviamente, não é o único algoritmo possível. Por exemplo, se considerarmos o limite inferior/superior de um canal a ser o nível de lentidão, teremos exatamente tantos ZigZags quanto métodos de cálculo de canal. Além disso, em uma análise mais aprofundada, a absoluta maioria de ZigZags conhecidos pelo autor acabou sendo completa ou parcialmente incluída na classe sob consideração. Mas não todos eles. Por exemplo, o ZigZag calculado nos fractals de Williams não podem ser incluídos nessa classe.

Modelo de ZigZag

Agora, vamos determinar as variáveis do estado ZigZag.

Primeiro, será a direção do segmento atual. Nomearemos a variável correspondente UpZ e atribuiremos a ela os valores de true para os segmentos superiores e false para os segmentos inferiores.

Obviamente, devemos adicionar à lista TempMax e TempMin introduzidos acima. Também adicionaremos suas coordenadas de tempo. Aqui, no entanto, teremos um certo grau de liberdade para definir as unidades de medida. Como coordenada de tempo, usaremos o número da barra começando do início do gráfico, isto é, usaremos o sistema de números sendo reverso ao aceito em MT4. Isso é simplificar o código e destacar sua taxa de execução. Assim, a lista será reabastecida com as variáveis TempMaxBar e TempMinBar.

Planejamos desenhar o ZigZag em um gráfico e usá-lo de alguma forma. Então, adicionaremos à lista as coordenadas do último pico ZigZag fixado: CurMax, CurMaxBar, CurMin, CurMinBar.

E isso é tudo quanto a lista. Um autor individual de um ZigZag específico pode reabastecer livremente a lista com as coisas que fará com seu ZigZag. Por exemplo, pode acabar sendo razoável adicionar as coordenadas aos picos precedentes: PreMax, PreMaxBar, PreMin, PreMinBar. Ou pode ser necessário adicionar as coordenadas de um número pré-definido de picos precedentes, usando séries, em tal caso.

Operador de transição

Na abordagem proposta, escrever um operador de transição para um ZigZag se torna uma tarefa um pouco simples. Só precisamos traduzir a definição da classe de ZigZag que temos interesse em MQL4. É assim que ela aparecerá:

// First, process the case of an up-segment
    if (UpZ) {
// Check whether the current maximum has changed
      if (High[pos]>TempMax) {
// If yes, then correct the corresponding variables
        TempMax = High[pos];
        TempMaxBar = Bars-pos;  // Here switching to direct numbering
      } else {
// If not, then check whether the slowing level has been broken through
        if (Low[pos]<SwitchLevel()) {
// If yes, then fix the maximum
          CurMax = TempMax;
          CurMaxBar = TempMaxBar;
// And draw a peak
          ZZ[Bars-CurMaxBar]=CurMax;  // Here switching to reverse numbering
// Correct the corresponding variables
          UpZ = false;
          TempMin = Low[pos];
          TempMinBar = Bars-pos;  // Here switching to direct numbering
        }
      }
    }  else {
// Now processing the case of down-segment
// Check whether the current minimum has changed
      if (Low[pos]<TempMin) {
// If yes, then correct the corresponding variables
        TempMin = Low[pos];
        TempMinBar = Bars-pos;  // Here switching to direct numbering
      } else {
// If not, then check whether the slowing level has been broken through
        if (High[pos]>SwitchLevel()) {
// If yes, then fix the minimum
          CurMin = TempMin;
          CurMinBar = TempMinBar;
// And draw a peak
          ZZ[Bars-CurMinBar]=CurMin;  // Here switching to reverse numbering
// Correct the corresponding variables
          UpZ = true;
          TempMax = High[pos];
          TempMaxBar = Bars-pos;  // Here switching to direct numbering
       }
      }
    }

O operador de transição está pronto. Agora, podemos nos referir às variáveis de estado do indicador a qualquer momento.

No entanto, tal operador tem um recurso especial que pode ser percebido como um erro desenhar o ZigZag. Vamos considerar o fragmento abaixo em mais detalhes.

      if (High[pos]>TempMax) {
// If yes, then correct the corresponding variables
        TempMax = High[pos];
        TempMaxBar = Bars-pos;  // Here switching to direct numbering
      } else {
// If not, then check whether the slowing level has been broken through
        if (Low[pos]<SwitchLevel()) {
// If yes, then fix the maximum
          CurMax = TempMax;
          CurMaxBar = TempMaxBar;

Usando o par de if - else significa que Low da barra contendo TempMax não será considerado. As situações em que o preço acaba estando abaixo do próximo mínimo fixo podem ser percebidas como um erro ao desenhar o ZigZag. Será mesmo um erro?

Considerando que o trabalho na história e no tempo real deve ser idêntico, o autor mantém a opinião de que não é um erro. De fato, dentro de um período de tempo, nunca saberemos o que aconteceu antes na história, a barra máxima ou mínima. Usar aqui a construção de if - else significa tomar uma decisão consciente: Preferimos o momento. Isso significa sacrificar o mínimo por um segmento superior e o máximo por um segmento inferior. Parece lógico que quanto menor o período de tempo, menos frequente esse dilema ocorrerá.

Outro fragmento precisa de alguns comentários:

// Correct the corresponding variables
          UpZ = false;
          TempMin = Low[pos];
          TempMinBar = Bars-pos;  // Here switching to direct numbering
        }
      }

Aqui, na verdade, o ponto de início da verificação de tempo mínimo é definido para o momento de troca entre os segmentos. É justificável pelo dado exemplo, mas, no geral, isso não deve ser feito. Seria mais razoável definir o mínimo temporário de um intervalo mínimo do máximo fixado para a posição atual (isto é, para o momento da troca). O código pode ser como segue:

   // Correct the corresponding variables
          UpZ = false;
          TempMinBar = CurMaxBar+1;
          TempExtPos = Bars - TempMinBar;  // Here switching to reverse numbering
          TempMin = Low[TempExtPos];
          for (i=TempExtPos-1;i>=pos;i--) {
            if (Low[i]<TempMin) {
              TempMin = Low[i];
              TempMinBar = Bars-i;  // Here switching to direct numbering
            }
          }

Aqui, o Low da barra em que o mínimo foi fixado é excluído de consideração novamente.

Essas suas observações também concernem o processamento de segmentos inferiores.

Indicador

Ele só permanece para completar o indicador para fazê-lo funcionar. Não há necessidade de comentar sobre init() e deinit(), tudo está claro e dentro do padrão aí. No entanto, tomaremos uma decisão importante sobre a função start(). Trabalharemos apenas com barras completas. O principal motivo para isso é permitir que alcancemos uma estrutura de código simples e compacta.

Há outra consideração importante sobre isso. Um trabalho sério quanto ao sistema de negociação implica a coleta de estatísticas históricas. Essas estatísticas serão válidas (corretas) somente se as características obtidas no tempo real corresponderem completamente àquelas obtidas na história. Não temos uma história de ticks reais, então podemos apenas conseguir essa conformidade completa trabalhando no tempo real somente com barras completas. O máximo que podemos fazer para reduzir o atraso é ir em períodos de tempo menores, até o M1.

Outro recurso essencial é não usar a função IndicatorCounted(). O principal motivo para fazer isso é que o código usado precisa de outra ação importante - a inicialização do estado de variáveis do indicador. Isso não pode ser feito na função init() já que usar a numeração diretamente exige recalcular o indicador no envio da história e, assim, reinicializar as variáveis de estado. A função init() não é lançada no envio de história.

Assim, precisamos adicionar mais uma função "padrão", Reset(). Eventualmente, o desejo de usar o IndicatorCounted() não ajuda tanto como impede a organização do novo cálculo de verificação necessário para um indicador desse tipo. Essa verificação é realizada como segue:

int start() {
//  Work with completed bars only
  if (Bars == PreBars) return(0);  
//  Check whether there are enough bars on the chart
  if (Bars < MinBars) {
    Alert(": Not enough bars on the chart");
    return(0);
  }  
//  If the history was not pumped, make calculations for the bar just completed
  if (Bars-PreBars == 1 && BarTime==Time[1]) StartPos = 1;
//  Otherwise, count the number of bars specified in function Reset() 
  else StartPos = Reset();
// Modify check variables
  PreBars = Bars;  
  BarTime=Time[0];
// Cycle on history
  for (pos=StartPos;pos>0;pos--) {

A função Reset() aparece como segue:

int Reset() {
  if (MinBars == 0) MinBars = Bars-1;
  StartPos = MinBars;
  PreBars = 0;
  BarTime = 0;
  dH = H*Point;
  UpZ = true;
  TempMaxBar = Bars-StartPos;
  TempMinBar = Bars-StartPos;
  TempMax = High[StartPos];
  TempMin = Low[StartPos];
  StartPos++;
  return(StartPos);
}

Aqui, poderíamos prestar atenção à variável adicional dH, a qual atribuímos definitivamente o valor limiar da troca de ZigZag (H ) transformado na escala de preço. Um pergunta pode surgir: Por que UpZ = true, e não false? A resposta é simples: Após um pequeno número de segmentos, o indicador resultará no mesmo gráfico, independente do valor inicial de UpZ.

Bem, por fim, os cálculos do nível de lentidão:

double SwitchLevel() {
  double SwLvl;
  if (UpZ) SwLvl = TempMax - dH;
  else SwLvl = TempMin + dH;
  return(SwLvl);
}

Tudo deve estar claro agora.

Conclusão

Um modelo para escrever ZigZags, ZZTemplate, está anexado a este artigo. Tudo que você precisa fazer é adicionar o código necessário à função SwitchLevel(). Para transformar o modelo no ZigZag usado aqui como exemplo, é necessário apenas encontrar as seguintes linhas e comentar nelas:

//extern int H = 33;
//double dH;
//  dH = H*Point;
//  if (UpZ) SwLvl = TempMax - dH;
//  else SwLvl = TempMin + dH;

A observação final concerne à velocidade do ZigZag. O modelo implica generalidade. Além disso, queremos ter uma estrutura o mais transparente possível. Acredito que as realizações mais específicas podem ser otimizadas adicionalmente para melhorar sua operação.

A recomendação geral é a seguinte: Quando possível, coloque as operações em operadores if. Como exemplo (mas não como modelo ideal) de otimização, encontre o indicador anexado HZZ, uma realização alternativa do ZigZag usado neste artigo. A simplicidade do problema nos permite abandonar a função SwitchLevel() e algumas variáveis de estado. Como um pequeno bônus, adicionei ao HZZ picos de ZigZag escritos ao arquivo e a verificação no voo de algumas características estatísticas do ZigZag.

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

Arquivos anexados |
HZZ.mq4 (4.22 KB)
ZZTemplate.mq4 (5.67 KB)
A ociosidade é o estímulo do progresso ou como trabalhar com gráficos de maneira interativa A ociosidade é o estímulo do progresso ou como trabalhar com gráficos de maneira interativa
Um indicador de trabalho interativo com linhas de tendência, níveis Fibo, ícones impostos manualmente em um gráfico. Ele permite que você desenhe as zonas coloridas dos níveis Fibo, mostre os momentos em que o preço cruzou a linha de tendência, gerencia o objeto "etiqueta de preço".
Passo a passo do HTML usando MQL4 Passo a passo do HTML usando MQL4
Hoje em dia, o HTML é um dos tipos de documentos amplamente disseminados. O terminal do cliente MetaTrader 4 permite que você salve declarações, testes e relatórios de otimização como arquivos .htm. Às vezes, é necessário conseguir as informações de tais arquivos em um programa MQL4. O artigo descreve uma das variações de como conseguir a estrutura de tag e os conteúdos de HTML.
Método de determinação de erros no código por comentários Método de determinação de erros no código por comentários
O artigo descreve um método de busca de erros no código MQL4 que é baseado em comentários. Acredita-se que esse método é útil no caso de problemas ocorrendo durante a compilação causada pelos erros em um código razoavelmente grande.
Operações de arquivo agrupadas Operações de arquivo agrupadas
Às vezes é necessário realizar operações idênticas com um grupo de arquivos. Se você tem uma lista de arquivos incluída em um grupo, então isso não é problema. Entretanto, se você precisar fazer essa lista, então surge uma questão: "Como posso fazer isso?" O artigo propõe fazer isso usando funções FindFirstFile() e FindNextFile() incluídas no kernel32.dll.