English Русский 中文 Español Deutsch 日本語
Como cortar um Código de EA para uma vida mais fácil e menos erros

Como cortar um Código de EA para uma vida mais fácil e menos erros

MetaTrader 4Sistemas de negociação | 22 fevereiro 2016, 12:57
990 0
Roman Kramar
Roman Kramar

Introdução

Há muitos sistemas de trading com base em análise técnica quer sejam indicadores ou desenhos gráficos, que têm uma propriedade importante. Quero dizer, a simetria de tais sistemas na direção do trading. Devido a esta propriedade, os sinais de trading e a mecânica de colocar ordens de trade em tais sistemas podem ser geralmente expressos como relacionados às suas direções.

Uma abordagem simples descrita abaixo permite usar esta propriedade de maneira eficaz para reduzir de forma significativa o comprimento do código de Expert Advisors com base em tais sistemas simétricos. Os Expert Advisors que usam esta abordagem utilizam o mesmo código para detectar sinais de trade e gerar ordens de trade para posições longas e curtas.

É uma prática comum desenvolver um Expert Advisor com base em um sistema simétrico, uma primeira geração de códigos e processamento de sinais de trade em uma direção e, em seguida, copiar o código e refina-lo para outra direção. Neste caso, é muito fácil cometer um erro e, em seguida, é muito difícil de detetar tal erro. Assim, a diminuição da quantidade de possíveis erros na lógica do Expert Advisor é uma vantagem adicional da abordagem considerada.

1. Incorporação da Invariante do Expert Advisor em relação a Direção do Trade

O conceito em questão é baseado na direção do trade. Essa direção pode ser longa (sinais de compra e ordens) ou curta (sinais de venda e ordens). O nosso objeto escreve Expert Advisors de tal maneira que seus códigos são invariantes no que diz respeito à direção do trade atual. Para evitar blocos no texto, vamos chamar este código da invariante considerando de que ele é invariante somente na direção do trade.

Para isso, vamos introduzir uma função ou uma variável cujo valor sempre mostrará a direção do trade atual com um dos dois valores possíveis.

A representação dessa variável no código é um aspecto muito importante. Embora o tipo booleano pareça se servir para esses fins, seria mais eficaz usar uma representação um pouco diferente - um número inteiro. A direção do trade em si é codificada como segue:

  • direção de trade longa: +1
  • direção de trade curta: -1

Uma vantagem desta representação, em comparação com uma representação lógica, é que ela pode ser utilizada de forma eficaz para fazer vários cálculos e verificações no código do Expert Advisor sem a ramificação condicional utilizada em abordagens convencionais.

2. Exemplo de como mudar do código convencional para o código invariante

Vamos elucidar esta declaração sobre alguns exemplos. No entanto, vamos começar considerando um par de funções auxiliares que usaremos repetidamente mais tarde:

int sign( double v )
{
    if( v < 0 ) return( -1 );
    return( 1 );
}
 
double iif( bool condition, double ifTrue, double ifFalse )
{
    if( condition ) return( ifTrue );
    
    return( ifFalse );
}
 
string iifStr( bool condition, string ifTrue, string ifFalse )
{
    if( condition ) return( ifTrue );
    
    return( ifFalse );
}
 
int orderDirection()
{
    return( 1 - 2 * ( OrderType() % 2 ) );
}

O propósito da função sign() é óbvio: ela returna 1 para valores não negativos do argumento e -1 para valores negativos.

A função iif() é um equivalente do operador C-language chamado "condition ? ifTrue : ifFalse" e permite uma significativa simplificação da invariante do Expert Advisors tornando-a mais compacta e representativa. Ela pega argumentos do tipo duplo , então pode ser usada com valores dos tipos int e datetime. Para o mesmo trabalho com strings, precisaremos de uma função totalmente análoga iifStr() que pega os valores do tipo string.

A função orderDirection() retorna a direção da ordem de comércio atual (isto é, selecionada pela função OrderSelect ()) de acordo com os nossos acordos sobre como representar direções comerciais.

Agora, vamos considerar em exemplos específicos como a abordagem invariante com tal codificação das direções de trade permite simplificar códigos do Expert Advisor:

2.1 Exemplo 1. Transformar a realização da paragem de rastreamento

Um código típico:

if( OrderType() == OP_BUY )
{
    bool modified = OrderModify( OrderTicket(), OrderOpenPrice(), Bid - Point *
        TrailingStop, OrderTakeProfit(), OrderExpiration() );
 
    int error = GetLastError();
    if( !modified && error != ERR_NO_RESULT )
    {
        Print( "Failed to modify order " + OrderTicket() + ", error code: " +
            error );
    }
}
else
{
    modified = OrderModify( OrderTicket(), OrderOpenPrice(), Ask + Point *
        TrailingStop, OrderTakeProfit(), OrderExpiration() );
 
    error = GetLastError();
    if( !modified && error != ERR_NO_RESULT )
    {
        Print( "Failed to modify order " + OrderTicket() + ", error code: " +
            error );
    }
}

O código da invariante:

double closePrice = iif( orderDirection() > 0, Bid, Ask );
 
bool modified = OrderModify( OrderTicket(), OrderOpenPrice(), closePrice -
    orderDirection() * Point * TrailingStop, OrderTakeProfit(),
    OrderExpiration() );
 
int error = GetLastError();
if( !modified && error != ERR_NO_RESULT )
{
    Print( "Failed to modify order " + OrderTicket() + ", error code: " +
        error );
}

Resumindo:

  1. conseguimos evitar a ramificação condicional pesada;
  2. utilizamos apenas uma string para chamar a função OrderModify() ao invés das duas iniciais; e,
  3. como implicação (2), encurtamos o código para processamento de erro.

Observe que conseguimos usar apenas uma chamada para OrderModify(), devido a isto utilizamos a direção da ordem do trade imediatamente na expressão aritmética para calcular o nível de paragem. Se usássemos uma representação lógica da direção de trade, isto seria impossível.

Basicamente, um escritor experiente de Expert Advisors seria capaz de fazer isto com apenas uma chamada para OrderModify() usando a abordagem convencional. No entanto, no nosso caso, isto acontece naturalmente e não exige quaisquer medidas adicionais.

2.2 Exemplo 2.Transformação da detecção do sinal de trade

Como exemplo, vamos considerar a detecção de sinais de trade em um sistema de duas médias móveis:

double slowMA = iMA( Symbol(), Period(), SlowMovingPeriod, 0, MODE_SMA,
    PRICE_CLOSE, 0 );
double fastMA = iMA( Symbol(), Period(), FastMovingPeriod, 0, MODE_SMA,
    PRICE_CLOSE, 0 );
 
if( fastMA > slowMA + Threshold * Point )
{
    // open a long position
    int ticket = OrderSend( Symbol(), OP_BUY, Lots, Ask, Slippage, 0, 0 );
    
    if( ticket == -1 )
    {
        Print( "Failed to open BUY order, error code: " + GetLastError() );
    }
}
else if( fastMA < slowMA - Threshold * Point )
{
    // open a short position
    ticket = OrderSend( Symbol(), OP_SELL, Lots, Bid, Slippage, 0, 0 );
    
    if( ticket == -1 )
    {
        Print( "Failed to open SELL order, error code: " + GetLastError() );
    }
}

Agora vamos fazer a invariante do código em relação à direção do trade:

double slowMA = iMA( Symbol(), Period(), SlowMovingPeriod, 0, MODE_SMA,
    PRICE_CLOSE, 0 );
double fastMA = iMA( Symbol(), Period(), FastMovingPeriod, 0, MODE_SMA,
    PRICE_CLOSE, 0 );
 
if( MathAbs( fastMA - slowMA ) > Threshold * Point )
{
    // open a position
    int tradeDirection = sign( fastMA - slowMA );
    int ticket = OrderSend( Symbol(), iif( tradeDirection > 0, OP_BUY, OP_SELL ),
        Lots, iif( tradeDirection > 0, Ask, Bid ), Slippage, 0, 0 );
 
    if( ticket == -1 )
    {
        Print( "Failed to open " + iifStr( tradeDirection > 0, "BUY", "SELL" ) +
            " order, error code: " + GetLastError() );
    }
}

Eu acho que é absolutamente óbvio que o código tornou-se mais compacto. E, naturalmente, duas verificações de erros se transformaram em apenas uma.

Apesar do fato de que os exemplos acima são muito simples, as principais vantagens da abordagem em consideração deve ser muito óbvia. Em alguns dos casos mais complicados, a diferença entre a abordagem tradicional e a abordagem em consideração é ainda mais significativa. Vamos ter certeza disto no exemplo de um Expert Advisor padrão, Amostra MACD.

3. Como simplificar a Amostra MACD

Para não tornar este artigo muito extenso, não vamos considerar o código completo deste Expert Advisor. Vamos para as áreas de código que serão alteradas sob o conceito considerado.

O código completo deste EA está incluído no ajuste de entrega MetaTrader 4. Para sua conveniência, ele também está anexado neste artigo (arquivo MACD Sample.mq4) juntamente com a sua versão simplificada (MACD Sample-2.mq4).

Vamos começar com o bloco escrito para a detecção de sinais de trade. Seu código inicial é fornecido abaixo:

// check for long position (BUY) possibility
if(MacdCurrent<0 && MacdCurrent>SignalCurrent && MacdPrevious<SignalPrevious &&
   MathAbs(MacdCurrent)>(MACDOpenLevel*Point) && MaCurrent>MaPrevious)
  {
   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());
     }
   else Print("Error opening BUY order : ",GetLastError()); 
   return(0); 
  }
// check for short position (SELL) possibility
if(MacdCurrent>0 && MacdCurrent<SignalCurrent && MacdPrevious>SignalPrevious && 
   MacdCurrent>(MACDOpenLevel*Point) && MaCurrent<MaPrevious)
  {
   ticket=OrderSend(Symbol(),OP_SELL,Lots,Bid,3,0,Bid-TakeProfit*Point,
     "macd sample",16384,0,Red);
   if(ticket>0)
     {
      if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES))
        Print("SELL order opened : ",OrderOpenPrice());
     }
   else Print("Error opening SELL order : ",GetLastError()); 
   return(0); 
  }

Agora, usando os métodos acima, vamos escrever novamente o código de maneira que ele seja o mesmo para os sinais de compra e venda:

int tradeDirection = -sign( MacdCurrent );
 
// check if we can enter the market
if( MacdCurrent * tradeDirection < 0 && ( MacdCurrent - SignalCurrent ) *
    tradeDirection > 0 && ( MacdPrevious - SignalPrevious ) * tradeDirection < 0
    && MathAbs(MacdCurrent)>(MACDOpenLevel*Point) && ( MaCurrent - MaPrevious ) *
    tradeDirection > 0 )
  {
   int orderType = iif( tradeDirection > 0, OP_BUY, OP_SELL );
   string orderTypeName = iifStr( tradeDirection > 0, "BUY", "SELL" );
   double openPrice = iif( tradeDirection > 0, Ask, Bid );
   color c = iif( tradeDirection > 0, Green, Red );
   ticket = OrderSend( Symbol(), orderType, Lots, openPrice, 3 , 0, openPrice +
     tradeDirection * TakeProfit * Point, "macd sample", 16384, 0, c );
   if(ticket>0)
     {
      if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES))
        Print( orderTypeName + " order opened : ", OrderOpenPrice() );
     }
   else Print("Error opening " + orderTypeName + " order : ",GetLastError()); 
   return(0); 
  }

Agora vamos para o bloco responsável por fechar posições abertas e processamento de paragem de rastreamento. Vamos primeiro estudar a sua versão inicial, como antes:

if(OrderType()==OP_BUY)   // long position is opened
  {
   // should it be closed?
   if(MacdCurrent>0 && MacdCurrent<SignalCurrent && MacdPrevious>SignalPrevious &&
      MacdCurrent>(MACDCloseLevel*Point))
       {
        OrderClose(OrderTicket(),OrderLots(),Bid,3,Violet); // close position
        return(0); // exit
       }
   // check for trailing stop
   if(TrailingStop>0)  
     {                 
      if(Bid-OrderOpenPrice()>Point*TrailingStop)
        {
         if(OrderStopLoss()<Bid-Point*TrailingStop)
           {
            OrderModify(OrderTicket(),OrderOpenPrice(),Bid-Point*TrailingStop,
               OrderTakeProfit(),0,Green);
            return(0);
           }
        }
     }
  }
else // go to short position
  {
   // should it be closed?
   if(MacdCurrent<0 && MacdCurrent>SignalCurrent &&
      MacdPrevious<SignalPrevious && MathAbs(MacdCurrent)>(MACDCloseLevel*Point))
     {
      OrderClose(OrderTicket(),OrderLots(),Ask,3,Violet); // close position
      return(0); // exit
     }
   // check for trailing stop
   if(TrailingStop>0)  
     {                 
      if((OrderOpenPrice()-Ask)>(Point*TrailingStop))
        {
         if((OrderStopLoss()>(Ask+Point*TrailingStop)) || (OrderStopLoss()==0))
           {
            OrderModify(OrderTicket(),OrderOpenPrice(),Ask+Point*TrailingStop,
               OrderTakeProfit(),0,Red);
            return(0);
           }
        }
     }
  }

Vamos transformar esse código em um uma invariante no que diz respeito à direção do trade:

tradeDirection = orderDirection();
double closePrice = iif( tradeDirection > 0, Bid, Ask );
c = iif( tradeDirection > 0, Green, Red );
 
// should it be closed?
if( MacdCurrent * tradeDirection > 0 && ( MacdCurrent - SignalCurrent ) *
    tradeDirection < 0 && ( MacdPrevious - SignalPrevious ) * tradeDirection > 0 
    && MathAbs( MacdCurrent ) > ( MACDCloseLevel * Point ) )
    {
     OrderClose(OrderTicket(),OrderLots(), closePrice, 3,Violet); // close position
     return(0); // exit
    }
// check for trailing stop
if(TrailingStop>0)  
  {                 
   if( ( closePrice - OrderOpenPrice() ) * tradeDirection > Point * TrailingStop )
     {
      if( OrderStopLoss() == 0 || ( OrderStopLoss() - ( closePrice - tradeDirection *
        Point * TrailingStop ) ) * tradeDirection < 0 )
        {
         OrderModify( OrderTicket(), OrderOpenPrice(), closePrice - tradeDirection *
            Point * TrailingStop, OrderTakeProfit(), 0, c );
         return(0);
        }
     }
  }

Por favor note que a versão inicial do EA verifica a condição OrderStopLoss() == 0 apenas para posições curtas em processamento de uma paragem de rastreamento. Isso é necessário para processar situações em que não se conseguiu definir o nível de paragem inicial (por exemplo, devido a ser muito próximo do preço de mercado).

O fato de que esta condição não está verificada para as posições longas pode ser considerado como um erro que é muito típico para escrita de tais Expert Advisors simétricos utilizando o método de copiar e colar.

Por favor note que este erro foi corrigido automaticamente para ambos os sentidos do trade no código melhorado. Também deve-se notar se este erro cometido ao escrever o código da invariante iria ocorrer no processamento de posições longas e curtas. Não saberíamos dizer se isto aumentaria a probabilidade de sua detecção durante o teste.

Bem, isso é tudo. Se você testar o Expert Advisors com as mesmas definições sobre os mesmos dados, você verá que eles são absolutamente equivalentes. No entanto, a versão simplificada é muito mais compacta e de fácil manutenção.

4. Recomendações sobre a escrita de Expert Advisors Simétricos "do zero"

Até agora, consideramos possibilidades de como mudar a partir do código convencional de um Expert Advisor para o invariante. No entanto, o desenvolvimento de um robô de trading com os princípios acima "do zero" é ainda mais eficaz.

À primeira vista, não parece ser fácil, uma vez que precisa de certas experiências e habilidades na formulação de condições e expressões invariantes com relação à direção do trade. No entanto, depois de ter alguma prática, escrever o código neste estilo será fácil.

Agora vou tentar dar algumas recomendações que podem ajudar a iniciar o uso de uma abordagem mais eficaz:

  1. Ao desenvolver esta ou aquela área de código, em primeiro lugar, processe a direção de trade maior - na maioria dos casos, será mais fácil de sintetizar o código da invariante, uma vez que esta direção do trade é representada pelo valor +1 e não é preciso muito ao escrever e analisar as relações invariantes.
  2. Se você começar a trabalhar no direção maior, primeiro tente escrever uma condição sem uma variável/função que reflita a direção do trade. Certifique-se de que a expressão está correta e adicione a direção do trade. Depois de ter ganho algumas experiências, você pode continuar sem essa divisão por etapas.
  3. Não "desligue" da direção longa do trade - às vezes é mais eficaz expressar a condição para a direção curta.
  4. Tente evitar a ramificação condicional e utilize a função iif() onde for possível fazer cálculos aritméticos.

Quanto à última cláusula, gostaria de acrescentar que haverá situações em que você não pode fazer isto sem a ramificação condicional. No entanto, você deve tentar tais situações e destiná-las à funções auxiliares separadas da direção do trade que não dependam de um EA específico. Estas funções, bem como as funções acima, sign(), iif() e orderDirection(), podem ser adequadas à uma biblioteca especial que será posteriormente utilizada por todos os seus Expert Advisors.

Para esclarecer tudo novamente, vamos considerar o seguinte problema:

em uma ordem de trade, o nível de paragem deve estar no nível mínimo da barra anterior para uma posição longa e no nível máximo da barra anterior para uma posição curta.

Isto pode ser representado como se segue no código:

double stopLevel = iif( tradeDirection > 0, Low[ 1 ], High[ 1 ] );

Parece ser fácil e claro, no entanto, mesmo essas construções simples podem e devem ser agrupadas em funções pequenas e simples para serem usadas repetidamente.

Vamos evitar o operador condicional colocando-o na função de auxiliar para fins mais gerais:

double barPeakPrice( int barIndex, int peakDirection )
{
    return( iif( peakDirection > 0, High[ barIndex ], Low[ barIndex ] ) );
}

Agora, podemos expressar o cálculo dos níveis de paragem como se segue:

double stopLevel = barPeakPrice( 1, -tradeDirection );

Se a diferença parecer ser bastante significante, por favor, não se deixe ser conduzido por suas primeiras impressões. Esta variante possui vantagens sérias:

  • ela expressa seu propósito de forma explícita e de forma invariante;
  • Ela estimula a escrita do código do EA em um estilo próprio;
  • é mais fácil de ler e torna o desenvolvimento posterior mais simples.

Isso é apenas um exemplo. Na verdade, muitos elementos padrões do código do Expert Advisors podem ser expressos de forma similar. Você será capaz de fazer isso sozinho. Então, eu recomendo que você analise seu código e o revise desta forma.

5. Por que tudo isso?

O conceito descrito acima possui, na minha opinião, as seguintes vantagens:

  • redução do código fonte sem perder a funcionalidade e, subsequentemente, consumir menos tempo durante o desenvolvimento e ajuste dos sistemas de trading;
  • redução da quantidade de possíveis erros;
  • aumento da probabilidade de detecção de erros existentes;
  • simplificação de modificação posterior de um Expert Advisor (as mudanças aplicam-se automaticamente para ambos os sinais, posições longas e curtas).

Só notei uma desvantagem: Este conceito pode causar algumas pequenas dificuldades em compreender e estudar fases anteriores. No entanto, esta desvantagem é mais do que compensada pelas vantagens listadas acima. Além disso, é apenas uma questão de tempo e de experiência, o desenvolvimento do código invariante torna-se natural e fácil.

Conclusão

A abordagem acima para escrever códigos do Expert Advisors em MQL4 é baseada no uso e na representação efetiva da noção de direção de trade. Ela permite evitar a duplicação de algumas áreas de código praticamente idênticas que podem ser normalmente vistas nos Expert Advisors escritos utilizando a abordagem convencional. A utilização do método descrito resulta na redução essencial do volume do código fonte com todas as vantagens.

Os exemplos são fornecidos no artigo a fim de ajudar os novatos e alguns desenvolvedores mais experientes de sistemas de trading na elaboração de seus códigos existentes, se assim desejarem. Estes exemplos, complementados por recomendações de algum autor irá ajudá-los a escrever códigos compactos para a invariante a respeito da direção de trade.

Para facilitar a leitura e a compreensão do assunto os exemplos considerados foram simples. No entanto, a abordagem descrita foi utilizada com sucesso na realização de sistemas muito mais complicados que haviam aplicado ferramentas de análise técnica como linhas de tendência, canais, Andrews Pitchfork, Elliot Waves, e alguns outros métodos médios e avançados de análise de mercado.



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

Arquivos anexados |
MACD_Sample-2.mq4 (5.69 KB)
MACD_Sample.mq4 (5.48 KB)
Matemática na negociação: Como estimar resultados de trading Matemática na negociação: Como estimar resultados de trading
Nós todos sabemos que "Nenhum lucro obtido no passado garantirá algum sucesso no futuro". No entanto, ser capaz de estimar sistemas de negociação, ainda é muito atual. Este artigo trata sobre alguns métodos simples e convenientes que ajudarão a estimar resultados de trade.
Verificador no terminal MetaTrader 4: Deve-se saber Verificador no terminal MetaTrader 4: Deve-se saber
A interface elaborada do terminal MetaTrader4 é de primeira linha, mas além disso, o terminal inclui um verificador de estratégias bem preparado. E enquanto o valor do MetaTrader 4 como sistema de trading é óbvio, a qualidade do verificador de estratégia só pode ser avaliada na prática. Este artigo mostra as vantagens e conveniências de testes no MetaTrader 4.
Romper o limite do verificador de estratégia em testes de cobertura do EA Romper o limite do verificador de estratégia em testes de cobertura do EA
Uma ideia de teste de cobertura dos Expert Advisors utilizando o verificador de estratégia.
Interação entre o MetaTrader 4 e o Matlab através de arquivos CSV Interação entre o MetaTrader 4 e o Matlab através de arquivos CSV
Instruções detalhadas de como organizar o intercâmbio de dados das matrizes entre o MetaTrader 4 e o Matlab através de arquivos CSV.