Modelo de continuação de movimento - estatísticas de desempenho e pesquisa em gráficos
- Introdução
- Descrição do modelo - disposições gerais
- Princípios do reconhecimento de modelos no gráfico
- Construção do algoritmo e escrita do código
- Parâmetros de entrada, função OnInit() e declaração inicial de variáveis
- Definição de parâmetros gerais
- Atualizando dados em matrizes
- Preenchendo matrizes quando aparece uma nova barra
- Preenchendo matrizes com dados da barra 0
- Atualizando dados de fractais
- Buscando extremos
- Buscando extremos para uma tendência baixista
- Buscando extremos para uma tendência altista
- Convertendo valores High/Low de ondas corretivas em variáveis individuais
- Condições para reconhecimento do modelo
- Criando controles
- Controlando a formação do ponto de entrada na zona de abertura da posição
- Controlando o recuo do preço para a zona de abertura da posição
- Eliminação de posições duplicadas dentro do mesmo modelo
- Condições para o ponto de entrada
- Condições de negociação
- Trabalhando com operações de negociação
- Coleta de dados estatísticos
- 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.
Fig. 1. Modelo de continuação de movimento
No gráfico, é assim:
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 |
---|---|---|---|
1 | O High/Low superior/inferior a dois High/Low de barras anteriores é considerado uma barra extremo | 1 | O High/Low superior/inferior a dois High/Low de barras anteriores é considerado uma barra extremo |
2 | A onda corretiva deve sempre terminar com a presença de um extremo superior (ponto C - veja imagem 1 e imagem 2) | 2 | A onda corretiva deve sempre terminar com a presença de um extremo inferior (ponto C - veja imagem 1 e imagem 2) |
3 | A duração da onda correcional não pode ser longa e deve ser limitada a várias barras. | 3 | A duração da onda correcional não pode ser longa e deve ser limitada a várias barras. |
4 | High 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) | 4 | Low 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) |
5 | Princípio de ponto de entrada oportuno significa que é necessário abrir a posição num determinado momento da formação do ponto de entrada | 5 | Princí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/p | Para tendência baixista | Para tendência altista |
---|---|---|
1 | Encontrar o high do movimento corretivo (ponto C) | Encontrar o low do movimento corretivo (ponto C) |
2 | A 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) |
3 | Entre os pontos C e A, localizar o ponto B - low do movimento corretivo | Entre os pontos C e A, localizar o ponto B - high do movimento corretivo |
//+------------------------------------------------------------------+ //| 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
Indicador | Valores do ponto A | Valores do ponto B | Valores do ponto C |
---|---|---|---|
Índice da barra | UpperFractal_downtrend_base_tf | Low_Corr_wave_downtrend_base_tf | High_Corr_wave_downtrend_base_tf |
Valor temporal | Time_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ço | High_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
Indicador | Valores do ponto A | Valores do ponto B | Valores do ponto C |
---|---|---|---|
Índice da barra | LowerFractal_uptrend_base_tf | High_Corr_wave_uptrend_base_tf | Low_Corr_wave_uptrend_base_tf |
Valor temporal | Time_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ço | Low_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/p | Condições para tendência baixista | Condições para tendência altista |
---|---|---|
1 | High 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) |
3 | Duraçã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.
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:
- Símbolo do instrumento;
- Tipo de trade;
- Hora de entrada;
- Preço de abertura;
- Nível de stop-loss;
- Tamanho do stop-loss;
- Nível de lucro máximo;
- Tamanho do lucro máximo;
- 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).
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:
Figura 5. Excluindo linhas de um relatório relacionadas a posições de fechamento
É necessário copiar as colunas:
- Hora de abertura;
- Símbolo;
- Tipo;
- Preço;
- S/L.
Como resultado, o arquivo report.csv ficará assim:
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:
- Tamanho do SL;
- Nível de lucro máximo;
- Tamanho do lucro máximo;
- 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:
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:
Fig. 8. Exemplo de reconhecimento do modelo de continuação de movimento
Fig. 9. Exemplo de reconhecimento do modelo de continuação de movimento
Fig. 10. Exemplo de reconhecimento do modelo de continuação de movimento
Fig. 11. Exemplo de reconhecimento do modelo de continuação de movimento
Programas utilizados no artigo:
# | Nome | Tipo | Descrição |
---|---|---|---|
1 | Trade.mqh | Biblioteca de classe | Classe de operações de negociação |
2 | MQL5_POST_final | Expert Advisor | EA determinando o modelo de continuação de movimento |
3 | Report_read | Script | Script para coletar estatísticas |
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/4222
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso