English Русский 中文 Español Deutsch 日本語
Como desenvolver no MQL4 um Robô Negociador seguro e confiável

Como desenvolver no MQL4 um Robô Negociador seguro e confiável

MetaTrader 4Sistemas de negociação | 19 fevereiro 2016, 14:25
2 152 0
Shashev Sergei
Shashev Sergei

Introdução

No processo de criação de qualquer solução séria para um programa, o desenvolvedor enfrenta o fato de que seu programa pode conter todos os erros possíveis e impossíveis. Os erros causam muitos problemas na fase de desenvolvimento, levam à insegurança em relação à soluções e, se acontecerem em um robô negociador, podem ocasionar resultados negativos nas finanças. Vamos analisar os erros mais comuns, sua origem, e os métodos de detecção e processamento dos erros do programa. No processo de desenvolvimento e utilização de um Expert Advisor para o terminal do cliente MetaTrader 4, os seguintes erros podem ocorrer:

  1. sintaxe - eles podem ser encontrados na fase de compilação e podem ser facilmente fixados pelo programador;
  2. lógica - eles não são detectados com um compilador. Os exemplos são: confusão com os nomes das variáveis, funções de chamada erradas, operação de dados de diferentes tipos e assim por diante;
  3. algorítmico - ocorrem quando os parênteses não estão posicionados corretamente, no caso de uma confusão com as declarações do ramo e assim por diante;
  4. crítico - estes são erros improváveis, você deve se esforçar para evocá-los. Eles geralmente ocorrem quando você trabalha com dll;
  5. trading - estes são os erros que ocorrem quando você trabalha com as ordens. Esse tipo de erro é um ponto crítico para robôs negociadores.
Primeiramente, recomendamos o estudo da documentação sobre erros de execução. Uma vez que esse procedimento for executado, você pode economizar muito tempo futuramente. Os erros induzidos por operações de trading são descritos aqui.

Erros de sintaxe

Os erros deste tipo são induzidos por erros de impressão dos operadores, variáveis e diferentes funções de chamadas. Durante a compilação, o código do programa é verificado e todos os erros de sintaxe são exibidos em "Ferramentas" na janela do MetaEditor. Na verdade, quase todos os erros, são detectados e podem ser corrigidos pelo programador.

A exceção é uma bagunça com os parênteses, quando o parêntese aberto/fechado é colocado em um local errado e é detectado na fase de compilação, mas o posicionamento do erro é mostrado de uma maneira errada. Então você tem que checar o código para ser capaz de encontrar o erro visualmente, o que, infelizmente, nem sempre dá certo. A segunda abordagem é a paralisação sucessiva dos blocos do código usando comentários. Neste caso, se após comentar um novo bloco o erro desaparece, ele está, obviamente, neste bloco de comentário. Ele restringe drasticamente a área de busca e ajuda a encontrar rapidamente o posicionamento errado dos parênteses.


Erros lógicos, algorítmicos e críticos

Os erros mais comuns deste tipo são a confusão nos nomes, os tipos de variáveis e também os erros algorítmicos nos ramos do Expert Advisor. Por exemplo, vamos estudar esse código:

bool Some = false;
 
void check()
  {
    // Much code
    Some = true;
  }
// Very much code
int start()
  {
    bool Some = false;
    //
    if(Some)   
      {
        //sending the order
      }
   return(0);
  }

O que podemos ver? A variável lógica "Algum", que é comum para todo o programa e é um indicador importante para a abertura da posição, foi acidentalmente definida mais baixa. Isto pode levar à abertura errada da ordem e, portanto, a perdas. Você pode configurar vários nomes para as variáveis! Mas por alguma razão, repetir esses nomes acidentalmente em grandes programas, leva ao problema mencionado acima.

Este tipo de erro ocorre quando as variáveis são misturadas ou a expressão de um tipo é atribuída à expressão de outro tipo. Por exemplo, nesta linha:

int profit = NormalizeDouble(SomeValue*point*2 / 3, digit);

Estamos tentando atribuir a expressão de valor do tipo "dobro" para a variável do tipo "int", o que resulta em zero. E estamos calculando o nível do takeprofit! Este tipo de erro leva ao trading errado.


O erro algorítmico nos ramos do Expert Advisor significa que os parênteses não foram colocados de acordo com o algoritmo, ou houve cobertura errada dos operadores "se" pelos operadores "outro". Como resultado temos um Expert Advisor que não funciona de acordo com a exigência técnica.

Alguns erros podem ser tão imperceptíveis que você pode passar várias horas "meditando sobre o código" para encontrá-los. Infelizmente, não há possibilidade de rastrear os valores das variáveis no MetaEditor, ao contrário dos ambientes para línguas da família C++. Assim, a única maneira é rastrear os erros através da saída de mensagens pela função Imprimir().

A função GetLastError() retorna o código do erro. Recomenda-se verificar o último valor após cada lugar potencialmente vulnerável do programa. Usando o código do erro, você pode facilmente encontrar sua descrição na documentação, e para alguns erros você pode encontrar até mesmo os métodos de tratamento.

Devemos dizer que os erros mencionados acima provavelmente serão detectados na fase de testes antes de usar a conta demo, sendo assim, perdas induzidas por eles são improváveis.

A principal característica dos erros críticos é que quando eles ocorrem, a execução do programa pára imediatamente. Não obstante, o código do erro permanece inalterado na variável predefinida "Last_Error". Isso nos dá a possibilidade de aprender o código do chamando a função GetLastError().


Erros de trading

Esses erros muitas vezes levam a perdas e a não operacionalidade do Expert Advisor na demo e, além disso, nas contas reais. Eles ocorrem quando você trabalha com envio e modificação de ordens, em outras palavras, ao interagir com o servidor de trading.
O processamento simples como este:

ticket = OrderSend(Symbol(), OP_SELL, LotsOptimized(), Bid, 3,
         Bid + StopLoss*Point, Bid - TakeProfit*Point, 0, MAGICMA, 
         0, Red);
if(ticket > 0) 
  {
    err = GetLastError();
    Print("While opening the order the error #  occured", err);
  }

não irá ajudar. Temos a certeza de que a ordem não foi enviada para o servidor e aprendeu o código do erro. E então, o que? Certamente, se tivéssemos um Expert Advisor com fins lucrativos, perderíamos uma entrada importante para o mercado.

A variante com o loop infinito:

while (true)
  {
    ticket = OrderSend(Symbol(), OP_SELL, Lots, Bid, slippage,
             Bid + StopLoss*Point, Bid - TakeProfit*Point, 0, 
             MAGICMA, 0, Red);
    if(ticket > 0) 
      {
        err = GetLastError();
        Print("While opening the order the error #  occured", err);
        break;      
      }
    Sleep(1000);
    RefleshRates();
  }

ajuda um pouco. A ordem provavelmente alcançaria o servidor mais facilmente. Mas podemos enfrentar alguns problemas:

  1. O corretor não iria gostar de pedidos frequentes;
  2. O erro pode ser fatal, neste caso, o pedido não iria alcançar o servidor de qualquer maneira;
  3. o Expert Advisor não responderia por um longo período;
  4. O servidor não poderia aceitar pedidos de trading - poderia demorar um fim de semana, um feriado, trabalhos de manutenção e assim por diante.

Quase todo erro é único e precisa ser tratado de uma maneira própria. Por isso, vamos discutir a variante com o operador de Troca e cultivar cada erro mais ou menos individualmente. O erro padrão # 146 - "fluxo de negócios ocupado", é processado usando o semáforo realizado na biblioteca TradeContext.mqh. Você pode achar a biblioteca e sua descrição detalha neste artigo.

//The library for differentiation of work with the trading flow
//written by komposter
#include <TradeContext.mqh>
 
//parameters for the signals
extern double MACDOpenLevel=3;
extern double MACDCloseLevel=2;
extern double MATrendPeriod=26;
 
// maximum acceptable slippage
int       slippage = 3;
//total number of transactions
int deals = 0;
//time for the pause after transaction
int TimeForSleep = 10;
//period of request
int time_for_action = 1;
//number of tries of opening/closing the position
int count = 5;
//indicator of operability of the EA
bool Trade = true;
//indicator of availability of funds for opening the position
bool NoOpen = false;
//+------------------------------------------------------------------+
//| Do not ask the server for quotes on weekends                    |
//+------------------------------------------------------------------+
bool ServerWork()
  {     
   if(DayOfWeek() == 0 || DayOfWeek() == 6)
       return(false);
   return(true);      
  }
//+------------------------------------------------------------------+
//| Generation of magik                                                  |
//+------------------------------------------------------------------+ 
int GenericMagik()
  {
   return(deals);
  }
//+------------------------------------------------------------------+   
//| Closing of transactions                                                  |
//+------------------------------------------------------------------+
bool CloseOrder(int magik)
  {
   int ticket,i;
   double Price_close;
   int err;
   int N;
//Function tries to shut the server at count attempts, if it fails,
//it gives an error message to the logfile
   while(N < count)
     {          
       for(i = OrdersTotal() - 1; i >= 0; i--) 
         {
           if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
               if(OrderSymbol() == Symbol()) 
                   if(OrderMagicNumber() == magik)
                     {
                       if(OrderType() == OP_BUY)        
                           Price_close = NormalizeDouble(Bid, Digits);
                       if(OrderType() == OP_SELL)        
                           Price_close = NormalizeDouble(Ask, Digits);
                       if(OrderClose(OrderTicket(), OrderLots(),
                          Price_close,slippage))
                         { 
                           //reduce the number of transactions for the EA
                           deals--;
                           //the piece of the margin became available - you can open again
                           NoOpen = false;                           
                           return(true);
                         }
                         //we have reached this place, it means that the order has not been sent
                       N++;
                       //processing of possible errors
                       err = ErrorBlock();
                       //if the error is seriuos
                       if(err > 1)
                         {
                           Print("Manual closing of the order #  needed",
                                 OrderTicket());
                           return(false);
                         }
                     }                                          
         }
        // taking a pause of 5 seconds and trying to close the transaction again
       Sleep(5000);
       RefreshRates();
     }
    //if we have reached this place, the transaction was not closed at count attempts 
   Print("Manual closing of the order #  needed",OrderTicket());
   return(false);
  }
//+------------------------------------------------------------------+
//|Tranaction for act 1-buy, 2-sell, the second parameter - the number of lots      |
//+------------------------------------------------------------------+ 
int Deal(int act, double Lot)
  {
   int N = 0;
   int ticket;
   int err;
   double Price_open;
   double Lots;
   int cmd;
   int magik;
   magik = GenericMagik();
   Lots = NormalizeDouble(Lot,1);
   if(act == 1)
     {
       Price_open = NormalizeDouble(Ask, Digits);
       cmd = OP_BUY;
     }
   if(act == 2)
     {
       Price_open = NormalizeDouble(Bid, Digits);
       cmd = OP_SELL;
     }
   //checking the margin for opening the position
   AccountFreeMarginCheck(Symbol(), cmd,Lots);
   err = GetLastError();
   if(err>0)
     {
       Print("No money for new position");
       NoOpen = true;
       return(0);
     }      
//Sending the order                  
   ticket = OrderSend(Symbol(), cmd, Lots, Price_open, slippage, 
                      0, 0, 0, magik);
   if(ticket > 0)
     {
       deals++;
       return(ticket);
     }
//If the order has not been sent, we will try to open it 5 times again      
   else
     {
       while(N < count)
         {
           N++;
           err = ErrorBlock();
           if(err == 1)
             {
               Sleep(5000);
               RefreshRates();
               if(act == 1)
                   Price_open = NormalizeDouble(Ask, Digits);
               if(act == 2)
                   Price_open = NormalizeDouble(Bid, Digits);              
               ticket = OrderSend(Symbol(), cmd, Lots, Price_open,
                                  slippage, 0, 0, 0, magik);
               if(ticket > 0)
                 {
                   deals++;
                   return(ticket);
                 }
             }
           // we have got a serious error  
           if(err > 1)            
               return(0);               
         }                                                       
     }    
   return(0);
  }
//+------------------------------------------------------------------+
//| // 0-no error, 1-need to wait and refresh, 2-transaction rejected,   |
//|    3-fatal error                                            |
//+------------------------------------------------------------------+
//Block of the error control 
int ErrorBlock()
  {
   int err = GetLastError();
   switch(err)
     {
       case 0: return(0);
       case 2:
         {
           Print("System failure. Reboot the computer/check the server");
           Trade = false;
           return(3);  
         }
       case 3:
         {
           Print("Error of the logic of the EA");
           Trade = false;
           return(3);   
         }
       case 4:
         {
           Print("Trading server is busy. Wait for 2 minutes.");
           Sleep(120000);
           return(2);   
         }
       case 6:
         { 
           bool connect = false;
           int iteration = 0;
           Print("Disconnect ");
           while((!connect) || (iteration > 60))
             {
               Sleep(10000);
               Print("Connection not restored", iteration*10,
                     "  seconds passed");
               connect = IsConnected();
               if(connect)
                 {
                   Print("Connection restored");
                   return(2);
                 }
               iteration++;
             }
           Trade = false; 
           Print("Connection problems");
           return(3);
         }
       case 8:
         {
           Print("Frequent requests");
           Trade = false; 
           return(3);
         }
       case 64:
         {
           Print("Account is blocked!");
           Trade = false; 
           return(3);            
         }
       case 65:
         {
           Print("Wrong account number???");
           Trade = false; 
           return(3);            
         }
       case 128:
         {
           Print("Waiting of transaction timed out");
           return(2);
         }
       case 129:
         {
           Print("Wrong price");
           return(1);            
         }
       case 130:
         {
           Print("Wrong stop");
           return(1);
         }
       case 131:
         {
           Print("Wrong calculation of trade volume");
           Trade = false;            
           return(3);
         }
       case 132:
         {
           Print("Market closed");
           Trade = false; 
           return(2);
         }
       case 134:
         {
           Print("Lack of margin for performing operation");
           Trade = false;             
           return(2);
         }
       case 135:
         {
           Print("Prices changed");
           return (1);
         }
       case 136:
         {
           Print("No price!");
           return(2);
         }
       case 138:
         {
           Print("Requote again!");
           return(1);
         }
       case 139:
         {
           Print("The order is in process. Program glitch");
           return(2);
         }
       case 141:
         {
           Print("Too many requests");
           Trade = false; 
           return(2);            
         }
       case 148:
         {
           Print("Transaction volume too large");
           Trade = false; 
           return(2);            
         }                                          
     }
   return (0);
  }
//+------------------------------------------------------------------+
//| generation of signals for opening/closing position on Macd       |
//+------------------------------------------------------------------+
int GetAction(int &action, double &lot, int &magik)
   {
   double MacdCurrent, MacdPrevious, SignalCurrent;
   double SignalPrevious, MaCurrent, MaPrevious;
   int cnt,total;
   
   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);
   
  if(MacdCurrent<0 && MacdCurrent>SignalCurrent && MacdPrevious<SignalPrevious &&
         MathAbs(MacdCurrent)>(MACDOpenLevel*Point) && MaCurrent>MaPrevious)
      {
         action=1;
         lot=1;
         return (0);
      }
  if(MacdCurrent>0 && MacdCurrent<SignalCurrent && MacdPrevious>SignalPrevious && 
         MacdCurrent>(MACDOpenLevel*Point) && MaCurrent<MaPrevious)
      {
         action=2;
         lot=1;
         return (0);               
      }
   total=OrdersTotal();
   for(cnt=0;cnt<total;cnt++)
     {
      OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
      if(OrderType()<=OP_SELL &&   // check for opened position 
         OrderSymbol()==Symbol())  // check for symbol
        {
         if(OrderType()==OP_BUY)   // long position is opened
           {
            // should it be closed?
            if(MacdCurrent>0 && MacdCurrent<SignalCurrent && MacdPrevious>SignalPrevious &&
               MacdCurrent>(MACDCloseLevel*Point))
                {
                 action=3;
                 magik=OrderMagicNumber();
                 return(0); // exit
                }
           }
         else // go to short position
           {
            // should it be closed?
            if(MacdCurrent<0 && MacdCurrent>SignalCurrent &&
               MacdPrevious<SignalPrevious && MathAbs(MacdCurrent)>(MACDCloseLevel*Point))
              {
               action=3;
               magik=OrderMagicNumber();
               return(0); 
              }
           }
        }
     }
   }
//+------------------------------------------------------------------+
//| The EA initialization function                                   |
//+------------------------------------------------------------------+
int init()
  { 
    if(!IsTradeAllowed())
      {
        Print("Trade not allowed!");
        return(0);     
      }
  }
//+------------------------------------------------------------------+
//| The EA deinitialization function                                 |
//+------------------------------------------------------------------+
int deinit()
  {
//Closing all orders
   for(int k = OrdersTotal() - 1; k >= 0 ; k--)
       if(OrderSymbol() == Symbol()) 
         {
           if(OrderType() == OP_BUY)
              OrderClose(OrderTicket(), OrderLots(), 
                         NormalizeDouble(Bid,Digits), 10);               
           if(OrderType() == OP_SELL)
               OrderClose(OrderTicket(), OrderLots(),
                          NormalizeDouble(Ask, Digits),10);                 
         }
  } 
//+------------------------------------------------------------------+
//| The EA start function                                            |
//+------------------------------------------------------------------+
int start()
  {
   int action =0;
   double lot = 1;
   int magik = 0;     
   while(Trade)
     {
       Sleep(time_for_action*1000);      
       RefreshRates();
       /*Logic of the EA where the we calculate the action, position limit and magik for closing the order
       action 1-buy, 2-sell, 3-close
       for example, take the EA on Macd*/
       GetAction(action,lot,magik);
       if(ServerWork())
         {
           if(((action == 1) || (action == 2)) && (!NoOpen))
             {                                        
               if(TradeIsBusy() < 0) 
                   return(-1); 
               Deal(action, lot);
               Sleep(TimeForSleep*1000);                                
               TradeIsNotBusy();
             }
           if(action == 3)
             {
               if(TradeIsBusy() < 0) 
                 { 
                   return(-1); 
                   if(!CloseOrder(magik))
                       Print("MANUAL CLOSE OF TRANSATION NEEDED");
                   Sleep(TimeForSleep*1000);   
                   TradeIsNotBusy();
                 } 
             }
         }
       else
         {
            Print("Weekends");
            if(TradeIsBusy() < 0) 
                  return(-1); 
            Sleep(1000*3600*48);
            TradeIsNotBusy();
         }
       action = 0;
       lot = 0;
       magik = 0;
     }
   Print("Critical error occured and the work of the EA terminated");  
   return(0);
  }
//+------------------------------------------------------------------+

Esta versão do robô negociador funciona em um loop infinito. Sua demanda ocorre quando o scalping multicurrency Expert Advisor é criado. O algoritmo de operação do EA é o seguinte:

1. Obtenha o sinal do bloco analítico GetAction();
2. Fazer a transação necessária nas funções Acordo() e и FecharPedido();
3. Retornar ao ponto 1 depois de uma curta pausa time_for_action no caso não houver falhas graves.

Depois de obter o sinal (comprar, vender, fechar) a partir do bloco de análise o Expert Advisor bloqueia o fluxo de trading (leia o artigo) e tenta fazer a transação, depois disso, acontece uma pausa de alguns segundos e libera o fluxo de trading para outros EAs. O Expert Advisor tenta enviar a ordem não mais do que "contar" vezes. Isto deve ser suficiente para a ordem passar no mercado instável, onde você pode obter cotações. Se ao emitir a ordem ocorrer um erro grave, o Expert Advisor pára de funcionar. Se ocorrer qualquer problema, uma mensagem de erro aparece na pasta "Expert Advisors". O Expert Advisor vai continuar funcionando se o erro não for crítico.

Os erros são processados no procedimento de o () de acordo com o seguinte esquema: o procedimento recebe o código de erro e fornece um pequeno algoritmo de processamento. Para a maioria dos erros é apenas uma mensagem no log. Se o erro for grave, os indicadores de mercado Trade e NoOpen mudam. Se for uma falha na conexão, o processamento da situação é um pouco mais complicado. O robô tenta alcançar o servidor sessenta vezes com a sequência periódica predefinida. Se o servidor não for alcançado, então provavelmente tem alguns problemas graves, e você deve parar seu trading por algum tempo. Dependendo da influência do erro no trading, o algoritmo de processamento devolve significados diferentes:

0 - nenhum erro;
1 - o erro está associado com a volatilidade do mercado, você pode tentar enviar a ordem mais uma vez;
2 - Ao enviar esta ordem, ocorreu um erro grave, pare de abrir posições por algum tempo;
3 - uma falha grave do EA, falha de conexão - pare de negociar até esclarecer as circunstâncias.

Conclusão

Erros de sintaxe, de lógica e de algoritmos ocorrem quando você não presta muita atenção no código do algoritmo. Esses erros são corrigidos pela verificação minuciosa e pela verificação dos valores das variáveis no log. Eles podem ser detectados na fase de compilação e de teste do Expert Advisor. Esses tipos de erros não existem há muito tempo, eles são geralmente consertados antes de usar uma conta demo.

Erros de trading ocorrem ao emitir ordens para o servidor. Eles estão relacionados com o trading real onde você pode encontrar uma nova cotação, queda de preço, briga de negociadores com scalping e falhas de equipamento. Tais erros não podem ser previstos, mas eles podem e devem ser processados. Você deve processá-los individualmente a cada semana, dependendo da lógica do Expert Advisor, a freqüência de transações e modificação de ordens.

Os erros que ocorrem durante a operação do Expert Advisor precisam de processamento. Não é uma tarefa trivial. Depende da complexidade e das características do EA. No artigo você pode encontrar o padrão exemplar do Expert Advisor que executa essa tarefa. É preciso muito tempo para criar um sistema de trading mais seguro e protegido. Mas o tempo gasto para o desenvolvimento do sistema de trading automatizado e livre de problemas será recompensado várias vezes pela segurança de seu depósito e sono tranquilo.

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

Arquivos anexados |
AntiError.mq4 (14.86 KB)
TradeContext.mqh (11.54 KB)
Transferência de um Código Indicador para um Código Expert Advisor. Conclusão Transferência de um Código Indicador para um Código Expert Advisor. Conclusão
Este é o artigo final, dedicado à Transferência de um Código Indicador para um Código Expert Advisor. Aqui o autor transforma em um determinado exemplo um código de um Expert Advisor para que esta área seja apresentada em um único arquivo sem chamar indicadores personalizados.
Exibição simultânea dos sinais de vários indicadores dos quatro calendários Exibição simultânea dos sinais de vários indicadores dos quatro calendários
No trading manual você tem que ficar de olho nos valores de de vários indicadores. É um pouco diferente do trading mecânico. Se você tiver dois ou três indicadores e tiver escolhido somente um calendário para trading, não é uma tarefa complicada. Mas o que você faria se tivesse cinco ou seis indicadores e sua estratégia de trading necessitasse considerar os sinais em vários prazos?
Base Teórica da Construção de Indicadores de cluster para FOREX Base Teórica da Construção de Indicadores de cluster para FOREX
Indicadores de cluster são conjuntos de indicadores que dividem pares de moedas correntes em moedas distintas. Os indicadores permitem traçar a flutuação da moeda corrente, determinar o potencial de formatação de novas tendências de moeda, receber sinais do mercado e seguir posições de médio e longo prazo.
Um Expert Advisor feito sob encomenda Manual para um operador Um Expert Advisor feito sob encomenda Manual para um operador
Nem todos os operadores são programadores. E nem todos programadores são realmente bons. Então, o que deve ser feito para você automatizar seu sistema sem ter tempo e vontade de estudar o MQL4?