Reimaginando Estratégias Clássicas (Parte 13): Minimizando o Atraso em Cruzamentos de Médias Móveis
Em nossas discussões anteriores, adotamos diferentes perspectivas sobre como podemos maximizar nossa eficiência ao usar estratégias de cruzamento de médias móveis. Para resumir brevemente, conduzimos anteriormente um exercício com mais de 200 símbolos diferentes e observamos que nosso computador parece aprender a prever valores futuros de médias móveis melhor do que tende a prever níveis futuros de preços corretamente, forneci um link rápido para o artigo, aqui. Ampliamos essa ideia e modelamos 2 médias móveis, com o objetivo único de prever cruzamentos mais cedo do que participantes casuais do mercado e ajustar nossas operações de acordo. Incluí um link para o segundo artigo também, disponível facilmente ao seu alcance, aqui.
Nossa discussão de hoje estenderá novamente a estratégia original, para minimizar o atraso inerente à estratégia de negociação. As estratégias tradicionais de cruzamento exigem que exista um intervalo temporal entre os 2 períodos das médias móveis. Entretanto, nos afastaremos dessa escola de pensamento convencional e usaremos o mesmo período em ambas as médias móveis.
Neste ponto, alguns leitores podem estar se questionando se isso ainda pode ser qualificado como uma estratégia de cruzamento de médias móveis, pois como suas médias móveis irão se cruzar se ambas compartilham o mesmo período? A resposta é surpreendentemente simples: aplicamos uma média móvel no preço de abertura e outra no preço de fechamento, respectivamente.
Quando a média móvel da abertura está acima da média móvel do fechamento, os níveis de preço estão terminando mais baixos do que começaram. Isso é idêntico a saber que os níveis de preço estão caindo. E o inverso é verdadeiro quando a média móvel do fechamento está acima da de abertura.
Um período comum em ambas as médias móveis é incomum; no entanto, selecionei-o para nosso exercício de hoje para demonstrar ao leitor uma possível forma de encerrar quaisquer críticas sobre o atraso associado aos cruzamentos de médias móveis.
Antes de mergulharmos no segmento técnico de nossa discussão, este artigo pode ser lido como um exemplo de uma classe generalizada de estratégias de negociação que pode ser facilmente estendida a muitos outros indicadores técnicos. Por exemplo, o Índice de Força Relativa também pode ser aplicado separadamente ao preço de abertura, fechamento, máxima ou mínima, e observaremos que o RSI acompanhando o preço de abertura tende a ficar acima do RSI acompanhando o preço de fechamento quando os mercados estão em tendência de baixa.
Visão Geral do Nosso Backtest
Para apreciarmos a relevância de nossa discussão de hoje, primeiro estabeleceremos um benchmark criado pela estratégia tradicional de cruzamento. Em seguida, compararemos o desempenho legado com o que podemos alcançar usando nossa versão reimaginada da estratégia.Selecionei o par EURUSD para nossa discussão de hoje. O EURUSD é o par de moedas mais negociado do mundo. É significativamente mais volátil do que a maioria dos pares de moedas e geralmente não é uma boa escolha para estratégias simples baseadas em cruzamentos. Como já discutimos anteriormente, focaremos no período diário. Faremos o backtest de nossa estratégia em aproximadamente 4 anos de dados históricos, de 1º de janeiro de 2020 até 24 de dezembro de 2024; o período do backtest está destacado abaixo na Fig. 1.

Fig 1: Visualizando nosso período de backtest de 4 anos do EURUSD em nosso terminal MetaTrader 5 usando o período Mensal
Embora estratégias tradicionais de cruzamento sejam intuitivas e fundamentadas em princípios razoavelmente sólidos, essas estratégias frequentemente exigem otimizações intermináveis para garantir eficácia. Além disso, os “períodos certos” para usar nos indicadores lento e rápido não são imediatamente óbvios e podem mudar drasticamente.
Para recapitular, a estratégia original é baseada nas interseções criadas por 2 médias móveis, ambas acompanhando o preço de fechamento do mesmo ativo, mas com períodos diferentes. Quando a média móvel com o período mais curto está por cima, interpretamos isso como um sinal de que os níveis de preço estiveram em tendência de alta e provavelmente continuarão subindo. O oposto se aplica quando a média móvel com o período mais longo está por cima; interpretamos isso como um sinal baixista. Um exemplo ilustrado é fornecido na Fig. 2 a seguir.

Fig 2: Um exemplo da estratégia tradicional de cruzamento de médias móveis em ação; a linha amarela é a média móvel rápida e a branca é a lenta
Na Fig. 2 acima, selecionamos aleatoriamente um período que mostra as limitações da estratégia clássica. À esquerda da linha vertical no gráfico, você observará que a ação do preço ficou presa em um intervalo por aproximadamente 2 meses. Essa ação letárgica gera sinais de negociação que são rapidamente revertidos e provavelmente não lucrativos. No entanto, após esse período de desempenho fraco, finalmente vimos os níveis de preço se afastarem em uma verdadeira tendência à direita da linha vertical. Estratégias tradicionais de cruzamento funcionam melhor em condições de mercado em tendência. No entanto, a estratégia proposta neste artigo lida com esses problemas de maneira elegante.
Estabelecendo um Benchmark
Nosso aplicativo de negociação pode ser conceituado como 4 componentes principais que trabalharão juntos para nos ajudar a negociar:
| Feature | Descrição |
|---|---|
| Constantes do Sistema | Ajudar-nos a isolar as melhorias trazidas pelas mudanças que estamos fazendo na lógica de negociação de nosso aplicativo. |
| Variáveis Globais | Responsável por acompanhar valores de indicadores, preços atuais de mercado e potencialmente mais informações necessárias. |
| Organizadores de Eventos | Executar várias tarefas no momento apropriado para atender ao nosso objetivo de negociar cruzamentos de médias móveis de forma eficaz. |
| Funções Personalizadas | Cada função personalizada em nosso sistema tem uma tarefa específica delegada a ela, e todas juntas as funções ajudam coletivamente a alcançar nosso objetivo. |
A versão de benchmark de nossa aplicação será minimalista em sua implementação. Nossa primeira tarefa é configurar constantes do sistema que permanecerão fixas nos dois testes que realizaremos. Nossas constantes do sistema são importantes para fazer comparações justas entre diferentes estratégias de negociação e evitam que mudemos acidentalmente configurações que não deveriam ser alteradas entre os testes, como o tamanho do nosso stop loss, que deve ser constante em ambos os testes para garantir uma comparação justa.
//+------------------------------------------------------------------+ //| Channel And MA.mq5 | //| Gamuchirai Zororo Ndawana | //| https://www.mql5.com/en/gamuchiraindawa | //+------------------------------------------------------------------+ #property copyright "Gamuchirai Zororo Ndawana" #property link "https://www.mql5.com/en/gamuchiraindawa" #property version "1.00" //+------------------------------------------------------------------+ //| System constants | //+------------------------------------------------------------------+ #define TF_1 PERIOD_D1 //--- Our main time frame #define TF_2 PERIOD_H1 //--- Our lower time frame #define ATR_PERIOD 14 //--- The period for our ATR #define ATR_MULTIPLE 3 //--- How wide should our stops be? #define VOL 0.01 //--- Trading volume
Também definiremos algumas variáveis globais importantes que usaremos para buscar valores de indicadores e obter preços atuais de mercado.
//+------------------------------------------------------------------+ //| Global variables | //+------------------------------------------------------------------+ int trade; int ma_f_handler,ma_s_handler,atr_handler; double ma_f[],ma_s[],atr[]; double bid,ask; double o,h,l,c; double original_sl;
Usaremos a biblioteca de trade para gerenciar nossas posições.
//+------------------------------------------------------------------+ //| Libraries | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> CTrade Trade;
Em MQL5, expert advisors são construídos a partir de manipuladores de eventos. Existem muitos tipos diferentes de eventos que acontecem no terminal MetaTrader 5. Esses eventos podem ser disparados por ações tomadas pelo usuário ou quando novos preços são cotados. Cada evento é emparelhado com um manipulador de eventos que é chamado cada vez que o evento é disparado. Portanto, projetei nossa aplicação de forma que cada manipulador de eventos tenha sua própria função designada, que será chamada por sua vez para executar as tarefas necessárias para uma estratégia de cruzamento de médias móveis.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Setup setup(); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Release our indicators release(); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Update system variables update(); } //+------------------------------------------------------------------+
A função setup é chamada quando nosso sistema é iniciado. O manipulador OnInit será chamado quando a aplicação de negociação for aplicada pela primeira vez a um gráfico, e ele repassará a cadeia de comando para nossa função setup personalizada, que aplicará nossos indicadores técnicos para nós.
//+------------------------------------------------------------------+ //| Custom functions | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Setup our system | //+------------------------------------------------------------------+ void setup(void) { atr_handler = iATR(Symbol(),TF_1,ATR_PERIOD); ma_f_handler = iMA(Symbol(),TF_1,10,0,MODE_EMA,PRICE_CLOSE); ma_s_handler = iMA(Symbol(),TF_1,60,0,MODE_EMA,PRICE_CLOSE); }
Se não estivermos mais usando nossa aplicação de negociação, o manipulador de eventos OnDeinit é chamado, e ele chamará nossa função release, que liberará os recursos do sistema que estavam sendo consumidos anteriormente pelos nossos indicadores técnicos.
//+------------------------------------------------------------------+ //| Release variables we do not need | //+------------------------------------------------------------------+ void release(void) { IndicatorRelease(atr_handler); IndicatorRelease(ma_f_handler); IndicatorRelease(ma_s_handler); }
Sempre que novos preços de mercado são cotados, o manipulador OnTick será chamado, e ele, por sua vez, chamará a função update para armazenar as novas informações de mercado disponíveis. Depois disso, se não tivermos posições abertas, procuraremos uma oportunidade de negociação. Caso contrário, gerenciaremos as posições que temos abertas.
/+------------------------------------------------------------------+ //| Update system variables | //+------------------------------------------------------------------+ void update(void) { //--- Update the system static datetime time_stamp; datetime current_time = iTime(Symbol(),TF_2,0); if(current_time != time_stamp) { time_stamp = current_time; CopyBuffer(atr_handler,0,0,1,atr); CopyBuffer(ma_s_handler,0,0,1,ma_s); CopyBuffer(ma_f_handler,0,0,1,ma_f); o = iOpen(Symbol(),TF_1,0); h = iHigh(Symbol(),TF_1,0); l = iLow(Symbol(),TF_1,0); c = iClose(Symbol(),TF_1,0); bid = SymbolInfoDouble(Symbol(),SYMBOL_BID); ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK); if(PositionsTotal() == 0) find_position(); if(PositionsTotal() > 0) manage_position(); } }
Nossas regras para entrar em posições são diretas e já foram explicadas em detalhes acima. Entramos em posições longas se nossa média móvel rápida estiver acima da média móvel lenta, e se o oposto for verdadeiro, assumiremos posições curtas.
//+------------------------------------------------------------------+ //| Find a position | //+------------------------------------------------------------------+ void find_position(void) { if((ma_s[0] > ma_f[0])) { Trade.Sell(VOL,Symbol(),bid,0,0,""); trade = -1; } if((ma_s[0] < ma_f[0])) { Trade.Buy(VOL,Symbol(),ask,0,0,""); trade = 1; } }
Por fim, nosso stop loss será ajustado dinamicamente usando o indicador Average True Range. Adicionaremos um múltiplo fixo da leitura do ATR acima e abaixo do nosso preço de entrada para marcar nossos níveis de stop loss e take profit. Além disso, também adicionaremos a leitura média do ATR dos últimos 90 dias (1 ciclo de negócios); nossa intenção ao fazer isso é contabilizar os níveis recentes de volatilidade no mercado. Por fim, usaremos um operador ternário para ajustar os níveis de take profit e stop loss. Nossa regra é que os stops só devem ser atualizados se a nova posição for mais lucrativa do que a antiga. Operadores ternários nos permitem expressar essa lógica de forma compacta. Além disso, operadores ternários também nos dão flexibilidade para ajustar facilmente take profit e stop loss independentemente um do outro.
//+------------------------------------------------------------------+ //| Manage our positions | //+------------------------------------------------------------------+ void manage_position(void) { //--- Select the position if(PositionSelect(Symbol())) { //--- Get ready to update the SL/TP double initial_sl = PositionGetDouble(POSITION_SL); double initial_tp = PositionGetDouble(POSITION_TP); //--- Calculate the average ATR move vector atr_mean; atr_mean.CopyIndicatorBuffer(atr_handler,0,0,90); double buy_sl = (ask - ((ATR_MULTIPLE * atr[0]) + atr_mean.Mean())); double sell_sl = (bid + ((ATR_MULTIPLE * atr[0]) + atr_mean.Mean())); double buy_tp = (ask + ((ATR_MULTIPLE * 0.5 * atr[0]) + atr_mean.Mean())); double sell_tp = (bid - ((ATR_MULTIPLE * 0.5 * atr[0]) + atr_mean.Mean())); double new_sl = ((trade == 1) && (initial_sl < buy_sl)) ? (buy_sl) : ((trade == -1) && (initial_sl > sell_sl)) ? (sell_sl) : (initial_sl); double new_tp = ((trade == 1) && (initial_tp < buy_tp)) ? (buy_tp) : ((trade == -1) && (initial_tp > sell_tp)) ? (sell_tp) : (initial_tp); if(initial_sl == 0 && initial_tp == 0) { if(trade == 1) { original_sl = buy_sl; Trade.PositionModify(Symbol(),buy_sl,buy_tp); } if(trade == -1) { original_sl = sell_sl; Trade.PositionModify(Symbol(),sell_sl,sell_tp); } } //--- Update the position else if((initial_sl * initial_tp) != 0) { Trade.PositionModify(Symbol(),new_sl,new_tp); } } } //+------------------------------------------------------------------+
Quando colocamos tudo junto, nosso código atual se apresenta assim até o momento.
//+------------------------------------------------------------------+ //| Channel And MA.mq5 | //| Gamuchirai Zororo Ndawana | //| https://www.mql5.com/en/gamuchiraindawa | //+------------------------------------------------------------------+ #property copyright "Gamuchirai Zororo Ndawana" #property link "https://www.mql5.com/en/gamuchiraindawa" #property version "1.00" //+------------------------------------------------------------------+ //| This version off the application is mean reverting | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| System constants | //+------------------------------------------------------------------+ #define TF_1 PERIOD_D1 //--- Our main time frame #define TF_2 PERIOD_H1 //--- Our lower time frame #define ATR_PERIOD 14 //--- The period for our ATR #define ATR_MULTIPLE 3 //--- How wide should our stops be? #define VOL 0.01 //--- Trading volume //+------------------------------------------------------------------+ //| Global variables | //+------------------------------------------------------------------+ int trade; int ma_f_handler,ma_s_handler,atr_handler; double ma_f[],ma_s[],atr[]; double bid,ask; double o,h,l,c; double original_sl; //+------------------------------------------------------------------+ //| Libraries | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> CTrade Trade; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Setup setup(); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Release our indicators release(); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Update system variables update(); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Custom functions | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Setup our system | //+------------------------------------------------------------------+ void setup(void) { atr_handler = iATR(Symbol(),TF_1,ATR_PERIOD); ma_f_handler = iMA(Symbol(),TF_1,10,0,MODE_EMA,PRICE_CLOSE); ma_s_handler = iMA(Symbol(),TF_1,60,0,MODE_EMA,PRICE_CLOSE); } //+------------------------------------------------------------------+ //| Release variables we do not need | //+------------------------------------------------------------------+ void release(void) { IndicatorRelease(atr_handler); IndicatorRelease(ma_f_handler); IndicatorRelease(ma_s_handler); } //+------------------------------------------------------------------+ //| Update system variables | //+------------------------------------------------------------------+ void update(void) { //--- Update the system static datetime time_stamp; datetime current_time = iTime(Symbol(),TF_2,0); if(current_time != time_stamp) { time_stamp = current_time; CopyBuffer(atr_handler,0,0,1,atr); CopyBuffer(ma_s_handler,0,0,1,ma_s); CopyBuffer(ma_f_handler,0,0,1,ma_f); o = iOpen(Symbol(),TF_1,0); h = iHigh(Symbol(),TF_1,0); l = iLow(Symbol(),TF_1,0); c = iClose(Symbol(),TF_1,0); bid = SymbolInfoDouble(Symbol(),SYMBOL_BID); ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK); if(PositionsTotal() == 0) find_position(); if(PositionsTotal() > 0) manage_position(); } } //+------------------------------------------------------------------+ //| Find a position | //+------------------------------------------------------------------+ void find_position(void) { if((ma_s[0] > ma_f[0])) { Trade.Sell(VOL,Symbol(),bid,0,0,""); trade = -1; } if((ma_s[0] < ma_f[0])) { Trade.Buy(VOL,Symbol(),ask,0,0,""); trade = 1; } } //+------------------------------------------------------------------+ //| Manage our positions | //+------------------------------------------------------------------+ void manage_position(void) { //--- Select the position if(PositionSelect(Symbol())) { //--- Get ready to update the SL/TP double initial_sl = PositionGetDouble(POSITION_SL); double initial_tp = PositionGetDouble(POSITION_TP); //--- Calculate the average ATR move vector atr_mean; atr_mean.CopyIndicatorBuffer(atr_handler,0,0,90); double buy_sl = (ask - ((ATR_MULTIPLE * atr[0]) + atr_mean.Mean())); double sell_sl = (bid + ((ATR_MULTIPLE * atr[0]) + atr_mean.Mean())); double buy_tp = (ask + ((ATR_MULTIPLE * 0.5 * atr[0]) + atr_mean.Mean())); double sell_tp = (bid - ((ATR_MULTIPLE * 0.5 * atr[0]) + atr_mean.Mean())); double new_sl = ((trade == 1) && (initial_sl < buy_sl)) ? (buy_sl) : ((trade == -1) && (initial_sl > sell_sl)) ? (sell_sl) : (initial_sl); double new_tp = ((trade == 1) && (initial_tp < buy_tp)) ? (buy_tp) : ((trade == -1) && (initial_tp > sell_tp)) ? (sell_tp) : (initial_tp); if(initial_sl == 0 && initial_tp == 0) { if(trade == 1) { original_sl = buy_sl; Trade.PositionModify(Symbol(),buy_sl,buy_tp); } if(trade == -1) { original_sl = sell_sl; Trade.PositionModify(Symbol(),sell_sl,sell_tp); } } //--- Update the position else if((initial_sl * initial_tp) != 0) { Trade.PositionModify(Symbol(),new_sl,new_tp); } } } //+------------------------------------------------------------------+
Como nossa estratégia atual não emprega IA ou qualquer técnica de ajuste de curva, podemos realizar um backtest simples sem nos preocuparmos com overfitting aos dados disponíveis. Além disso, não temos parâmetros de entrada que precisem ser ajustados. Portanto, configuramos “Forward” para “No”, porque não precisamos empregar as capacidades de forward testing do terminal MetaTrader 5 agora.

Fig 3: Selecionando as datas para nosso backtest
Além disso, é uma boa prática testar suas aplicações de negociação sob o ambiente mais estressante que você puder simular. Portanto, selecionamos o modo “Random delay” para nosso backtest de hoje, com nossa modelagem configurada para usar ticks reais. Ao usar ticks reais, o tempo necessário para baixar dados históricos pode ser maior do que ao usar outros modos, como “Open prices only”. No entanto, os resultados tendem a ser mais próximos dos resultados reais em ticks ao vivo.

Fig 4: Nosso segundo conjunto de configurações para nosso backtest de cruzamento do EURUSD
Quando analisamos os resultados obtidos usando a estratégia simples de cruzamento, podemos rapidamente ver possíveis problemas que enfrentaríamos se seguíssemos a estratégia em sua forma original. Observe que desde o início do backtest até julho de 2022, nossa estratégia trabalhou duro apenas para empatar. Este é um período de drawdown que corresponde a quase metade do nosso backtest, ou 2 anos. Isso é indesejável e não é uma característica do tipo de estratégia na qual podemos confiar para negociar nosso dinheiro sem supervisão.

Fig 5: Analisando nossa curva de lucro e prejuízo produzida ao seguir a estratégia de cruzamento de médias móveis
Nossa estratégia em sua forma original é pouco lucrativa e perde cerca de 61% de todas as negociações realizadas. Isso nos dá expectativas negativas em relação à estratégia, e nosso pessimismo é ainda validado pelo fato de que nosso índice de Sharpe está muito próximo de 0. Mas observe o quanto podemos melhorar drasticamente nossa estratégia fazendo alguns ajustes simples na lógica de negociação empregada.

Fig 6: Um resumo detalhado de nosso desempenho usando a estratégia legada de cruzamento de médias móveis
Melhorando a Estratégia Original
Na Fig. 7 abaixo, forneci uma ilustração visual de nossa nova estratégia sugerida de cruzamento. A linha azul e a verde são médias móveis de período 5 acompanhando o preço de fechamento (azul) e de abertura (verde). Observe que quando a média móvel azul está acima da verde, os níveis de preço estavam subindo. Além disso, preste atenção em quão responsivos são nossos cruzamentos às mudanças nos níveis de preço. Estratégias tradicionais de cruzamento levam um tempo variável para refletir qualquer mudança na tendência do mercado. No entanto, quando seguimos os níveis de preço usando nossa nova estratégia, podemos rapidamente observar mudanças na tendência e até períodos de consolidação, nos quais nossas 2 médias móveis continuam se cruzando, mas sem fazer progresso real. 
Fig 7: Visualizando nossa nova estratégia de cruzamento no gráfico diário do EURUSD
Até agora, nosso sistema conseguiu negociar com lucro, mas podemos melhorar. A única mudança que faremos em nosso sistema é alterar as condições sob as quais nossas posições são acionadas:
| Change | Descrição |
|---|---|
| Trading Rules | Nossas regras tradicionais compram quando a média móvel rápida está acima da lenta. Em vez disso, agora compraremos quando nossa média móvel da abertura estiver acima da média móvel do fechamento. |
Para realizar a melhoria desejada, precisamos fazer algumas alterações na forma original de nossa estratégia de negociação atual:
| Change | Descrição |
|---|---|
| Variáveis adicionais do sistema | Precisaremos de uma nova variável de sistema responsável por definir o período da média móvel da abertura e do fechamento. |
| Novas variáveis globais | Novas variáveis globais serão criadas para acompanhar as novas informações às quais estamos prestando atenção. |
| Modificar funções personalizadas | Algumas das funções personalizadas que construímos até agora precisam ser estendidas à luz do novo design de sistema que estamos seguindo. |
Em grande parte, todas as outras partes do nosso sistema serão preservadas. Queremos isolar as melhorias resultantes da mudança em nossa perspectiva sobre cruzamentos de médias móveis. Portanto, para alcançar nosso objetivo, começaremos criando uma nova constante de sistema para fixar o período de nossas médias móveis.
//--- Omitted code that has not changed #define MA_PERIOD 2 //--- The period for our moving average following the close
Em seguida, precisamos definir novas variáveis globais para as novas informações que estamos acompanhando. Agora estamos criando manipuladores de médias móveis para cada um dos 4 preços cotados (Abertura, Máxima, Mínima e Fechamento).
//--- Omitted code that have not changed int ma_h_handler,ma_l_handler,ma_c_handler,ma_o_handler; double ma_h[],ma_l[],ma_c[],ma_o[];
Quando nossa aplicação de negociação estiver sendo iniciada, precisaremos carregar alguns indicadores adicionais além daqueles com os quais já estamos familiarizados.
//+------------------------------------------------------------------+ //| Setup our system | //+------------------------------------------------------------------+ void setup(void) { atr_handler = iATR(Symbol(),TF_1,ATR_PERIOD); ma_h_handler = iMA(Symbol(),TF_1,MA_PERIOD,0,MODE_EMA,PRICE_HIGH); ma_l_handler = iMA(Symbol(),TF_1,MA_PERIOD,0,MODE_EMA,PRICE_LOW); ma_c_handler = iMA(Symbol(),TF_1,MA_PERIOD,0,MODE_EMA,PRICE_CLOSE); ma_o_handler = iMA(Symbol(),TF_1,MA_PERIOD,0,MODE_EMA,PRICE_OPEN); }
O mesmo se aplica à nossa função personalizada responsável por remover nosso expert advisor; ela será estendida para acomodar os novos indicadores que introduzimos no sistema.
//+------------------------------------------------------------------+ //| Release variables we do not need | //+------------------------------------------------------------------+ void release(void) { IndicatorRelease(atr_handler); IndicatorRelease(ma_h_handler); IndicatorRelease(ma_l_handler); IndicatorRelease(ma_c_handler); IndicatorRelease(ma_o_handler); }
Por fim, as condições que usaremos para abrir nossas operações devem ser atualizadas para que estejam alinhadas com nossa nova lógica de negociação.
//+------------------------------------------------------------------+ //| Find a position | //+------------------------------------------------------------------+ void find_position(void) { if((ma_o[0] > ma_c[0])) { Trade.Sell(VOL,Symbol(),bid,0,0,""); trade = -1; } if((ma_o[0] < ma_c[0])) { Trade.Buy(VOL,Symbol(),ask,0,0,""); trade = 1; } }
Agora vejamos qual diferença isso faz no nosso resultado final. Primeiro configuraremos nossa nova aplicação de negociação para operar no mesmo período que usamos em nosso primeiro teste.

Fig 8: Configurando nosso novo e aprimorado algoritmo de negociação para operar no mesmo período utilizado em nosso primeiro teste
Além disso, queremos realizar ambos os backtests sob condições idênticas para garantir que nosso teste seja imparcial. Caso contrário, se uma estratégia receber vantagem injusta, a integridade de nossos testes até agora seria comprometida.

Fig 9: Queremos garantir que as condições de teste sejam idênticas em ambos os testes para permitir uma comparação justa
Já podemos ver uma grande melhoria em relação aos resultados iniciais que obtivemos. Passamos os primeiros 2 anos de nosso backtest inicial tentando empatar. No entanto, com nossa nova estratégia de negociação, rompemos essa limitação e fomos lucrativos ao longo de todos os 4 anos.

Fig 10: Nossa nova estratégia de negociação rompeu o período improdutivo de operações que seria desafiador resolver usando estratégias tradicionais de cruzamento
Em nosso backtest inicial, nosso sistema fez 41 operações no total e em nosso último backtest fizemos 42 operações no total. Portanto, nosso novo sistema está assumindo mais risco do que a forma antiga de negociar. Porque, se permitirmos que mais tempo passe, a diferença entre eles pode continuar crescendo. No entanto, embora nosso novo sistema pareça realizar mais operações do que o antigo, nosso lucro total no sistema antigo foi de US$ 59,57, e agora nosso lucro total mais que dobrou para US$ 125,36. Lembre-se de que, por enquanto, limitamos nosso sistema de negociação a abrir 1 operação no lote mínimo. Além disso, em nosso primeiro sistema nossa perda bruta foi de US$ 410,02 e com nossa nova estratégia nossa perda bruta caiu para US$ 330,74.
Ao projetarmos sistemas, precisamos pensar em termos de trade-offs. Nosso novo sistema está apresentando melhor desempenho. No entanto, devemos observar que o tamanho de nosso lucro médio caiu de US$ 29,35 para US$ 22,81. Isso ocorre porque, ocasionalmente, nosso novo sistema perderá operações das quais nosso sistema antigo obteve lucro. No entanto, esse arrependimento ocasional pode ser justificável considerando os ganhos de desempenho que obtemos.
Nosso índice de Sharpe aumentou de 0.18 em nosso primeiro teste, para 0.5 em nosso teste atual. Este é um bom sinal que indica que estamos utilizando melhor nosso capital. Além disso, nossa proporção de operações perdedoras caiu de 60.98% no primeiro teste para um novo mínimo de 52.38%.

Fig 11: Uma análise detalhada do desempenho de nossa nova estratégia de negociação
Conclusão
A maioria dos membros de nossa comunidade tende a ser composta por desenvolvedores independentes, trabalhando em seus projetos sozinhos. Para nossos colegas nessas posições, acredito que algoritmos simples, como o que sugerimos aqui, podem ser soluções mais práticas. É mais fácil de manter, desenvolver e expandir. Gerenciar uma base de código grande e complexa como desenvolvedor solo não é uma tarefa fácil, mesmo para programadores experientes. E se você é novo em nossa comunidade de negociação algorítmica, então essa estratégia pode ser especialmente útil para você. Se você gostou de ler este artigo, então junte-se a nós em nossa próxima discussão, onde tentaremos superar os melhores resultados que produzimos hoje.
| Nome do Arquivo | Descrição |
|---|---|
| Abrir e fechar MA Cross | Este arquivo contém nossa nova versão reimaginada da estratégia de cruzamento de médias móveis.Cruzamento MA tradicional |
| Cruzamento MA tradicional | Este arquivo contém a implementação clássica de cruzamentos de médias móveis. |
Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/16758
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.
Desenvolvimento de um sistema de monitoramento de entradas de swing (EA)
Redes neurais em trading: Detecção de anomalias no domínio da frequência (Conclusão)
Automatização de estratégias de trading com MQL5 (Parte 13): Criação de um algoritmo de negociação para o padrão "Cabeça e Ombros"
- 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
Obrigado pelo código e pelas ideias, gostei da forma como você estruturou o código. Você me apresentou o tipo de dados Vector, que eu nunca tinha usado antes.