Como desenvolver no MQL4 um Robô Negociador seguro e confiável
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:
- sintaxe - eles podem ser encontrados na fase de compilação e podem ser facilmente fixados pelo programador;
- 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;
- 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;
- crítico - estes são erros improváveis, você deve se esforçar para evocá-los. Eles geralmente ocorrem quando você trabalha com dll;
- 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.
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);
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:
- O corretor não iria gostar de pedidos frequentes;
- O erro pode ser fatal, neste caso, o pedido não iria alcançar o servidor de qualquer maneira;
- o Expert Advisor não responderia por um longo período;
- 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.
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
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso