Que testes deve passar o robô de negociação antes da publicação no Mercado

MetaQuotes | 10 agosto, 2016

Por que os produtos são verificados antes de serem publicados no Mercado

Todos os produtos do Mercado, antes de serem publicados, passam uma revisão preliminar obrigatória, visto que um pequeno erro na lógica do conselheiro ou indicador pode causar perdas na conta de negociação. É por isso que temos desenvolvido uma série de verificações básicas para assegurar o nível de qualidade exigido pelos produtos do Mercado.

Se, no processo de verificação do seu produto, os moderadores do Mercado identificarem erros, você terá que corrigi-los. Neste artigo vamos falar sobre os erros mais freqüentes cometido pelos desenvolvedores nos seus robôs de negociação e indicadores técnicos. Também recomendamos que você leia os seguintes artigos:


Como capturar rapidamente e corrigir erros no robô de negociação

O testador de estratégias embutido na plataforma permite não só verificar com base no histórico dos sistemas de negociação, mas também identificar erros lógicos e algorítmicos, ao escrever o robô de negociação. Durante o teste, todas as mensagens sobre as operações de negociação e erros detetados são exibidos no Diário do testador. Você pode analisar convenientemente essas mensagens no Visualizador de entradas, que é chamado a partir do menu de contexto.


Após o teste do conselheiro, abra o Visualizador e ative o modo "Apenas erros", como é exibido na imagem. Se no seu robô de negociação houver erros, você vai vê-los imediatamente. Se não tiverem sido encontrados erros da primeira vez, realize uma série de testes, mudando os instrumentos/timeframes/parâmetros de entrada e diferentes valores para o depósito inicial. 99% dos erros são identificados usando estas técnicas simples, saiba sobre eles neste artigo.

Para um estudo detalhado dos erros detetados, use, no MetaEditor, a Depuração no histórico, assim, no modo de teste visual, você poderá observar não só os gráficos de preços e valores dos indicadores utilizados, mas também seguir, em cada tick, os valores de cada variável do programa. Isso permite que você depure a sua negociação sem ter que perder toda uma semana no modo de tempo real.

Falta de fundos para negociar

Antes de enviar cada pedido de negociação, é necessário verificar a quantidade de fundos na sua conta. A falta de fundos para fornecer a seguinte posição aberta ou ordem é considerada um erro crasso.

Leve em conta que inclusive colocar uma ordem pendente pode exigir uma garantia, isto é, a margem.


Recomendamos testar o seu robô de negociação com um depósito inicial deliberadamente pequeno, por exemplo, 1 USD ou 1 Euro.

Se a verificação mostrar que os fundos para a realização da operação de negociação não são suficientes, será necessário, em vez de chamar a função OrderSend(), emitir, no diário, uma mensagem de erro. Exemplos de verificação:

MQL5

bool CheckMoneyForTrade(string symb,double lots,ENUM_ORDER_TYPE type)
  {
//--- obtemos o preço de abertura
   MqlTick mqltick;
   SymbolInfoTick(symb,mqltick);
   double price=mqltick.ask;
   if(type==ORDER_TYPE_SELL)
      price=mqltick.bid;
//--- valores da margem necessária e livre
   double margin,free_margin=AccountInfoDouble(ACCOUNT_MARGIN_FREE);
   //--- chamamos a função de verificação
   if(!OrderCalcMargin(type,symb,lots,price,margin))
     {
      //--- algo deu errado, informamos e retornamos false
      Print("Error in ",__FUNCTION__," code=",GetLastError());
      return(false);
     }
   //--- se não houver fundos suficientes para realizar a operação
   if(margin>free_margin)
     {
      //--- informamos sobre o erro e retornamos false
      Print("Not enough money for ",EnumToString(type)," ",lots," ",symb," Error code=",GetLastError());
      return(false);
     }
//--- a verificação foi realizada com sucesso
   return(true);
  }

MQL4

bool CheckMoneyForTrade(string symb, double lots,int type)
  {
   double free_margin=AccountFreeMarginCheck(symb,type,lots);
   //-- se não houver dinheiro suficiente
   if(free_margin<0)
     {
      string oper=(type==OP_BUY)? "Buy":"Sell";
      Print("Not enough money for ", oper," ",lots, " ", symb, " Error code=",GetLastError());
      return(false);
     }
   //--- a verificação foi realizada com sucesso
   return(true);
  }

Volumes incorretos nas operações de negociação

Antes de enviar os pedidos de negociação, é necessário verificar a validez dos volumes indicados na ordem. Antes de chamar a função OrderSend(), é preciso verificar a quantidade de lotes que vai indicar na ordem do conselheiro. Para instrumentos financeiros, nas Especificações, são indicados os volumes mínimo e máximo permitidos para negociar, bem como gradação do volume. Na MQL5, é possível obter estes valores a partir da enumeração ENUM_SYMBOL_INFO_DOUBLE usando a função SymbolInfoDouble()

SYMBOL_VOLUME_MIN

Volume mínimo para realização da transação

SYMBOL_VOLUME_MAX

Volume máximo para realização da transação

SYMBOL_VOLUME_STEP

Incremento mínimo de alteração do volume para o realização da transação

Exemplo de função para verificar a validez do volume

//+------------------------------------------------------------------+
//|  Verifica a validez do volume da ordem                           |
//+------------------------------------------------------------------+
bool CheckVolumeValue(double volume,string &description)
  {
//--- valor mínimo permitido para operações de negociação
   double min_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN);
   if(volume<min_volume)
     {
      description=StringFormat("Volume inferior ao mínimo permitido SYMBOL_VOLUME_MIN=%.2f",min_volume);
      return(false);
     }

//--- volume máximo permitido para operações de negociação
   double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX);
   if(volume>max_volume)
     {
      description=StringFormat("Volume superior ao máximo permitido SYMBOL_VOLUME_MAX=%.2f",max_volume);
      return(false);
     }

//--- obtemos a gradação mínima do volume
   double volume_step=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP);

   int ratio=(int)MathRound(volume/volume_step);
   if(MathAbs(ratio*volume_step-volume)>0.0000001)
     {
      description=StringFormat("O volume não é múltiplo da gradação mínima SYMBOL_VOLUME_STEP=%.2f, volume mais próximo do valido %.2f",
                               volume_step,ratio*volume_step);
      return(false);
     }
   description="Valor válido do volume";
   return(true);
  }


Restrição no número de ordens pendentes

Existe também uma restrição no número de ordens pendentes que podem ser colocadas, simultaneamente, nessa conta. Exemplo da função IsNewOrderAllowed(), ela verifica se é possível colocar mais uma ordem pendente.

//+------------------------------------------------------------------+
//| verifica se é possível colocar mais uma ordem pendente                    |
//+------------------------------------------------------------------+
bool IsNewOrderAllowed()
  {
//--- obtemos o número de ordens pendentes permitidas na conta
   int max_allowed_orders=(int)AccountInfoInteger(ACCOUNT_LIMIT_ORDERS);

//--- se não houver restrição, retornamos true, e poderemos enviar a ordem
   if(max_allowed_orders==0) return(true);

//--- se chegamos até esta linha, significa que existe restrição; ficamos sabendo quantas ordens foram colocadas
   int orders=OrdersTotal();

//--- retornamos o resultado da comparação
   return(orders<max_allowed_orders);
  }

A função é simples: na variável max_allowed_orders obtemos o número permitido de ordens pendentes, e se esse valor for diferente do zero, então comparamos com o número atual de ordens. Na verdade, com esta função não levamos em conta mais uma possibilidade de restrição, isto é, o conjunto do volume permitido da posição aberta e das ordens pendentes de acordo com um símbolo em concreto.


Restrição no número de lotes num símbolo

Para obter o tamanho do volume de uma posição aberta no símbolo definido, é necessário previamente selecionar a posição usando a função PositionSelect(). E só depois disso, pede-se o volume da posição selecionada usando a função PositionGetDouble(), que retorna as diferentes propriedades da posição selecionada e tem o tipo double.  Para obter o volume da posição no símbolo, escrevemos a função PositionVolume().

//+------------------------------------------------------------------+
//| Retorna o valor da posição no símbolo indicado                  |
//+------------------------------------------------------------------+
double PositionVolume(string symbol)
  {
//--- tentamos selecionar a posição segundo o símbolo
   bool selected=PositionSelect(symbol);
//--- a posição existe
   if(selected)
      //--- retornamos o volume da posição
      return(PositionGetDouble(POSITION_VOLUME));
   else
     {
      //--- informamos sobre a tentativa vã de selecionar a posição
      Print(__FUNCTION__," Não foi possível executar PositionSelect() para o símbolo ",
            symbol," Erro ",GetLastError());
      return(-1);
     }
  }

Para contas com cobertura, é preciso percorrer todas as posições nesse instrumento.

Antes de fazer o pedido de negociação, para a colocação da ordem pendente no símbolo, é necessário verificar a restrição do volume total para a posição aberta e ordens pendentes num símbolo, SYMBOL_VOLUME_LIMIT. Se não houver restrições, o volume para a ordem pendente não pode ultrapassar o o volume máximo definido, que pode ser obtido usando a função SymbolInfoDouble().

double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_LIMIT);
if(max_volume==0) volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX);

Tal abordagem não tem em conta o volume nas atuais ordens pendentes no símbolo indicado. Pomos um exemplo para a função que calcula este valor:

//+------------------------------------------------------------------+
//|  retorna o volume das ordens pendentes de acordo com o símbolo   |
//+------------------------------------------------------------------+
double   PendingsVolume(string symbol)
  {
   double volume_on_symbol=0;
   ulong ticket;
//---  obtemos o número de todas as ordens ativas em todos os símbolos
   int all_orders=OrdersTotal();

//--- passamos no ciclo de todas as ordens
   for(int i=0;i<all_orders;i++)
     {
      //--- obtemos o bilhete da ordem segundo sua posição na lista
      if(ticket=OrderGetTicket(i))
        {
         //--- se na ordem especificado nosso símbolo, adicionamos o volume dessa ordem
         if(symbol==OrderGetString(ORDER_SYMBOL))
            volume_on_symbol+=OrderGetDouble(ORDER_VOLUME_INITIAL);
        }
     }
//--- retornamos o volume total das ordens pendentes atuais para o símbolo especificado
   return(volume_on_symbol);
  }

Tendo em conta o volume da posição aberta e o volume das ordens pendentes, a verificação final terá a seguinte aparência:

//+------------------------------------------------------------------+
//|  Retorna o valor máximo permitido para a ordem no símbolo        |
//+------------------------------------------------------------------+
double NewOrderAllowedVolume(string symbol)
  {
   double allowed_volume=0;
//--- obtemos a restrição do volume máximo na ordem
   double symbol_max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX);
//--- obtemos a restrição do volume no símbolo
   double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_LIMIT);

//--- obtemos o volume da posição aberta no símbolo
   double opened_volume=PositionVolume(symbol);
   if(opened_volume>=0)
     {
      // --- se nós esgotamos o volume
      if(max_volume-opened_volume<=0)
         return(0);

      //--- o volume da posição aberta não excede o max_volume
      double orders_volume_on_symbol=PendingsVolume(symbol);
      allowed_volume=max_volume-opened_volume-orders_volume_on_symbol;
      if(allowed_volume>symbol_max_volume) allowed_volume=symbol_max_volume;
     }
   return(allowed_volume);
  }


Definindo os níveis TakeProfit e StopLoss nos limites do nível mínimo SYMBOL_TRADE_STOPS_LEVEL

Muitos conselheiros negociam com ordens TakeProfit e StopLoss, cujos níveis são calculados dinamicamente no momento da compra ou venda. A ordem TakeProfit serve para fechar a posição, quando o movimento do preço vai numa direção favorável, enquanto a ordem StopLoss é usada para restringir as perdas, quando o movimento do preço vai numa direção desfavorável

Portanto, é necessário comparar os níveis TakeProfit e StopLoss com o preço atual, com ajuda dele é possível negociar na direção oposta:

  • A compra é realizada segundo o preço Ask, aqui é preciso comparar os níveis TakeProfit e StopLoss com o atual preço de venda Bid.
  • A venda realiza-se segundo o preço Bid, aqui é preciso comparar os níveis TakeProfit e StopLoss com o atual preço de compra Ask.
A compra acontece segundo o preço Ask
A venda acontece segundo o preço Bid
TakeProfit >= Bid
StopLoss <= Bid
TakeProfit <= Ask
StopLoss >= Ask



Para instrumentos financeiros, nas configurações do símbolo, é possível definir o parâmetro SYMBOL_TRADE_STOPS_LEVEL. Ele indica em pontos o recuo mínimo dos níveis StopLoss e TakeProfit a partir do preço atual para fechar a posição aberta. Se o valor dessa propriedade for igual a zero, isso significa que não foi estabelecido o recuo mínimo para ordens SL/TP ao comprar.

Em geral, a verificação dos níveis TakeProfit e StopLoss tendo em conta a distância mínima SYMBOL_TRADE_STOPS_LEVEL tem a seguinte aparência:

  • A compra é realizada segundo o preço Ask, aqui os níveis TakeProfit e StopLoss devem estar a uma distância maior do que SYMBOL_TRADE_STOPS_LEVEL pontos a partir do preço atual de compra Bid.
  • A venda é realizada segundo o preço Bid, aqui os níveis TakeProfit e StopLoss devem estar a uma distância maior do que SYMBOL_TRADE_STOPS_LEVEL pontos a partir do preço atual de venda Ask.
A compra acontece segundo o preço Ask
A venda acontece segundo o preço Bid
TakeProfit - Bid >= SYMBOL_TRADE_STOPS_LEVEL
Bid - StopLoss >= SYMBOL_TRADE_STOPS_LEVEL
Ask - TakeProfit >= SYMBOL_TRADE_STOPS_LEVEL
StopLoss - Ask >= SYMBOL_TRADE_STOPS_LEVEL

É por isso que é possível criar uma função de verificação CheckStopLoss_Takeprofit(), ela exige que a distância entre o TakeProfit/StopLoss e o preço de fechamento seja maior do que SYMBOL_TRADE_STOPS_LEVEL pontos:

bool CheckStopLoss_Takeprofit(ENUM_ORDER_TYPE type,double SL,double TP)
  {
//--- obtemos o nível SYMBOL_TRADE_STOPS_LEVEL
   int stops_level=(int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL);
   if(stops_level!=0)
     {
      PrintFormat("SYMBOL_TRADE_STOPS_LEVEL=%d: StopLoss e TakeProfit devem ser"+
                  " pelo menos %d pontos a partir do preço de fechamento",stops_level,stops_level);
     }
//---
   bool SL_check=false,TP_check=false;
//--- verificamos apenas dos tipos de ordens
   switch(type)
     {
      //--- operação de compra
      case  ORDER_TYPE_BUY:
        {
         //--- verificamos o StopLoss
         SL_check=(Bid-SL>stops_level*_Point);
         if(!SL_check)
            PrintFormat("For order %s StopLoss=%.5f must be less than %.5f"+
                        " (Bid=%.5f - SYMBOL_TRADE_STOPS_LEVEL=%d pontos)",
                        EnumToString(type),SL,Bid-stops_level*_Point,Bid,stops_level);
         //--- verificamos o TakeProfit
         TP_check=(TP-Bid>stops_level*_Point);
         if(!TP_check)
            PrintFormat("For order %s TakeProfit=%.5f must be greater than %.5f"+
                        " (Bid=%.5f + SYMBOL_TRADE_STOPS_LEVEL=%d pontos)",
                        EnumToString(type),TP,Bid+stops_level*_Point,Bid,stops_level);
         //--- retornamos o resultado da verificação
         return(SL_check&&TP_check);
        }
      //--- operação de venda
      case  ORDER_TYPE_SELL:
        {
         //--- verificamos o StopLoss
         SL_check=(SL-Ask>stops_level*_Point);
         if(!SL_check)
            PrintFormat("For order %s StopLoss=%.5f must be greater than %.5f "+
                        " (Ask=%.5f + SYMBOL_TRADE_STOPS_LEVEL=%d пунктов)",
                        EnumToString(type),SL,Ask+stops_level*_Point,Ask,stops_level);
         //--- verificamos o TakeProfit
         TP_check=(Ask-TP>stops_level*_Point);
         if(!TP_check)
            PrintFormat("For order %s TakeProfit=%.5f must be less than %.5f "+
                        " (Ask=%.5f - SYMBOL_TRADE_STOPS_LEVEL=%d pontos)",
                        EnumToString(type),TP,Ask-stops_level*_Point,Ask,stops_level);
         //--- retornamos o resultado da verificação
         return(TP_check&&SL_check);
        }
      break;
     }
//--- para ordens pendentes é necessário utilizar uma função ligeiramente diferente
   return false;
  }

A verificação pode ter esta aparência:

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- obtemos de modo aleatório o tipo de operação
   int oper=(int)(GetTickCount()%2); // o resto da divisão por dois é sempre 0 ou 1
   switch(oper)
     {
      //--- compramos
      case  0:
        {
         //--- obtemos o preço de abertura e definimos deliberadamente os TP/SL incorretos
         double price=Ask;
         double SL=NormalizeDouble(Bid+2*_Point,_Digits);
         double TP=NormalizeDouble(Bid-2*_Point,_Digits);
         //--- fazemos a verificação
         PrintFormat("Buy at %.5f   SL=%.5f   TP=%.5f  Bid=%.5f",price,SL,TP,Bid);
         if(!CheckStopLoss_Takeprofit(ORDER_TYPE_BUY,SL,TP))
            Print("Nível StopLoss ou TakeProfit inválido!");
         //--- de qualquer modo, só tentamos comprar para ver o resultado da execução
         Buy(price,SL,TP);
        }
      break;
      //--- vendemos
      case  1:
        {
         //--- obtemos o preço de abertura e definimos deliberadamente os TP/SL incorretos
         double price=Bid;
         double SL=NormalizeDouble(Ask-2*_Point,_Digits);
         double TP=NormalizeDouble(Ask+2*_Point,_Digits);
         //--- fazemos a verificação
         PrintFormat("Sell at %.5f   SL=%.5f   TP=%.5f  Ask=%.5f",price,SL,TP,Ask);
         if(!CheckStopLoss_Takeprofit(ORDER_TYPE_SELL,SL,TP))
            Print("Nível StopLoss ou TakeProfit inválido!");
         //--- de qualquer modo, só tentamos vender para ver o resultado da execução
         Sell(price,SL,TP);
        }
      break;
      //---
     }
  }

Encontre outro exemplo de função nos scripts anexados Check_TP_and_SL.mq4 e Check_TP_and_SL.mq5. Exemplo de execução:

MQL5
Check_TP_and_SL (EURUSD,H1) Buy at 1.11433   SL=1.11425   TP=1.11421  Bid=1.11423
Check_TP_and_SL (EURUSD,H1) SYMBOL_TRADE_STOPS_LEVEL=30: StopLoss e TakeProfit não devem ser mais próximos do que 30 pontos a partir do preço de fechamento
Check_TP_and_SL (EURUSD,H1) Para a ordem ORDER_TYPE_BUY StopLoss=1.11425 deve ser inferior a 1.11393 (Bid=1.11423 - SYMBOL_TRADE_STOPS_LEVEL=30 pontos)
Check_TP_and_SL (EURUSD,H1) Para a ordem ORDER_TYPE_BUY TakeProfit=1.11421 deve ser superior a 1.11453 (Bid=1.11423 + SYMBOL_TRADE_STOPS_LEVEL=30 pontos)
Check_TP_and_SL (EURUSD,H1) Nível StopLoss ou TakeProfit inválido!
Check_TP_and_SL (EURUSD,H1) OrderSend error 4756
Check_TP_and_SL (EURUSD,H1) retcode=10016  deal=0  order=0
MQL4
Check_TP_and_SL EURUSD,H1:  Sell at 1.11430   SL=1.11445   TP=1.11449  Ask=1.11447
Check_TP_and_SL EURUSD,H1:  SYMBOL_TRADE_STOPS_LEVEL=1: o StopLoss e TakeProfit não devem ser mais próximos do que 1 pontos a partir do preço de fechamento
Check_TP_and_SL EURUSD,H1:  Para a ordem ORDER_TYPE_SELL StopLoss=1.11445 deve ser superior a 1.11448  (Ask=1.11447 + SYMBOL_TRADE_STOPS_LEVEL=1 pontos)
Check_TP_and_SL EURUSD,H1:  Para a ordem ORDER_TYPE_SELL TakeProfit=1.11449 deve ser inferior a 1.11446  (Ask=1.11447 - SYMBOL_TRADE_STOPS_LEVEL=1 pontos)
Check_TP_and_SL EURUSD,H1:  Nível StopLoss ou TakeProfit inválido!
Check_TP_and_SL EURUSD,H1:  OrderSend error 130 

Para simular a situação com os valores inválidos TakeProfit e StopLoss, ao artigo foram anexados os conselheirosTest_Wrong_TakeProfit_LEVEL.mq5 e Test_Wrong_StopLoss_LEVEL.mq5. Apenas é possível executá-los na conta demo. Estude estes exemplos para ver por si mesmo sob quais condições é possível realizar uma compra com sucesso.

Exemplo de execução do conselheiro Test_Wrong_StopLoss_LEVEL.mq5:

Test_Wrong_StopLoss_LEVEL.mq5
Point=0.00001 Digits=5
SYMBOL_TRADE_EXECUTION=SYMBOL_TRADE_EXECUTION_INSTANT
SYMBOL_TRADE_FREEZE_LEVEL=20: está proibido alterar a ordem ou posição, se ficarem 20 pontos antes do preço de ativação
SYMBOL_TRADE_STOPS_LEVEL=30: StopLoss e TakeProfit não devem ser mais próximos do que 30 pontos a partir do preço de fechamento
1. Buy 1.0 EURUSD at 1.11442 SL=1.11404 Bid=1.11430 ( StopLoss-Bid=-26 points ))
CTrade::OrderSend: instant buy 1.00 EURUSD at 1.11442 sl: 1.11404 [invalid stops]
2. Buy 1.0 EURUSD at 1.11442 SL=1.11404 Bid=1.11431 ( StopLoss-Bid=-27 points ))
CTrade::OrderSend: instant buy 1.00 EURUSD at 1.11442 sl: 1.11404 [invalid stops]
3. Buy 1.0 EURUSD at 1.11442 SL=1.11402 Bid=1.11430 ( StopLoss-Bid=-28 points ))
CTrade::OrderSend: instant buy 1.00 EURUSD at 1.11442 sl: 1.11402 [invalid stops]
4. Buy 1.0 EURUSD at 1.11440 SL=1.11399 Bid=1.11428 ( StopLoss-Bid=-29 points ))
CTrade::OrderSend: instant buy 1.00 EURUSD at 1.11440 sl: 1.11399 [invalid stops]
5. Buy 1.0 EURUSD at 1.11439 SL=1.11398 Bid=1.11428 ( StopLoss-Bid=-30 points ))
Buy 1.0 EURUSD done at 1.11439 with StopLoss=41 points (spread=12 + SYMBOL_TRADE_STOPS_LEVEL=30)

Exemplo de execução do conselheiro Test_Wrong_TakeProfit_LEVEL.mq5:

Test_Wrong_TakeProfit_LEVEL.mq5
Point=0.00001 Digits=5
SYMBOL_TRADE_EXECUTION=SYMBOL_TRADE_EXECUTION_INSTANT
SYMBOL_TRADE_FREEZE_LEVEL=20: está proibido alterar a ordem ou posição, se ficarem 20 pontos antes do preço de ativação
SYMBOL_TRADE_STOPS_LEVEL=30: StopLoss e TakeProfit não devem ser mais próximos do que 30 pontos a partir do preço de fechamento
1. Buy 1.0 EURUSD at 1.11461 TP=1.11478 Bid=1.11452 (TakeProfit-Bid=26 points)
CTrade::OrderSend: instant buy 1.00 EURUSD at 1.11461 tp: 1.11478 [invalid stops]
2. Buy 1.0 EURUSD at 1.11461 TP=1.11479 Bid=1.11452 (TakeProfit-Bid=27 points)
CTrade::OrderSend: instant buy 1.00 EURUSD at 1.11461 tp: 1.11479 [invalid stops]
3. Buy 1.0 EURUSD at 1.11461 TP=1.11480 Bid=1.11452 (TakeProfit-Bid=28 points)
CTrade::OrderSend: instant buy 1.00 EURUSD at 1.11461 tp: 1.11480 [invalid stops]
4. Buy 1.0 EURUSD at 1.11461 TP=1.11481 Bid=1.11452 (TakeProfit-Bid=29 points)
CTrade::OrderSend: instant buy 1.00 EURUSD at 1.11461 tp: 1.11481 [invalid stops]
5. Buy 1.0 EURUSD at 1.11462 TP=1.11482 Bid=1.11452 (TakeProfit-Bid=30 points)
Buy 1.0 EURUSD done at 1.11462 with TakeProfit=20 points (SYMBOL_TRADE_STOPS_LEVEL=30 - spread=10)

A verificação dos níveis StopLoss e TakeProfit nas ordens pendentes é muito mais simples, eles devem ser definidos com base no preço de abertura da ordem. Isto é, a verificação dos níveis tendo em conta a distância mínima SYMBOL_TRADE_STOPS_LEVEL tem a seguinte aparência: os níveis TakeProfit e StopLoss devem estar a uma distância maior do que SYMBOL_TRADE_STOPS_LEVEL pontos a partir do preço de ativação da ordem.

BuyLimit и BuyStop
SellLimit и SellStop
TakeProfit - Open >= SYMBOL_TRADE_STOPS_LEVEL
Open - StopLoss >= SYMBOL_TRADE_STOPS_LEVEL
Open - TakeProfit >= SYMBOL_TRADE_STOPS_LEVEL
StopLoss - Open >= SYMBOL_TRADE_STOPS_LEVEL

No conselheiro Test_StopLoss_Level_in_PendingOrders.mq5 são feitas uma série de tentativas de instalar as ordens BuyStop e BuyLimt até que a operação seja bem sucedida. A cada tentativa, o nível StopLoss ou TakeProfit desloca-se 1 ponto na direção adequada. Exemplo de execução deste conselheiro:

Test_StopLoss_Level_in_PendingOrders.mq5
SYMBOL_TRADE_EXECUTION=SYMBOL_TRADE_EXECUTION_INSTANT
SYMBOL_TRADE_FREEZE_LEVEL=20: está proibido alterar a ordem ou posição, se ficarem 20 pontos antes do preço de ativação
SYMBOL_TRADE_STOPS_LEVEL=30: o StopLoss e TakeProfit não devem estar mais próximos do que 30 pontos a partir do preço de fechamento
1. BuyStop 1.0 EURUSD at 1.11019 SL=1.10993 (Open-StopLoss=26 points)
CTrade::OrderSend: buy stop 1.00 EURUSD at 1.11019 sl: 1.10993 [invalid stops]
2. BuyStop 1.0 EURUSD at 1.11019 SL=1.10992 (Open-StopLoss=27 points)
CTrade::OrderSend: buy stop 1.00 EURUSD at 1.11019 sl: 1.10992 [invalid stops]
3. BuyStop 1.0 EURUSD at 1.11020 SL=1.10992 (Open-StopLoss=28 points)
CTrade::OrderSend: buy stop 1.00 EURUSD at 1.11020 sl: 1.10992 [invalid stops]
4. BuyStop 1.0 EURUSD at 1.11021 SL=1.10992 (Open-StopLoss=29 points)
CTrade::OrderSend: buy stop 1.00 EURUSD at 1.11021 sl: 1.10992 [invalid stops]
5. BuyStop 1.0 EURUSD at 1.11021 SL=1.10991 (Open-StopLoss=30 points)
BuyStop 1.0 EURUSD done at 1.11021 with StopLoss=1.10991 (SYMBOL_TRADE_STOPS_LEVEL=30)
 --------- 
1. BuyLimit 1.0 EURUSD at 1.10621 TP=1.10647 (TakeProfit-Open=26 points)
CTrade::OrderSend: buy limit 1.00 EURUSD at 1.10621 tp: 1.10647 [invalid stops]
2. BuyLimit 1.0 EURUSD at 1.10621 TP=1.10648 (TakeProfit-Open=27 points)
CTrade::OrderSend: buy limit 1.00 EURUSD at 1.10621 tp: 1.10648 [invalid stops]
3. BuyLimit 1.0 EURUSD at 1.10621 TP=1.10649 (TakeProfit-Open=28 points)
CTrade::OrderSend: buy limit 1.00 EURUSD at 1.10621 tp: 1.10649 [invalid stops]
4. BuyLimit 1.0 EURUSD at 1.10619 TP=1.10648 (TakeProfit-Open=29 points)
CTrade::OrderSend: buy limit 1.00 EURUSD at 1.10619 tp: 1.10648 [invalid stops]
5. BuyLimit 1.0 EURUSD at 1.10619 TP=1.10649 (TakeProfit-Open=30 points)
BuyLimit 1.0 EURUSD done at 1.10619 with TakeProfit=1.10649 (SYMBOL_TRADE_STOPS_LEVEL=30)

Os exemplos de verificação dos níveis TakeProfit e StopLoss nas ordens pendentes estão localizados nos códigos fonte anexados: Check_TP_and_SL.mq4 e Check_TP_and_SL.mq5.


Tentativa de modificar a ordem ou posição nos limites do nível do congelamento SYMBOL_TRADE_FREEZE_LEVEL

Nas configurações do símbolo, é possível definir o parâmetro SYMBOL_TRADE_FREEZE_LEVEL, ele mostra em pontos a distância de congelamento das operações de negociação para ordens pendentes e posições abertas. Por exemplo, se a negociação no instrumento financeiro é reencaminhada para o processamento num sistema de negociação externo, então, a ordem pendente BuyLimit pode, nesse momento, se localizar muito perto do preço Ask atual. E se nós enviarmos o pedido para alterar esta ordem quando o preço de abertura estiver suficientemente perto do preço Ask, então, pode suceder que a ordem seja executada e a modificação seja impossível.

Por isso, para ordens pendentes e posições abertas, nas configurações do símbolo, você pode indicar a distância de congelamento, em cujos limites é impossível de modificar. Em geral, antes de tentar enviar o pedido para realizar a modificação, é necessário levar a cabo uma verificação usando SYMBOL_TRADE_FREEZE_LEVEL:

Tipo de ordem/posição
Ativação segundo o preço
Verificação
Ordem Buy Limit
 Ask
Ask-OpenPrice >= SYMBOL_TRADE_FREEZE_LEVEL
Ordem Buy Stop AskOpenPrice-Ask >= SYMBOL_TRADE_FREEZE_LEVEL
Ordem Sell Limit BidOpenPrice-Bid >= SYMBOL_TRADE_FREEZE_LEVEL
Ordem Sell Stop BidBid-OpenPrice >= SYMBOL_TRADE_FREEZE_LEVEL
Posição Buy
 BidTakeProfit-Bid >= SYMBOL_TRADE_FREEZE_LEVEL
Bid-StopLoss >= SYMBOL_TRADE_FREEZE_LEVEL
Posição Sell
 AskAsk-TakeProfit >= SYMBOL_TRADE_FREEZE_LEVEL
StopLoss-Ask >= SYMBOL_TRADE_FREEZE_LEVEL

Nos scripts anexados Check_FreezeLevel.mq5 e Check_FreezeLevel.mq4, você pode encontrar exemplos completos das funções para verificação de ordens e posições para o nível SYMBOL_TRADE_FREEZE_LEVEL.

//--- verificamos os tipos de ordem
   switch(type)
     {
      //--- ordem pendente BuyLimit
      case  ORDER_TYPE_BUY_LIMIT:
        {
         //--- verificamos a distância a partir do preço de abertura para o preço de ativação
         check=((Ask-price)>freeze_level*_Point);
         if(!check)
            PrintFormat("Ordem %s #%d é impossível modificar: Ask-Open=%d pontos < SYMBOL_TRADE_FREEZE_LEVEL=%d pontos",
                        EnumToString(type),ticket,(int)((Ask-price)/_Point),freeze_level);
         return(check);
        }
      //--- ordem pendente BuyLimit
      case  ORDER_TYPE_SELL_LIMIT:
        {
         //--- verificamos a distância a partir do preço de abertura para o preço de ativação
         check=((price-Bid)>freeze_level*_Point);
         if(!check)
            PrintFormat("Ordem %s #%d é impossível modificar: Open-Bid=%d pontos < SYMBOL_TRADE_FREEZE_LEVEL=%d pontos",
                        EnumToString(type),ticket,(int)((price-Bid)/_Point),freeze_level);
         return(check);
        }
      break;
      //--- ordem pendente BuyStop
      case  ORDER_TYPE_BUY_STOP:
        {
         //--- verificamos a distância a partir do preço de abertura para o preço de ativação
         check=((price-Ask)>freeze_level*_Point);
         if(!check)
            PrintFormat("Ordem %s #%d é impossível modificar: Ask-Open=%d pontos < SYMBOL_TRADE_FREEZE_LEVEL=%d pontos",
                        EnumToString(type),ticket,(int)((price-Ask)/_Point),freeze_level);
         return(check);
        }
      //--- ordem pendente SellStop
      case  ORDER_TYPE_SELL_STOP:
        {
         //--- verificamos a distância a partir do preço de abertura para o preço de ativação
         check=((Bid-price)>freeze_level*_Point);
         if(!check)
            PrintFormat("Ordem %s #%d é impossível modificar: Bid-Open=%d pontos < SYMBOL_TRADE_FREEZE_LEVEL=%d pontos",
                        EnumToString(type),ticket,(int)((Bid-price)/_Point),freeze_level);
         return(check);
        }
      break;
     }

Você pode simular por si mesmo a situação onde se tenta modificar uma ordem pendente dentro do nível de congelamento. Para fazer isso, abra uma conta demo na qual haja instrumentos financeiros com um nível zero SYMBOL_TRADE_FREEZE_LEVEL, execute o conselheiro Test_SYMBOL_TRADE_FREEZE_LEVEL.mq5 (Test_SYMBOL_TRADE_FREEZE_LEVEL.mq4) dentro do gráfico e instale manualmente qualquer ordem pendente. O próprio conselheiro aproxima a ordem ao preço do mercado tanto quanto for possível e começa a fazer tentativas proibidas de modificação. Isso irá executar o som usando a função PlaySound().


Erros que ocorrem quando se trabalha com símbolos nos quais o histórico de cotações é insuficiente

Se conselheiro ou indicador funciona num gráfico com um histórico em falta, então há duas opções:

  1. o programa verifica a presença do histórico exigido em toda a profundidade necessária. Se as barras disponíveis estiverem abaixo do exigido, o programa solicitará os dados ausentes e completará o seu trabalho antes da chegada do próximo tick. Este caminho é o mais correto e ajuda a evitar muitos erros, a saber: sair da matriz ou dividir por zero;
  2. o programa não faz verificação nenhuma e começa imediatamente o seu trabalho, como se todo o histórico, necessário para todos os símbolos e os timeframes, exigidos estivesse disponível mediante a primeira solicitação. Esta abordagem está cheia de erros imprevisíveis.

Você pode tentar por si mesmo simular essa situação. Para fazer isso, execute o indicador ou conselheiro que deseja testar no gráfico, feche o terminal e remova todo o histórico, finalmente, reinicie o terminal. Se, após essa reinicialização, não aparecerem erros nos Diários, em seguida, comece a mudar os símbolos e timeframes nos gráficos, nos quais o programa está em execução. Muitos dos indicadores dão erros ao executar nos timeframes semanais ou mensais para os quais o número de barras é geralmente limitada. Além disso, durante uma mudança brusca do símbolo do gráfico, por exemplo, de EURUSD para CADJPY, o indicador ou conselheiro executado no gráfico pode dar um erro causado pela falta do pagamento exigido para calcular o seu histórico.


Saída além dos limites da matriz (array out of range)

Ao trabalhar com matrizes, o acesso aos seus elementos é realizado de acordo com o número do índice, ele não pode ser negativo e deve ser menor do que o tamanho da matriz. Você pode obter o número da matriz usando a função ArraySize().

Este erro pode ocorrer ao trabalhar com a matriz dinâmica, quando o seu tamanho ainda não está definido claramente pela função ArrayResize(), ou ao utilizar essa matriz nas funções que definem de forma independe o tamanho das matrizes dinâmicas transferidas por ele. Por exemplo, a função CopyTicks() tenta armazenar na matriz a quantidade solicitada de ticks, mas, se houver menos ticks do que solicitou, o tamanho da matriz obtida será menor do que o esperado.

Outra forma, muito provável, para obter este erro, consiste numa tentativa de acessar os dados do buffer de indicador, quando o seu tamanho ainda não foi inicializado. Lembramos que os buffers de indicador são matrizes dinâmicos, e os seus tamanhos são estabelecidos pelo sistema de execução do terminal apenas após a inicialização do gráfico. Por tanto, por exemplo, a tentativa de acessar os dados desse buffer, na função OnInit(), causará o erro "array out of range".


Anexamos ao arquivo Test_Out_of_range.mq5 um exemplo simples do indicador que dá esse erro,


Divisão por zero (zero divide)

Outro erro crítico consiste em tentar dividir por zero. Neste caso, o programa pára imediatamente, o testador exibe no Diário o nome da função e o número da linha no código fonte onde ocorreu o erro.


Geralmente, a divisão por zero ocorre devido a uma situação imprevista pelo programador, por exemplo, receber uma propriedade ou cálculo da expressão com dados "ruins".

Você pode facilmente dividir por zero usando o conselheiro simples TestZeroDivide.mq5, cujo código fonte é exibido na capturas de tela. utro erro crítico consiste em usar um ponteiro do objeto inválido. A Depuração no histórico será útil para determinar a causa desse erro.


Envio de um pedido para modificar os níveis sem serem alterados na realidade

Se, de acordo com as regras do sistema de negociação, for necessário modificar as ordens pendentes ou as posições abertas, antes de enviar o pedido de negociação para realizar a transação, você deverá garantir que a operação solicitada realmente alterará os parâmetros da ordem ou posição. O envio do pedido de negociação que na verdade não faz alteração nenhuma é considerado como um erro. O servidor de negociação, em resposta a esta ação, retornará o código de resposta TRADE_RETCODE_NO_CHANGES=10025 (MQL5) ou o código ERR_NO_RESULT=1 (MQL4)

O exemplo de verificação para MQL5 está no script Check_OrderLevels.mq5:

//--- classe para negociação
#include <Trade\Trade.mqh>
CTrade trade;
#include <Trade\Trade.mqh>
//--- classe para trabalhar com ordens
#include <Trade\OrderInfo.mqh>
COrderInfo orderinfo;
//--- classe para trabalhar com posições
#include <Trade\PositionInfo.mqh>
CPositionInfo positioninfo;
//+------------------------------------------------------------------+
//| verificação de novos valores de níveis antes da modificação da ordem |
//+------------------------------------------------------------------+
bool OrderModifyCheck(ulong ticket,double price,double sl,double tp)
  {
//--- selecionamos uma ordem por bilhete
   if(orderinfo.Select(ticket))
     {
      //--- O tamanho do ponto e nome do símbolo pelo qual foi colocada ordem pendente
      string symbol=orderinfo.Symbol();
      double point=SymbolInfoDouble(symbol,SYMBOL_POINT);
      int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
      //--- verificamos se há mudança no preço de abertura
      bool PriceOpenChanged=(MathAbs(orderinfo.PriceOpen()-price)>point);
      //--- verificamos se há mudança no nível StopLoss
      bool StopLossChanged=(MathAbs(orderinfo.StopLoss()-sl)>point);
      //--- verificamos se há mudança no nível Takeprofit
      bool TakeProfitChanged=(MathAbs(orderinfo.TakeProfit()-tp)>point);
      //--- se houver quaisquer alterações nos níveis
      if(PriceOpenChanged || StopLossChanged || TakeProfitChanged)
         return(true);  // é possível modificar a ordem      
      //--- não há alterações nos níveis de abertura, StopLoss e Takeprofit
      else
      //--- informamos sobre o erro
         PrintFormat("A ordem #%d já tem níveis Open=%.5f SL=%.5f TP=%.5f",
                     ticket,orderinfo.PriceOpen(),orderinfo.StopLoss(),orderinfo.TakeProfit());
     }
//--- chegamos ao fim, não há alterações para a ordem
   return(false);       // não faz sentido nenhum modificar 
  }
//+------------------------------------------------------------------+
//| verificação de novos valores de níveis antes da modificação da ordem |
//+------------------------------------------------------------------+
bool PositionModifyCheck(ulong ticket,double sl,double tp)
  {
//--- selecionamos uma ordem por bilhete
   if(positioninfo.SelectByTicket(ticket))
     {
      //--- O tamanho do ponto e nome do símbolo pelo qual foi colocada ordem pendente
      string symbol=positioninfo.Symbol();
      double point=SymbolInfoDouble(symbol,SYMBOL_POINT);
      //--- verificamos se há mudança no nível StopLoss
      bool StopLossChanged=(MathAbs(positioninfo.StopLoss()-sl)>point);
      //--- verificamos se há mudança no nível Takeprofit
      bool TakeProfitChanged=(MathAbs(OrderTakeProfit()-tp)>point);
      //--- se houver quaisquer alterações nos níveis
      if(StopLossChanged || TakeProfitChanged)
         return(true);  // é possível modificar a ordem      
      //--- não há alterações nos níveis StopLoss e Takeprofit
      else
      //--- informamos sobre o erro
         PrintFormat("A ordem #%d já tem níveis Open=%.5f SL=%.5f TP=%.5f",
                     ticket,orderinfo.PriceOpen(),orderinfo.StopLoss(),orderinfo.TakeProfit());
     }
//--- chegamos ao fim, não há alterações para a ordem
   return(false);       // não faz sentido nenhum modificar 
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- níveis de preços para ordens e posições
   double priceopen,stoploss,takeprofit;
//--- bilhete da ordem atual e posições
   ulong orderticket,positionticket;
/*
   ... obtemos o bilhete da ordem e novos níveis StopLoss/TakeProfit/Priceopen
*/
//--- verificamos os níveis antes da modificação da ordem pendente   
   if(OrderModifyCheck(orderticket,priceopen,stoploss,takeprofit))
     {
      //--- a verificação foi realizada com sucesso
      trade.OrderModify(orderticket,priceopen,stoploss,takeprofit,
                        orderinfo.TypeTime(),orderinfo.TimeExpiration());
     }
/*
   ... obtemos o bilhete da ordem e novos níveis StopLoss/TakeProfit
*/
//--- verificamos os níveis antes da modificação da posição
   if(PositionModifyCheck(positionticket,stoploss,takeprofit))
     {
      //--- a verificação foi realizada com sucesso
      trade.PositionModify(positionticket,stoploss,takeprofit);
     }
//---
  }

O exemplo de verificação na linguagem MQL5 está no script Check_OrderLevels.mq4:

#property strict
//+------------------------------------------------------------------+
//| verificação de novos valores de níveis antes da modificação da ordem |
//+------------------------------------------------------------------+
bool OrderModifyCheck(int ticket,double price,double sl,double tp)
  {
//--- selecionamos uma ordem por bilhete
   if(OrderSelect(ticket,SELECT_BY_TICKET))
     {
      //--- O tamanho do ponto e nome do símbolo pelo qual foi colocada ordem pendente
      string symbol=OrderSymbol();
      double point=SymbolInfoDouble(symbol,SYMBOL_POINT);
      //--- verificamos se há mudança no preço de abertura
      bool PriceOpenChanged=true;
      int type=OrderType();
      if(!(type==OP_BUY || type==OP_SELL))
        {
         PriceOpenChanged=(MathAbs(OrderOpenPrice()-price)>point);
        }
      //--- verificamos se há mudança no nível StopLoss
      bool StopLossChanged=(MathAbs(OrderStopLoss()-sl)>point);
      //--- verificamos se há mudança no nível Takeprofit
      bool TakeProfitChanged=(MathAbs(OrderTakeProfit()-tp)>point);
      //--- se houver quaisquer alterações nos níveis
      if(PriceOpenChanged || StopLossChanged || TakeProfitChanged)
         return(true);  // é possível modificar a ordem      
      //--- não há alterações nos níveis de abertura, StopLoss e Takeprofit
      else
      //--- informamos sobre o erro
         PrintFormat("A ordem #%d já tem níveis Open=%.5f SL=%.5f TP=%.5f",
                     ticket,OrderOpenPrice(),OrderStopLoss(),OrderTakeProfit());
     }
//--- chegamos ao fim, não há alterações para a ordem
   return(false);       // não faz sentido nenhum modificar 
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- níveis de preços para ordens e posições
   double priceopen,stoploss,takeprofit;
//--- bilhete da ordem atual 
   int orderticket;
/*
   ... obtemos o bilhete da ordem e novos níveis StopLoss/TakeProfit/Priceopen
*/
//--- verificamos os níveis antes da modificação da ordem   
   if(OrderModifyCheck(orderticket,priceopen,stoploss,takeprofit))
     {
      //--- a verificação foi realizada com sucesso
      OrderModify(orderticket,priceopen,stoploss,takeprofit,OrderExpiration());
     }
  }

Também recomendamos ler o artigo:

  1. Como facilitar a detecção e recuperação de erros no código do expert advisor
  2. Como desenvolver no mql4 um robô de negociação seguro e confiável


Tentativa de importar arquivos compilados (mesmo EX4/EX5) e DLL

Os programas distribuídos através do Mercado devem ter uma garantia de segurança para os usuários. Por isso, qualquer tentativa de usar o DLL ou as funções de arquivos compilados EX4/EX5 é considerada como erro. Esse tipo de produtos não são publicados no Mercado.

Se o seu programa precisar indicadores adicionais que não estão à disposição, utilize os Recursos.


Acessando indicadores personalizados via iCustom()

Se o seu programa necessitar acessar os dados de um indicador personalizado, você precisará colocar todos os indicadores nos Recursos. Os produtos do Mercado devem estar preparados para trabalhar em qualquer ambiente despreparado, é por isso que devem conter tudo que você precisa no seu arquivo EX4/EX5. Artigos relacionados recomendados:


Passando um parâmetro inválido para a função (erro de tempo de execução)

Este tipo de erro é raro, muitos deles têm códigos prontos projetados para ajudar a encontrar a causa.

Constante Valor Descrição
ERR_INTERNAL_ERROR 4001 Erro interno inesperado
ERR_WRONG_INTERNAL_PARAMETER 4002 Parâmetro não válido, ao chamar internamente as funções do terminal de cliente
ERR_INVALID_PARAMETER 4003 Parâmetro errado, quando a função do sistema faz a chamada
ERR_NOTIFICATION_WRONG_PARAMETER 4516 Parâmetro inválido para envio da notificação, uma linha vazia ou NULL foi passada para a função SendNotification()
ERR_BUFFERS_WRONG_INDEX 4602 Índice errado do seu buffer de indicador
ERR_INDICATOR_WRONG_PARAMETERS 4808 Número incorreto de parâmetros ao criar o indicador
ERR_INDICATOR_PARAMETERS_MISSING 4809 Sem parâmetros ao criar o indicador
ERR_INDICATOR_CUSTOM_NAME 4810 O primeiro parâmetro na matriz deve ser o nome do indicador personalizado
ERR_INDICATOR_PARAMETER_TYPE 4811 Tipo de parâmetro errado na matriz ao criar o indicador
ERR_NO_STRING_DATE 5030 Linha sem data
ERR_WRONG_STRING_DATE 5031 Data errada na linha
ERR_TOO_MANY_FORMATTERS 5038 Mais especificadores de formato do que parâmetros
ERR_TOO_MANY_PARAMETERS 5039 Mais parâmetros do que especificadores de formato

A tabela não lista todos os erros que podem ocorrer durante a execução do programa.

 

Access violation

Esse erro ocorre quando você tenta acessar uma memória que está restrita. Nestes casos, é necessário entrar em contato com os desenvolvedores através do Service Desk, via página de perfil ou usando Contatos. Uma descrição detalhada dos passos antes do erro acontecer e um anexo do código fonte acelerará significativamente a procura das causas deste erro e ajudará a melhorar o compilador de código fonte.




Consumo de recursos da CPU e da memória

Ao escrever o programa, é importante usar um algoritmo cujo tempo de execução seja ótimo, de outra forma, o trabalho de outros programas em execução seria difícil ou mesmo impossível, no terminal.

É importante lembrar que, na Observação do mercado, o terminal aloca um fluxo comum, no qual são processados todos os indicadores e gráficos para trabalhar com cada símbolo.

Isto significa que si você tem 5 gráficos abertos do par EURUSD em diferentes timeframes, e nestes gráficos há 15 indicadores em execução, todos estes indicadores recebem um único fluxo para cálculo e exibição de informações nos gráficos. É por isso que um indicador ineficiente ocupa muitos recursos, retardando o trabalho de outros indicadores e até congelando a exibição de preços nos outros gráficos deste símbolo.

Você pode verificar o tempo gasto pelo seu algoritmo usando a função GetMicrosecondCount(). Após medir o tempo entre as duas linhas do código, é fácil de obter o tempo de execução em microssegundos. Para converter em milissegundos (ms), é necessário dividir esse tempo por 1000 (1 milissegundo tem 1000 microssegundos). Para os indicadores, o lugar crítico no tempo de execução é geralmente o manipulador OnCalculate(). Normalmente, o primeiro cálculo do indicador é fortemente dependente do parâmetro Max. de barras na janela, indique nele o valor "Unlimited" e execute o seu indicador no símbolo com um histórico superior a 10 anos no timeframe M1. Se a primeira execução levar muito tempo (por exemplo, mais do que 100 ms), será necessário otimizar o código.

A seguir, um exemplo de como medir o tempo de execução do manipulador OnCalculate() no indicador ROC, que fornece na entrega padrão do terminal com código fonte. As inserções são destacadas em amarelo:

//+------------------------------------------------------------------+
//| Rate of Change                                                   |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,const int prev_calculated,const int begin,const double &price[])
  {
//--- check for rates count
   if(rates_total<ExtRocPeriod)
      return(0);
//--- hora de início dos cálculos
   ulong start=GetMicrosecondCount();  
//--- preliminary calculations
   int pos=prev_calculated-1; // set calc position
   if(pos<ExtRocPeriod)
      pos=ExtRocPeriod;
//--- the main loop of calculations
   for(int i=pos;i<rates_total && !IsStopped();i++)
     {
      if(price[i]==0.0)
         ExtRocBuffer[i]=0.0;
      else
         ExtRocBuffer[i]=(price[i]-price[i-ExtRocPeriod])/price[i]*100;
     }
//--- hora final dos cálculos     
   ulong finish=GetMicrosecondCount();  
   PrintFormat("Função %s em %s levou %.1f ms",__FUNCTION__,__FILE__,(finish-start)/1000.);
//--- OnCalculate done. Return new prev_calculated.
   return(rates_total);
  }

A memória em uso pode ser medida usando a função MQLInfoInteger(MQL_MEMORY_USED). E, claro, use o Criador de perfil de código para encontrar as funções mais desvantajosas no seu programa. Também recomendamos ler os artigos Os princípios do cálculo econômico de indicadores e Depuração dos programas do mql5.

Os conselheiros trabalham nas suas próprias fontes, mas todo o dito acima é aplicado a eles. É necessário escrever um código ideal em todos os tipos de programas: conselheiros, indicadores, scripts e bibliotecas.


Não existe tal coisa como demasiadas verificações

Todas as anteriores dicas sobre a verificação de indicadores e conselheiros estão recomendadas não só para publicação de produtos no Mercado, mas também na prática diária, quando você escreve para si mesmo. Neste artigo, não examinamos todos os erros que podem ocorrer ao negociar em contas reais. Aqui, não são consideradas todas as regras para o processamento de erros de negociação que ocorrem durante a perda de conexão com o servidor de negociação, requotes, rejeições das transações e muitas outras coisas que podem perturbar as regras do sistema de negociação. Para estes casos, cada desenvolvedor de robôs tem as suas próprias receitas acumuladas pela experiência.

Também recomendamos que os novos leiam todos os artigos sobre o tratamento de erros e façam perguntas sobre no fórum e nos comentários deste artigo. Outros membros mais experientes da comunidade MQL5.community ajudarão a compreender as questões que ainda são confusas para você. Esperamos que as informações recolhidas no artigo seja úteis para você criar robôs de negociação mais confiáveis ​​num curto período.


Artigos relacionados recomendados: