English Русский 中文 Español Deutsch 日本語
Modelo de continuação de movimento - estatísticas de desempenho e pesquisa em gráficos

Modelo de continuação de movimento - estatísticas de desempenho e pesquisa em gráficos

MetaTrader 5Exemplos | 4 dezembro 2018, 07:52
2 167 0
Almat Kaldybay
Almat Kaldybay
  1. Introdução
  2. Descrição do modelo - disposições gerais
  3. Princípios do reconhecimento de modelos no gráfico
  4. Construção do algoritmo e escrita do código
    1. Parâmetros de entrada, função OnInit() e declaração inicial de variáveis
    2. Definição de parâmetros gerais
    3. Atualizando dados em matrizes
      1. Preenchendo matrizes quando aparece uma nova barra
      2. Preenchendo matrizes com dados da barra 0
      3. Atualizando dados de fractais
    4. Buscando extremos
      1. Buscando extremos para uma tendência baixista
      2. Buscando extremos para uma tendência altista
      3. Convertendo valores High/Low de ondas corretivas em variáveis individuais
    5. Condições para reconhecimento do modelo
    6. Criando controles
      1. Controlando a formação do ponto de entrada na zona de abertura da posição
      2. Controlando o recuo do preço para a zona de abertura da posição
      3. Eliminação de posições duplicadas dentro do mesmo modelo
    7. Condições para o ponto de entrada
    8. Condições de negociação
    9. Trabalhando com operações de negociação
      1. Definição de posições
      2. Definido take-profit
      3. Movendo posição para break-even.
  5. Coleta de dados estatísticos
  6. Fim do artigo


1. Introdução

Nesse artigo, quero descrever como funciona um dos modelos de continuação de movimento. O trabalho é baseado na definição de duas ondas — uma principal e outra corretiva. Como extremos serão usados fractais e, como eu os chamo, potenciais fractais — extremos que ainda não se formaram como fractais. Em seguida, tentarei coletar dados estatísticos sobre o movimento das ondas. Os dados serão colocados num arquivo CSV.


2. Descrição do modelo - disposições gerais

O modelo de continuação de movimento descrito nesse artigo consiste em duas ondas: uma onda principal e outra corretiva. O modelo é esquematicamente descrito na figura 1. Onde, AB é a onda principal, BC é a onda corretiva, CD é a continuação do movimento em direção à tendência principal.

Modelo de continuação de movimento

Fig. 1. Modelo de continuação de movimento

No gráfico, é assim:

Modelo de continuação de movimento no gráfico H4 para o par AUDJPY

Fig. 2. Modelo de continuação de movimento no gráfico H4 para o par AUDJPY


3. Princípios de reconhecimento de modelo no gráfico

Os princípios de reconhecimento de modelo são descritos na tabela 1.

Tabela 1. Princípios de reconhecimento da continuação de modelo de movimento quanto a tendências

№ Princípios de reconhecimento de modelo para uma tendência baixista №Princípios de reconhecimento de modelos para tendência altista
1O High/Low superior/inferior a dois High/Low de barras anteriores é considerado uma barra extremo1O High/Low superior/inferior a dois High/Low de barras anteriores é considerado uma barra extremo
2A onda corretiva deve sempre terminar com a presença de um extremo superior (ponto C - veja imagem 1 e imagem 2)2A onda corretiva deve sempre terminar com a presença de um extremo inferior (ponto C - veja imagem 1 e imagem 2)
 3A duração da onda correcional não pode ser longa e deve ser limitada a várias barras. 3A duração da onda correcional não pode ser longa e deve ser limitada a várias barras.
 4High do movimento corretivo (ponto C - veja imagem 1 e imagem 2) deve ser inferior ao High do movimento principal (ponto A - veja imagem 1 e imagem 2) 4Low do movimento corretivo (ponto C - veja imagem 1 e imagem 2) deve ser superior ao Low do movimento principal (ponto A - veja imagem 1 e imagem 2)
 5Princípio de ponto de entrada oportuno significa que é necessário abrir a posição num determinado momento da formação do ponto de entrada 5Princípio de ponto de entrada oportuno significa que é necessário abrir a posição num determinado momento da formação do ponto de entrada


4. Construção do algoritmo e escrita do código

1. Parâmetros de entrada, função OnInit() e declaração inicial de variáveis

Primeiro, precisamos conectar a classe CTrade para facilitar o acesso às operações de negociação:

//--- conexão de arquivos
#include <Trade\Trade.mqh> 
//--- objeto para negociação
CTrade  trade;

Em seguida, descrevemos os parâmetros de entrada:

//--- parâmetros de entrada
input ENUM_TIMEFRAMES base_tf;  //timeframe do período base
input ENUM_TIMEFRAMES work_tf;  //timeframe do período de trabalho
input double SummRisk=100;      //risco do trade
input double sar_step=0.1;      //set parabolic step
input double maximum_step=0.11; //set parabolic maximum step
input bool TP_mode=true;        //permissão para posicionar take-profit
input int M=2;                  //relação entre lucro e risco
input bool Breakeven_mode=true; //permissão para deslocar a posição para break-even
input double breakeven=1;       //relação entre o tamanho do lucro e o tamanho do stop-loss

No período base, o EA determina a direção de entrada, no período gráfico usado determina o ponto de entrada.

O programa calcula o tamanho do lote dependendo da risco do trade.

Além disso, o EA pode definir o take-profit de acordo como a relação lucro/risco especificada (parâmetro M), bem como mover a posição para o break-even segundo a relação lucro/stop-loss (parâmetro breakeven).

Após descrever os parâmetros de entrada, precisamos declarar variáveis para identificadores de indicadores e matrizes para os timeframes base_tf e work_tf:

//--- declaração de variáveis para identificadores de indicadores
int Fractal_base_tf,Fractal_work_tf;             //identificador do indicador iFractals
int Sar_base_tf,Sar_work_tf;                     //identificador para o indicador iSar
//--- declaração de matrizes para base_tf
double High_base_tf[],Low_base_tf[];             //matrizes para armazenar preços High e Low das barras
double Close_base_tf[],Open_base_tf[];           //matrizes para armazenas os preços Close e Open das barras
datetime Time_base_tf[];                         //matriz para armazenar a hora de abertura de barras
double Sar_array_base_tf[];                      //matriz para armazenar os preços do indicador iSar (Parabolic)
double FractalDown_base_tf[],FractalUp_base_tf[];//matriz para armazenar os preços do indicador iFractals
//--- declaração de matrizes para work_tf
double High_work_tf[],Low_work_tf[];
double Close_work_tf[],Open_work_tf[];
datetime Time_work_tf[];
double Sar_array_work_tf[];
double FractalDown_work_tf[],FractalUp_work_tf[];;

O EA usa dois indicadores: fractais para determinar parte dos extremos e Parabolic para colocar o trailing-stop da posição. Também pretendo usar Parabolic para definir o ponto de entrada no timeframe de trabalho work_tf.

Em seguida, na função OnInit() precisamos obter os identificadores dos indicadores e preencher as matrizes com dados originais.

int OnInit()
  {
//--- obtemos o identificador do indicador iSar
   Sar_base_tf=iSAR(Symbol(),base_tf,sar_step,maximum_step);
   Sar_work_tf=iSAR(Symbol(),work_tf,sar_step,maximum_step);
//--- obtemos os identificadores do indicador iFractals
   Fractal_base_tf=iFractals(Symbol(),base_tf);
   Fractal_work_tf=iFractals(Symbol(),work_tf);
//--- arrumando matrizes, como na série temporal para base_tf
   ArraySetAsSeries(High_base_tf,true);
   ArraySetAsSeries(Low_base_tf,true);
   ArraySetAsSeries(Close_base_tf,true);
   ArraySetAsSeries(Open_base_tf,true);
   ArraySetAsSeries(Time_base_tf,true);;
   ArraySetAsSeries(Sar_array_base_tf,true);
   ArraySetAsSeries(FractalDown_base_tf,true);
   ArraySetAsSeries(FractalUp_base_tf,true);
//--- preenchimento inicial de matrizes para base_tf
   CopyHigh(Symbol(),base_tf,0,1000,High_base_tf);
   CopyLow(Symbol(),base_tf,0,1000,Low_base_tf);
   CopyClose(Symbol(),base_tf,0,1000,Close_base_tf);
   CopyOpen(Symbol(),base_tf,0,1000,Open_base_tf);
   CopyTime(Symbol(),base_tf,0,1000,Time_base_tf);
   CopyBuffer(Sar_base_tf,0,TimeCurrent(),1000,Sar_array_base_tf);
   CopyBuffer(Fractal_base_tf,0,TimeCurrent(),1000,FractalUp_base_tf);
   CopyBuffer(Fractal_base_tf,1,TimeCurrent(),1000,FractalDown_base_tf);
//--- arrumando matrizes, como na série temporal para work_tf
   ArraySetAsSeries(High_work_tf,true);
   ArraySetAsSeries(Low_work_tf,true);
   ArraySetAsSeries(Close_work_tf,true);
   ArraySetAsSeries(Open_work_tf,true);
   ArraySetAsSeries(Time_work_tf,true);
   ArraySetAsSeries(Sar_array_work_tf,true);
   ArraySetAsSeries(FractalDown_work_tf,true);
   ArraySetAsSeries(FractalUp_work_tf,true);
//--- preenchimento inicial de matrizes para work_tf
   CopyHigh(Symbol(),work_tf,0,1000,High_work_tf);
   CopyLow(Symbol(),work_tf,0,1000,Low_work_tf);
   CopyClose(Symbol(),work_tf,0,1000,Close_work_tf);
   CopyOpen(Symbol(),work_tf,0,1000,Open_work_tf);
   CopyTime(Symbol(),work_tf,0,1000,Time_work_tf);
   CopyBuffer(Sar_work_tf,0,TimeCurrent(),1000,Sar_array_work_tf);
   CopyBuffer(Fractal_work_tf,0,TimeCurrent(),1000,FractalUp_work_tf);
   CopyBuffer(Fractal_work_tf,1,TimeCurrent(),1000,FractalDown_work_tf);

//---
   return(INIT_SUCCEEDED);
  }

Primeiro, obtivemos o identificador dos indicadores, em seguida, definimos a ordem das matrizes, como na série temporal e preenchemos a matriz com dados. Eu considerei que os dados sobre 1 000 barras são mais que suficientes para o trabalho desse EA.

2. Definição de parâmetros gerais

A partir dessa etapa, volto a trabalhar com a função OnTick().

Na seção "Parâmetros gerais", geralmente registro informações de mercado e também declaro as variáveis ​​que serão usadas para definir posições.

//+------------------------------------------------------------------+
//| 1. Parâmetros gerais (início)                                    |
//+------------------------------------------------------------------+
//--- informações de mercado
// número de casas decimais no preço do instrumento
   int Digit=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
// definição do número de bits do símbolo atual
   double f=1;
   if(Digit==5) {f=100000;}
   if(Digit==4) {f=10000;}
   if(Digit==3) {f=1000;}
   if(Digit==2) {f=100;}
   if(Digit==1) {f=10;}
//---
   double spread=SymbolInfoInteger(Symbol(),SYMBOL_SPREAD)/f;//spread expresso em valor fracionário, levando em conta o número de bits do preço
   double bid=SymbolInfoDouble(_Symbol,SYMBOL_BID);//informações sobre o preço Bid
   double ask=SymbolInfoDouble(_Symbol,SYMBOL_ASK);//informações sobre o preço Ask
   double CostOfPoint=SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_VALUE);//informações sobre o valor do tick
//--- variáveis para calcular o lote a fim de definir a posição
   double RiskSize_points;//variável para armazenar o tamanho do stop-loss da posição atual
   double CostOfPoint_position;//variável para armazenar o valor do ponto da posição atual, levando em conta o risco do trade
   double Lot;//variável para armazenar o tamanho do lote para abertura de posição
   double SLPrice_sell,SLPrice_buy;//variáveis para armazenar os níveis de preço de stop-loss
//--- variáveis para armazenar informações sobre o número de barras
   int bars_base_tf=Bars(Symbol(),base_tf);
   int bars_work_tf=Bars(Symbol(),work_tf);
//--- variáveis para armazenar informações sobre a posição
   string P_symbol; //símbolo da posição
   int P_type,P_ticket,P_opentime;//tipo, boleta e hora de abertura da posição
//+------------------------------------------------------------------+
//| 1. Parâmetros gerais (final)                                     |
//+------------------------------------------------------------------+ 

3. Atualizando dados em matrizes

As matrizes foram inicialmente preenchidas na função OnInit(), mas os dados nas matrizes devem estar constantemente atualizados. O preenchimento de matrizes toda vez que um novo tick chega carrega muito o sistema, o que leva a uma séria lentidão dos processos. Portanto, é recomendável preencher novamente as matrizes quando uma nova barra aparecer.

Para fazer isso, podemos usar a seguinte estrutura:

   static datetime LastBar_base_tf=0;//variável para determinar o surgimento de uma nova barra
   datetime ThisBar_base_tf=(datetime)SeriesInfoInteger(_Symbol,base_tf,SERIES_LASTBAR_DATE);//hora da barra atual
   if(LastBar_base_tf!=ThisBar_base_tf)//se a hora não coincidir é porque apareceu uma nova barra
     {
         //preenchendo matrizes
     }

Mas com essa abordagem, os dados da barra zero são perdidos, portanto, para os dados da barra com o índice 0, criei matrizes separadas.

Também devemos atualizar separadamente as matrizes com os dados dos fractais. Elas devem ser recarregadas toda vez que os extremos da barra 0 estão acima ou abaixo dos dois anteriores.

Abaixo estão exemplos de preenchimento de matrizes.

1. Preenchendo matrizes quando aparece uma nova barra

Primeiro, preenchemos os matrizes quando uma nova barra aparecer:

//+------------------------------------------------------------------+
//| 2.1 Preenchendo matrizes quando aparece uma nova barra (início)  |
//+------------------------------------------------------------------+
//--- para base_tf
//--- ordenando matrizes, como na série temporal
   ArraySetAsSeries(High_base_tf,true);
   ArraySetAsSeries(Low_base_tf,true);
   ArraySetAsSeries(Close_base_tf,true);
   ArraySetAsSeries(Open_base_tf,true);
   ArraySetAsSeries(Time_base_tf,true);
   ArraySetAsSeries(Sar_array_base_tf,true);
   ArraySetAsSeries(FractalDown_base_tf,true);
   ArraySetAsSeries(FractalUp_base_tf,true);
//--- preenchimento de matrizes
   static datetime LastBar_base_tf=0;//variável para determinar o surgimento de uma nova barra
   datetime ThisBar_base_tf=(datetime)SeriesInfoInteger(_Symbol,base_tf,SERIES_LASTBAR_DATE);//hora de abertura da barra atual
   if(LastBar_base_tf!=ThisBar_base_tf)//se a hora não coincidir é porque apareceu uma nova barra
     {
      CopyHigh(Symbol(),base_tf,0,1000,High_base_tf);
      CopyLow(Symbol(),base_tf,0,1000,Low_base_tf);
      CopyClose(Symbol(),base_tf,0,1000,Close_base_tf);
      CopyOpen(Symbol(),base_tf,0,1000,Open_base_tf);
      CopyTime(Symbol(),base_tf,0,1000,Time_base_tf);
      CopyBuffer(Sar_base_tf,0,TimeCurrent(),1000,Sar_array_base_tf);
      CopyBuffer(Fractal_base_tf,0,TimeCurrent(),1000,FractalUp_base_tf);
      CopyBuffer(Fractal_base_tf,1,TimeCurrent(),1000,FractalDown_base_tf);
      LastBar_base_tf=ThisBar_base_tf;
     }
//--- para work_tf
//--- ordenando matrizes, como na série temporal
   ArraySetAsSeries(High_work_tf,true);
   ArraySetAsSeries(Low_work_tf,true);
   ArraySetAsSeries(Close_work_tf,true);
   ArraySetAsSeries(Open_work_tf,true);
   ArraySetAsSeries(Time_work_tf,true);
   ArraySetAsSeries(Sar_array_work_tf,true);
   ArraySetAsSeries(FractalDown_work_tf,true);
   ArraySetAsSeries(FractalUp_work_tf,true);
//--- preenchimento de matrizes
   static datetime LastBar_work_tf=0;//variável para definir o aparecimento de uma nova barra
   datetime ThisBar_work_tf=(datetime)SeriesInfoInteger(_Symbol,work_tf,SERIES_LASTBAR_DATE);//hora de abertura da barra atual
   if(LastBar_work_tf!=ThisBar_work_tf)//se a hora não coincidir é porque apareceu uma nova barra
     {
      CopyHigh(Symbol(),work_tf,0,1000,High_work_tf);
      CopyLow(Symbol(),work_tf,0,1000,Low_work_tf);
      CopyClose(Symbol(),work_tf,0,1000,Close_work_tf);
      CopyOpen(Symbol(),work_tf,0,1000,Open_work_tf);
      CopyTime(Symbol(),work_tf,0,1000,Time_work_tf);
      CopyBuffer(Sar_work_tf,0,TimeCurrent(),1000,Sar_array_work_tf);
      CopyBuffer(Fractal_work_tf,0,TimeCurrent(),1000,FractalUp_work_tf);
      CopyBuffer(Fractal_work_tf,1,TimeCurrent(),1000,FractalDown_work_tf);
      LastBar_work_tf=ThisBar_work_tf;
     }
//+------------------------------------------------------------------+
//| 2.1 Preenchendo matrizes quando aparece uma nova barra (final)   |
//+------------------------------------------------------------------+

2. Preenchendo matrizes com dados da barra 0

As informações sobre barras com índice 1 e acima serão agora relevantes, enquanto os dados sobre barras com índice 0 ainda permanecerão irrelevantes. Para armazenar informações sobre barras zero, criei matrizes separadas:

//+------------------------------------------------------------------+
//| 2.2 Preenchendo matrizes com dados da barra 0 (início)           |
//+------------------------------------------------------------------+
//--- para base_tf
//--- declaração de matrizes
   double High_base_tf_0[],Low_base_tf_0[];
   double Close_base_tf_0[],Open_base_tf_0[];
   datetime Time_base_tf_0[];
   double Sar_array_base_tf_0[];
//--- ordenando matrizes, como na série temporal
   ArraySetAsSeries(High_base_tf_0,true);
   ArraySetAsSeries(Low_base_tf_0,true);
   ArraySetAsSeries(Close_base_tf_0,true);
   ArraySetAsSeries(Open_base_tf_0,true);
   ArraySetAsSeries(Time_base_tf_0,true);
   ArraySetAsSeries(Sar_array_base_tf_0,true);
//--- preenchimento de matrizes
   CopyHigh(Symbol(),base_tf,0,1,High_base_tf_0);
   CopyLow(Symbol(),base_tf,0,1,Low_base_tf_0);
   CopyClose(Symbol(),base_tf,0,1,Close_base_tf_0);
   CopyOpen(Symbol(),base_tf,0,1,Open_base_tf_0);
   CopyTime(Symbol(),base_tf,0,1,Time_base_tf_0);
   CopyBuffer(Sar_base_tf,0,TimeCurrent(),1,Sar_array_base_tf_0);
//--- para work_tf
//--- declaração de matrizes
   double High_work_tf_0[],Low_work_tf_0[];
   double Close_work_tf_0[],Open_work_tf_0[];
   datetime Time_work_tf_0[];
   double Sar_array_work_tf_0[];
//--- ordenando matrizes, como na série temporal
   ArraySetAsSeries(High_work_tf_0,true);
   ArraySetAsSeries(Low_work_tf_0,true);
   ArraySetAsSeries(Close_work_tf_0,true);
   ArraySetAsSeries(Open_work_tf_0,true);
   ArraySetAsSeries(Time_work_tf_0,true);
   ArraySetAsSeries(Sar_array_work_tf_0,true);
//--- preenchimento de matrizes
   CopyHigh(Symbol(),work_tf,0,1,High_work_tf_0);
   CopyLow(Symbol(),work_tf,0,1,Low_work_tf_0);
   CopyClose(Symbol(),work_tf,0,1,Close_work_tf_0);
   CopyOpen(Symbol(),work_tf,0,1,Open_work_tf_0);
   CopyTime(Symbol(),work_tf,0,1,Time_work_tf_0);
   CopyBuffer(Sar_work_tf,0,TimeCurrent(),1,Sar_array_work_tf_0);
//+------------------------------------------------------------------+
//| 2.2 Preenchendo matrizes com dados da barra 0 (final)            |
//+------------------------------------------------------------------+

3. Atualizando dados de fractais

Matrizes com dados de fractais também precisam ser atualizadas. Toda vez que os extremos da barra 0 estão acima ou abaixo dos dois anteriores, as matrizes devem ser recarregadas:

//+------------------------------------------------------------------+
//| 2.3 Atualizando dados de fractais (início)                       |
//+------------------------------------------------------------------+
//--- para base_tf
   if(High_base_tf_0[0]>High_base_tf[1] && High_base_tf_0[0]>High_base_tf[2])
     {
      CopyBuffer(Fractal_base_tf,0,TimeCurrent(),1000,FractalUp_base_tf);
     }
   if(Low_base_tf_0[0]<Low_base_tf[1] && Low_base_tf_0[0]<Low_base_tf[2])
     {
      CopyBuffer(Fractal_base_tf,1,TimeCurrent(),1000,FractalDown_base_tf);
     }
//--- para work_tf
   if(High_work_tf_0[0]>High_work_tf[1] && High_work_tf_0[0]>High_work_tf[2])
     {
      CopyBuffer(Fractal_work_tf,0,TimeCurrent(),1000,FractalUp_work_tf);
     }
   if(Low_work_tf_0[0]<Low_work_tf[1] && Low_work_tf_0[0]<Low_work_tf[2])
     {
      CopyBuffer(Fractal_work_tf,1,TimeCurrent(),1000,FractalDown_work_tf);
     }
//+------------------------------------------------------------------+
//| 2.3 Atualizando dados de fractais (final)                        |
//+------------------------------------------------------------------+

4. Buscando extremos

Vamos voltar ao modelo de continuação de movimento. Para isso você, precisamos regressar para a figura 2.

O segmento AB é a onda principal, o segmento BC é uma onda corretiva. De acordo com os princípios de reconhecimento de modelo, a onda corretiva deve sempre terminar num extremo — em sua essência, um fractal. Na figura, esse é o ponto C. A busca de extremos precisa começar desse ponto. E, logo, sempre encontrar o resto. Mas no momento do ponto de entrada, o fractal formado (confirmado) provavelmente estará ausente. Portanto, precisamos procurar uma situação em que o extremo da barra seja maior/menor do que as duas barras anteriores — high/low dessa barra será o ponto C. Deve-se notar também que, no momento do ponto de entrada, high/low do movimento corretivo (ponto C) pode estar tanto na barra zero quanto na barra com um índice acima de zero.

A tabela 2 mostra a sequência para determinar os extremos.

Tabela 2. Sequência de definição de extremos

№ p/pPara tendência baixistaPara tendência altista
1Encontrar o high do movimento corretivo (ponto C)Encontrar o low do movimento corretivo (ponto C)
2A partir do high do movimento corretivo, encontrar o seguinte extremo superior (ponto A)A partir do low do movimento corretivo, encontrar o seguinte extremo inferior (ponto A)
3Entre os pontos C e A, localizar o ponto B - low do movimento corretivoEntre os pontos C e A, localizar o ponto B - high do movimento corretivo
1. Buscando extremos para uma tendência baixista
//+------------------------------------------------------------------+
//| 3.1 Buscando extremos para uma tendência baixista (início)       |
//+------------------------------------------------------------------+
//--- declaração de variáveis
   int High_Corr_wave_downtrend_base_tf;//para a barra high do movimento corretivo (ponto C)
   int UpperFractal_downtrend_base_tf;  //para a barra - o seguinte extremo superior (ponto A)
   int Low_Corr_wave_downtrend_base_tf; //para a barra low do movimento corretivo (ponto B)
//--- 
//--- Encontrar high do movimento corretivo (ponto C)
   if(High_base_tf_0[0]>High_base_tf[1] && High_base_tf_0[0]>High_base_tf[2])
     {
      High_Corr_wave_downtrend_base_tf=0;
     }
   else
     {
      for(n=0; n<(bars_base_tf);n++)
        {
         if(High_base_tf[n]>High_base_tf[n+1] && High_base_tf[n]>High_base_tf[n+2])
            break;
        }
      High_Corr_wave_downtrend_base_tf=n;
     }
//--- 
//--- A partir do high do movimento corretivo, encontrar o seguinte extremo superior (ponto A)
   for(n=High_Corr_wave_downtrend_base_tf+1; n<(bars_base_tf);n++)
     {
      // --- if a non-empty value, terminate the loop
      if(FractalUp_base_tf[n]!=EMPTY_VALUE)
         break;
     }
   UpperFractal_downtrend_base_tf=n;
//---
//--- Entre os pontos C e A, localizar o ponto B - low do movimento corretivo
   int CountToFind_arrmin=UpperFractal_downtrend_base_tf-High_Corr_wave_downtrend_base_tf;
   Low_Corr_wave_downtrend_base_tf=ArrayMinimum(Low_base_tf,High_Corr_wave_downtrend_base_tf,CountToFind_arrmin);
//+------------------------------------------------------------------+
//| 3.1 Buscando extremos para uma tendência baixista (final)        |
//+------------------------------------------------------------------+

2. Buscando extremos para uma tendência altista

//+------------------------------------------------------------------+
//| 3.1 Buscando extremos para uma tendência altista (início)        |
//+------------------------------------------------------------------+
//--- declaração de variáveis
   int Low_Corr_wave_uptrend_base_tf;//para a barra Low do movimento corretivo (ponto C)
   int LowerFractal_uptrend_base_tf;  //para a barra - o seguinte extremo inferior (ponto A)
   int High_Corr_wave_uptrend_base_tf; //para a barra High do movimento corretivo (ponto B)
//--- 
//--- Encontrar o low do movimento corretivo (ponto C)
   if(Low_base_tf_0[0]<Low_base_tf[1] && Low_base_tf_0[0]<Low_base_tf[2])
     {
      Low_Corr_wave_uptrend_base_tf=0;
     }
   else
     {
      //buscamos o low do recuo
      for(n=0; n<(bars_base_tf);n++)
        {
         if(Low_base_tf[n]<Low_base_tf[n+1] && Low_base_tf[n]<Low_base_tf[n+2])
            break;
        }
      Low_Corr_wave_uptrend_base_tf=n;
     }
//---
//--- A partir do low do movimento corretivo, encontrar o seguinte extremo inferior (ponto A)
   for(n=Low_Corr_wave_uptrend_base_tf+1; n<(bars_base_tf);n++)
     {
      if(FractalDown_base_tf[n]!=EMPTY_VALUE)
         break;
     }
   LowerFractal_uptrend_base_tf=n;
//---
//--- Entre os pontos C e A, localizar o ponto B - high do movimento corretivo
int CountToFind_arrmax=LowerFractal_uptrend_base_tf-Low_Corr_wave_uptrend_base_tf;
High_Corr_wave_uptrend_base_tf=ArrayMaximum(High_base_tf,Low_Corr_wave_uptrend_base_tf,CountToFind_arrmax);
//+------------------------------------------------------------------+
//| 3.2 Encontrando extremos para a tendência altista (final)        |
//+------------------------------------------------------------------+

3. Convertendo valores High/Low de ondas corretivas em variáveis individuais

Assim, os índices de barras, isto é, são encontrados os extremos. Mas será necessário usar não apenas aos índices, mas também aos valores de preço e tempo dessas barras. Para acessar os valores high ou low das ondas corretivas, temos que usar duas matrizes diferentes, já que as ondas corretivas high ou low podem estar numa barra com índice zero, ou numa barra com um índice superior a zero. Isso não é muito conveniente, seria melhor trazer seus valores para uma variável comum usando o operador if.

//+----------------------------------------------------------------------------------------+
//| 3.3 Convertendo valores High/Low de ondas corretivas em variáveis individuais (início) |
//+----------------------------------------------------------------------------------------+
//--- declaração de variáveis
   double High_Corr_wave_downtrend_base_tf_double,Low_Corr_wave_uptrend_base_tf_double;
   datetime High_Corr_wave_downtrend_base_tf_time,Low_Corr_wave_uptrend_base_tf_time;
//--- para High_Corr_wave_downtrend_base_tf
   if(High_Corr_wave_downtrend_base_tf==0)
     {
      High_Corr_wave_downtrend_base_tf_double=High_base_tf_0[High_Corr_wave_downtrend_base_tf];
      High_Corr_wave_downtrend_base_tf_time=Time_base_tf_0[High_Corr_wave_downtrend_base_tf];
     }
   else
     {
      High_Corr_wave_downtrend_base_tf_double=High_base_tf[High_Corr_wave_downtrend_base_tf];
      High_Corr_wave_downtrend_base_tf_time=Time_base_tf[High_Corr_wave_downtrend_base_tf];
     }
//-- para Low_Corr_wave_uptrend_base_tf
   if(Low_Corr_wave_uptrend_base_tf==0)
     {
      Low_Corr_wave_uptrend_base_tf_double=Low_base_tf_0[Low_Corr_wave_uptrend_base_tf];
      Low_Corr_wave_uptrend_base_tf_time=Time_base_tf_0[Low_Corr_wave_uptrend_base_tf];
     }
   else
     {
      Low_Corr_wave_uptrend_base_tf_double=Low_base_tf[Low_Corr_wave_uptrend_base_tf];
      Low_Corr_wave_uptrend_base_tf_time=Time_base_tf[Low_Corr_wave_uptrend_base_tf];
     }
//+---------------------------------------------------------------------------------------+
//| 3.3 Convertendo valores High/Low de ondas corretivas em variáveis individuais (final) |
//+---------------------------------------------------------------------------------------+

Assim, valores de preço e de tempo de high/low de ondas corretivas são registrados em variáveis, ​​e não há necessidade de acessar matrizes diferentes a cada vez.

Se resumirmos a busca de extremos, verifica-se que os pontos A, B e C são encontrados de acordo com o conceito de reconhecimento de modelo (ver tabelas 4 e 5).

Tabela 4. Valores dos pontos A, B e C para tendência baixista

IndicadorValores do ponto AValores do ponto BValores do ponto C
Índice da barraUpperFractal_downtrend_base_tfLow_Corr_wave_downtrend_base_tfHigh_Corr_wave_downtrend_base_tf
Valor temporalTime_base_tf[UpperFractal_downtrend_base_tf]Time_base_tf[Low_Corr_wave_downtrend_base_tf]High_Corr_wave_downtrend_base_tf_time
Valor do preçoHigh_base_tf[UpperFractal_downtrend_base_tf]Low_base_tf[Low_Corr_wave_downtrend_base_tf]High_Corr_wave_downtrend_base_tf_double

Tabela 5. Valores dos pontos A, B e C para tendência altista

IndicadorValores do ponto AValores do ponto BValores do ponto C
Índice da barraLowerFractal_uptrend_base_tfHigh_Corr_wave_uptrend_base_tfLow_Corr_wave_uptrend_base_tf
Valor temporalTime_base_tf[LowerFractal_uptrend_base_tf]Time_base_tf[High_Corr_wave_uptrend_base_tf]Low_Corr_wave_uptrend_base_tf_time
Valor do preçoLow_base_tf[LowerFractal_uptrend_base_tf]High_base_tf[High_Corr_wave_uptrend_base_tf]Low_Corr_wave_uptrend_base_tf_double


5. Condições para reconhecimento do modelo

Nessa seção, vou descrever apenas as condições básicas mais necessárias, na minha opinião, que são características do modelo apresentado nesse artigo.

Tabela 6. Conjunto mínimo de condições para reconhecer um modelo de continuação de movimento

№ p/pCondições para tendência baixistaCondições para tendência altista
1High da onda corretiva (ponto C) abaixo do high do extremo que a segue (ponto A)Low gh da onda corretiva (ponto C) abaixo do low do extremo que a segue (ponto A)
2Índice low da onda corretiva (ponto B) é maior que o índice high da onda corretiva (ponto C)Índice high da onda corretiva (ponto B) é maior que o índice low da onda corretiva (ponto C)
3Duração do movimento de correção de 2 a 6 barras (número de barras a partir do ponto B)Duração do movimento de correção de 2 a 6 barras (número de barras a partir do ponto B)

Em seguida, forneço um código descrevendo as condições para reconhecer o modelo. As condições são coletadas em duas variáveis ​​lógicas: uma para a tendência baixista e a segunda para a tendência altista:

//+------------------------------------------------------------------+
//| 4. Condições para reconhecimento do modelo (início)              |
//+------------------------------------------------------------------+
//--- para tendência baixista
/*1. High da onda corretiva (ponto C) abaixo do high do extremo que a segue (ponto A)*/
/*2. Índice low da onda corretiva (ponto B) é maior que o índice high da onda corretiva (ponto C)*/
/*3. Duração do movimento de correção de 2 a 6 barras (número de barras a partir do ponto B)*/
   bool Model_downtrend_base_tf=(
                                 /*1.*/High_Corr_wave_downtrend_base_tf_double<High_base_tf[UpperFractal_downtrend_base_tf] && 
                                 /*2.*/Low_Corr_wave_downtrend_base_tf>High_Corr_wave_downtrend_base_tf && 
                                 /*3.*/Low_Corr_wave_downtrend_base_tf>=1 && Low_Corr_wave_downtrend_base_tf<=6
                                 );
//--- para tendência altista
/*1. Low gh da onda corretiva (ponto C) abaixo do low do extremo que a segue (ponto A)*/
/*2. Índice high da onda corretiva (ponto B) é maior que o índice low da onda corretiva (ponto C)*/
/*3. Duração do movimento de correção de 2 a 6 barras (número de barras a partir do ponto B)*/*/
   bool Model_uptrend_base_tf=(
                               /*1.*/Low_Corr_wave_uptrend_base_tf_double>Low_base_tf[LowerFractal_uptrend_base_tf] && 
                               /*2.*/High_Corr_wave_uptrend_base_tf>Low_Corr_wave_uptrend_base_tf && 
                               /*3.*/High_Corr_wave_uptrend_base_tf>=1 && High_Corr_wave_uptrend_base_tf<=6
                               );
//+------------------------------------------------------------------+
//| 4. Condições para reconhecimento do modelo (final)               |
//+------------------------------------------------------------------+

6. Criando controles

O EA, no mínimo, deve realizar três verificações.

As duas primeiras verificações são sobre se a entrada é oportuna. A terceira verificação é se apenas uma posição é aberta dentro de um modelo, isto é, a exclusão da criação de posições duplicadas.

Veja a figura 3. Nela, as linhas pontilhadas marcam as zonas de abertura de posições onde pode ser localizado o ponto de entrada — em algum lugar entre os pontos B e C. A entrada mais tarde, quando o preço rompe o nível do ponto B, é indesejável, pois isso aumenta os riscos. Essa é a primeira verificação que o programa deve executar.

Modelo de continuação de movimento

Fig. 3. Modelo de continuação de movimento no gráfico H4 para o par AUDJPY

Também pode haver situações em que o preço tenha quebrado o nível do ponto B e depois retornado à zona de abertura de posição. Essa situação não pode ser considerada como de negociação. Essa é a segunda verificação que o programa deve executar. Finalmente, para evitar muitas posições criadas, deve ser feita uma restrição: 1 modelo — 1 uma posição aberta. Essa é a terceira verificação que o programa deve executar.

1. Controlando a formação do ponto de entrada na zona de abertura da posição

Tudo é simples aqui: para um modelo de venda, o preço bid deve estar acima do low do movimento corretivo (ponto B). Para um modelo de compra, o preço bid deve estar acima do high do movimento corretivo (ponto B).

//+----------------------------------------------------------------------------------------+
//| 5.1 Controlando a formação do ponto de entrada na zona de abertura da posição (início) |
//+----------------------------------------------------------------------------------------+
//--- para tendência baixista
bool First_downtrend_control_bool=(bid>=Low_base_tf[Low_Corr_wave_downtrend_base_tf]);
//--- para tendência altista
bool First_uptrend_control_bool=(bid<=High_base_tf[High_Corr_wave_uptrend_base_tf]);
//+----------------------------------------------------------------------------------------+
//| 5.1 Controlando a formação do ponto de entrada na zona de abertura da posição (final)  |
//+----------------------------------------------------------------------------------------+

2. Controlando o recuo do preço para a zona de abertura da posição

Para implementar esse controle, precisamos determinar a barra com o menor valor low (para vendas) ou a barra com o maior valor high (para compras), começando do índice atual e até à barra high/low do movimento corretivo (ponto B). Para fazer isso, é usada a função ArrayMinimum() para o modelo de venda e ArrayMaximum() para o modelo de compra.

Em seguida, são comparados os índices low/high do movimento corretivo (ponto B) e os índices, obtidos pela função ArrayMinimum() e ArrayMaximum(). Se eles coincidirem, não houve rompimento low/high de movimento corretivo e a situação pode ser considerada como de negociação. Se os índices não coincidem é porque o movimento começou mais cedo e é tarde demais para abrir uma posição.

//+------------------------------------------------------------------------------+
//| 5.2 Controlando o recuo do preço para a zona de abertura da posição (início) |
//+------------------------------------------------------------------------------+
//--- para tendência baixista
//encontramos a barra com o menor preço entre a barra 0 e o low do movimento corretivo
   int Second_downtrend_control_int=ArrayMinimum(Low_base_tf,0,Low_Corr_wave_downtrend_base_tf+1);
//se o low da barra atual estiver abaixo do low do movimento de correção
   if(Low_base_tf_0[0]<Low_base_tf[Second_downtrend_control_int])
     {
      Second_downtrend_control_int=0; //significa mínimo na barra 0
     }
//se a barra com o menor preço e o low do movimento corretivo coincidirem, isto é, são a mesma barra
//significa que não houve saída do preço da zona de abertura de posição
   bool Second_downtrend_control_bool=(Second_downtrend_control_int==Low_Corr_wave_downtrend_base_tf);
//---
//--- para tendência altista
//encontramos a barra com o maior preço entre a barra 0 e o high do movimento corretivo
   int Second_uptrend_control_int=ArrayMaximum(High_base_tf,0,High_Corr_wave_uptrend_base_tf+1);
   //se o high da barra atual estiver acima do high do movimento de correção
   if(High_base_tf_0[0]>High_base_tf[Second_uptrend_control_int])
     {
      Second_uptrend_control_int=0;//significa máximo na barra 0
     }
//se a barra com o maior preço e o high do movimento corretivo coincidirem, isto é, são a mesma barra
//significa que não houve saída do preço da zona de abertura de posição
   bool Second_uptrend_control_bool=(Second_uptrend_control_int==High_Corr_wave_uptrend_base_tf);
//+-----------------------------------------------------------------------------+
//| 5.2 Controlando o recuo do preço para a zona de abertura da posição (final) |
//+-----------------------------------------------------------------------------+

3. Eliminação de posições duplicadas dentro do mesmo modelo

Esse controle é usado para limitar o número de posições abertas. Essência do controle: um modelo — uma posição aberta. Princípio de operação: é realizada uma pesquisa detalhada de posições abertas, em seguida, se uma posição estiver aberta no gráfico atual, é determinada a barra - mais próxima do ponto de entrada - que é extremo, isto é, o high/low do movimento corretivo (ponto C do ponto de entrada) dependendo do tipo de trade.

Depois, é estabelecido o tempo da barra encontrada — o high/low do movimento de correção (ponto C a partir do ponto de entrada) é comparado com o tempo do high/low do movimento corretivo atual (ponto C atual). Se eles corresponderem, não deve ocorrer a abertura posição, pois já existe uma posição nesse modelo.

Criando controle para vendas:

//+---------------------------------------------------------------------------+
//| 5.3.1 Para vendas (início)                                                |
//+---------------------------------------------------------------------------+
//--- declaração de variáveis
   int Bar_sell_base_tf,High_Corr_wave_downtrend_base_tf_sell;
   bool Third_downtrend_control_bool=false;
//--- pesquisa detalhada de todas as posições abertas
   if(PositionsTotal()>0)
     {
      for(i=0;i<=PositionsTotal();i++)
        {
         if(PositionGetTicket(i))
           {
            //--- determinamos o símbolo, tipo e hora de abertura da posição
            P_symbol=string(PositionGetString(POSITION_SYMBOL));
            P_type=int(PositionGetInteger(POSITION_TYPE));
            P_opentime=int(PositionGetInteger(POSITION_TIME));
            //--- se o símbolo da posição corresponder ao gráfico atual e o tipo de trade for "sell"
            if(P_symbol==Symbol() && P_type==1)
              {
               //--- encontramos a barra onde a posição foi aberta
               Bar_sell_base_tf=iBarShift(Symbol(),base_tf,P_opentime);
               //--- a partir da barra na qual a posição foi aberta, realizamos a busca do high do movimento corretivo
               //se a posição foi aberta na barra atual
               if(Bar_sell_base_tf==0)
                 {
                  //desde que a barra atual seja um extremo
                  if(High_base_tf_0[Bar_sell_base_tf]>High_base_tf[Bar_sell_base_tf+1] && High_base_tf_0[Bar_sell_base_tf]>High_base_tf[Bar_sell_base_tf+2])
                    {
                     High_Corr_wave_downtrend_base_tf_sell=Bar_sell_base_tf;//high do movimento corretivo será igual à barra atual
                    }
                  else
                    {
                     //se a barra atual não for um extremo, iniciamos o ciclo para buscar o extremo
                     for(n=Bar_sell_base_tf; n<(bars_base_tf);n++)
                       {
                        if(High_base_tf[n]>High_base_tf[n+1] && High_base_tf[n]>High_base_tf[n+2])//se o extremo for encontrado
                           break;//interrompemos o ciclo
                       }
                     High_Corr_wave_downtrend_base_tf_sell=n;
                    }
                  //--- descrevemos as condições de controle
                  Third_downtrend_control_bool=(
                                                /*1. Tempo high do movimento corretivo, encontrado na abertura da posição
                                                 coincide com o tempo do atual high do movimento corretivo*/Time_base_tf[High_Corr_wave_downtrend_base_tf_sell]==High_Corr_wave_downtrend_base_tf_time
                                                );
                 }
               //--- se a posição não foi aberta na barra atual
               if(Bar_sell_base_tf!=0 && Bar_sell_base_tf!=1000)
                 {
                  //--- iniciamos o ciclo para buscar o extremo-barra
                  for(n=Bar_sell_base_tf; n<(bars_base_tf);n++)
                    {
                     //--- se o extremo for encontrado
                     if(High_base_tf[n]>High_base_tf[n+1] && High_base_tf[n]>High_base_tf[n+2])
                        break;//interrompemos o ciclo
                    }
                  High_Corr_wave_downtrend_base_tf_sell=n;
                 }
               Third_downtrend_control_bool=(
                                             /*1. Tempo high do movimento corretivo, encontrado na abertura da posição
                                                 coincide com o tempo do atual high do movimento corretivo*/Time_base_tf[High_Corr_wave_downtrend_base_tf_sell]==High_Corr_wave_downtrend_base_tf_time
                                             );
              }
           }
        }
     }
//+---------------------------------------------------------------------------+
//| 5.3.1 Para vendas (final)                                                 |
//+---------------------------------------------------------------------------+
Criando controle para compras:
//+---------------------------------------------------------------------------+
//| 5.3.2 Para compras (início)                                               |
//+---------------------------------------------------------------------------+
//--- declaração de variáveis
   int Bar_buy_base_tf,Low_Corr_wave_uptrend_base_tf_buy;
   bool Third_uptrend_control_bool=false;
//--- pesquisa detalhada de todas as posições abertas
   if(PositionsTotal()>0)
     {
      for(i=0;i<=PositionsTotal();i++)
        {
         if(PositionGetTicket(i))
           {
            //determinamos o símbolo, tipo e hora de abertura da posição
            P_symbol=string(PositionGetString(POSITION_SYMBOL));
            P_type=int(PositionGetInteger(POSITION_TYPE));
            P_opentime=int(PositionGetInteger(POSITION_TIME));
            //se o símbolo da posição corresponder ao gráfico atual e o tipo de trade for "buy"
            if(P_symbol==Symbol() && P_type==0)
              {
               //encontramos a barra onde a posição foi aberta
               Bar_buy_base_tf=iBarShift(Symbol(),base_tf,P_opentime);
               //a partir da barra na qual a posição foi aberta, realizamos a busca do low do movimento corretivo
               //se a posição foi aberta na barra atual
               if(Bar_buy_base_tf==0)
                 {
                 //desde que a barra atual seja um extremo
                  if(Low_base_tf_0[Bar_buy_base_tf]<Low_base_tf[Bar_buy_base_tf+1] && Low_base_tf_0[Bar_buy_base_tf]<Low_base_tf[Bar_buy_base_tf+2])
                    {
                     Low_Corr_wave_uptrend_base_tf_buy=Bar_buy_base_tf;
                    }
                  else
                    {
                    //se a barra atual não for um extremo, iniciamos o ciclo para buscar o extremo
                     for(n=Bar_buy_base_tf; n<(bars_base_tf);n++)
                       {
                        if(Low_base_tf[n]<Low_base_tf[n+1] && Low_base_tf[n]<Low_base_tf[n+2])//se o extremo for encontrado
                           break;//interrompemos o ciclo
                       }
                     Low_Corr_wave_uptrend_base_tf_buy=n;
                    }
                  //--- descrevemos as condições de controle  
                  Third_uptrend_control_bool=(
                                               /*1. Tempo low do movimento corretivo, encontrado na abertura da posição
                                                 coincide com o tempo do low atual do movimento de correção*/Time_base_tf[Low_Corr_wave_uptrend_base_tf_buy]==Low_Corr_wave_uptrend_base_tf_time
                                               );
                 }
               //--- se a posição não foi aberta na barra atual
               if(Bar_buy_base_tf!=0 && Bar_buy_base_tf!=1000)
                 {
                  //--- iniciamos o ciclo para buscar o extremo-barra
                  for(n=Bar_buy_base_tf; n<(bars_base_tf);n++)
                    {
                     //--- se o extremo for encontrado
                     if(Low_base_tf[n]<Low_base_tf[n+1] && Low_base_tf[n]<Low_base_tf[n+2])
                        break;//interrompemos o ciclo
                    }
                  Low_Corr_wave_uptrend_base_tf_buy=n;
                 }
                 //--- descrevemos as condições de controle  
               Third_uptrend_control_bool=(
                                            /*1. Tempo low do movimento corretivo, encontrado na abertura da posição
                                                 coincide com o tempo do low atual do movimento de correção*/Time_base_tf[Low_Corr_wave_uptrend_base_tf_buy]==Low_Corr_wave_uptrend_base_tf_time
                                            );
              }
           }
        }
     }
//+---------------------------------------------------------------------------+
//| 5.3.2 Para compra (final)                                                 |
//+---------------------------------------------------------------------------+

7. Condições para o ponto de entrada

O ponto de entrada deve ser determinado no período de trabalho — work_tf. Isso é necessário para a entrada oportuna no mercado e, se possível, reduzir o tamanho do risco em pontos. As leituras do indicador Parabolic são usadas como sinal: se na barra atual o valor do indicador for maior do que o high da barra atual, e na barra anterior o valor do indicador for menor que o low da mesma barra, podemos vender. Para compras, o oposto.

//+------------------------------------------------------------------+
//| 6. Descrição das condições para o ponto de entrada (início)      |
//+------------------------------------------------------------------+
//--- para vendas
   bool PointSell_work_tf_bool=(
                                /*1. Low da barra 1 acima de iSar[1]*/Low_work_tf[1]>Sar_array_work_tf[1] && 
                                /*2. High da barra 0 abaixo de iSar[0]*/High_work_tf_0[0]<Sar_array_work_tf_0[0]
                                );
//--- para compras
   bool PointBuy_work_tf_bool=(
                               /*1. High da barra 1 abaixo de iSar*/High_work_tf[1]<Sar_array_work_tf[1] && 
                               /*2. Low da barra 0 acima de iSar[0]*/Low_work_tf_0[0]>Sar_array_work_tf_0[0]
                               );
//+------------------------------------------------------------------+
//| 6. Descrição das condições para o ponto de entrada (final)       |
//+------------------------------------------------------------------+

8. Condições de negociação

Nessa etapa, combinamos numa variável lógica todas as condições e controles que criamos anteriormente.

//+------------------------------------------------------------------+
//| 7. Descrição das condições de negociação (início)                |
//+------------------------------------------------------------------+
//--- para vendas
   bool OpenSell=(
                  /*1. foi formado o modelo*/Model_downtrend_base_tf==true && 
                  /*2. 1 controle permite abrir a posição*/First_downtrend_control_bool==true && 
                  /*3. 2 controle permite abrir a posição*/Second_downtrend_control_bool==true && 
                  /*4. 3 controle permite abrir a posição*/Third_downtrend_control_bool==false && 
                  /*5. Ponto de entrada para work_tf*/PointSell_work_tf_bool==true
                  );
//--- para compras
   bool OpenBuy=(
                 /*1. foi formado o modelo*/Model_uptrend_base_tf==true && 
                 /*2. 1 controle permite abrir a posição*/First_uptrend_control_bool==true && 
                 /*3. 2 controle permite abrir a posição*/Second_uptrend_control_bool==true && 
                 /*4. 3 controle permite abrir a posição*/Third_uptrend_control_bool==false && 
                 /*5. Ponto de entrada para work_tf*/PointBuy_work_tf_bool==true
                 );
//+------------------------------------------------------------------+
//| 7. Descrição das condições de negociação (final)                 |
//+------------------------------------------------------------------+

9. Trabalhando com operações de negociação

O trabalho com operações de negociação é dividido em:

  • Definição de posições;
  • Definição de take-profit;
  • Deslocamento de posição para break-even.

1. Definição de posições

//+------------------------------------------------------------------+
//| 8. Trabalhando com operações de negociação(início)               |
//+------------------------------------------------------------------+
//--- definição de níveis de stop-loss
   SLPrice_sell=High_Corr_wave_downtrend_base_tf_double+spread;
   SLPrice_buy=Low_Corr_wave_uptrend_base_tf_double-spread;
//+------------------------------------------------------------------+
//| 8.1 Definição de posições (início)                               |
//+------------------------------------------------------------------+
//--- para venda
   if(OpenSell==true)
     {
      RiskSize_points=(SLPrice_sell-bid)*f;//definimos o sl em pontos e realizamos seu valor inteiro
      if(RiskSize_points==0)//controle de divisão por 0
        {
         RiskSize_points=1;
        }
      CostOfPoint_position=SummRisk/RiskSize_points;//determinamos o valor - em pontos - da posição levando em consideração o tamanho do sl
      Lot=CostOfPoint_position/CostOfPoint;//calculamos o lote para abertura da posição
      //abrimos a posição
      trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,NormalizeDouble(Lot,2),bid,NormalizeDouble(SLPrice_sell,5),0,"");
     }
//--- para compra
   if(OpenBuy==true)
     {
      RiskSize_points=(bid-SLPrice_buy)*f;//definimos o sl em pontos e realizamos seu valor inteiro
      if(RiskSize_points==0)//controle de divisão por 0
        {
         RiskSize_points=1;
        }
      CostOfPoint_position=SummRisk/RiskSize_points;//determinamos o valor - em pontos - da posição levando em consideração o tamanho do sl
      Lot=CostOfPoint_position/CostOfPoint;//calculamos o lote para abertura da posição
      //abrimos a posição
      trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,NormalizeDouble(Lot,2),ask,NormalizeDouble(SLPrice_buy,5),0,"");
     }
//+------------------------------------------------------------------+
//| 8.1 Definição de posições (final)                                |
//+------------------------------------------------------------------+

2. Definido take-profit

//+------------------------------------------------------------------+
//| 8.2 Definição de take-profit (início)                            |
//+------------------------------------------------------------------+
   if(TP_mode==true)
     {
      if(PositionsTotal()>0)
        {
         for(i=0;i<=PositionsTotal();i++)
           {
            if(PositionGetTicket(i))
              {
              //obtemos o valor da posição
               SL_double=double (PositionGetDouble(POSITION_SL));
               OP_double=double (PositionGetDouble(POSITION_PRICE_OPEN));
               TP_double=double (PositionGetDouble(POSITION_TP));
               P_symbol=string(PositionGetString(POSITION_SYMBOL));
               P_type=int(PositionGetInteger(POSITION_TYPE));
               P_profit=double (PositionGetDouble(POSITION_PROFIT));
               P_ticket=int (PositionGetInteger(POSITION_TICKET));
               P_opentime=int(PositionGetInteger(POSITION_TIME));
               if(P_symbol==Symbol())
                 {
                  if(P_type==0 && TP_double==0)
                    {
                     double SL_size_buy=OP_double-SL_double;//definimos o tamanho do sl em pontos
                     double TP_size_buy=SL_size_buy*M;//multiplicamos o tamanho do sl pela proporção dada nos parâmetros de entrada
                     double TP_price_buy=OP_double+TP_size_buy;//definimos o nível de tp
                     //modificamos a posição
                     trade.PositionModify(PositionGetInteger(POSITION_TICKET),SL_double,NormalizeDouble(TP_price_buy,5));
                    }
                  if(P_type==1 && TP_double==0)
                    {
                     double SL_size_sell=SL_double-OP_double;//definimos o tamanho do sl em pontos
                     double TP_size_sell=SL_size_sell*M;//multiplicamos o tamanho do sl pela proporção dada nos parâmetros de entrada
                     double TP_price_sell=OP_double-TP_size_sell;//definimos o nível de tp
                     //modificamos a posição
                     trade.PositionModify(PositionGetInteger(POSITION_TICKET),SL_double,NormalizeDouble(TP_price_sell,5));
                    }
                 }
              }
           }
        }
     }
//+------------------------------------------------------------------+
//| 8.2 Definição de take-profit (final)                             |
//+------------------------------------------------------------------+

3. Movendo posição para break-even.

//+------------------------------------------------------------------+
//| 8.3 Deslocamento de posição para break-even (início)             |
//+------------------------------------------------------------------+
   double Size_Summ=breakeven*SummRisk;//definimos o nível de lucro que ao ser atingido, a posição deva ser movida para o break-even
   if(Breakeven_mode==true && breakeven!=0)
     {
      if(PositionsTotal()>0)
        {
         for(i=0;i<=PositionsTotal();i++)
           {
            if(PositionGetTicket(i))
              {
              //obtemos o valor da posição
               SL_double=double (PositionGetDouble(POSITION_SL));
               OP_double=double (PositionGetDouble(POSITION_PRICE_OPEN));
               TP_double=double (PositionGetDouble(POSITION_TP));
               P_symbol=string(PositionGetString(POSITION_SYMBOL));
               P_type=int(PositionGetInteger(POSITION_TYPE));
               P_profit=double (PositionGetDouble(POSITION_PROFIT));
               P_ticket=int (PositionGetInteger(POSITION_TICKET));
               P_opentime=int(PositionGetInteger(POSITION_TIME));
               if(P_symbol==Symbol())
                 {
                  if(P_type==0 && P_profit>=Size_Summ && SL_double<OP_double)
                    {
                     trade.PositionModify(PositionGetInteger(POSITION_TICKET),OP_double,TP_double);
                    }
                  if(P_type==1 && P_profit>=Size_Summ && SL_double>OP_double)
                    {
                     trade.PositionModify(PositionGetInteger(POSITION_TICKET),OP_double,TP_double);
                    }
                 }
              }
           }
        }
     }
//+------------------------------------------------------------------+
//| 8.3 Deslocamento de posição para break-even (final)              |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 8. Trabalhando com operações de negociação(final)                |
//+------------------------------------------------------------------+

5. Coleta de dados estatísticos

Primeiro, precisamos definir o conjunto de leituras para as estatísticas:

  1. Símbolo do instrumento;
  2. Tipo de trade;
  3. Hora de entrada;
  4. Preço de abertura;
  5. Nível de stop-loss;
  6. Tamanho do stop-loss;
  7. Nível de lucro máximo;
  8. Tamanho do lucro máximo;
  9. Duração do trade.

É necessário supor que o ponto de lucro máximo é o high/low do primeiro fractal superior/inferior no período de base formado após a barra onde a posição foi aberta.

Primeiro, precisamos conferir o trabalho do EA no testador de estratégias. Para o teste, escolhi o par AUDJPY para o período 01.01.2018—29.08.2018. Como período base escolhi D1, como o período de trabalho, H6. Ricos por trade — $100. Deslocamento de posição para break-even 1/2, definição de take-profit — 1/3 (risco/lucro).

Parâmetros de entrada do EA

Fig. 4. Parâmetros de entrada do EA

Após o teste, salvamos o relatório num arquivo CSV. Na pasta do terminal local, criamos um novo arquivo report.csv. Para ele copiamos os dados do relatório. Mas precisamos copiar apenas parte da seção Ordem. Além disso, precisamos excluir as linhas relacionadas às posições de fechamento, conforme mostrado na Figura 5:

Excluindo linhas de um relatório relacionadas a posições de fechamento

Figura 5. Excluindo linhas de um relatório relacionadas a posições de fechamento

É necessário copiar as colunas:

  1. Hora de abertura;
  2. Símbolo;
  3. Tipo;
  4. Preço;
  5. S/L.

Como resultado, o arquivo report.csv ficará assim:

Conteúdo do arquivo report.csv

Fig. 6. Conteúdo do arquivo report.csv

Agora precisamos criar um script para ler os dados do arquivo report.csv e para formar um novo file_stat.csv com informações estatísticas adicionais:

  1. Tamanho do SL;
  2. Nível de lucro máximo;
  3. Tamanho do lucro máximo;
  4. Duração do trade em barras.

Para este problema, usei uma solução pronta do artigo. MQL5 programações básicas: arquivos na seção Ler um arquivo de texto num array. De minha parte, adicionei matrizes e seu preenchimento para armazenar os valores de coluna no arquivo file_stat.csv.

Criamos um novo script, sob função OnStart(), escrevemos o código da função para ler arquivos na matriz

//+------------------------------------------------------------------+
//| Função para leitura na matriz (início)                           |
//+------------------------------------------------------------------+
bool ReadFileToArrayCSV(string FileName,SLine  &Lines[])
  {
   ResetLastError();
   int h=FileOpen(FileName,FILE_READ|FILE_ANSI|FILE_CSV,";");
   if(h==INVALID_HANDLE)
     {
      int ErrNum=GetLastError();
      printf("Erro ao abrir o arquivo %s # %i",FileName,ErrNum);
      return(false);
     }
   int lcnt=0; // variável para contar linhas 
   int fcnt=0; // variável para contar campos de linha    
   while(!FileIsEnding(h))
     {
      string str=FileReadString(h);
      // nova linha (novo elemento da matriz de estrutura)
      if(lcnt>=ArraySize(Lines))
        { // matriz de estruturas completamente preenchida
         ArrayResize(Lines,ArraySize(Lines)+1024); // aumentamos o tamanho da matriz em 1024 elementos
        }
      ArrayResize(Lines[lcnt].field,64);// alteramos o tamanho da matriz na estrutura
      Lines[lcnt].field[0]=str; // atribuímos o valor do primeiro campo
                                // começamos a ler os campos restantes na linha
      fcnt=1; // no momento, um elemento está ocupado na matriz de campos
      while(!FileIsLineEnding(h))
        { // lemos os campos restantes na linha
         str=FileReadString(h);
         if(fcnt>=ArraySize(Lines[lcnt].field))
           { // matriz de campo completamente preenchida
            ArrayResize(Lines[lcnt].field,ArraySize(Lines[lcnt].field)+64); // aumentamos o tamanho da matriz em 64 elementos
           }
         Lines[lcnt].field[fcnt]=str; // atribuímos o valor do próximo campo
         fcnt++; // incrementamos o contador de campos
        }
      ArrayResize(Lines[lcnt].field,fcnt); // alteramos o tamanho da matriz de campos de acordo com o número real de campos
      lcnt++; // incrementamos o contador de linhas
     }
   ArrayResize(Lines,lcnt); // alteramos a matriz de estruturas (linhas) de acordo com o número real de linhas
   FileClose(h);
   return(true);
  }
//+------------------------------------------------------------------+
//| Função para leitura na matriz (final)                            |
//+------------------------------------------------------------------+

Em seguida, especificamos os parâmetros de entrada:

#property script_show_inputs 
//--- parâmetros de entrada
input ENUM_TIMEFRAMES base_tf;  //timeframe do período base
input double sar_step=0.1;      //set parabolic step
input double maximum_step=0.11; //set parabolic maximum step
//--- declaração de variáveis para identificadores de indicadores
int Fractal_base_tf;             //identificador do indicador iFractal
//--- declaração de matrizes para base_tf
double High_base_tf[],Low_base_tf[];             //matrizes para armazenar preços High e Low das barras
double FractalDown_base_tf[],FractalUp_base_tf[];//matriz para armazenar os preços do indicador iFractall
//--- estrutura da matriz
struct SLine
  {
   string            field[];
  };

Dentro da função OnStart(), obtemos o identificador do indicador iFractals, declaramos e preenchemos as matrizes dos preços High/Low. Além disso, precisamos inserir não só a variável bars_base_tf usada no ciclo for, mas também a variável f para armazena o número de bits do preço dependendo do número de casas decimais no preço do instrumento. Essa variável será usada para trazer as leituras de stop-loss e do lucro máximo para um valor inteiro.

//--- obtemos os identificadores do indicador iFractal
   Fractal_base_tf=iFractals(Symbol(),base_tf);
//--- arrumando matrizes, como na série temporal para base_tf
   ArraySetAsSeries(High_base_tf,true);
   ArraySetAsSeries(Low_base_tf,true);
   ArraySetAsSeries(FractalDown_base_tf,true);
   ArraySetAsSeries(FractalUp_base_tf,true);
//--- preenchimento inicial de matrizes para base_tf
   CopyHigh(Symbol(),base_tf,0,1000,High_base_tf);
   CopyLow(Symbol(),base_tf,0,1000,Low_base_tf);
   CopyBuffer(Fractal_base_tf,0,TimeCurrent(),1000,FractalUp_base_tf);
   CopyBuffer(Fractal_base_tf,1,TimeCurrent(),1000,FractalDown_base_tf);
//--- variáveis para armazenar informações sobre o número de barras
   int bars_base_tf=Bars(Symbol(),base_tf);
// número de casas decimais no preço do instrumento
   int Digit=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
// definição do número de bits do símbolo atual
   double f=1;
   if(Digit==5) {f=100000;}
   if(Digit==4) {f=10000;}
   if(Digit==3) {f=1000;}
   if(Digit==2) {f=100;}
   if(Digit==1) {f=10;}

Em seguida, precisamos declarar matrizes e variáveis:

//--- declaração de variáveis ​​e matrizes
   int i,j,n; //variáveis para ciclos
   datetime opentime[];//matriz para armazenar a hora de posicionamento de posições
   string symbol[];//matriz para armazenar símbolos
   string type[];////matriz para armazenar tipos de trades
   string openprice[];//matriz para armazenar preços de abertura
   string  sl_price[];//matriz para armazenar níveis de stop loss
   int index[];//matriz para armazenar o índice da barra em que foi definida a posição
   int down_fractal[];//matriz para armazenar os índices dos fractais inferiores 
   int up_fractal[];//matriz para armazenar os índices dos fractais superiores 
   double sl_size_points[];//matriz para armazenar os tamanhos dos stop-loss em pontos
   string maxprofit_price[];//matriz para armazenar os níveis do lucro máximo
   double maxprofit_size_points[];//matriz para armazenar o tamanho do lucro máximo
   int duration[];//matriz para armazenar informações sobre a duração da onda em barras
   bool maxprofit_bool[];//matriz para determinar que a posição não bate no sl
   int maxprofit_int[];//matriz para determinar a barra menor/maior. Será usado como maxprofit_bool[]

Depois disso, prosseguimos com a leitura de dados a partir de arquivo em matrizes:

   SLine lines[];
   int size=0;
   if(!ReadFileToArrayCSV("report.csv",lines))
     {
      Alert("Erro, veja detalhes na guia \"EAs\"");
     }
   else
     {
      size=ArraySize(lines);
      ArrayResize(opentime,ArraySize(lines));
      ArrayResize(symbol,ArraySize(lines));
      ArrayResize(type,ArraySize(lines));
      ArrayResize(openprice,ArraySize(lines));
      ArrayResize(sl_price,ArraySize(lines));
      ArrayResize(index,ArraySize(lines));
      ArrayResize(down_fractal,ArraySize(lines));
      ArrayResize(up_fractal,ArraySize(lines));
      ArrayResize(sl_size_points,ArraySize(lines));
      ArrayResize(maxprofit_price,ArraySize(lines));
      ArrayResize(maxprofit_size_points,ArraySize(lines));
      ArrayResize(duration,ArraySize(lines));
      ArrayResize(maxprofit_bool,ArraySize(lines));
      ArrayResize(maxprofit_int,ArraySize(lines));
      for(i=0;i<size;i++)
        {
         for(j=0;j<ArraySize(lines[i].field);j=j+5)//precisamos selecionar os campos pela coluna de hora de abertura de posição
           {
            opentime[i]=(datetime)(lines[i].field[j]);//registramos os dados na matriz
           }
         for(j=1;j<ArraySize(lines[i].field);j=j+4)//precisamos selecionar os campos pela coluna do símbolo
           {
            symbol[i]=(lines[i].field[j]);//registramos os dados na matriz
           }
         for(j=2;j<ArraySize(lines[i].field);j=j+3)//precisamos selecionar os campos pela coluna do tipo de trade
           {
            type[i]=(lines[i].field[j]);//registramos os dados na matriz
           }
         for(j=3;j<ArraySize(lines[i].field);j=j+2)//precisamos selecionar os campos pela coluna do preço de fechamento
           {
            openprice[i]=(lines[i].field[j]);//registramos os dados na matriz
           }
         for(j=4;j<ArraySize(lines[i].field);j=j+1)//precisamos selecionar os campos pela coluna do stop-loss
           {
            sl_price[i]=(lines[i].field[j]);//registramos os dados na matriz
           }
        }
     }
//-----------------------------------------------------

As matrizes openrpice[] e sl_price[] possuem dados de string e, para serem usados em cálculos, eles devem ser convertidos em dados do tipo double com ajuda da função StringToDouble(). Mas, ao acontecer isso, serão perdidos os sinais após as vírgulas. Para evitar isso, substituímos as vírgulas por pontos com ajuda da funçãofunção StringReplace():

   for(i=0;i<size;i++)
     {
      StringReplace(openprice[i],",",".");
      StringReplace(sl_price[i],",",".");
     }

Em seguida, precisamos determinar os índices das barras nas quais foram colocadas as posições:

// --- determinação de índices de barras em que foram abertas posições
   for(i=0;i<size;i++)
     {
      index[i]=iBarShift(Symbol(),PERIOD_D1,opentime[i]);//registramos os dados na matriz
     }

Depois disso, encontramos os fractais inferior e superior mais próximos da posição:

//--- busca do fractal descendente para venda
   for(i=0;i<size;i++)
     {
      if(type[i]=="sell")
        {
         for(n=index[i];n>0;n--)
           {
            if(FractalDown_base_tf[n]!=EMPTY_VALUE)
               break;
           }
         down_fractal[i]=n;
        }
     }
//--- busca do fractal ascendente para compra
   for(i=0;i<size;i++)
     {
      if(type[i]=="buy")
        {
         for(n=index[i];n>0;n--)
           {
            if(FractalUp_base_tf[n]!=EMPTY_VALUE)
               break;
           }
         up_fractal[i]=n;
        }
     }

Em seguida, definimos o tamanho do stop-loss em pontos e damos o número de pontos num valor inteiro:

//--- tamanho de stop-loss em pontos
   for(i=0;i<size;i++)
     {
      if(type[i]=="sell")
        {
         sl_size_points[i]=(StringToDouble(sl_price[i])-StringToDouble(openprice[i]))*f;
        }
      if(type[i]=="buy")
        {
         sl_size_points[i]=(StringToDouble(openprice[i])-StringToDouble(sl_price[i]))*f;
        }
     }

Com base nos fractais encontrados anteriormente, podemos determinar o nível de lucro máximo. Mas primeiro necessitamos verificar se a posição não será fechada antes por stop-loss. Código de verificação:

//--- verificando se a posição não fechará por sl antes de atingir o nível de lucro máximo
//--- para trades sell
   for(i=0;i<size;i++)
     {
      if(type[i]=="sell")
        {
         for(n=index[i];n>down_fractal[i];n--)
           {
            if(High_base_tf[n]>=StringToDouble(sl_price[i]))
               break;
           }
         maxprofit_int[i]=n;
         maxprofit_bool[i]=(n==down_fractal[i]);
        }
     }
//--- para trades buy
   for(i=0;i<size;i++)
     {
      if(type[i]=="buy")
        {
         for(n=index[i];n>up_fractal[i];n--)
           {
            if(Low_base_tf[n]<=StringToDouble(sl_price[i]))
               break;
           }
         maxprofit_int[i]=n;
         maxprofit_bool[i]=(n==up_fractal[i]);
        }
     }

Agora podemos escrever o código para determinar o nível de lucro máximo:

//--- nível de lucro máximo
   for(i=0;i<size;i++)
     {
      if(type[i]=="sell" && maxprofit_bool[i]==true)
        {
         maxprofit_price[i]=(string)Low_base_tf[down_fractal[i]];
        }
      if(type[i]=="sell" && maxprofit_bool[i]==false)
        {
         maxprofit_price[i]="";
        }
      if(type[i]=="buy" && maxprofit_bool[i]==true)
        {
         maxprofit_price[i]=(string)High_base_tf[up_fractal[i]];
        }
      if(type[i]=="buy" && maxprofit_bool[i]==false)
        {
         maxprofit_price[i]="";
        }
     }

Em seguida, podemos determinar o tamanho do lucro máximo. A margem de lucro será negativa para o tamanho do stop-loss se o controle for acionado:

   for(i=0;i<size;i++)
     {
      if(type[i]=="sell" && maxprofit_bool[i]==true)
        {
         maxprofit_size_points[i]=(StringToDouble(openprice[i])-Low_base_tf[down_fractal[i]])*f;
        }
      if(type[i]=="sell" && maxprofit_bool[i]==false)
        {
         maxprofit_size_points[i]=sl_size_points[i]*-1;
        }
      if(type[i]=="buy" && maxprofit_bool[i]==true)
        {
         maxprofit_size_points[i]=(High_base_tf[up_fractal[i]]-StringToDouble(openprice[i]))*f;
        }
      if(type[i]=="buy" && maxprofit_bool[i]==false)
        {
         maxprofit_size_points[i]=sl_size_points[i]*-1;
        }
     }

Finalmente, definimos a duração medida no número de barras entre a barra na qual a posição é definida e a barra de lucro máximo. Se acionado o controle para fechar a posição pelo sl, a duração é determinada como a diferença entre a barra na qual a posição é definida e a barra na qual é acionado o S/L:

//--- cálculo da duração do trade em barras
   for(i=0;i<size;i++)
     {
      if(type[i]=="sell" && maxprofit_bool[i]==true)
        {
         duration[i]=index[i]-(int)down_fractal[i];
        }
      if(type[i]=="sell" && maxprofit_bool[i]==false)
        {
         duration[i]=index[i]-maxprofit_int[i];
        }
      if(type[i]=="buy" && maxprofit_bool[i]==true)
        {
         duration[i]=index[i]-(int)up_fractal[i];
        }
      if(type[i]=="buy" && maxprofit_bool[i]==false)
        {
         duration[i]=index[i]-maxprofit_int[i];
        }
     }

Depois disso, para uma exibição normal de indicadores no relatório, substituímos os pontos por vírgulas:

   for(i=0;i<size;i++)
     {
      StringReplace(openprice[i],".",",");
      StringReplace(sl_price[i],".",",");
      StringReplace(maxprofit_price[i],".",",");
     }

No final, resta apenas armazenar os dados recebidos no arquivo file_stat.csv:

//--- escrevemos os dados num novo arquivo de estatísticas
   int h=FileOpen("file_stat.csv",FILE_READ|FILE_WRITE|FILE_ANSI|FILE_CSV,";");
//--- verificação de abertura
   if(h==INVALID_HANDLE)
     {
      Alert("Erro ao abrir o arquivo1");
      return;
     }
   else
     {
      FileWrite(h,
                /*1 símbolo*/"Símbolo",
                /*2 tipo de trade*/"Tipo de trade",
                /*3 hora de entrada*/"Hora de abertura",
                /*4 preço de abertura*/"Preço de abertura",
                /*5 nível de sl*/"SL",
                /*6 tamanho de sl*/"Tamanho do SL",
                /*7 nível de lucro máximo*/"Nível de lucro máximo",
                /*8 tamanho do lucro máximo*/"Tamanho do lucro máximo",
                /*9 duração*/"Duração em barras");
      //--- deslocamento para o final
      FileSeek(h,0,SEEK_END);
      for(i=0;i<size;i++)
        {
         FileWrite(h,
                   /*1 símbolo*/symbol[i],
                   /*2 tipo de trade*/type[i],
                   /*3 hora de entrada*/TimeToString(opentime[i]),
                   /*4 preço de abertura*/openprice[i],
                   /*5 nível de sl*/sl_price[i],
                   /*6 tamanho de sl*/NormalizeDouble(sl_size_points[i],2),
                   /*7 nível de lucro máximo*/maxprofit_price[i],
                   /*8 tamanho de lucro máximo*/NormalizeDouble(maxprofit_size_points[i],2),
                   /*9 duração*/duration[i]);
        }
     }
   FileClose(h);
   Alert("Arquivo file_stat.csv criado");

Verificamos: instalamos o script no gráfico, sem esquecer de definir o período do timeframe básico nos parâmetros de entrada (no meu caso - D1), após o qual um novo arquivo file_stat.csv aparecerá na pasta do terminal local com o seguinte conjunto de indicadores:

Conteúdo do arquivo file_stat.csv

Fig.7. Conteúdo do arquivo file_stat.csv

6. Fim do artigo

O artigo descreve como determinar programaticamente um dos modelos de continuação de movimento. A ideia chave desse método é encontrar o extremo high/low de um movimento corretivo sem o uso de nenhum indicador. Logo após isso, são pesquisados os seguintes pontos do modelo a partir do extremo encontrado.

O artigo também discute um método de coleta de dados estatísticos com base nos resultados do teste no testador de estratégias, registrando os resultados do teste num array e processando-os logo a seguir. Acredito que seria possível construir uma maneira mais eficiente de coletar e processar dados estatísticos. No entanto, esse método me pareceu o mais simples e compreensível.

Deve-se ter em mente que o artigo descreve os mais mínimos, na opinião do autor, requisitos para definir um modelo e, o mais importante, o conjunto mínimo de controles que o EA faz. É claro que, para negociação real, o conjunto de controles precisa ser expandido.

No final, forneço imagens com exemplos do reconhecimento da continuação do movimento:

Exemplo de reconhecimento de modelo

Fig. 8. Exemplo de reconhecimento do modelo de continuação de movimento

Exemplo de reconhecimento de modelo

Fig. 9. Exemplo de reconhecimento do modelo de continuação de movimento

Exemplo de reconhecimento de modelo de continuação de tendência

Fig. 10. Exemplo de reconhecimento do modelo de continuação de movimento

Exemplo de reconhecimento de modelo de continuação de tendência

Fig. 11. Exemplo de reconhecimento do modelo de continuação de movimento

Programas utilizados no artigo:

#NomeTipoDescrição
1Trade.mqhBiblioteca de classeClasse de operações de negociação
2MQL5_POST_finalExpert AdvisorEA determinando o modelo de continuação de movimento
3Report_readScriptScript para coletar estatísticas

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

Arquivos anexados |
MQL5.zip (128.44 KB)
Receitas MQL5 – Obtendo as propriedades de uma posição de cobertura aberta Receitas MQL5 – Obtendo as propriedades de uma posição de cobertura aberta
A plataforma MetaTrader 5 não é apenas multimercado, pois ela também permite que utilizar diferentes sistemas de registro de posição. Esses recursos expandem significativamente as ferramentas para a implementação e formalização de ideias de negociação. O artigo trata de como processar e levar em conta as propriedades das posições quando elas são registradas independentemente (cobertura - 'hedge'). Além disso, é proposta uma classe derivada, é exemplificado como processar e obter as propriedades de uma posição de cobertura.
Usando indicadores para otimização RealTime de EAs Usando indicadores para otimização RealTime de EAs
Não é segredo que o sucesso de qualquer robô de negociação depende da seleção correta de parâmetros (otimização). Mas os parâmetros que são ótimos para um intervalo de tempo nem sempre são os melhores em outros períodos. Muitas vezes, os EAs que são lucrativos nos testes se revelam não lucrativos em tempo real. Nesse momento, surge a necessidade de estar otimizando continuamente, o que se torna uma rotina, porém, sempre há alguém que procura maneiras de automatizar o trabalho. Nesse artigo, proponho uma abordagem não padrão para resolver esse problema.
Os 100 melhores passes de otimização (parte 1). Desenvolvimento de um analisador de otimizações Os 100 melhores passes de otimização (parte 1). Desenvolvimento de um analisador de otimizações
O artigo trata do desenvolvimento de um aplicativo para selecionar os melhores passes de otimização usando várias opções possíveis. O aplicativo é capaz de ordenar os resultados de otimização por diversos fatores. Os passes de cada otimização são sempre gravadas em um banco de dados, portanto, você sempre poderá selecionar os novos parâmetros do robô sem realizar a re-otimização. Além disso, você pode ver todos os passes de otimização em um único gráfico, calcular a métrica do VaR paramétrico e construir o gráfico de distribuição normal de passes e resultados da negociação de um determinado conjunto de métricas. Além disso, os gráficos de algumas taxas calculadas são construídos dinamicamente, começando com o início da otimização (ou de uma data selecionada para outra data selecionada).
Modelando séries temporais usando símbolos personalizados de acordo com as leis de distribuição especificadas Modelando séries temporais usando símbolos personalizados de acordo com as leis de distribuição especificadas
O artigo fornece uma visão geral das capacidades do terminal para criar e trabalhar com símbolos personalizados, oferece opções para modelar um histórico de negociação usando símbolos personalizados, tendências e vários padrões gráficos.