Trailing Stop padrão e saída de mercado
Introdução
Desenvolvedores de algoritmos de modificação/fechamento de ordem sofrem de uma aflição contínua - como comparar resultados obtidos por métodos diferentes? O mecanismo de verificação é bem conhecido - é o Testador de Estratégia. Mas como fazer um EA funcionar igualmente para ordens de abertura/fechamento? O artigo descreve uma ferramenta que fornece uma forte repetição de aberturas de ordem que nos permite manter uma plataforma matematicamente correta para comparar os resultados de algoritmos diferentes para trailing stops e para sair do mercado.
Se você está depurando um EA complexo que deve calcular independentemente o tempo para entrar no mercado, trailing stops e saída de mercado, então é praticamente impossível obter um padrão repetitivo para ser comparado aos outros. Imagine uma situação onde há um sinal bem longo para abrir uma ordem. Idealmente, a ordem será aberta. Então, se a direção escolhida estiver correta e o preço se mover na direção prevista, a trailing stop começa a trabalhar. De acordo com as flutuações de preço, uma stop posicionada próximo demais pode fechar muito cedo uma ordem que poderia, de outra forma, finalizar lucros. Se o sinal de abertura ainda estiver válido naquele momento, o EA irá fechar uma nova ordem. Como resultado, teremos que comparar os resultados de uma ordem "correta" com os resultados de algumas outras ordens que foram abertas depois de fechamentos prematuros. Para evitar essa situação, o seguinte será sugerido.
Declaração do problema
- Padrões de abertura/fechamento de ordem estão marcados no gráfico.
- O tempo de abertura/fechamento e a direção de trade (comprar/vender) estão salvos em um arquivo.
- O Expert Advisor para ler os arquivos preparados e estritamente executar seus comandos é criado.
Pontos de abertura devem ser configurados em reversões de mercado - é bom que sejam bastante óbvios no histórico. Entretanto, os pontos de fechamento deveriam ser selecionados não quando o preço alcança o ponto de reversão oposto, mas depois disso. Não esqueça que nossa tarefa é otimizar trailing e saída de mercado, então, deveríamos permitir que qualquer algoritmo, até mesmo um que seja impróprio, trabalhe "com essa finalidade". Se ele ainda não for capaz de fixar o lucro, veremos que seus prejuízos se tornarão um sinal para nós retrabalharmos o algoritmo.
Observe a imagem acima. A linha violeta mostra entrada e saída idealmente corretas. Pode ser usada para cálculo dos lucros máximos que queremos/podemos obter. Entretanto, para o propósito de testes de trailing, usaremos algo similar à linha azul. Mostra a natureza do trading real: entrar com algum atraso (como se estivéssemos esperando por confirmação de reversão) e fechar no limite de break-even (ponto de equilíbrio) (como se estivéssemos assustados com o início de uma forte reversão e com a possibilidade de que perderíamos tudo).
No trading realizado "ao longo da linha azul", existem três pontos potenciais de acionamento de stops depois de trailings:
- Trailing agressivo na distância mínima do preço atual.
- Trailing normal e "paciente".
- Trailing ideal "tirando o máximo" dos lucros até o final.
Além disso, um falso acionamento do trailing "impaciente" demais pode acontecer na área ao redor do ponto 4.
Agora que sabemos como "marcar" áreas ideais, a única coisa que falta é torná-las o mais confortável possível.
Ferramentas de marcação
Para facilitar a marcação do gráfico com linhas ideais, vamos preparar um conjunto de scripts. Dois scripts, TL_Buy e TL_Sell, irão criar linhas de marcação para operações de compra e venda, respectivamente. Script TL_Write irá observar todas as linhas criadas e salvar suas características em um arquivo para que o Expert Advisor TL_Trade trabalhe com elas. Mais um script TL_Read será capaz de ler o arquivo criado e reformular todas as linhas com base nele. Isso pode se tornar útil para corrigir as linhas disponíveis, para adicionar novas linhas ou para remover linhas existentes.
Para que os scripts de leitura/escrita sejam capazes de trabalhar com suas linhas, iremos nomear todas as linhas de acordo com certas regras:
- os nomes de todas as linhas ideais começam com o mesmo prefixo (TL_). Você pode usar o prefixo para selecionar e deletar as linhas depois;
- o prefixo é seguido por um caractere que é o código de operação: B-buy, S-sell;
- o código de operação no nome da linha é seguido pelo número de linha para distinguir as linhas umas das outras.
Como resultado, deveríamos colocar no gráfico, por exemplo, as linhas dos seguintes nomes: TL_B1 TL_B2, TL_S3, etc.
Scripts que constroem linhas estão simplesmente perdidos no gráfico e a linha correspondente aparece no ponto de perda. Você pode mover sua extremidade de tal forma que elas marquem a "linha azul" ideal necessária de trading. Quando anexados ao gráfico, os scripts de leitura/escrita solicitam o nome do arquivo a ser salvo e lido. Isso nos permitirá usar facilmente diferentes conjuntos de linhas, por exemplo, para diferentes pares cambiais.
O código dos scripts é bastante transparente e fornecido com todos os comentários necessários, então tomarei a liberdade de pular a descrição de seus algoritmos - você pode vê-los a partir de seus códigos.
**************************************************************** PATTERN TRADING: TL_Buy - creation of a new, pattern buying line Copyright © 2006-2008, Sergey Kravchuk. http://forextools.com.ua *****************************************************************/ #include <WinUser32.mqh> #define _prefix_ "TL_" int start() { int MaxNo=0,i,No; if(WindowOnDropped()!=0) { MessageBox("Script should be dropped in the main window","ERROR", IDOK + MB_ICONERROR); return(1); } // find the maximum suffix number for all lines for(i=0;i<ObjectsTotal();i++) { if(StringFind(ObjectName(i),_prefix_)==0) { No=StrToInteger(StringSubstr(ObjectName(i),StringLen(_prefix_)+1)); // select the line number if(MaxNo<No) MaxNo=No; // store it, if it is larger } } datetime t0=WindowTimeOnDropped(); double p0=WindowPriceOnDropped(); // find the coordinates of the script dropping point int width = 5*Period()*60; // width of the created line in bars converted into time units double height = 20*MarketInfo(Symbol(),MODE_TICKSIZE); // height of the created line in ticks converted into price units string LineName = _prefix_+"B"+(MaxNo+1); // create a name for a new line ObjectCreate(LineName,OBJ_TREND,0,t0-width,p0-height, t0+width,p0+height); // create a line ObjectSet(LineName,OBJPROP_RAY,False); // make it a section, not a ray ObjectSet(LineName,OBJPROP_WIDTH,2); // set its width ObjectSet(LineName,OBJPROP_COLOR,Blue); // set its color }
/**************************************************************** PATTERN TRADING: TL_Sell - creation of a new, pattern selling line Copyright © 2006-2008, Sergey Kravchuk. http://forextools.com.ua *****************************************************************/ #include <WinUser32.mqh> #define _prefix_ "TL_" int start() { int MaxNo=0,i,No; if(WindowOnDropped()!=0) { MessageBox("Script should be dropped in the main window","ERROR", IDOK + MB_ICONERROR); return(1); } // find the maximum suffix number for all lines for(i=0;i<ObjectsTotal();i++) { if(StringFind(ObjectName(i),_prefix_)==0) { No=StrToInteger(StringSubstr(ObjectName(i),StringLen(_prefix_)+1)); // select the line number if(MaxNo<No) MaxNo=No; // store it, if it is larger } } datetime t0=WindowTimeOnDropped(); double p0=WindowPriceOnDropped(); // find the coordinates of the script dropping point int width = 5*Period()*60; // width of the created line in bars converted into time units double height = 20*MarketInfo(Symbol(),MODE_TICKSIZE); // height of the created line in ticks converted into price units string LineName = _prefix_+"S"+(MaxNo+1); // create a name for a new line ObjectCreate(LineName,OBJ_TREND,0,t0-width,p0+height, t0+width,p0-height); // create a line ObjectSet(LineName,OBJPROP_RAY,False); // make it a section, not a ray ObjectSet(LineName,OBJPROP_WIDTH,2); // set its width ObjectSet(LineName,OBJPROP_COLOR,Red); // set its color }
/**************************************************************** PATTERN TRADING: TL_Write - saving the coordinates of pattern lines in a file Copyright © 2006-2008, Sergey Kravchuk. http://forextools.com.ua *****************************************************************/ #include <WinUser32.mqh> #define _prefix_ "TL_" #property show_inputs extern string FileNameForWrite = "TL_DATA.TXT"; int start() { int LinesCNT=0,i; string Operation; double p; datetime t; int fh=FileOpen(FileNameForWrite,FILE_CSV|FILE_WRITE,';'); // look through all lines created and save the opening commands for the EA from them for(i=0;i<ObjectsTotal();i++) { if(StringFind(ObjectName(i),_prefix_)==0) // our line { string LineName = ObjectName(i); datetime t1=ObjectGet(LineName,OBJPROP_TIME1); datetime t2=ObjectGet(LineName,OBJPROP_TIME2); double p1=ObjectGet(LineName,OBJPROP_PRICE1); double p2=ObjectGet(LineName,OBJPROP_PRICE2); LinesCNT++; // increase the counter for producing the final message Operation = StringSubstr(ObjectName(i),StringLen(_prefix_),1); // prices are necessary only for restoring the line in the chart FileWrite(fh,Operation,TimeToStr(t1),DoubleToStr(p1,Digits),TimeToStr(t2),DoubleToStr(p2,Digits)); } } FileClose(fh); MessageBox("Stored sections "+(LinesCNT)+" pcs.","Done", IDOK + MB_ICONINFORMATION); }
/**************************************************************** PATTERN TRADING: TL_Read - drawing pattern lines from the file Copyright © 2006-2008, Sergey Kravchuk. http://forextools.com.ua *****************************************************************/ #include <WinUser32.mqh> #define _prefix_ "TL_" #property show_inputs extern string FileNameForRead = "TL_DATA.TXT"; int start() { int LinesCNT=0,i; int fh=FileOpen(FileNameForRead,FILE_CSV|FILE_READ,';'); if(fh<0) { MessageBox("Error opening file \"" + FileNameForRead + "\"","ERROR", IDOK + MB_ICONERROR); return(1); } // first of all, delete everything for(i=0;i<ObjectsTotal();i++) { if(StringFind(ObjectName(i),_prefix_)==0) { ObjectDelete(ObjectName(i)); i--; } } // look through all lines created and save the opening commands for the EA from them while(true) { string Operation=FileReadString(fh); if(FileIsEnding(fh)) break; // file ended? - exit // read the section's coordinates datetime t1=StrToTime(FileReadString(fh)); double p1=StrToDouble(FileReadString(fh)); datetime t2=StrToTime(FileReadString(fh)); double p2=StrToDouble(FileReadString(fh)); // draw a section LinesCNT++; string LineName = _prefix_+Operation+(LinesCNT); // create a name for a new line ObjectCreate(LineName,OBJ_TREND,0,t1,p1, t2,p2); // create a line ObjectSet(LineName,OBJPROP_RAY,False); // make it a section, not a ray ObjectSet(LineName,OBJPROP_WIDTH,2); // set its width if(Operation=="B") ObjectSet(LineName,OBJPROP_COLOR,Blue); else ObjectSet(LineName,OBJPROP_COLOR,Red);// set its color } FileClose(fh); MessageBox("Read sections "+(LinesCNT)+" pcs.","Done", IDOK + MB_ICONINFORMATION); }
/**************************************************************** PATTERN TRADING: TL_Clear - deletion of all pattern lines Copyright © 2006-2008, Sergey Kravchuk. http://forextools.com.ua *****************************************************************/ #include <WinUser32.mqh> #define _prefix_ "TL_" int start() { int LinesCNT=0,i; for(i=0;i<ObjectsTotal();i++) { if(StringFind(ObjectName(i),_prefix_)==0) { ObjectDelete(ObjectName(i)); i--; LinesCNT++; } } }
Posicionamento do arquivo
Posicionar arquivos é um ponto muito importante. Usando meios padrão, os scripts que estão trabalhando só podem criar arquivos no diretório c:\Program Files\MetaTrader 4\experts\files. Entretanto, ao testar Expert Advisors, o testador tem acesso à pasta de mesmo nome localizada "dentro do seu próprio diretório", c:\Program Files\MetaTrader 4\tester\files.
É por isso que depois da criação de arquivos e antes de usá-los no EA de teste, você deveria copiá-los independentemente a partir de c:\Program Files\MetaTrader 4\experts\files para c:\Program Files\MetaTrader 4\tester\files.
Você terá que repetir essa operação depois da recriação dos arquivos ter mudado algo nas linhas.
EA de teste
O EA de teste não produz quaisquer dificuldades em seu código. Os blocos seguintes enfatizam isso:
- bloco de fechamento de ordem ao alcançar o final da seção padrão;
- bloco de abertura de ordem ao alcançar o início da seção padrão;
- bloco de teste de trailing stop e saída de mercado.
Seu trabalho é bem óbvio no código fonte. Somente alguns comentários devem ser feitos aqui:
- Uma vez que as linhas podem ser criadas em uma ordem não particular, todo o conjunto de linhas será testado a cada "tick" do Testador para encontrar as linhas nas quais seja necessário abrir/fechar.
- Os tempos de abertura/fechamentos estão escritos no comentário da ordem no formato interno de representação de tempo e data. Isso é necessário, primeiramente, para nós não abrirmos a mesma ordem várias vezes, se ela for fechada por trailing bem antes de sua linha padrão terminar. Em segundo lugar, ao verificar ordens de abertura e pegar seus tempos de fechamento nos comentários, iremos fechar a ordem exatamente no mesmo momento que a sua linha de controle for finalizada, uma vez que o tempo de fechamento é escrito na própria ordem de abertura.
- O parâmetro ProcedTrailing habilita/desabilita o processamento de trailing. Isso irá nos permitir passar o EA sem nenhum trailing para obter o resultado mais pessimista e compará-lo aos resultados de otimização obtidos.
/**************************************************************** PATTERN TRADING: TL_Trader - trading on pattern lines Copyright © 2006-2008, Sergey Kravchuk. http://forextools.com.ua *****************************************************************/ #include <WinUser32.mqh> #define _prefix_ "TL_" extern string FileNameForRead = "TL_DATA.TXT"; extern double Lots = 0.1; extern double StopLoss = 0; extern double TrailingStop = 30; extern bool ProcedTrailing=true; // process the trailing block double SL; // to calculate the SL values for opening an order int start() { int LinesCNT=0,i,ticket,pos; double p; datetime t; string s; int fh=FileOpen(FileNameForRead,FILE_CSV|FILE_READ,';'); // to test the file, it is necessary to pout it into tester\files\TL_DATA.txt if(fh<0) { MessageBox("Error opening file \"" + FileNameForRead + "\"","ERROR", IDOK + MB_ICONERROR); return(1); } // check all entries: if the opening time has already passed and no order with such a comment is found in history or in open orders // then it has not been opened yet - open it as it's said there while(true) { string Operation=FileReadString(fh); if(FileIsEnding(fh)) break; // file ended? - exit // count the section coordinates string st1=FileReadString(fh); string sp1=FileReadString(fh); string st2=FileReadString(fh); string sp2=FileReadString(fh); datetime t1=StrToTime(st1); double p1=StrToDouble(sp1); datetime t2=StrToTime(st2); double p2=StrToDouble(sp2); // what if sections' ends are mixed? if(t1>t2) { p=p1; p1=p2; p2=p; t=t1; t1=t2; t2=t; s=st1; st1=st2; st2=s; s=sp1; sp1=sp2; sp2=s; } string MarkComent = t1+"="+t2; //********************************************************************************** // 1) block closing the orders as soon as the end of the pattern section is reached. //********************************************************************************** for(i=0;i<OrdersTotal();i++) { if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) continue; if(OrderComment()==MarkComent && TimeCurrent()>=t2) // order must be closed { if(OrderType()==OP_BUY) OrderClose(OrderTicket(),OrderLots(),Bid,3,Violet); // close position else OrderClose(OrderTicket(),OrderLots(),Ask,3,Violet); // close position } } //********************************************************************************** // 2) block opening orders as soon as the beginning of the pattern section is passed. //********************************************************************************** bool OrderNotPresent=true; // a sign showing that we haven't opened such an order yet if(t1<=TimeCurrent() && TimeCurrent()<t2) // time to open - make sure that this order is not opened or closed { for(i=0;i<OrdersTotal();i++) { if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) continue; if(OrderComment()==MarkComent) { OrderNotPresent=false; break; } // order already exists } for(i=0;i<OrdersHistoryTotal() && OrderNotPresent;i++) { if(OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)==false) continue; // order in history is added to the end, something like "[sl]" - it must be cut off!! pos = StringFind(OrderComment(),"["); string CurOrderComment = StringSubstr(OrderComment(),0,pos); if(CurOrderComment==MarkComent) { OrderNotPresent=false; break; } // order already exists } if(OrderNotPresent) // no such order - open it { // open an order if(Operation=="B") // Buy { if(StopLoss<=0) SL=0; else SL=Ask-StopLoss*Point; ticket=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,SL,0,MarkComent,1235,0,Blue); OrderSelect(ticket,SELECT_BY_TICKET); } else // Sell { if(StopLoss<=0) SL=0; else SL=Bid+StopLoss*Point; ticket=OrderSend(Symbol(),OP_SELL,Lots,Bid,3,SL,0,MarkComent,1235,0,Red); OrderSelect(ticket,SELECT_BY_TICKET); } } } } FileClose(fh); //****************************************************** // 3) block testing trailing stop and exit the market //****************************************************** if(ProcedTrailing) { for(i=0;i<OrdersTotal();i++) { if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) continue; if(OrderType()==OP_BUY) { if(Bid-OrderOpenPrice()>Point*TrailingStop) { if(OrderStopLoss()<Bid-Point*TrailingStop) { OrderModify(OrderTicket(),OrderOpenPrice(),Bid-Point*TrailingStop,OrderTakeProfit(),0,Green); return(0); } } } if(OrderType()==OP_SELL) { if((OrderOpenPrice()-Ask)>(Point*TrailingStop)) { if((OrderStopLoss()>(Ask+Point*TrailingStop)) || (OrderStopLoss()==0)) { OrderModify(OrderTicket(),OrderOpenPrice(),Ask+Point*TrailingStop,OrderTakeProfit(),0,Red); return(0); } } } } } return(0); }
Como o sistema trabalha quando "totalmente integrado"
Para testar o sistema, usamos um microconjunto de 14 linhas. Abaixo está o conteúdo do arquivo padrão nomeado TL_DATA.txt:
B;2007.12.28 05:00;1.4605;2008.01.09 22:00;1.4658 B;2008.01.29 05:00;1.4767;2008.02.05 05:00;1.4811 B;2008.02.15 16:00;1.4687;2008.02.21 09:00;1.4735 B;2008.02.21 14:00;1.4738;2008.02.26 07:00;1.4812 B;2008.02.28 14:00;1.5129;2008.03.05 12:00;1.5186 B;2008.03.05 22:00;1.5261;2008.03.11 20:00;1.5316 B;2008.03.13 01:00;1.5539;2008.03.18 22:00;1.5620 B;2008.03.26 14:00;1.5724;2008.03.28 10:00;1.5758 S;2007.11.30 13:00;1.4761;2007.12.10 22:00;1.4711 S;2007.12.14 04:00;1.4626;2007.12.28 00:00;1.4610 S;2008.01.17 17:00;1.4688;2008.01.24 13:00;1.4671 S;2008.02.07 12:00;1.4633;2008.02.14 11:00;1.4617 S;2008.03.19 23:00;1.5641;2008.03.25 23:00;1.5629 S;2008.03.31 19:00;1.5811;2008.04.08 04:00;1.5796É assim que fica em um gráfico:
Não é o trading mais eficaz, mas uma base ideal para testar o trailing. ;)
Se iniciarmos o Testador com trailing stops desabilitado (ProcedTrailing=falso), iremos obter um resultado pobre:
Uma simples otimização por dimensão de trailing-stop fornece o valor ótimo de 95 pontos.
O valor ótimo produzindo o maior lucro está de acordo com a estatística de tamanho de barra do período em teste: 95 pontos fazem 98% de todas as barras desse período, o que é claramente visto no gráfico do ft.BarStat indicator.
Como você pode ver, os resultados otimizados parecem muito mais atrativos:
Eles são vistos no gráfico ainda mais claramente. Por favor, note que todas as ordens foram abertas precisamente no início das linhas padrão!
Observe a primeira seção (5 de março). Os lucros foram atingidos por um pico para baixo, mas a tendência de compra geral permaneceu a mesma. Se testamos trailing com um EA normal, provavelmente ele abre uma posição que iria manter-se até o final da segunda linha (depois de 17 de março). Nesse caso, iríamos obter resultados incomparáveis. O lucro, obtido no segundo caso, resultaria da abertura repetida bem sucedida de uma nova ordem, não ao mecanismo de trailing stop. Entretanto, o problema não é obter o maior lucro, mas obter o mecanismo de trailing stop mais eficaz possível. É por isso que é muito importante para nós que todas as ordens sejam abertas ao mesmo tempo e não sejam mantidas por tempo demais. Entretanto, nesse caso, o aumento nos lucros que resulta da otimização irá refletir a eficácia do algoritmo de trailing!
Conclusão
Espero que a discussão sobre o presente artigo não seja criticada de forma ruim por algo que NÃO fiz - um leitor interessado irá achar um uso para o algoritmo de pesquisa proposto até mesmo da forma que está apresentado aqui. O artigo descreve uma ferramenta que eu finalmente comecei a considerar. A ideia que, por muito tempo, não passava pela minha cabeça, finalmente se tornou clara e foi colocada em prática no código MQL4. Eu não tive tempo para conduzir a pesquisa para a qual essa ferramenta foi criada. É por isso que o artigo não fornece análises comparativas de vários algoritmos de trailing - vou tratar deles em um futuro próximo, então a segunda parte do artigo ficará pronta para publicação em breve. Não obstante, esse artigo parece ser útil para a Comunidade MQL4 como ele é.
Mais uma razão pela qual decidi publicar a ferramenta "nua" é que sou um programador profissional e um trader novato. Isso significa que eu posso desenvolver meu código MQL4 independentemente com a mais alta simplicidade ou complexidade, mas eu só posso desenvolver táticas de trailing e algoritmos de trailing tão bem quanto programo os códigos juntamente com traders que são tão profissionais em trading quanto sou em programação. É por isso que estou altamente interessado em construir uma comunicação com meus colegas "companheiros de corporação" do Forex e ficarei feliz em trabalhar em cooperação para colocar minha ferramenta em um estado onde seja possível usá-la no trading real. Se você estiver interessado em cooperar comigo, por favor, entre em contato através do meu perfil.
Como desenvolvimento adicional, podemos testar vários algoritmos de trailing em um gráfico simultaneamente. No início da seção padrão, iremos abrir algumas ordens (na mesma quantidade de algoritmos em teste), cada uma passando por trailing de acordo com seu algoritmo. Em termos de conceito, deveríamos obter o quadro que representa várias imagens sobrepostas umas às outras. Então, seremos capazes de comparar os resultados por quantidade (lucros líquidos) e por qualidade - seremos capazes de ver quais algoritmos preenchem suas funções mais precisamente. Entretanto, isso irá exigir que ensinemos o EA a distinguir ordens por trailing alternativo, mas esse problema é bem fácil de se resolver.
A propósito, esses métodos podem ser usados para comparar as táticas de entrada e de saída! Para isso, você deveria apenas preparar um conjunto de linhas um pouco diferentes e ter certeza que seus pontos de abertura de ordem de acordo com seus métodos estão localizados o mais perto possível das aberturas padrão no gráfico, Entretanto, esse é um assunto para uma pesquisa separada e um novo artigo.
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/1527
- 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