![Criando uma nova estratégia de negociação usando uma tecnologia de resolução de entradas em indicadores](https://c.mql5.com/i/articles/overlay.png)
Criando uma nova estratégia de negociação usando uma tecnologia de resolução de entradas em indicadores
Introdução
Sabe-se bem que apenas 5% dos traders obtêm lucros estáveis nos mercados financeiros, enquanto que todos os 100% desejam alcançar isso.
Para ter uma negociação bem sucedida, você precisa de uma estratégia de negociação rentável. Os sites relacionados ao assunto e a literatura sobre negociações descrevem centenas de estratégias de negociação. Uma interpretação detalhada dos sinais é anexada a todos os indicadores, mas as estatísticas permanecem inalteradas: os 5% não foram nem em 100 ou pelo menos em 10. Os ideólogos de mercado culpam a instabilidade do mercado, o que implica que as estratégias lucrativas anteriores perdem a eficácia.
No meu artigo anterior eu já disse sobre a resolução de entradas em indicadores e mostrei um exemplo de como melhorar a estratégia existente. Agora eu proponho criar uma estratégia personalizada "do zero" usando a tecnologia especificada. Isso nos permitirá examinar os indicadores que você conhece com "novos olhos", coletar modelos de indicadores personalizados, bem como reconsiderar os seus sinais. A aplicação da tecnologia sugerida implica uma abordagem criativa da interpretação dos sinais dos indicadores, o que permite que cada usuário crie sua própria e única estratégia.
1. Criação de um modelo para testes e análises
A primeira coisa que vemos em um terminal de negociação é o movimento contínuo do preços. Potencialmente, tendo aberto uma negociação a qualquer momento, nós podemos obter lucro. Mas, como você pode determinar onde e com que intensidade o preço irá seguir para o próximo momento? Os traders tentam encontrar uma resposta a esta questão na análise técnica e fundamentalista. Para a realização de análises técnicas, vários indicadores são constantemente inventados e aprimorados. A novidade aqui é a interpretação dos sinais desses indicadores; pode ser diferente do comum.
Assim, a tecnologia para a resolução das entradas em indicadores implica a comparação de posições abertas com os valores dos indicadores. Mais uma vez, potencialmente, nós podemos obter lucro a qualquer momento. Com base nesses dados de entrada, no início de cada vela, abra duas posições bidirecionais com os parâmetros definidos. Então, analise como o fator de lucro de cada operação depende dos valores dos indicadores.
Para resolver este problema, realize um trabalho de preparação menor.
1.1. Criação de uma classe de ordem virtual
Eu uso uma conta netting. Portanto, para abrir ordens bidirecionais, eu criarei as ordens virtuais que não serão monitoradas pelo terminal (de acordo com as configurações da conta), mas pelo Expert Advisor. Para este fim, crie a classe CDeal. Ao inicializar uma instância da classe, nós passaremos para ele: nome do símbolo, tipo de posição, horário de abertura e preço, bem como o stoploss e takeprofit. O volume da posição é omitido intencionalmente porque aqui não nos interessa. O importante para nós é o movimento dos preços, portanto o lucro/perda será calculado em pontos em vez de termos monetários.
Para a manutenção, a classe foi adicionada com as funções da verificação do estado da posição:
- IsClosed — retorna um valor lógico, se a posição está fechada ou não;
- Type - retorna o tipo de posição
- GetProfit — retorna o lucro de uma posição fechada (para uma posição perdedora, o valor será negativo);
- GetTime — retorna a hora de abertura da posição.
class CDeal : public CObject { private: string s_Symbol; datetime dt_OpenTime; // Horário da abertura da posição double d_OpenPrice; // Preço de abertura da posição double d_SL_Price; // Stop Loss da posição double d_TP_Price; // Take Profit da posição ENUM_POSITION_TYPE e_Direct; // Direção da posição aberta double d_ClosePrice; // Preço do fechamento da posição int i_Profit; // Lucro da posição em pips //--- double d_Point; public: CDeal(string symbol, ENUM_POSITION_TYPE type,datetime time,double open_price,double sl_price, double tp_price); ~CDeal(); //--- Verifica o estado bool IsClosed(void); ENUM_POSITION_TYPE Type(void) { return e_Direct; } double GetProfit(void); datetime GetTime(void) { return dt_OpenTime; } //--- void Tick(void); };
Os ticks recebidos serão processados na função Tick, que verificará e, se necessário, encerrará uma posição por stoploss ou takeprofit e poupando o lucro acumulado.
void CDeal::Tick(void) { if(d_ClosePrice>0) return; double price=0; switch(e_Direct) { case POSITION_TYPE_BUY: price=SymbolInfoDouble(s_Symbol,SYMBOL_BID); if(d_SL_Price>0 && d_SL_Price>=price) { d_ClosePrice=price; i_Profit=(int)((d_ClosePrice-d_OpenPrice)/d_Point); } else { if(d_TP_Price>0 && d_TP_Price<=price) { d_ClosePrice=price; i_Profit=(int)((d_ClosePrice-d_OpenPrice)/d_Point); } } break; case POSITION_TYPE_SELL: price=SymbolInfoDouble(s_Symbol,SYMBOL_ASK); if(d_SL_Price>0 && d_SL_Price<=price) { d_ClosePrice=price; i_Profit=(int)((d_OpenPrice-d_ClosePrice)/d_Point); } else { if(d_TP_Price>0 && d_TP_Price>=price) { d_ClosePrice=price; i_Profit=(int)((d_OpenPrice-d_ClosePrice)/d_Point); } } break; } }
1.2. Criação de uma classe para trabalhar com os indicadores
Para salvar e analisar os dados dos indicadores, eu usei as classes detalhadas no artigo anterior. Além disso, eu criei aqui a classe CDealsToIndicators, generalizando todas as classes de indicadores. Ele irá armazenar os arrays de classe dos indicadores e organizar o seu funcionamento.
class CDealsToIndicators { private: CADX *ADX[]; CAlligator *Alligator[]; COneBufferArray *OneBuffer[]; CMACD *MACD[]; CStaticOneBuffer *OneBufferStatic[]; CStaticMACD *MACD_Static[]; CStaticADX *ADX_Static[]; CStaticAlligator *Alligator_Static[]; template<typename T> void CleareArray(T *&array[]); public: CDealsToIndicators(); ~CDealsToIndicators(); //--- bool AddADX(string symbol, ENUM_TIMEFRAMES timeframe, int period, string name); bool AddADX(string symbol, ENUM_TIMEFRAMES timeframe, int period, string name, int &handle); bool AddAlligator(string symbol,ENUM_TIMEFRAMES timeframe,uint jaw_period, uint jaw_shift, uint teeth_period, uint teeth_shift, uint lips_period, uint lips_shift, ENUM_MA_METHOD method, ENUM_APPLIED_PRICE price, string name); bool AddAlligator(string symbol,ENUM_TIMEFRAMES timeframe,uint jaw_period, uint jaw_shift, uint teeth_period, uint teeth_shift, uint lips_period, uint lips_shift, ENUM_MA_METHOD method, ENUM_APPLIED_PRICE price, string name, int &handle); bool AddMACD(string symbol, ENUM_TIMEFRAMES timeframe, uint fast_ema, uint slow_ema, uint signal, ENUM_APPLIED_PRICE applied_price, string name); bool AddMACD(string symbol, ENUM_TIMEFRAMES timeframe, uint fast_ema, uint slow_ema, uint signal, ENUM_APPLIED_PRICE applied_price, string name, int &handle); bool AddOneBuffer(int handle, string name); //--- bool SaveNewValues(long ticket); //--- bool Static(CArrayObj *deals); };
1.3. Criação do Expert Advisor para testes
Tudo está preparado. Agora, procedemos para a criação do EA para trabalhar no testador de estratégia. Primeiro, defina a lista de indicadores aplicados e seus parâmetros. Para demonstrar a tecnologia, eu tirei os seguintes indicadores:
- ADX;
- Alligator;
- CCI;
- Chaikin;
- Force Index;
- MACD.
Para cada um deles, três conjuntos de parâmetros são criados, os dados são rastreados em três intervalos de tempo.
A negociações com stoploss e takeprofit estão vinculadas aos valores dos indicadores ATR e são definidas através da relação risco retorno.
//--- parâmetros de entrada input double Reward_Risk = 1.0; input int ATR_Period = 288; input ENUM_TIMEFRAMES TimeFrame1 = PERIOD_M5; input ENUM_TIMEFRAMES TimeFrame2 = PERIOD_H1; input ENUM_TIMEFRAMES TimeFrame3 = PERIOD_D1; input string s1 = "ADX" ; //--- input uint ADX_Period1 = 14 ; input uint ADX_Period2 = 28 ; input uint ADX_Period3 = 56 ; input string s2 = "Alligator" ; //--- input uint JAW_Period1 = 13 ; input uint JAW_Shift1 = 8 ; input uint TEETH_Period1 = 8 ; input uint TEETH_Shift1 = 5 ; input uint LIPS_Period1 = 5 ; input uint LIPS_Shift1 = 3 ; input uint JAW_Period2 = 26 ; input uint JAW_Shift2 = 16 ; input uint TEETH_Period2 = 16 ; input uint TEETH_Shift2 = 10 ; input uint LIPS_Period2 = 10 ; input uint LIPS_Shift2 = 6 ; input uint JAW_Period3 = 42 ; input uint JAW_Shift3 = 32 ; input uint TEETH_Period3 = 32 ; input uint TEETH_Shift3 = 20 ; input uint LIPS_Period3 = 20 ; input uint LIPS_Shift3 = 12 ; input ENUM_MA_METHOD Alligator_Method = MODE_SMMA ; input ENUM_APPLIED_PRICE Alligator_Price = PRICE_MEDIAN ; input string s5 = "CCI" ; //--- input uint CCI_Period1 = 14 ; input uint CCI_Period2 = 28 ; input uint CCI_Period3 = 56 ; input ENUM_APPLIED_PRICE CCI_Price = PRICE_TYPICAL ; input string s6 = "Chaikin" ; //--- input uint Ch_Fast_Period1 = 3 ; input uint Ch_Slow_Period1 = 14 ; input uint Ch_Fast_Period2 = 6 ; input uint Ch_Slow_Period2 = 28 ; input uint Ch_Fast_Period3 = 12 ; input uint Ch_Slow_Period3 = 56 ; input ENUM_MA_METHOD Ch_Method = MODE_EMA ; input ENUM_APPLIED_VOLUME Ch_Volume = VOLUME_TICK ; input string s7 = "Force Index" ; //--- input uint Force_Period1 = 14 ; input uint Force_Period2 = 28 ; input uint Force_Period3 = 56 ; input ENUM_MA_METHOD Force_Method = MODE_SMA ; input ENUM_APPLIED_VOLUME Force_Volume = VOLUME_TICK ; input string s8 = "MACD" ; //--- input uint MACD_Fast1 = 12 ; input uint MACD_Slow1 = 26 ; input uint MACD_Signal1 = 9 ; input uint MACD_Fast2 = 24 ; input uint MACD_Slow2 = 52 ; input uint MACD_Signal2 = 18 ; input uint MACD_Fast3 = 48 ; input uint MACD_Slow3 = 104 ; input uint MACD_Signal3 = 36 ; input ENUM_APPLIED_PRICE MACD_Price = PRICE_CLOSE ;
No bloco de variáveis globais, declare:
- array para armazenar as transações da classe de negociação,
- instância de classe para trabalhar com os indicadores IndicatorsStatic,
- variável para armazenar o manipulador do indicador ATR,
- duas variáveis de serviço para armazenar o horário da última barra processada (last_bar) e a última ordem fechada (last_closed_deal). Nós precisamos que esta última não perca posições já fechadas em cada tick.
Na função OnInit, execute a inicialização de variáveis globais e as classes de indicadores necessários.
int OnInit() { //--- last_bar=0; last_closed_deal=0; //--- Deals = new CArrayObj(); if(CheckPointer(Deals)==POINTER_INVALID) return INIT_FAILED; //--- IndicatorsStatic = new CDealsToIndicators(); if(CheckPointer(IndicatorsStatic)==POINTER_INVALID) return INIT_FAILED; //--- atr=iATR(_Symbol,TimeFrame1,ATR_Period); if(atr==INVALID_HANDLE) return INIT_FAILED; //--- AddIndicators(TimeFrame1); AddIndicators(TimeFrame2); AddIndicators(TimeFrame3); //--- return(INIT_SUCCEEDED); }
Nós usaremos o mesmo conjunto de indicadores em três intervalos diferentes. É por isso que é razoável colocar a inicialização das classes dos indicadores em uma função separada - AddIndicators. Em seus parâmetros, o tempo gráfico necessário será especificado.
bool AddIndicators(ENUM_TIMEFRAMES timeframe) { if(CheckPointer(IndicatorsStatic)==POINTER_INVALID) { IndicatorsStatic = new CDealsToIndicators(); if(CheckPointer(IndicatorsStatic)==POINTER_INVALID) return false; } string tf_name=StringSubstr(EnumToString(timeframe),7); string name="ADX("+IntegerToString(ADX_Period1)+") "+tf_name; if(!IndicatorsStatic.AddADX(_Symbol, timeframe, ADX_Period1, name)) return false; name="ADX("+IntegerToString(ADX_Period2)+") "+tf_name; if(!IndicatorsStatic.AddADX(_Symbol, timeframe, ADX_Period2, name)) return false; name="ADX("+IntegerToString(ADX_Period3)+") "+tf_name; if(!IndicatorsStatic.AddADX(_Symbol, timeframe, ADX_Period3, name)) return false; name="Alligator("+IntegerToString(JAW_Period1)+","+IntegerToString(TEETH_Period1)+","+IntegerToString(LIPS_Period1)+") "+tf_name; if(!IndicatorsStatic.AddAlligator(_Symbol, timeframe, JAW_Period1, JAW_Shift1, TEETH_Period1, TEETH_Shift1, LIPS_Period1, LIPS_Shift1, Alligator_Method, Alligator_Price, name)) return false; name="Alligator("+IntegerToString(JAW_Period2)+","+IntegerToString(TEETH_Period2)+","+IntegerToString(LIPS_Period2)+") "+tf_name; if(!IndicatorsStatic.AddAlligator(_Symbol, timeframe, JAW_Period2, JAW_Shift2, TEETH_Period2, TEETH_Shift2, LIPS_Period2, LIPS_Shift2, Alligator_Method, Alligator_Price, name)) return false; name="Alligator("+IntegerToString(JAW_Period3)+","+IntegerToString(TEETH_Period3)+","+IntegerToString(LIPS_Period3)+") "+tf_name; if(!IndicatorsStatic.AddAlligator(_Symbol, timeframe, JAW_Period3, JAW_Shift3, TEETH_Period3, TEETH_Shift3, LIPS_Period3, LIPS_Shift3, Alligator_Method, Alligator_Price, name)) return false; name="MACD("+IntegerToString(MACD_Fast1)+","+IntegerToString(MACD_Slow1)+","+IntegerToString(MACD_Signal1)+") "+tf_name; if(!IndicatorsStatic.AddMACD(_Symbol, timeframe, MACD_Fast1, MACD_Slow1, MACD_Signal1, MACD_Price, name)) return false; name="MACD("+IntegerToString(MACD_Fast2)+","+IntegerToString(MACD_Slow2)+","+IntegerToString(MACD_Signal2)+") "+tf_name; if(!IndicatorsStatic.AddMACD(_Symbol, timeframe, MACD_Fast2, MACD_Slow2, MACD_Signal2, MACD_Price, name)) return false; name="MACD("+IntegerToString(MACD_Fast3)+","+IntegerToString(MACD_Slow3)+","+IntegerToString(MACD_Signal3)+") "+tf_name; if(!IndicatorsStatic.AddMACD(_Symbol, timeframe, MACD_Fast3, MACD_Slow3, MACD_Signal3, MACD_Price, name)) return false; name="CCI("+IntegerToString(CCI_Period1)+") "+tf_name; int handle = iCCI(_Symbol, timeframe, CCI_Period1, CCI_Price); if(handle<0 || !IndicatorsStatic.AddOneBuffer(handle, name) ) return false; name="CCI("+IntegerToString(CCI_Period2)+") "+tf_name; handle = iCCI(_Symbol, timeframe, CCI_Period2, CCI_Price); if(handle<0 || !IndicatorsStatic.AddOneBuffer(handle, name) ) return false; handle = iCCI(_Symbol, timeframe, CCI_Period3, CCI_Price); name="CCI("+IntegerToString(CCI_Period3)+") "+tf_name; if(handle<0 || !IndicatorsStatic.AddOneBuffer(handle, name) ) return false; handle = iForce(_Symbol, timeframe, Force_Period1, Force_Method, Force_Volume); name="Force("+IntegerToString(Force_Period1)+") "+tf_name; if(handle<0 || !IndicatorsStatic.AddOneBuffer(handle, name) ) return false; handle = iForce(_Symbol, timeframe, Force_Period2, Force_Method, Force_Volume); name="Force("+IntegerToString(Force_Period2)+") "+tf_name; if(handle<0 || !IndicatorsStatic.AddOneBuffer(handle, name) ) return false; handle = iForce(_Symbol, timeframe, Force_Period3, Force_Method, Force_Volume); name="Force("+IntegerToString(Force_Period3)+") "+tf_name; if(handle<0 || !IndicatorsStatic.AddOneBuffer(handle, name) ) return false; name="CHO("+IntegerToString(Ch_Slow_Period1)+","+IntegerToString(Ch_Fast_Period1)+") "+tf_name; handle = iChaikin(_Symbol, timeframe, Ch_Fast_Period1, Ch_Slow_Period1, Ch_Method, Ch_Volume); if(handle<0 || !IndicatorsStatic.AddOneBuffer(handle, name) ) return false; handle = iChaikin(_Symbol, timeframe, Ch_Fast_Period2, Ch_Slow_Period2, Ch_Method, Ch_Volume); name="CHO("+IntegerToString(Ch_Slow_Period2)+","+IntegerToString(Ch_Fast_Period2)+") "+tf_name; if(handle<0 || !IndicatorsStatic.AddOneBuffer(handle, name) ) return false; handle = iChaikin(_Symbol, timeframe, Ch_Fast_Period3, Ch_Slow_Period3, Ch_Method, Ch_Volume); name="CHO("+IntegerToString(Ch_Slow_Period3)+","+IntegerToString(Ch_Fast_Period3)+") "+tf_name; if(handle<0 || !IndicatorsStatic.AddOneBuffer(handle, name) ) return false; return true; }
As operações realizadas na OnTick podem ser divididas em dois blocos: controle de posições abertas e abertura de novas posições.
O primeiro bloco de operações é realizado em cada tick. Nela, todos os negócios abertos anteriormente são, consequentemente, recuperados do array e a função Tick é chamada para cada um deles. Ele verifica o gatilho do stop loss e de take profit da posição e, quando necessário, uma ordem é encerrada no preço atual, poupando o lucro gerado. Para não verificar novamente os negócios encerrado anteriormente, na variável last_closed_deal, o número da negociação que precede o primeiro não encerrado é salvo.
void OnTick() { //--- int total=Deals.Total(); CDeal *deal; bool found=false; for(int i=last_closed_deal;i<total;i++) { deal = Deals.At(i); if(CheckPointer(deal)==POINTER_INVALID) continue; if(!found) { if(deal.IsClosed()) { last_closed_deal=i; continue; } else found=true; } deal.Tick(); }
O segundo bloco de operações começa com a verificação da ocorrência de novas barras. No início de cada barra, obtém-se o valor do indicador ATR da última vela fechada, calcula-se a o stop loss e o take profit de acordo com os parâmetros definidos e as posições virtuais abertas. Para cada posição, é salvo os dados do indicador chamando a função SaveNewValues da nossa classe para trabalhar com os indicadores.
//--- datetime cur_bar=(datetime)SeriesInfoInteger(_Symbol,PERIOD_CURRENT,SERIES_LASTBAR_DATE); datetime cur_time=TimeCurrent(); if(cur_bar==last_bar || (cur_time-cur_bar)>10) return; double atrs[]; if(CopyBuffer(atr,0,1,1,atrs)<=0) return; last_bar=cur_bar; double ask=SymbolInfoDouble(_Symbol,SYMBOL_ASK); double bid=SymbolInfoDouble(_Symbol,SYMBOL_BID); double stops=MathMax(2*atrs[0],SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)*_Point); double sl=NormalizeDouble(stops,_Digits); double tp=NormalizeDouble(Reward_Risk*(stops+ask-bid),_Digits); deal = new CDeal(_Symbol,POSITION_TYPE_BUY,TimeCurrent(),ask,bid-sl,ask+tp); if(CheckPointer(deal)!=POINTER_INVALID) if(Deals.Add(deal)) IndicatorsStatic.SaveNewValues(Deals.Total()-1); deal = new CDeal(_Symbol,POSITION_TYPE_SELL,TimeCurrent(),bid,ask+sl,bid-tp); if(CheckPointer(deal)!=POINTER_INVALID) if(Deals.Add(deal)) IndicatorsStatic.SaveNewValues(Deals.Total()-1); return; }
No OnTester, é coletado os resultados do teste executado e construído os gráficos para análise. Para isso, chama-se a função estática da classe para trabalhar com os indicadores.
Certifique-se de limpar a memória na função OnDeinit!
O código completo do EA e as classes utilizadas são fornecidas em anexo.
2. Análise dos resultados dos testes
Então, nós criamos o EA de testes. Agora, vamos pensar sobre o período analisado. Ao escolher um período, leve em consideração que ele deve ser suficientemente longo para que a objetividade da análise seja alcançada. Outro requisito para um período: deve incluir não apenas movimentos unidirecionais, mas também períodos de movimentos de tendências bidirecionais, bem como de forma lateralizada. Essa abordagem permite criar uma estratégia de negociação capaz de gerar lucro dentro dos períodos de qualquer movimento. Meu exemplo analisa o par de moedas EURUSD para o período de 01/01/2016 a 1/10/2017.
Até agora, nosso processo terá um caráter iterativo que eu recomendo depois de configurar todos os parâmetros necessários para testar o EA para salvar o conjunto de parâmetros do arquivo para trabalhos futuros.
Cada estágio de teste será realizado em 2 execuções com relação lucro/ risco retorno igual a 1/1 e 15/1. Na primeira execução, avaliaremos a probabilidade de um movimento direcionado, enquanto que na segunda a força do movimento.
O EA mostra um grande número de gráficos para análise, portanto, eles não são fornecidos no artigo na íntegra - todos os relatórios são fornecidos em anexo. Aqui, nós mostraremos apenas os gráficos, através dos quais foram tomadas as decisões sobre o uso de indicadores na nova estratégia.
2.1. Primeira etapa
Como nós esperávamos, a primeira etapa de teste não mostrou áreas lucrativas definidas. Mas, ao mesmo tempo, deve-se prestar atenção ao indicador índice de força. No tempo gráfico M5, o gráfico de dependência do lucro das operações em relação aos valores dos indicadores cai para a área zero. A importância desta observação é evidenciada pelo fato de que esse fenômeno aparece nos gráficos dos indicadores analíticos com todos os parâmetros utilizados para testes. Para o nosso modelo, selecione os parâmetros com o caractere do fenômeno mais aparente (rebaixamento máximo).
Vamos ampliar o gráfico analisado. Como você pode ver, o efeito desse fator está disponível no intervalo de -0.01 a 0.01. O fenômeno observado é igualmente verdadeiro tanto para negociações de compra quanto para venda.
Esta observação pode ser explicada pela ausência de volatilidade dentro do intervalo de valores observado. Para a nossa estratégia, certifique-se de marcar a proibição para abrir quaisquer ordens dentro deste intervalo.
Adicione nossa EA a este filtro. Para fazer isso, primeiro adicione a variável global para armazenar o manipulador do indicador.
int force;
Na medida em que o indicador necessário como o filtro já é aplicado no EA, nós não vamos anexá-lo ao gráfico mais uma vez. Basta copiar seu manipulador para a nossa variável global em AddIndicators. Mas certifique-se de lembrar que esta função é chamada três vezes para a inicialização do indicador em diferentes tempos gráficos. Portanto, antes de copiar o manipulador do indicador, nós devemos verificar a conformidade do tempo gráfico.
handle = iForce(_Symbol, timeframe, Force_Period3, Force_Method, Force_Volume); if(timeframe==TimeFrame1) force=handle;
Agora, adicione o filtro imediatamente ao OnTick. Ao construir o gráfico, lembre-se de que, na função de construção do gráfico analítico, os dados foram arredondados. Portanto, deve-se arredondar também os dados ao filtrar os valores do indicador de negociação.
double atrs[]; double force_data[]; if(CopyBuffer(atr,0,1,1,atrs)<=0 || CopyBuffer(force,0,1,1,force_data)<=0) return; // Algum erro no carregamento dos dados do indicador last_bar=cur_bar; double d_Step=_Point*1000; if(MathAbs(NormalizeDouble(force_data[0]/d_Step,0)*d_Step)<=0.01) return; // Filtrado pelo Índice de Força
O código completo do EA é fornecido em anexo a este artigo.
Depois de adicionar o filtro no EA, implemente o segundo estágio de teste. Antes de testar, certifique-se de baixar os parâmetros salvos anteriormente.
2.2. Segunda etapa
Após repetidos testes do EA, eu prestei atenção no indicador MACD. As áreas rentáveis apareceram no gráfico.
No gráfico com a proporção de lucro/risco 15/1, essas áreas são mais expressas; Isso pode evidenciar potencial de sinais dentro dessas faixas.
Este filtro também deve ser adicionado ao nosso código do EA. A lógica da adição do filtro é análoga à fornecida na descrição da primeira fase.
Em variáveis globais:
int macd;
Na função AddIndicators:
name="MACD("+IntegerToString(MACD_Fast2)+","+IntegerToString(MACD_Slow2)+","+IntegerToString(MACD_Signal2)+") "+tf_name; if(timeframe==TimeFrame1) { if(!IndicatorsStatic.AddMACD(_Symbol, timeframe, MACD_Fast2, MACD_Slow2, MACD_Signal2, MACD_Price, name, macd)) return false; } else { if(!IndicatorsStatic.AddMACD(_Symbol, timeframe, MACD_Fast2, MACD_Slow2, MACD_Signal2, MACD_Price, name)) return false; }
Na OnTick:
double macd_data[]; if(CopyBuffer(atr,0,1,1,atrs)<=0 || CopyBuffer(force,0,1,1,force_data)<=0 || CopyBuffer(macd,0,1,1,macd_data)<=0) return;
e
double macd_Step=_Point*50; macd_data[0]=NormalizeDouble(macd_data[0]/macd_Step,0)*macd_Step; if(macd_data[0]>=0.0015 && macd_data[0]<=0.0035) { deal = new CDeal(_Symbol,POSITION_TYPE_BUY,TimeCurrent(),ask,bid-sl,ask+tp); if(CheckPointer(deal)!=POINTER_INVALID) if(Deals.Add(deal)) IndicatorsStatic.SaveNewValues(Deals.Total()-1); } if(macd_data[0]<=(-0.0015) && macd_data[0]>=(-0.0035)) { deal = new CDeal(_Symbol,POSITION_TYPE_SELL,TimeCurrent(),bid,ask+sl,bid-tp); if(CheckPointer(deal)!=POINTER_INVALID) if(Deals.Add(deal)) IndicatorsStatic.SaveNewValues(Deals.Total()-1); }
Depois de adicionar o deslocamento do filtro para a terceira etapa do teste.
2.3. Terceira etapa
Na terceira etapa, eu prestei atenção no oscilador Chaikin. Nas gráficos analíticos do oscilador no tempo gráfico D1, nós vemos o crescimento do lucro em posições longas na redução de valores, enquanto que no crescimento do valor dos indicadores, o lucro cresce em posições vendidas.
Minha observação é confirmada também ao analisar os gráficos com a razão lucro/risco igual a 15/1.
Adicione nossa observação ao código do EA.
Em variáveis globais:
int cho;
Na função AddIndicators:
handle = iChaikin(_Symbol, timeframe, Ch_Fast_Period2, Ch_Slow_Period2, Ch_Method, Ch_Volume); name="CHO("+IntegerToString(Ch_Slow_Period2)+","+IntegerToString(Ch_Fast_Period2)+") "+tf_name; if(handle<0 || !IndicatorsStatic.AddOneBuffer(handle, name) ) return false; if(timeframe==TimeFrame3) cho=handle;
Na OnTick:
double cho_data[]; if(CopyBuffer(atr,0,1,1,atrs)<=0 || CopyBuffer(force,0,1,1,force_data)<=0 || CopyBuffer(macd,0,1,1,macd_data)<=0 || CopyBuffer(cho,0,1,2,cho_data)<2) return;
e
if(macd_data[0]>=0.0015 && macd_data[0]<=0.0035 && (cho_data[1]-cho_data[0])<0) { deal = new CDeal(_Symbol,POSITION_TYPE_BUY,TimeCurrent(),ask,bid-sl,ask+tp); if(CheckPointer(deal)!=POINTER_INVALID) if(Deals.Add(deal)) IndicatorsStatic.SaveNewValues(Deals.Total()-1); } if(macd_data[0]<=(-0.0015) && macd_data[0]>=(-0.0035) && (cho_data[1]-cho_data[0])>0) { deal = new CDeal(_Symbol,POSITION_TYPE_SELL,TimeCurrent(),bid,ask+sl,bid-tp); if(CheckPointer(deal)!=POINTER_INVALID) if(Deals.Add(deal)) IndicatorsStatic.SaveNewValues(Deals.Total()-1); }
Prossiga para a etapa seguinte.
2.4. Quarta etapa
Depois de mais um teste do EA, minha atenção foi novamente destinada para o tempo gráfico D1. Desta vez, eu considerei o indicador CCI. Seus gráficos analíticos demonstraram crescimento de lucro em posições vendidas na redução de valores do indicador e crescimento do lucro nas posições compradas - com o crescimento nos valores do indicador. Essa tendência foi observada em todos os três períodos estudados, mas o lucro máximo foi alcançado quando se usa o período 14, que é padrão para este oscilador.
Os gráficos analíticos recebidos no teste com razão lucro/risco igual a 15/1 confirmam a nossa observação.
Adicione esta observação também ao teste do código do EA.
Em variáveis globais:
int cci;
Para o AddIndicators:
name="CCI("+IntegerToString(CCI_Period1)+") "+tf_name; int handle = iCCI(_Symbol, timeframe, CCI_Period1, CCI_Price); if(handle<0 || !IndicatorsStatic.AddOneBuffer(handle, name) ) return false; if(timeframe==TimeFrame3) cci=handle;
Para o OnTick:
double cci_data[]; if(CopyBuffer(atr,0,1,1,atrs)<=0 || CopyBuffer(force,0,1,1,force_data)<=0 || CopyBuffer(macd,0,1,1,macd_data)<=0 || CopyBuffer(cho,0,1,2,cho_data)<2 || CopyBuffer(cci,0,1,2,cci_data)<2) return;
e
if(macd_data[0]>=0.0015 && macd_data[0]<=0.0035 && (cho_data[1]-cho_data[0])<0 && (cci_data[1]-cci_data[0])>0) { deal = new CDeal(_Symbol,POSITION_TYPE_BUY,TimeCurrent(),ask,bid-sl,ask+tp); if(CheckPointer(deal)!=POINTER_INVALID) if(Deals.Add(deal)) IndicatorsStatic.SaveNewValues(Deals.Total()-1); } if(macd_data[0]<=(-0.0015) && macd_data[0]>=(-0.0035) && (cho_data[1]-cho_data[0])>0 && (cci_data[1]-cci_data[0])<0) { deal = new CDeal(_Symbol,POSITION_TYPE_SELL,TimeCurrent(),bid,ask+sl,bid-tp); if(CheckPointer(deal)!=POINTER_INVALID) if(Deals.Add(deal)) IndicatorsStatic.SaveNewValues(Deals.Total()-1); }
O código completo do EA em todas as etapas é fornecido em anexo ao artigo.
3. Criação e teste do EA nos sinais selecionados
Não há limite para a perfeição, você pode continuar analisando e adicionando os filtros para aumentar o fator de lucro da estratégia. Mas eu acredito que as quatro etapas fornecidas são suficientes para a demonstração da tecnologia. No próximo passo, crie um EA simples para verificar nossa estratégia no testador. Isso nos permitirá avaliar o fator de lucro e o rebaixamento de nossa estratégia, bem como a variação do saldo em andamento.
Na estratégia, nós utilizamos os quatro indicadores para tomar uma decisão sobre a negociação e o indicador ATR para definir o stop loss e take profit. Consequentemente, nos parâmetros de entrada do EA, nós devemos definir todas as informações de entrada necessárias para os indicadores. Nesta fase, nós não criaremos o gerenciamento de dinheiro, todos as ordens usarão uma quantidade fixa do volume.
//--- parâmetros de entrada input double Lot = 0.1 ; input double Reward_Risk = 15.0 ; input ENUM_TIMEFRAMES ATR_TimeFrame = PERIOD_M5 ; input int ATR_Period = 288 ; input string s1 = "CCI" ; //--- input ENUM_TIMEFRAMES CCI_TimeFrame = PERIOD_D1 ; input uint CCI_Period = 14 ; input ENUM_APPLIED_PRICE CCI_Price = PRICE_TYPICAL ; input string s2 = "Chaikin" ; //--- input ENUM_TIMEFRAMES Ch_TimeFrame = PERIOD_D1 ; input uint Ch_Fast_Period = 6 ; input uint Ch_Slow_Period = 28 ; input ENUM_MA_METHOD Ch_Method = MODE_EMA ; input ENUM_APPLIED_VOLUME Ch_Volume = VOLUME_TICK ; input string s3 = "Force Index" ; //--- input ENUM_TIMEFRAMES Force_TimeFrame = PERIOD_M5 ; input uint Force_Period = 56 ; input ENUM_MA_METHOD Force_Method = MODE_SMA ; input ENUM_APPLIED_VOLUME Force_Volume = VOLUME_TICK ; input string s4 = "MACD" ; //--- input ENUM_TIMEFRAMES MACD_TimeFrame = PERIOD_M5 ; input uint MACD_Fast = 12 ; input uint MACD_Slow = 26 ; input uint MACD_Signal = 9 ; input ENUM_APPLIED_PRICE MACD_Price = PRICE_CLOSE ;
Nas variáveis globais, declare:
- instância de classe para realizar as operações de negociação,
- variáveis para lidar com o armazenamento dos indicadores usados,
- variáveis auxiliares para as datas de gravação da última barra processada e da última negociação,
- variáveis para armazenar o tempo gráfico máximo e mínimo.
Na função OnInit, inicialize os indicadores e defina os valores iniciais das variáveis.
int OnInit() { //--- last_bar=0; last_deal=0; //--- atr=iATR(_Symbol,ATR_TimeFrame,ATR_Period); if(atr==INVALID_HANDLE) return INIT_FAILED; //--- force=iForce(_Symbol,Force_TimeFrame,Force_Period,Force_Method,Force_Volume); if(force==INVALID_HANDLE) return INIT_FAILED; //--- macd=iMACD(_Symbol,MACD_TimeFrame,MACD_Fast,MACD_Slow,MACD_Signal,MACD_Price); if(macd==INVALID_HANDLE) return INIT_FAILED; //--- cho=iChaikin(_Symbol,Ch_TimeFrame,Ch_Fast_Period,Ch_Slow_Period,Ch_Method,Ch_Volume); if(cho==INVALID_HANDLE) return INIT_FAILED; //--- cci=iCCI(_Symbol,CCI_TimeFrame,CCI_Period,CCI_Price); if(cci==INVALID_HANDLE) return INIT_FAILED; //--- MaxPeriod=fmax(Force_TimeFrame,MACD_TimeFrame); MaxPeriod=fmax(MaxPeriod,Ch_TimeFrame); MaxPeriod=fmax(MaxPeriod,CCI_TimeFrame); MinPeriod=fmin(Force_TimeFrame,MACD_TimeFrame); MinPeriod=fmin(MinPeriod,Ch_TimeFrame); MinPeriod=fmin(MinPeriod,CCI_TimeFrame); //--- return(INIT_SUCCEEDED); }
Na função OnDeinit, feche os indicadores utilizados.
void OnDeinit(const int reason) { //--- if(atr!=INVALID_HANDLE) IndicatorRelease(atr); //--- if(force==INVALID_HANDLE) IndicatorRelease(force); //--- if(macd==INVALID_HANDLE) IndicatorRelease(macd); //--- if(cho==INVALID_HANDLE) IndicatorRelease(cho); //--- if(cci==INVALID_HANDLE) IndicatorRelease(cci); }
As principais ações serão realizadas no OnTick. No início da função, verifique a ocorrência de uma nova barra. Uma nova posição será aberta somente na abertura de uma nova barra pelo tempo gráfico mínimo (eu limitei 10 segundos da abertura da barra) e, somente, a menos que uma posição for aberta dentro da barra atual pelo tempo gráfico máximo. De tal maneira, eu limitava a abertura de apenas uma ordem para um sinal.
void OnTick() { //--- datetime cur_bar=(datetime)SeriesInfoInteger(_Symbol,MinPeriod,SERIES_LASTBAR_DATE); datetime cur_max=(datetime)SeriesInfoInteger(_Symbol,MaxPeriod,SERIES_LASTBAR_DATE); datetime cur_time=TimeCurrent(); if(cur_bar<=last_bar || (cur_time-cur_bar)>10 || cur_max<=last_deal) return;
Além disso, obtenha os dados dos indicadores utilizados. Em caso de erro de obtenção dos dados de pelo menos um dos indicadores, saia da função.
last_bar=cur_bar; double atrs[]; double force_data[]; double macd_data[]; double cho_data[]; double cci_data[]; if(CopyBuffer(atr,0,1,1,atrs)<=0 || CopyBuffer(force,0,1,1,force_data)<=0 || CopyBuffer(macd,0,1,1,macd_data)<=0 || CopyBuffer(cho,0,1,2,cho_data)<2 || CopyBuffer(cci,0,1,2,cci_data)<2) { return; }
Em seguida, em conformidade com a nosso estratégia, verifique o índice de força. Se não conseguir satisfazer o nosso filtro, saia da função até abrir a próxima barra.
double force_Step=_Point*1000; if(MathAbs(NormalizeDouble(force_data[0]/force_Step,0)*force_Step)<=0.01) return;
Na próxima etapa, verifique o sinal para a abertura de posição comprada. Se houver um sinal positivo, verifique se uma posição aberta já está disponível. Se disponível e é vendida, encerre ela. Se uma posição comprada está sendo perdedora, ignora o sinal e sai da função.
Depois disso, calcule os parâmetros para a nova posição e envie uma ordem.
Execute as mesmas operações para a posição vendida.
double macd_Step=_Point*50; macd_data[0]=NormalizeDouble(macd_data[0]/macd_Step,0)*macd_Step; if(macd_data[0]>=0.0015 && macd_data[0]<=0.0035 && (cho_data[1]-cho_data[0])<0 && (cci_data[1]-cci_data[0])>0) { if(PositionSelect(_Symbol)) { switch((int)PositionGetInteger(POSITION_TYPE)) { case POSITION_TYPE_BUY: if(PositionGetDouble(POSITION_PROFIT)<=0) return; break; case POSITION_TYPE_SELL: Trade.PositionClose(_Symbol); break; } } last_deal=cur_max; double stops=MathMax(2*atrs[0],SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)*_Point); double ask=SymbolInfoDouble(_Symbol,SYMBOL_ASK); double bid=SymbolInfoDouble(_Symbol,SYMBOL_BID); double sl=NormalizeDouble(stops,_Digits); double tp=NormalizeDouble(Reward_Risk*(stops+ask-bid),_Digits); double SL=NormalizeDouble(bid-sl,_Digits); double TP=NormalizeDouble(ask+tp,_Digits); if(!Trade.Buy(Lot,_Symbol,ask,SL,TP,"New Strategy")) Print("Error of open BUY ORDER "+Trade.ResultComment()); } if(macd_data[0]<=(-0.0015) && macd_data[0]>=(-0.0035) && (cho_data[1]-cho_data[0])>0 && (cci_data[1]-cci_data[0])<0) { if(PositionSelect(_Symbol)) { switch((int)PositionGetInteger(POSITION_TYPE)) { case POSITION_TYPE_SELL: if(PositionGetDouble(POSITION_PROFIT)<=0) return; break; case POSITION_TYPE_BUY: Trade.PositionClose(_Symbol); break; } } last_deal=cur_max; double stops=MathMax(2*atrs[0],SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)*_Point); double ask=SymbolInfoDouble(_Symbol,SYMBOL_ASK); double bid=SymbolInfoDouble(_Symbol,SYMBOL_BID); double sl=NormalizeDouble(stops,_Digits); double tp=NormalizeDouble(Reward_Risk*(stops+ask-bid),_Digits); double SL=NormalizeDouble(ask+sl,_Digits); double TP=NormalizeDouble(bid-tp,_Digits); if(!Trade.Sell(Lot,_Symbol,bid,SL,TP,"New Strategy")) Print("Error of open SELL ORDER "+Trade.ResultComment()); } return; }
O código completo do EA é fornecido em anexo.
Depois de preparar o EA, nós podemos testar nossa estratégia. Para evitar a situação "adequação da estratégia com o período", estenda o período testado: teste a estratégia de 1/01/2015 até 01/12/2017. O capital inicial dos testes é de USD 10 000, volume - 1 lote.
De acordo com os resultados dos testes, o EA demonstrou um lucro de 74.8% com um rebaixamento máximo de 12.4% do saldo e patrimônio de 23.8%. No total, foram feitas 44 negociações (22 posições vendidas e 22 compradas). A participação das posições lucrativas representa 18.2% e é igual tanto para posições vendidas quanto para as compradas. Essa baixa percentagem de posições lucrativas é estipulada pelo uso de alta proporção de risco retorno (15:1) e deixa espaço para uma maior melhoria da estratégia.
Conclusão
O artigo demonstrou a tecnologia para criação de uma estratégia de negociação "do zero" usando um método de entrada nos indicadores. A estratégia obtida como resultado é capaz de gerar lucro para um período de tempo prolongado, que se comprova através do teste durante três anos. Não obstante o fato de que, ao criar os indicadores da estratégia do pacote padrão do MetaTrader, os sinais para a realização de negócios estão longe dos descritos na literatura para os indicadores selecionados. A tecnologia sugerida permite uma abordagem criativa para usar os indicadores nas estratégias de negociação e não está limitada pelos indicadores utilizados. Você pode usar qualquer indicador e variantes para avaliar a qualidade de seus sinais.
Referências
Programas usados no artigo:
# |
Nome |
Tipo |
Descrição |
---|---|---|---|
New_Strategy_Gizlyk.zip | |||
1 | NewStrategy1.mq5 | EA | EA para implementar a primeira etapa da criação da estratégia |
2 | NewStrategy2.mq5 | EA | EA para implementar a segunda etapa da criação da estratégia |
3 | NewStrategy3.mq5 | EA | EA para implementar a terceira etapa da criação da estratégia |
4 | NewStrategy4.mq5 | EA |
EA para implementar a quarta etapa da criação da estratégia |
5 | NewStrategy_Final.mq5 | EA |
Teste da estratégia do EA |
6 | DealsToIndicators.mqh | Biblioteca da classe | Classe para trabalhar com as classes dos indicadores |
7 | Deal.mqh | Biblioteca da classe | Classe para salvar as informações de uma negociação |
8 | Value.mqh | Biblioteca da classe | Classe para salvar os dados no buffer de estado do indicador |
9 | OneBufferArray.mqh | Biblioteca da classe | Classe para salvar os dados históricos de indicadores de um buffer |
10 | StaticOneBuffer.mqh | Biblioteca da classe | Classe para coletar e análisar as estatísticas de indicadores de um buffer |
11 | ADXValue.mqh | Biblioteca da classe | Classe para salvar os dados do estado do indicador ADX |
12 | ADX.mqh | Biblioteca da classe | Classe para salvar os dados históricos do estado do indicador ADX |
13 | StaticADX.mqh | Biblioteca da classe | Classe para coleta e análise de estatísticas do indicador ADX |
14 | AlligatorValue.mqh | Biblioteca da classe | Classe para salvar os dados do estado do indicador Alligator |
15 | Alligator.mqh | Biblioteca da classe | Classe para salvar os dados históricos do estado do indicador Alligator |
16 | StaticAlligator.mqh | Biblioteca da classe | Classe para coleta e análise de estatísticas do indicador Alligator |
17 | MACDValue.mqh | Biblioteca da classe | Classe para salvar os dados do estado do indicador MACD |
18 | MACD.mqh | Biblioteca da classe | Classe para salvar os dados históricos do estado do indicador MACD |
19 | StaticMACD.mqh | Biblioteca da classe | Classe para coleta e análise de estatísticas do indicador MACD |
Common.zip | |||
20 | NewStrategy1_Report_1to1_2016-17.html | Arquivo da Internet | Gráficos analíticos da primeira etapa da criação da estratégia, lucro/risco = 1/1 |
21 | NewStrategy1_Report_15to1_2016-17.html | Arquivo da Internet | Gráficos analíticos da primeira etapa da criação da estratégia, lucro/risco = 15/1 |
22 | NewStrategy2_Report_1to1_2016-17.html | Arquivo da Internet | Gráficos analíticos da segunda etapa da criação da estratégia, lucro/risco = 1/1 |
23 | NewStrategy2_Report_15to1_2016-17.html | Arquivo da Internet | Gráficos analíticos da segunda etapa da criação da estratégia, lucro/risco = 15/1 |
24 | NewStrategy3_Report_1to1_2016-17.html | Arquivo da Internet | Gráficos analíticos da terceira etapa da criação da estratégia, lucro/risco = 1/1 |
25 | NewStrategy3_Report_15to1_2016-17.html | Arquivo da Internet | Gráficos analíticos da terceira etapa da criação da estratégia, lucro/risco = 15/1 |
26 | NewStrategy4_Report_1to1_2016-17.html | Arquivo da Internet | Gráficos analíticos da quarta etapa da criação da estratégia, lucro/risco = 1/1 |
27 | NewStrategy4_Report_15to1_2016-17.html | Arquivo da Internet | Gráficos analíticos da quarta etapa da criação da estratégia, lucro/risco = 15/1 |
28 | NewStrategy_Final_Report.html | Arquivo da Internet | Relatório do teste da estratégia |
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/4192
![Como reduzir os riscos trader](https://c.mql5.com/2/30/risk.png)
![Seleção automática de sinais promissores](https://c.mql5.com/2/30/xf1zfo07t1b6ty_wozfke_cxp3ajzhsku9i_e6dfkszd.png)
![Testador de estratégia personalizada com base em cálculos matemáticos rápido](https://c.mql5.com/2/30/Custom_math_tester.png)
![Avaliação do risco numa sequência de trades com um ativo. Continuação](https://c.mql5.com/2/30/Risk_estimation.png)
![MQL5 - Linguagem para estratégias de negociação inseridas no terminal do cliente MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- 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