English Русский 中文 Español Deutsch 日本語
Filtragem de acordo com o histórico

Filtragem de acordo com o histórico

MetaTrader 4Sistemas de negociação | 18 fevereiro 2016, 15:24
591 0
Andrey Khatimlianskii
Andrey Khatimlianskii

Introdução


Há vários filtros diferentes: valores de indicadores, volatilidade do mercado, tempo, dia da semana. Todos eles podem ser usados para fazer a seleção de negociações desvantajosas. É bastante fácil adicionar um filtro do tipo a um expert advisor: basta adicionar mais uma condição antes do bloco de abertura.

Mas o que deve ser feito caso você queira usar o histórico do EA como um filtro? Se você desligar o seu sistema de negociação após várias negociações mal sucedidas, você não terá um histórico mais tarde, e portanto não haverá nada a ser analisado. Para resolver este problema, nós precisamos ensinar o expert advisor a realizar negociações virtualmente, ou seja, simular a abertura, modificação e fechamento de negociações sem realizar negociações reais.

É disso que trata este artigo.

Estratégia experimental


Para a implementação do nosso sistema nós faremos algumas modificações no arquivo CrossMACD_DeLuxe.mq4 do expert advisor:
  • durante a abertura/modificação/fechamento de cada posição, mudanças serão escritas em uma matriz de posições virtuais;
  • adição de monitoramento de atuação do StopLoss e TakeProfit de posições virtuais;
  • adição de um critério de filtragem: uma condição na qual negociações reais não serão abertas.
Eu tentarei descrever cada passo da modificação do EA com maior quantidade de detalhes possível. Caso você não esteja interessado nisso, você pode fazer o download do expert advisor pronto e pular para a parte "O jogo não vale a candle?".

Contabilização de posições virtuais


Então, um sinal para abrir uma posição surgiu. Os parâmetros StopLoss e TakeProfit são calculados, e tudo está pronto para a chamada da função OrderSend(). Precisamente neste momento nós abrimos a negociação virtual - basta salvar todos os parâmetros necessários nas variáveis apropriadas:

void OpenBuy()
  {
    int _GetLastError = 0;
    double _OpenPriceLevel, _StopLossLevel, _TakeProfitLevel;
    _OpenPriceLevel = NormalizeDouble(Ask, Digits);
 
    if(StopLoss > 0)
        _StopLossLevel = NormalizeDouble(_OpenPriceLevel - 
                                     StopLoss*Point, Digits); 
    else
        _StopLossLevel = 0.0; 
    if(TakeProfit > 0)
        _TakeProfitLevel = NormalizeDouble(_OpenPriceLevel + 
                                   TakeProfit*Point, Digits); 
    else
        _TakeProfitLevel = 0.0; 
 
    //---- open the virtual position
    virtOrderSend(OP_BUY, _OpenPriceLevel, _StopLossLevel, 
                  _TakeProfitLevel);
 
    if(OrderSend(Symbol(), OP_BUY, 0.1, _OpenPriceLevel, 3, 
       _StopLossLevel, _TakeProfitLevel, "CrossMACD", 
       _MagicNumber, 0, Green) < 0)
      {
        _GetLastError = GetLastError();
        Alert("Error OrderSend № ", _GetLastError);
        return(-1);
      }
  }
 
//---- Save parameters of the opened position in main variables
void virtualOrderSend(int type, double openprice, double stoploss,
                      double takeprofit)
  {
    virtTicket = 1;
    virtType = type;
    virtOpenPrice = openprice;
    virtStopLoss = stoploss;
    virtTakeProfit = takeprofit;
  }

Nós podemos usar apenas cinco variáveis:

int       virtTicket     = 0;   
// determines, if there is an open virtual position
int       virtType       = 0;   // position type
double    virtOpenPrice  = 0.0; // position opening price
double    virtStopLoss   = 0.0; // position StopLoss
double    virtTakeProfit = 0.0; // position TakeProfit

Nós não precisamos de outras características para obter a salvação da nossa tarefa. Se você quiser ampliar a funcionalidade deste exemplo, basta adicionar a quantidade necessária de variáveis.

Para rastrear o fechamento e a modificação de posições, nós precisamos tomar mais medidas. Copie o bloco de controle de posições abertas, que está no expert advisor, e transforme as características do pedido em características virtuais:

int start()
  {
    // skipped...
 
    //+------------------------------------------------------------------+
    //| Control block of "virtual" positions                             |
    //+------------------------------------------------------------------+
    if(virtTicket > 0)
      {
        //---- if BUY-position is open,
        if(virtType == OP_BUY)
          {
            //---- if MACD crossed 0-line downwards,
            if(NormalizeDouble(MACD_1 + CloseLuft*Point*0.1, 
               Digits + 1) <= 0.0)
              {
                //---- close position
                virtOrderClose(Bid);
              }
            //---- if the signal did not change, accompany the position by 
            //     TrailingStop
            else
              {
                if(TrailingStop > 0)
                  {
                    if(NormalizeDouble(Bid - virtOpenPrice, 
                       Digits ) > 0.0)
                      {
                        if(NormalizeDouble( Bid - TrailingStop*Point - 
                           virtStopLoss, Digits) > 0.0 || virtStopLoss < Point)
                        {
                          virtStopLoss = Bid - TrailingStop*Point;
                        }
                      }
                  }
              }
          }
        //---- if SELL position is open
        if(virtType == OP_SELL)
          {
            //---- if MACD crossed 0-line upwards,
            if(NormalizeDouble(MACD_1 - CloseLuft*Point*0.1, 
               Digits + 1 ) >= 0.0)
              {
                //---- close the position
                virtOrderClose(Ask);
              }
            //---- if the signal did not change, accompany the position by 
            //     TrailingStop
            else
              {
                if ( TrailingStop > 0 )
                  {
                    if(NormalizeDouble( virtOpenPrice - Ask, 
                       Digits ) > 0.0 )
                      {
                        if(NormalizeDouble( virtStopLoss - ( Ask + 
                           TrailingStop*Point ), Digits ) > 0.0 ||
                           virtStopLoss <= Point )
                          {
                            virtStopLoss = Ask + TrailingStop*Point;
                          }
                      }
                  }
              }
          }
      }
    // skipped...
    return(0);
  } 
 
 
//---- virtual position closing function
void virtOrderClose(double closeprice)
  {
    //---- Save the parameters of the closed position in the array
    ArrayResize(virtClosedOrders, virtClosedOrdersCount + 1);
 
    virtClosedOrders[virtClosedOrdersCount][0] = virtType;
    virtClosedOrders[virtClosedOrdersCount][1] = virtOpenPrice;
    virtClosedOrders[virtClosedOrdersCount][2] = virtStopLoss;
    virtClosedOrders[virtClosedOrdersCount][3] = virtTakeProfit;
    virtClosedOrders[virtClosedOrdersCount][4] = closeprice;
 
    virtClosedOrdersCount ++;
 
    //---- clear variables
    virtTicket = 0;
    virtType = 0;
    virtOpenPrice = 0.0;
    virtStopLoss = 0.0;
    virtTakeProfit = 0.0;
  }

A modificação se transformou na simples atribuição de um novo valor à variável virtStopLoss. E o fechamento é bastante difícil: todas as características do pedido fechado são salvas em uma matriz. Mais tarde, todo o histórico virtual será salvo nela. A partir dela, nós iremos obter as informações sobre as posições fechadas para tomar uma decisão a respeito da abertura de uma nova posição.

Agora nós precisamos processar o fechamento da posição no StopLoss e no TakeProfit. Para isso, adicione várias strings ao bloco de controle criado.

if(virtType == OP_BUY)
  {
    //---- check, whether SL was not activated 
    if(virtStopLoss > 0.0 && NormalizeDouble(virtStopLoss - Bid, 
       Digits ) >= 0.0)
      {
        virtOrderClose(virtStopLoss);
      }
    //---- check, whether TPL was not activated
    if(virtTakeProfit > 0.0 && NormalizeDouble( Bid - virtTakeProfit, 
       Digits ) >= 0.0)
      {
        virtOrderClose(virtTakeProfit);
      }
  }

Agora o nosso histórico virtual está pronto e nós podemos adicionar um critério de filtragem.

O que é "bom" e o que é "ruim"?


Nós precisamos proibir a abertura de posições após uma determinada condição ser implementada. Mas que condição devemos escolher? Várias negociações desvantajosas em sequência, a ativação do StopLoss ou a redução do lucro médio de várias negociações recentes. Certamente é difícil responder a essa pergunta. Cada opção possui as suas vantagens e desvantagens.

Para verificar a eficiência de cada condição, vamos tentar escrever o código de todas as três e testá-las no histórico.

extern int TradeFiltrVariant = 0;
 
//---- Function of checking the necessity of the real trading
bool virtCheckCondition()
  {
    int pos, check_pos = 2;
    double last_profit = 0.0, pre_last_profit = 0.0;
    
    //---- depending on the value of TradeFiltrVariant:
    switch(TradeFiltrVariant)
      {
        //---- 1: prohibit real trading, if 2 last deals are losing
        case 1:
          {
            //---- if the virtual history contains enough orders,
            if(virtClosedOrdersCount >= check_pos)
              {
                for(pos = 1; pos check_pos; pos ++)
                  {
                    //---- if the deal is profitable, return true
                    if((virtClosedOrders[virtClosedOrdersCount-pos][0] == 0 && 
                        virtClosedOrders[virtClosedOrdersCount-pos][4] - 
                        virtClosedOrders[virtClosedOrdersCount-pos][1] >= 0.0) ||
                        (virtClosedOrders[virtClosedOrdersCount-pos][0] == 1 && 
                        virtClosedOrders[virtClosedOrdersCount-pos][1] - 
                        virtClosedOrders[virtClosedOrdersCount-pos][4] >= 0.0))
                      {
                        return(true);
                      }
                    }
              }
            return(false);
          }
        //---- 2: prohibit real trading if the last position was closed 
        //        by StopLoss
        case 2:
          {
            //---- if the virtual history contains enough orders,
            if(virtClosedOrdersCount > 0)
              {
                //---- if the closing price of the last order is equal to StopLoss,
                if(virtClosedOrders[virtClosedOrdersCount-1][2] - 
                   virtClosedOrders[virtClosedOrdersCount-1][4] < Point &&
                   virtClosedOrders[virtClosedOrdersCount-1][4] - 
                   virtClosedOrders[virtClosedOrdersCount-1][2] < Point)
                  {
                    return(false);
                  }
              }
            return(true);
          }
        //---- 3: prohibit real trading, if the profit of the last position  
        //----    is lower than that of the last but one position (or loss is higher)
        case 3:
          {
            if(virtClosedOrdersCount >= 2)
              {
                if(virtClosedOrders[virtClosedOrdersCount-1][0] == 0)
                  {
                    last_profit =  virtClosedOrders[virtClosedOrdersCount-1][4] - 
                                   virtClosedOrders[virtClosedOrdersCount-1][1];
                  }
                else
                  {
                    last_profit =  virtClosedOrders[virtClosedOrdersCount-1][1] - 
                                   virtClosedOrders[virtClosedOrdersCount-1][4];
                  }
                if(virtClosedOrders[virtClosedOrdersCount-2][0] == 0)
                  {
                    pre_last_profit = virtClosedOrders[virtClosedOrdersCount-2][4] - 
                                      virtClosedOrders[virtClosedOrdersCount-2][1];
                  }
                else
                  {
                    pre_last_profit = virtClosedOrders[virtClosedOrdersCount-2][1] - 
                                      virtClosedOrders[virtClosedOrdersCount-2][4];
                  }
 
                if(pre_last_profit - last_profit > 0.0)
                  {
                    return(false);
                  }
              }
            return(true);
          }
        //---- by default the filter is off, i.e. positions will be always opened in reality
        default: return(true);
      }
    return(true);
  }
 
void OpenBuy()
  {
    int _GetLastError = 0;
    double _OpenPriceLevel, _StopLossLevel, _TakeProfitLevel;
    _OpenPriceLevel = NormalizeDouble(Ask, Digits);
 
    if(StopLoss > 0)
      { 
        _StopLossLevel = NormalizeDouble(_OpenPriceLevel - StopLoss*Point, Digits); 
      }
    else
      { 
        _StopLossLevel = 0.0; 
      }
 
    if(TakeProfit > 0)
      { 
        _TakeProfitLevel = NormalizeDouble(_OpenPriceLevel + TakeProfit*Point, Digits); 
      }
    else
      { 
        _TakeProfitLevel = 0.0; 
      }
 
    //---- open a virtual position
    virtOrderSend(OP_BUY, _OpenPriceLevel, _StopLossLevel, _TakeProfitLevel);
 
    //---- if virtual positions filter prohibits trading, exit
    if(virtCheckCondition() == false) 
      { 
        return(0); 
      }
 
    if(OrderSend( Symbol(), OP_BUY, 0.1, _OpenPriceLevel, 3, _StopLossLevel, 
          _TakeProfitLevel, "CrossMACD", _MagicNumber, 0, Green) < 0 )
      {
        _GetLastError = GetLastError();
        Alert("Error OrderSend № ", _GetLastError);
        return(-1);
      }
  }

Agora nós temos uma variável externa, TradeFiltrVariant. Ela é responsável pela escolha do critério de filtragem:

extern int TradeFiltrVariant = 0;
//---- 0: filter is off, i.e. position is always opened in reality
//---- 1: prohibit real trading, if 2 last positions are losing
//---- 2: prohibit real trading, if the last position closed by StopLoss
//---- 3: prohibit real trading, if the profit of the last position is lower, 
//----    than that of the last but one psition (or loss is higher)

Agora teste o expert advisors com filtros diferentes e compare os resultados.

O jogo não vale a candle?


Eu escolhi os seguintes parâmetros para o teste:
Símbolo: Período GBPUSD
: H4, 01.01.2005 - 01.01.2006
Modo de modelagem: todos os ticks (qualidade de modelagem 90%, cotas do HistoryCenter)

Parâmetros do EA:
StopLoss: 50
TakeProfit: 0 (desativado)
TrailingStop: 0 (desativado)
FastEMAPeriod: 12
SlowEMAPeriod: 26
OpenLuft: 10
CloseLuft: 0

A seguinte tabela mostra a dependência dos resultados em relação ao filtro usado:

TradeFiltrVariantLucro/perda total
Total de negociações
Negociações lucrativas
Negociações desvantajosas
0 1678,75
41 9 (22%)
32 (78%)
1 105,65 20
2 (10%)
18 (90%)
2 -550,20 11 0 (0%)
11 (100%)
3 1225,13 28 7 (25%)
21 (75%)









Como você pode ver, a teoria a respeito da utilidade do filtro não foi comprovada. Além disso, os resultados das negociações com o filtro foram mais baixos do que aqueles das negociações sem o filtro. A única exceção foi a terceira opção - a taxa de negociações lucrativas foi mais alta (25 % contra 22%), mas o lucro total foi menor com todas as opções.

O que deu errado? Provavelmente, o critério de filtragem estava errado. Vamos tentar transformar esses três filtros nos filtros opostos, ou seja, fechar negociações reais se:
  • as duas últimas negociações tiverem sido lucrativas;
  • a última posição for lucrativa (nós não temos um análogo ao StopLoss, pois o TakeProfit está desativado);
  • O lucro da última posição for maior do que o da penúltima.
Para não restringir o expert advisor, simplesmente adicione mais três valores de TradeFiltrVariant - 4, 5 e 6:

//---- 4: prohibit real trading, if two last trades are profitable
//---- 5: prohibit real trading, if the last position is profitable
//---- 6: prohibit real trading, if the profit of the last position is higher, 
//----    than that of the last but one position (or loss is lower)
 
    //---- 4: prohibit real trading, if two last trades are profitable
    case 4:
      {
        if(virtClosedOrdersCount >= check_pos)
          {
            for(pos = 1; pos check_pos; pos ++)
              {
                //---- if the trade is losing, return true
                if((virtClosedOrders[virtClosedOrdersCount-pos][0] == 0 && 
                   virtClosedOrders[virtClosedOrdersCount-pos][1] - 
                   virtClosedOrders[virtClosedOrdersCount-pos][4] > 0.0) ||
                   (virtClosedOrders[virtClosedOrdersCount-pos][0] == 1 && 
                   virtClosedOrders[virtClosedOrdersCount-pos][4] - 
                   virtClosedOrders[virtClosedOrdersCount-pos][1] > 0.0))
                  {
                    return(true);
                  }
              }
          }
        return(false);
      }
    //---- 5: prohibit real trading, if the last position is profitable
    case 5:
      {
        if(virtClosedOrdersCount >= 1)
          {
            if(virtClosedOrders[virtClosedOrdersCount-1][0] == 0)
              {
                last_profit =  virtClosedOrders[virtClosedOrdersCount-1][4] - 
                               virtClosedOrders[virtClosedOrdersCount-1][1];
              }
            else
              {
                last_profit =  virtClosedOrders[virtClosedOrdersCount-1][1] - 
                               virtClosedOrders[virtClosedOrdersCount-1][4];
              }
 
            if(last_profit > 0.0)
              {
                return(false);
              }
          }
        return(true);
      }
    //---- 6: prohibit real trading, if the profit of the last position is higher, 
    //----    than that of the last but one position (or loss is lower)
    case 6:
      {
        if(virtClosedOrdersCount >= 2)
          {
            if(virtClosedOrders[virtClosedOrdersCount-1][0] == 0) 
              {
                last_profit =  virtClosedOrders[virtClosedOrdersCount-1][4] - 
                               virtClosedOrders[virtClosedOrdersCount-1][1];
              }
            else
              {
                last_profit =  virtClosedOrders[virtClosedOrdersCount-1][1] - 
                               virtClosedOrders[virtClosedOrdersCount-1][4];
              }
            if(virtClosedOrders[virtClosedOrdersCount-2][0] == 0)
              {
                pre_last_profit = virtClosedOrders[virtClosedOrdersCount-2][4] - 
                                  virtClosedOrders[virtClosedOrdersCount-2][1];
              }
            else
              {
                pre_last_profit = virtClosedOrders[virtClosedOrdersCount-2][1] - 
                                  virtClosedOrders[virtClosedOrdersCount-2][4];
              }
 
            if(last_profit - pre_last_profit > 0.0)
              {
                return(false);
              }
          }
        return(true);
      }

Agora vamos testar essas três novas opções e adicioná-las à nossa tabela:

AdaptVariantLucro/perda total
Total de negociações
Transações lucrativas
Negociações desvantajosas
0 1678,75
41 9 (22%)
32 (78%)
1 105,65 20 2 (10%)
18 (90%)
2 -550,20 11 0 (0%)
11 (100%)
3 1225,13 28 7 (25%)
21 (75%)
4
1779,24
39 9 (23%) 30 (77%)
5
2178,95
31
9 (29%)
22 (71%)
6
602,32
24
5 (21%)
19 (79%)














A sexta opção filtrou metade das negociações, tanto as lucrativas quanto as desvantajosas. A quarta excluiu duas negociações desvantajosas, aumentado o lucro total em $100,49.

E a melhor opção foi aquela que proíbe negociações após toda negociação lucrativa: ela filtrou 10 negociações desvantajosas e nenhuma lucrativa.

Portanto, há esperança. Até mesmo uma estratégia tão simples e popular pode ser melhorada!

Conclusão


Eu acho que este tipo de filtro não é suficiente para uma melhoria real do sistema. Pesquisas muito mais profundas devem ser realizadas, e novas conclusões devem ser feitas.

Os filtros descritos podem ser tornados mais complexos e absolutamente diferentes para cada estratégia. A sua eficiência depende diretamente da troca entre as posições lucrativas e desvantajosas.

Este artigo se limita à questão da filtragem. Mas eu espero que ele lhe inspire a ir além.




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

Arquivos anexados |
Modelagem de mudanças de cotação no provador e análise da estabilidade do expert advisor Modelagem de mudanças de cotação no provador e análise da estabilidade do expert advisor
A mudança de cotação é um grande problema para muitos expert advisors, especialmente para aqueles que possuem condições bastante sensíveis para a entrada/saída de uma negociação. Neste artigo, é oferecida uma forma de verificar a estabilidade de mudança de cotações de um EA.
Exibição dos níveis de apoio/resistência Exibição dos níveis de apoio/resistência
O artigo trata da detecção e indicação dos níveis de apoio/resistência no programa MetaTrader 4. O indicador conveniente e universal é baseado em um algoritmo simples. O artigo lida com um tópico bastante útil: a criação de um indicador simples, capaz de exibir os resultados de diferentes períodos de tempo em um espaço de trabalho.
Diagramas tridimensionais - uma ferramenta profissional de análise de mercado Diagramas tridimensionais - uma ferramenta profissional de análise de mercado
Neste artigo escreveremos uma biblioteca simples para a construção de diagramas 3D e sua exibição posterior no Microsoft Excel. Usaremos as opções padrão do MQL4 para preparar e exportar os dados ao arquivo *.csv.
Um método para o desenho dos níveis de apoio/resistência Um método para o desenho dos níveis de apoio/resistência
Este artigo descreve o processo de criação de um script simples para a detecção dos níveis de apoio/resistência. Ele foi escrito para iniciantes, então você encontrará uma explicação detalhada de cada estágio do processo. Contudo, apesar do script ser bastante simples, o artigo também será útil para traders avançados e usuários da plataforma MetaTrader 4. Ele contém exemplos da exportação de dados para o formato tabular, da importação da tabela para o Microsoft Excel, e do delineamento de gráficos para a realização de uma análise mais detalhada.