English Русский 中文 Español Deutsch 日本語
preview
Estratégia de negociação simples: Reversão à média

Estratégia de negociação simples: Reversão à média

MetaTrader 5Negociação | 9 outubro 2023, 17:24
819 0
Javier Santiago Gaston De Iriarte Cabrera
Javier Santiago Gaston De Iriarte Cabrera

Introdução

A reversão à média é um tipo de negociação contra-tendência em que o trader espera que o preço retorne a algum tipo de equilíbrio, geralmente medido por uma média ou outra estatística de tendência central. Este artigo discute uma estratégia de negociação realmente simples, a reversão à média.


Breve introdução à estratégia

Os mercados geralmente se movem em ciclos irregulares. Isso significa que, quando olhamos para os gráficos, tendemos a ver altas, baixas e fases relativamente lateralizadas. A chave para a negociação e investimento é ser capaz de determinar as mudanças nessas fases, que também são chamadas de regimes de mercado.

A reversão à média pode se manifestar na forma de uma média móvel, onde, se o mercado se afasta muito dela, é provável que retorne à sua área.


O que é uma reversão à média?

Reversão à média é um termo financeiro que pressupõe que o preço de um ativo tende a convergir para o preço médio ao longo do tempo.

Usar a reversão à média como estratégia de timing envolve tanto a identificação da faixa de negociação de um ativo quanto o cálculo do preço médio usando métodos quantitativos. A reversão à média é um fenômeno que pode ser observado em uma série de dados de séries temporais financeiras, desde dados de preço até dados de lucros e valor contábil.

Quando o preço de mercado atual está abaixo do preço médio passado, o ativo é considerado atrativo para compra, com a expectativa de que o preço subirá. Quando o preço de mercado atual está acima do preço médio passado, espera-se que o preço de mercado caia. Em outras palavras, desvios do preço médio são esperados para reverter à média. Esse conhecimento serve como a base de várias estratégias de negociação.

Os serviços de relatórios de ações geralmente oferecem médias móveis para períodos como 50 e 100 dias. Embora os serviços de relatórios forneçam as médias, ainda é necessário identificar os preços máximos e mínimos para o período de estudo.

A reversão à média tem a aparência de um método mais científico para escolher pontos de compra e venda de ações do que a análise de gráficos, porque valores numéricos precisos são derivados de dados históricos para identificar os valores de compra/venda, em vez de tentar interpretar movimentos de preço usando gráficos (análise gráfica, também conhecida como análise técnica), embora o indicador RSI e a Média de Amplitude Real (average true range, ATR) sejam tentativas incipientes de capturar tal padrão sistemático.

Muitas classes de ativos, incluindo taxas de câmbio, são observadas como revertendo à média; no entanto, esse processo pode durar anos e, portanto, não é de valor para um investidor de curto prazo.

A reversão à média deve demonstrar uma forma de simetria, uma vez que uma ação pode estar acima de sua média histórica aproximadamente com a mesma frequência que está abaixo.

Um modelo histórico de reversão à média não incorporará completamente o comportamento real do preço de um ativo. Por exemplo, novas informações podem se tornar disponíveis e afetar permanentemente a avaliação de longo prazo de uma ação subjacente. No caso de falência, a ação pode deixar de ser negociada completamente e nunca se recuperar para sua média histórica anterior.

Na área financeira, o termo "reversão à média" tem um significado ligeiramente diferente do termo "retorno ou regressão à média" nas estatísticas. Jeremy Siegel usa o termo "retorno à média" para descrever um princípio geral, uma série temporal financeira em que "os retornos podem ser muito instáveis a curto prazo, mas muito estáveis a longo prazo." Quantitativamente, é o desvio padrão dos retornos médios anuais que diminui mais rapidamente do que o inverso do período de retenção, o que implica que o processo não é um passeio aleatório, mas que períodos de retornos mais baixos são seguidos por períodos compensadores de retornos mais altos, por exemplo, em negócios sazonais.


A Figura a seguir ilustra o exemplo.

compra

Mas como medimos o "muito longe"? Vamos tentar de uma maneira muito simples, baseada apenas na posição do preço em relação à média móvel.


Desenvolvimento da estratégia

Agora que temos uma distância normalizada de 50 períodos entre o mercado e sua média móvel de 200 períodos, estamos prontos para codificar os seguintes sinais de negociação:

  • Um sinal de compra é gerado sempre que o índice normalizado cai abaixo do nível 100 depois de ter sido igual a 100, enquanto o preço de fechamento atual está abaixo do preço de fechamento de cinco períodos atrás e abaixo da média móvel de 200 períodos.
  • Um sinal de venda é gerado sempre que o índice normalizado cai abaixo do nível 100 depois de ter sido igual a 100, enquanto o preço de fechamento atual está acima do preço de fechamento de cinco períodos atrás e acima da média móvel de 200 períodos.

Portanto, esta pode não ser a estratégia mais simples dadas as condições, mas, mesmo assim, é muito intuitiva e direta. A função de sinal é a seguinte:

Da última seção, temos objetivos claros para começar a projetar a estratégia:

  • Um sinal de compra é gerado sempre que o mercado cai o suficiente abaixo de sua média móvel, indicando que provavelmente retornará a um valor médio superior.
  • Um sinal de venda é gerado sempre que o mercado sobe o suficiente acima de sua média móvel, indicando que provavelmente retornará a um valor médio inferior.

Farei uma modificação na estratégia para tentar obter melhores resultados onde a estratégia não funciona bem (quando o patrimônio diminui). Vou modificar isso da estratégia:

- Em vez de olhar para os últimos fechamentos, vou olhar para cima, os máximos e mínimos, para tentar filtrar melhores ordens.

As ordens são abertas com menos frequência.

Este é o código modificado:

if(previousValue==100)
        {
         if(Normalizado<100 && array_ma[0]>tick.bid  && rates[5].high < rates[1].low )
           {
            Print("Open Order Buy");
            Alert(" Buying");
            Orden="Buy";
            sl=NormalizeDouble(tick.ask - ptsl*_Point,_Digits);
            tp=NormalizeDouble(tick.bid + pttp*_Point,_Digits);
            //trade.Buy(get_lot(tick.bid),_Symbol,tick.bid,sl,tp);
            trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,get_lot(tick.bid),tick.bid,sl,tp,"Buy");
           }
         if(Normalizado<100 && array_ma[0]<tick.ask  && rates[5].low > rates[1].high )
           {
            Print("Open Order Sell");
            Alert(" Selling");
            Orden="Sell";
            sl=NormalizeDouble(tick.bid + ptsl*_Point,_Digits);
            tp=NormalizeDouble(tick.ask - pttp*_Point,_Digits);
            //trade.Sell(get_lot_s(tick.ask),_Symbol,tick.ask,sl,tp);
            trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,get_lot(tick.ask),tick.ask,sl,tp,"Sell");

           }
        }

Com essas mudanças, a estratégia ganha menos, mas parece mais estável (stop loss = 4000 pips).

4500 sl 30 min


data 4500 sl 30 min


Este EA deve ser otimizado para obter melhores resultados. Você também pode variar o código. Este é um exemplo do que você pode ver na estratégia com um stop loss de 300 para um período de 30 minutos. Basta procurar o ajuste ideal de acordo com o símbolo e o período de tempo usado.

350 sl 30 min


Dados de 350 sl 30 min


O gráfico parece mais estável com um stop loss de 300 pips, mas mais lucrativo com um stop loss de 4000 pips. Devemos fazer uma otimização para o stop loss para encontrar um equilíbrio para o período de 30 minutos. Mas esse trabalho eu vou deixar para você.



Código

Os parâmetros de entrada são os seguintes:


input ENUM_LOT_TYPE        inp_lot_type               = LOT_TYPE_FIX;              // type of lot
input double               inp_lot_fix                = 0.01;                        // fix lot
input double               inp_lot_risk               = 0.01;      
input bool     InpPrintLog          = false;       // Print log
ulong                    Expert_MagicNumber       =66777;            //
bool                     Expert_EveryTick         =false;            //
input ENUM_TIMEFRAMES my_timeframe=PERIOD_CURRENT;                  // Timeframe
int    handle_iMA;
input int                  Inp_MA_ma_period     = 200;          // MA: averaging period
input int                  Inp_MA_ma_shift      = 5;           // MA: horizontal shift
input ENUM_MA_METHOD       Inp_MA_ma_method     = MODE_SMA;    // MA: smoothing type
input ENUM_APPLIED_PRICE   Inp_MA_applied_price = PRICE_CLOSE; // MA: type of price
int shift = 49; // loockback normalization
input int ptsl = 350; // points for stoploss
input int pttp = 5000; // points for takeprofit

A primeira linha define o tipo de lote a ser usado, que é um tamanho de lote fixo. A próxima linha define o tamanho do lote fixo, seguido pelo tamanho do lote de risco. A próxima linha define um valor booleano para determinar se deve imprimir um log ou não. A linha seguinte define o número mágico para o Expert Advisor. A próxima linha define um valor booleano para determinar se o EA deve ser executado a cada tick ou não. A linha seguinte define o período de tempo para o EA. As próximas linhas definem os parâmetros do indicador de média móvel, como o período de média, o deslocamento horizontal, o tipo de suavização e o tipo de preço. A linha seguinte define o deslocamento para a normalização do histórico de dados. As duas linhas seguintes definem os pips para o stop loss e take profit.

Em OnInit():

int OnInit()
  {
//---
   handle_iMA=iMA(_Symbol,my_timeframe,Inp_MA_ma_period,Inp_MA_ma_shift,
                  Inp_MA_ma_method,Inp_MA_applied_price);

// Initialize the variable here if needed
   previousValue = 0.0;

//---
   return(INIT_SUCCEEDED);
  }

Este código é escrito na linguagem MQL5 e é usado para inicializar uma variável. A primeira linha de código cria um identificador para a função iMA, que é usada para calcular a média móvel de um símbolo dado em um período de tempo especificado. Os parâmetros da função iMA são definidos com os valores das variáveis de entrada Inp_MA_ma_period, Inp_MA_ma_shift, Inp_MA_ma_method e Inp_MA_applied_price. A segunda linha de código inicializa a variável previousValue com 0.0. A última linha de código retorna um valor de INIT_SUCCEEDED, que indica que a inicialização foi bem-sucedida.

Em OnTick():

MqlTick tick;
   double last_price = tick.ask;
   SymbolInfoTick(_Symbol,tick);

e

   if(SymbolInfoTick(_Symbol,tick))
      last=tick.last;

   double Last = NormalizeDouble(last,_Digits);

Este código é escrito na linguagem MQL5 e é usado para comparar o preço atual de venda de um símbolo com o último preço de venda. A primeira linha cria uma variável chamada 'tick' do tipo MqlTick. A segunda linha armazena o último preço de venda na variável 'last_price'. A terceira linha obtém as informações do tick para o símbolo especificado na variável '_Symbol' e as armazena na variável 'tick'. A quarta linha verifica se o preço atual de venda é maior do que o último preço de venda armazenado na variável 'last_price'. Se for, então algo é feito.

Este código é usado para calcular a porcentagem do spread de um determinado símbolo. Ele começa recuperando o último preço do símbolo usando a função SymbolInfoTick(). O último preço é então normalizado para o número de casas decimais especificado pelo parâmetro _Digits. Se o último preço normalizado for maior que 0, os preços de compra (ask) e venda (bid) do símbolo são obtidos e normalizados. O spread é calculado subtraindo o preço de venda normalizado do preço de compra normalizado. Em seguida, o spread é dividido pelo valor do símbolo em pips (calculado usando a função Pow()) para obter o spread em pips. Por fim, o spread em pips é dividido pelo último preço normalizado e multiplicado por 100 para obter a porcentagem do spread. Se a porcentagem do spread for menor ou igual ao parâmetro Max_Spread, alguma ação é tomada.

Para a Média Móvel (MA), usaremos o seguinte:

   handle_iMA=iMA(_Symbol,my_timeframe,Inp_MA_ma_period,Inp_MA_ma_shift,
                  Inp_MA_ma_method,Inp_MA_applied_price);
//---
   double array_ma[];
   ArraySetAsSeries(array_ma,true);
   int start_pos=0,count=3;
   if(!iGetArray(handle_iMA,0,start_pos,count,array_ma))
      return;
   string text="";
   for(int i=0; i<count; i++)
      text=text+IntegerToString(i)+": "+DoubleToString(array_ma[i],Digits()+1)+"\n";
//---
   Comment(text);

bool iGetArray(const int handle,const int buffer,const int start_pos,
               const int count,double &arr_buffer[])
  {
   bool result=true;
   if(!ArrayIsDynamic(arr_buffer))
     {
      //if(InpPrintLog)
      PrintFormat("ERROR! EA: %s, FUNCTION: %s, this a no dynamic array!",__FILE__,__FUNCTION__);
      return(false);
     }
   ArrayFree(arr_buffer);
//--- reset error code
   ResetLastError();
//--- fill a part of the iBands array with values from the indicator buffer
   int copied=CopyBuffer(handle,buffer,start_pos,count,arr_buffer);
   if(copied!=count)
     {
      //--- if the copying fails, tell the error code
      //if(InpPrintLog)
      PrintFormat("ERROR! EA: %s, FUNCTION: %s, amount to copy: %d, copied: %d, error code %d",
                  __FILE__,__FUNCTION__,count,copied,GetLastError());
      //--- quit with zero result - it means that the indicator is considered as not calculated
      return(false);
     }
   return(result);
  }

Este código é usado para calcular e exibir a média móvel de um determinado símbolo em um determinado período de tempo. A função iMA() é usada para calcular a média móvel, e a função iGetArray() é usada para extrair os valores da média móvel do buffer do indicador. A função ArraySetAsSeries() é usada para configurar o array como uma série, e as funções IntegerToString() e DoubleToString() são usadas para converter os valores do array em strings. Por fim, a função Comment() é usada para exibir os valores da média móvel no gráfico.


Para evitar erros de volume ao abrir ordens, usaremos o seguinte:

//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
double get_lot(double price)
  {
   if(inp_lot_type==LOT_TYPE_FIX)
      return(normalize_lot(inp_lot_fix));
   double one_lot_margin;
   if(!OrderCalcMargin(ORDER_TYPE_BUY,_Symbol,1.0,price,one_lot_margin))
      return(inp_lot_fix);
   return(normalize_lot((AccountInfoDouble(ACCOUNT_BALANCE)*(inp_lot_risk/100))/ one_lot_margin));
  }
//+------------------------------------------------------------------+
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
double normalize_lot(double lt)
  {
   double lot_step = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);
   lt = MathFloor(lt / lot_step) * lot_step;
   double lot_minimum = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
   lt = MathMax(lt, lot_minimum);
   return(lt);
  }

Este código é usado para calcular o tamanho do lote para uma ordem de compra. A primeira função, get_lot(), recebe um preço como argumento e retorna o tamanho do lote. O tamanho do lote é determinado pelo tipo de lote, que pode ser fixo ou baseado na porcentagem de risco. Se o tipo de lote for fixo, a função retorna o tamanho do lote normalizado. Se o tipo de lote for baseado na porcentagem de risco, a função calcula a margem para um lote, calcula o tamanho do lote com base no saldo e na porcentagem de risco, e retorna o tamanho do lote normalizado. A segunda função, normalize_lot(), recebe um tamanho de lote como argumento e retorna o tamanho do lote normalizado. O tamanho do lote normalizado é calculado dividindo o tamanho do lote pelo passo de volume e, em seguida, multiplicando pelo passo de volume. O tamanho do lote normalizado é então comparado com o tamanho mínimo do lote, e o maior valor entre os dois é retornado.

Para abrir ordens, usaremos o seguinte:

trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,get_lot(tick.bid),tick.bid,sl,tp,"Buy");

Este código é escrito em MQL5 e é usado para abrir uma nova posição no mercado. O primeiro parâmetro é o símbolo do ativo a ser negociado. O segundo parâmetro é o tipo de ordem, neste caso, uma ordem de compra. O terceiro parâmetro é o tamanho do lote, calculado usando a função get_lot() e o preço de compra atual. O quarto parâmetro é o preço de compra atual. O quinto e sexto parâmetros são os níveis de stop loss e take profit, respectivamente. O último parâmetro é um comentário que será adicionado à ordem.

O início será este, de acordo com a estratégia inicial, apenas alterando as condições de fechamento para high e low (para obter resultados mais robustos).

   if(0<=Normalizado<=100 )
     {
      //------------------------------------------------------------------------------
      if(previousValue==100)
        {
         if(Normalizado<100 && array_ma[0]>tick.bid  && rates[5].high < rates[1].low )
           {
            Print("Open Order Buy");
            Alert(" Buying");
            Orden="Buy";
            sl=NormalizeDouble(tick.ask - ptsl*_Point,_Digits);
            tp=NormalizeDouble(tick.bid + pttp*_Point,_Digits);
            //trade.Buy(get_lot(tick.bid),_Symbol,tick.bid,sl,tp);
            trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,get_lot(tick.bid),tick.bid,sl,tp,"Buy");
           }
         if(Normalizado<100 && array_ma[0]<tick.ask  && rates[5].low > rates[1].high )
           {
            Print("Open Order Sell");
            Alert(" Selling");
            Orden="Sell";
            sl=NormalizeDouble(tick.bid + ptsl*_Point,_Digits);
            tp=NormalizeDouble(tick.ask - pttp*_Point,_Digits);
            //trade.Sell(get_lot_s(tick.ask),_Symbol,tick.ask,sl,tp);
            trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,get_lot(tick.ask),tick.ask,sl,tp,"Sell");
           }
        }
     }
   previousValue = Normalizado;
   if(Orden=="Sell" && rates[0].low <array_ma[0])
     {
      trade.PositionClose(_Symbol,5);
      Print("cerro sell");
      return;
     }
   if(Orden=="Buy" && rates[0].high >array_ma[0])
     {
      trade.PositionClose(_Symbol,5);
      Print("cerro buy");
      return;
     }
  }

Este código é escrito na linguagem MQL5 e é usado para abrir e fechar ordens no mercado Forex. O código primeiro verifica se o valor da variável "Normalizado" está entre 0 e 100. Se estiver, então verifica se o valor anterior de "Normalizado" era 100. Se fosse, então verifica se o valor de "Normalizado" é menor que 100 e se o valor do array_ma[0] é maior que o preço atual de compra (bid) e se a alta das últimas 5 cotações é menor que a baixa da primeira cotação. Se todas essas condições forem atendidas, o código imprime "Abrir Ordem de Compra", alerta o usuário que está comprando, define a variável "Orden" como "Comprar", define os níveis de stop loss e take profit e abre uma ordem de compra com os parâmetros especificados.

Se o valor de "Normalizado" for menor que 100 e o valor de array_ma[0] for menor que o preço atual de venda (ask) e a baixa das últimas 5 cotações for maior que a alta da primeira cotação, então o código imprime "Abrir Ordem de Venda", alerta o usuário que está vendendo, define a variável "Orden" como "Vender", define os níveis de stop loss e take profit e abre uma ordem de venda com os parâmetros especificados. Após isso, o código define o valor anterior de "Normalizado" como o valor atual. Finalmente, o código verifica se a variável "Orden" está definida como "Vender" e se a baixa da cotação atual é menor que o valor de array_ma[0]. Se essas condições forem atendidas, o código fecha a ordem de venda e imprime "Encerrou Venda". Da mesma forma, se a variável "Orden" estiver definida como "Comprar" e a alta da cotação atual for maior que o valor de array_ma[0], então o código fecha a ordem de compra e imprime "Encerrou Compra".



Considerações finais

A estratégia deve ser otimizada para ser aplicada nos mercados, mas a ideia é apresentar uma forma de pensar sobre análise de mercado com reversão à média.



Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/12830

Desenvolvendo um sistema de Replay (Parte 30): Projeto Expert Advisor - Classe C_Mouse (IV) Desenvolvendo um sistema de Replay (Parte 30): Projeto Expert Advisor - Classe C_Mouse (IV)
Aqui demonstrarei uma técnica que pode lhe ajudar muito, em vários momentos durante a sua vida como programador. Diferente do que muitos dizem, não é a plataforma que é limitada, mas sim o conhecimento do individuo que diz que tal coisa. O que será explicado aqui, mostrar que com um pouco de bom senso e criatividade, você pode tornar a plataforma MetaTrader 5 muito mais interessante e versátil. E sem precisar de fato criar programas malucos ou coisas do estilo. Você pode criar um código simples, porém seguro e confiável. Usando de perspicácia, domar o código a fim de modificar algo já existente, sem se quer remover ou adicionar uma única linha se quer, no código original.
Desenvolvimento de um indicador Heiken Ashi personalizado usando MQL5 Desenvolvimento de um indicador Heiken Ashi personalizado usando MQL5
Neste artigo, aprenderemos a criar nosso próprio indicador usando MQL5 com base em nossas preferências, que será usado no MetaTrader 5 para interpretar gráficos ou como parte de Expert Advisors.
ChatGPT da OpenAI dentro do framework de desenvolvimento MQL4 e MQL5 ChatGPT da OpenAI dentro do framework de desenvolvimento MQL4 e MQL5
Neste artigo, vamos experimentar e explorar a inteligência artificial ChatGPT da OpenAI, a fim de entender suas capacidades com o objetivo de reduzir o tempo e o esforço de desenvolvimento de seus Expert Advisors, indicadores e scripts. Vou rapidamente abordar essa tecnologia e tentar mostrar como usá-la corretamente para programar nas linguagens MQL4 e MQL5.
Como detectar tendências e padrões gráficos usando MQL5 Como detectar tendências e padrões gráficos usando MQL5
Neste artigo, é apresentado um método de detecção automática de padrões de ação de preços usando o MQL5, como tendências (de alta, de baixa e laterais) e padrões gráficos (topo duplo, fundo duplo).