Proteção contra falsos positivos do robô de negociação

Aleksandr Masterskikh | 19 setembro, 2016

Introdução

Neste artigo vamos nos concentrar na estabilidade do robô de negociação através da eliminação de possíveis execuções repetidas (pulo) de diferentes maneiras: tanto separadamente como conjuntamente nos algoritmos de entrada e saída.


Natureza do problema

Os falsos positivos são especialmente evidentes durante as oscilações e colapsos repentinos do mercado, quando a amplitude da vela é tão grande que o algoritmo do robô não previstas medidas especiais contra o "pulo". Isso faz com que na vela atual aconteçam aberturas e fechamentos consecutivos de posições.

As conseqüências financeiras podem ser varias e também depender de certos algoritmos que levam em conta parâmetros de negociação estabelecidos pelo desenvolvedor do robô. Mas, em todo caso, as perdas do trader, durante os pulos, são diretamente proporcionais ao número de execuções involuntárias.

Neste artigo, não vou considerar questões sobre a análise de instrumentos financeiros (de natureza técnica e fundamental) que podem afetar a estabilidade do EA e ajudá-lo a evitar a pulos. Aqui falamos sobre medidas sobre programação informática que não dependem dos métodos de análise de mercados financeiros.

Então, passamos a lidar com o problema. Como modelo, eu usarei o EA "MACD Sample" do conjunto padrão disponível no terminal de cliente Metatrader 4.

A seguir é apresentado um problema de pulo ou alteração brusca de preço para o par EURUSD no dia 2 de outubro deste ano (timeframe M15, EA "MACD Sample" com configurações padrão):

Na imagem você pode ver que existem 8 execuções consecutivas (entradas Buy) em uma vela. Aqui, em termos de lógica de mercado normal, apenas a primeira entrada é correta, as 7 restantes são pulos.

Razões pelas quais acontecem esses falsos positivos:

Concordamos que deliberadamente não vamos tomar em conta questões sobre filtragens de flutuações do mercado (porque cada trader tem seu algoritmo de entrada e saída), por isso, para resolver o problema, de modo universal, consideraremos os seguintes fatores, eles são de natureza mais geral:


Decisão no algoritmo de entrada

A opção mais simples, mas, ao mesmo tempo, mais confiável para fixar o ponto de entrada é o fator de tempo, a seguir, as causas:

Assim, o tempo de execução do algoritmo de entrada é o fator principal, ou melhor, o tempo de execução da ordem para abrir a posição (OrderSend), já que estas duas coisas podem não corresponder, se, no algoritmo, houver algum atraso na apertura da ordem.

Então, você pode memorizar o tempo (tempo atual) de abertura da posição. Mas como usar este parâmetro no algoritmo para evitar entradas subseqüentes nessa vela? Pois, não conhecemos com antecedência esse tempo (nem seu valor absoluto), portanto, não podemos "introduzi-lo" antecipadamente no algoritmo de entrada. O algoritmo deve levar em conta (incluir) uma condição geral, que permita a primeira entrada na vela, mas impedirá todas as entradas subseqüentes na vela, sem contar as execuções (a versão com contador foi rejeitada anteriormente).

A solução é bastante simples. Em primeiro lugar, vou escrever o código com alguns comentários, a seguir, vou explicar mais detalhadamente. Você precisa inserir o código adicional abaixo (marcado com fundo amarelo) no algoritmo do EA (ver o arquivo MACD_Sample_plus1.mq4):

//+------------------------------------------------------------------+
//|                                                  MACD Sample.mq4 |
//|                   Copyright 2005-2014, MetaQuotes Software Corp. |
//|                                              https://www.mql4.com |
//+------------------------------------------------------------------+
#property copyright   "2005-2014, MetaQuotes Software Corp."
#property link        "https://www.mql4.com"

input double TakeProfit    =50;
input double Lots          =0.1;
input double TrailingStop  =30;
input double MACDOpenLevel =3;
input double MACDCloseLevel=2;
input int    MATrendPeriod =26;
//--- inserimos uma nova variável (dimensão em segundos para uma barra desse timeframe, para M15 igual a 60 c x 15 = 900 c)
datetime Time_open=900;
//--- inserimos a nova variável (tempo de abertura da barra onde será a primeira entrada)
datetime Time_bar = 0;

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnTick(void)
  {
   double MacdCurrent,MacdPrevious;
   double SignalCurrent,SignalPrevious;
   double MaCurrent,MaPrevious;
   int    cnt,ticket,total;
//---
// initial data checks
// it is important to make sure that the expert works with a normal
// chart and the user did not make any mistakes setting external 
// variables (Lots, StopLoss, TakeProfit, 
// TrailingStop) in our case, we check TakeProfit
// on a chart of less than 100 bars
//---
   if(Bars<100)
     {
      Print("bars less than 100");
      return;
     }
   if(TakeProfit<10)
     {
      Print("TakeProfit less than 10");
      return;
     }
//--- to simplify the coding and speed up access data are put into internal variables
   MacdCurrent=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,0);
   MacdPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,1);
   SignalCurrent=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_SIGNAL,0);
   SignalPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_SIGNAL,1);
   MaCurrent=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,0);
   MaPrevious=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,1);

   total=OrdersTotal();
   if(total<1)
     {
      //--- no opened orders identified
      if(AccountFreeMargin()<(1000*Lots))
        {
         Print("We have no money. Free Margin = ",AccountFreeMargin());
         return;
        }
      //--- check for long position (BUY) possibility
      
      //--- inserimos a nova cadeia de caracteres (remove a proibição de re-entrada, se você abriu um novo bar)
      if( (TimeCurrent() - Time_bar) > 900 ) Time_open = 900; 
      
      if(MacdCurrent<0 && MacdCurrent>SignalCurrent && MacdPrevious<SignalPrevious && 
         MathAbs(MacdCurrent)>(MACDOpenLevel*Point) && MaCurrent>MaPrevious && 
         (TimeCurrent()-Time[0])<Time_open) //inserimos uma nova cadeia de caracteres no algoritmo de entrada (é executado apenas 1 vez, pois outra condição nessa vela não é viável)
        {
         ticket=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,0,Ask+TakeProfit*Point,"macd sample",16384,0,Green);
         if(ticket>0)
           {
            if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES))
             {
              Print("BUY order opened : ",OrderOpenPrice());
              Time_open = TimeCurrent()-Time[0]; //inserimos uma nova cadeia de caracteres (é preciso memorizamos o intervalo a partir do tempo de abertura da barra, onde foi feita a entrada, até o momento da entrada)
              Time_bar = Time[0]; //inserimos uma nova cadeia de caracteres (memorizamos o tempo de abertura dessa barra onde foi feita a primeira entrada)
             }
           }
         else
            Print("Error opening BUY order : ",GetLastError());
         return;
        }

Detalhes:

Em vez do tempo absoluto (de entrada), usamos o tempo relativo, ele é o intervalo de tempo desde o momento da abertura da vela atual até o momento da entrada. A seguir, comparamos este valor com valor de tempo -suficientemente grande - especificado com antecedência (comprimento da vela inteira), isso irá permitir que você execute a primeira entrada. No momento de abertura da posição, mudamos (reduzimos) o valor da variável Time_open, escrevendo nela o valor do intervalo de tempo desde o início da vela até o momento de abertura real. Como, em qualquer momento posterior, o valor (TimeCurrent() - Time[0]) < Time_open será superior à dimensão registrada por nós no momento da entrada, e a condição (TimeCurrent() - Time[0]) < Time_open se tornará inaplicável, é assim como é conseguido o bloqueio das seguintes entradas na vela atual.

Desse modo nós resolvemos o problema de falsos positivos, sem usar um contador de número de entradas e análise de amplitude de movimentos de preços.

A seguir, o resultado desse simples refinamento do algoritmo fonte de entrada do EA ("MACD Sample_plus1"):

Podemos ver que, na vela, existe apenas uma entrada, sem falsos positivos, ademais, o pulo foi eliminado completamente. Além disso, as configurações padrão foram salvas, para deixar claro que o problema foi resolvido sem alterar as configurações do EA.

Agora que o problema, na entrada, está resolvido, refinamos o algoritmo de saída, para excluir um possível pulo ao fechar rapidamente a posição, o que, neste caso em particular, aumenta o lucro (bom impulso com uma saída rápida, prematura).


Solução no algoritmo de saída

Não vou abordar questões da análise da dinâmica dos instrumentos financeiros em relação ao aumento de lucros, porque o problema prioritário é a eliminação do possível pulo do robô, também me vou limitar tendo em conta a fixação do parâmero selecionado sem consideração desta dinâmica.

Antes usávamos o factor tempo, e vamos usá-lo novamente. Definimos estritamente o momento de fechamento da posição no momento da abertura da próxima (após a entrada) vela. Então vamos refletir este momento no algoritmo de saída assim:

if(!OrderSelect(cnt,SELECT_BY_POS,MODE_TRADES))
         continue;
      if(OrderType()<=OP_SELL &&   // check for opened position 
         OrderSymbol()==Symbol())  // check for symbol
        {
         //--- long position is opened
         if(OrderType()==OP_BUY)
           {
            //--- should it be closed?
            if(/* MacdCurrent>0 && MacdCurrent<SignalCurrent && MacdPrevious>SignalPrevious && // removemos o código de gatilho para MACD, para não perturbar os novos critérios (ver seguinte)
               MacdCurrent>(MACDCloseLevel*Point) &&
             */
               Bid > OrderOpenPrice() &&  // inserimos uma nova cadeia de caracteres (o preço na zona positiva depende do nível de entrada)
               TimeCurrent() == Time[0] ) // inserimos uma nova cadeia de caracteres (simples edição do algoritmo de saída: a saída é estritamente no momento da abertura da vela atual)
              {
               //--- close order and exit
               if(!OrderClose(OrderTicket(),OrderLots(),Bid,3,Violet))
                  Print("OrderClose error ",GetLastError());

               return;
              }

Essa pequena modificação permite "trabalhar" para o algoritmo de entrada (a posição é aberta, sem condições para fechar), a posição dura até TimeCurrent() == Time[0] tempo [0] e é fechada simultaneamente no início de uma nova após o impulso de velas. Como resultado, além da proteção contra o pulo, tiramos lucro (ver imagem, "MACD Sample_plus2"):

Além disso, foi necessário remover, do algoritmo de saída, o gatilho no MACD, caso contrário a condição de saída não poderia ser realizada.

Assim, vemos que podemos resolver o problema de pulo separadamente no algoritmo de entrada, separadamente no algoritmo de saída. Agora vamos falar sobre a resolução de problemas devidos à coordenação entre estes algoritmos para abrir e fechar posições.


Coordenação dos algoritmos de entrada e saída

A coordenação consiste em uma simulação preliminar de todo o processo: abertura de posição — seguimento — fechamento de posição. Isto é refletido, nomeadamente, na escolha do deslocamento nos indicadores e funções usadas nos algoritmos de entrada e saída.

Por exemplo, se você usar o algoritmo TimeCurrent() = Time[0], isto é, se o ponto de saída for definido estritamente pelo início da vela atual, o algoritmo de entrada deverá funcionar nas barras anteriores fechadas, para que a condição de saída possa ocorrer. Ou seja, para fechar a posição na condição TimeCurrent() = Time[0] sem condições adicionais, é necessário que todo o algoritmo de comparação (na entrada) seja realizado (concluído) na barra anterior. Para isso, nas configurações dos indicadores envolvidos na comparação de valores, deve existir uma deslocação igual a 1. Neste caso, a comparação de valores seria correta, e o início da vela atual será a conclusão lógica do algoritmo de saída.

Assim, a coordenação dos algoritmos de entrada e saída também está relacionada com o fator tempo.


Conclusão

O problema das falsos execuções dos EA é eficientemente resolvido usando o fator tempo no algoritmo de saída. Além disso, é possível fornecer - à durabilidade do trabalho do EA - a fixação do ponto de entrada (por exemplo por tempo), bem como a coordenação dos algoritmos de entrada e saída usando uma simulação prévia não apenas da logica base de gatilho, mas também do cálculo de deslocamento (barra one será calculado o indicador ou função).

A seguir encontram-se os códigos dos EA: na sua forma original (MACD_Sample.mq4), com modificação da entrada (MACD_Sample_plus1.mq4), com modificação da saída (MACD_Sample_plus2.mq4). Além disso, estão modificados apenas os canais Buy, enquanto os canais Sell foram deixados intencionalmente inalterados, para que fosse possível comparar os algoritmos fonte com os modificados.

E, claro, todos estes EA são demonstrações e não estão destinados à negociação em mercados financeiros.